Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a75434fe7b | ||
|
|
020558ed4d | ||
|
|
39a82d03bc | ||
|
|
41ecb5dbc6 | ||
|
|
2d057ca9f6 | ||
|
|
b71b23d2a0 | ||
|
|
b32649435d | ||
|
|
c0096126df | ||
|
|
d632360932 | ||
|
|
400001fa38 | ||
|
|
4ff32a8081 | ||
|
|
33b4ae2f2f | ||
|
|
62194bd627 | ||
|
|
83bb2950fa | ||
|
|
262dee3cfd | ||
|
|
7ccf6ed610 | ||
|
|
21b189f5a8 | ||
|
|
d8d8a050ce | ||
|
|
b9ca810903 | ||
|
|
6ad750d3e9 | ||
|
|
8423b64825 | ||
|
|
742ddef227 | ||
|
|
85d7f0abfb | ||
|
|
c4b4fa293d | ||
|
|
0204abfead | ||
|
|
3510f7b9d3 | ||
|
|
2d4c063dfa | ||
|
|
67b3e2f312 | ||
|
|
82006de30f | ||
|
|
5bde9d7fe6 | ||
|
|
f7cd428366 | ||
|
|
d5e797de9e | ||
|
|
b9d5e7b206 | ||
|
|
2d8c7eb4fd | ||
|
|
f69e07fe68 | ||
|
|
84aab38954 | ||
|
|
dcf37481bd | ||
|
|
1a9cca84d5 | ||
|
|
c5272d67de | ||
|
|
3bcfb14177 | ||
|
|
566280f280 | ||
|
|
a814f7eefb | ||
|
|
097b8ed534 | ||
|
|
da47d72749 | ||
|
|
abd4d18a01 | ||
|
|
2adbce3c21 | ||
|
|
c41324b324 | ||
|
|
151a4f6c2d | ||
|
|
d23777a83f | ||
|
|
bb0aee8337 | ||
|
|
2a23acb73a | ||
|
|
ad5eb0cef6 | ||
|
|
07dd8f55fe | ||
|
|
bafa91cc47 | ||
|
|
16399510fa | ||
|
|
e9f54eec0a | ||
|
|
fbbbc9f215 | ||
|
|
69e4f575c0 | ||
|
|
e95659d2e0 | ||
|
|
35f34310ec | ||
|
|
acc18e20fd | ||
|
|
72460eac08 | ||
|
|
3e5f9d4eca | ||
|
|
e873fa252c | ||
|
|
ff2c083a19 | ||
|
|
a30a84c34b | ||
|
|
97d2023298 | ||
|
|
1ce8430a14 | ||
|
|
1c0b638818 | ||
|
|
e852588eeb | ||
|
|
08b9bfd854 | ||
|
|
7ee2f3a29a | ||
|
|
5ee6a38f39 | ||
|
|
8f0bfd5f83 | ||
|
|
c54f39fc77 | ||
|
|
eb40769ccf | ||
|
|
418c62b40d | ||
|
|
198a480c35 | ||
|
|
2d4bf4e847 | ||
|
|
774e3d1a62 |
19
README.md
19
README.md
@@ -38,7 +38,6 @@ There are 2 main ways to get a miner (and the functions attached to it), via sca
|
||||
#### Scanning for miners
|
||||
```python
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from pyasic.network import MinerNetwork
|
||||
|
||||
@@ -47,7 +46,7 @@ from pyasic.network import MinerNetwork
|
||||
async def scan_and_get_data():
|
||||
# Define network range to be used for scanning
|
||||
# This can take a list of IPs, a constructor string, or an IP and subnet mask
|
||||
# The standard mask is /24, and you can pass any IP address in the subnet
|
||||
# The standard mask is /24 (x.x.x.0-255), and you can pass any IP address in the subnet
|
||||
net = MinerNetwork("192.168.1.69", mask=24)
|
||||
# Scan the network for miners
|
||||
# This function returns a list of miners of the correct type as a class
|
||||
@@ -72,9 +71,8 @@ if __name__ == "__main__":
|
||||
#### Getting a miner if you know the IP
|
||||
```python
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from pyasic.miners import get_miner
|
||||
from pyasic import get_miner
|
||||
|
||||
|
||||
# define asynchronous function to get miner and data
|
||||
@@ -95,12 +93,15 @@ if __name__ == "__main__":
|
||||
|
||||
If needed, this library exposes a wrapper for the miner API that can be used for advanced data gathering.
|
||||
|
||||
You can see more information on basic usage of the APIs past this example in the docs [here](https://pyasic.readthedocs.io/en/latest/API/api/).
|
||||
|
||||
Please see the appropriate API documentation page (pyasic docs -> Advanced -> Miner APIs -> your API type) for a link to that specific miner's API documentation page and more information.
|
||||
|
||||
#### List available API commands
|
||||
```python
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from pyasic.miners import get_miner
|
||||
from pyasic import get_miner
|
||||
|
||||
|
||||
async def get_api_commands(miner_ip: str):
|
||||
@@ -108,7 +109,8 @@ async def get_api_commands(miner_ip: str):
|
||||
miner = await get_miner(miner_ip)
|
||||
|
||||
# List all available commands
|
||||
print(miner.api.get_commands())
|
||||
# Can also be called explicitly with the function miner.api.get_commands()
|
||||
print(miner.api.commands)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -121,9 +123,8 @@ The miner API commands will raise an `APIError` if they fail with a bad status c
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import sys
|
||||
|
||||
from pyasic.miners import get_miner
|
||||
from pyasic import get_miner
|
||||
|
||||
|
||||
async def get_api_commands(miner_ip: str):
|
||||
|
||||
@@ -6,3 +6,10 @@
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## HashBoard Data
|
||||
::: pyasic.data.HashBoard
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
@@ -117,10 +117,11 @@ These functions are
|
||||
[`get_hostname`](#get-hostname),
|
||||
[`get_model`](#get-model),
|
||||
[`reboot`](#reboot),
|
||||
[`restart_backend`](#restart-backend), and
|
||||
[`stop_mining`](#stop-mining), and
|
||||
[`resume_mining`](#resume-mining), and
|
||||
[`send_config`](#send-config).
|
||||
[`restart_backend`](#restart-backend),
|
||||
[`stop_mining`](#stop-mining),
|
||||
[`resume_mining`](#resume-mining),
|
||||
[`send_config`](#send-config), and
|
||||
[`set_power_limit`](#set-power-limit).
|
||||
|
||||
<br>
|
||||
|
||||
@@ -228,6 +229,14 @@ These functions are
|
||||
|
||||
<br>
|
||||
|
||||
### Set Power Limit
|
||||
::: pyasic.miners.BaseMiner.set_power_limit
|
||||
handler: python
|
||||
options:
|
||||
heading_level: 4
|
||||
|
||||
<br>
|
||||
|
||||
## [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
||||
|
||||
Pyasic implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
||||
|
||||
@@ -50,111 +50,381 @@ details {
|
||||
<summary>Stock Firmware Whatsminers:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary>M5X Series:</summary>
|
||||
<summary>M2X Series:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M5X/#m50">M50</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M5X/#m50vh50">VH50</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M2X/#M20'>M20</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M20V10'>M20V10</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M2X/#M20S'>M20S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M20SV10'>M20SV10</a></li>
|
||||
<li><a href='../whatsminer/M2X/#M20SV20'>M20SV20</a></li>
|
||||
<li><a href='../whatsminer/M2X/#M20SV30'>M20SV30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M2X/#M20S_1'>M20S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M20S_1V30'>M20S+V30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M2X/#M21'>M21</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M21V10'>M21V10</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M2X/#M21S'>M21S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M21SV20'>M21SV20</a></li>
|
||||
<li><a href='../whatsminer/M2X/#M21SV60'>M21SV60</a></li>
|
||||
<li><a href='../whatsminer/M2X/#M21SV70'>M21SV70</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M2X/#M21S_1'>M21S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M21S_1V20'>M21S+V20</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M2X/#M29'>M29</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M2X/#M29V10'>M29V10</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>M3X Series:</summary>
|
||||
<summary>M3X Series:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m30s">M30S</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X/#m30sve10">VE10</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30svg20">VG20</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30sve20">VE20</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30sv50">V50</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M3X/#M30'>M30</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M30V10'>M30V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30V20'>M30V20</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m30s_1">M30S+</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X/#m30svf20">VF20</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30sve40">VE40</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30svg60">VG60</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M3X/#M30S'>M30S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M30SV10'>M30SV10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV20'>M30SV20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV30'>M30SV30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV40'>M30SV40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV50'>M30SV50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV60'>M30SV60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV70'>M30SV70</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SV80'>M30SV80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE10'>M30SVE10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE20'>M30SVE20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE30'>M30SVE30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE40'>M30SVE40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE50'>M30SVE50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE60'>M30SVE60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVE70'>M30SVE70</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVF10'>M30SVF10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVF20'>M30SVF20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVF30'>M30SVF30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVG10'>M30SVG10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVG20'>M30SVG20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVG30'>M30SVG30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVG40'>M30SVG40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVH10'>M30SVH10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVH20'>M30SVH20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVH30'>M30SVH30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVH40'>M30SVH40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVH50'>M30SVH50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVH60'>M30SVH60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30SVI20'>M30SVI20</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m30s_2">M30S++</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X/#m30svg30">VG30</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30svg40">VG40</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m30svh60">VH60</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M3X/#M30S_1'>M30S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V10'>M30S+V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V20'>M30S+V20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V30'>M30S+V30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V40'>M30S+V40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V50'>M30S+V50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V60'>M30S+V60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V70'>M30S+V70</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V80'>M30S+V80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V90'>M30S+V90</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1V100'>M30S+V100</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE30'>M30S+VE30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE40'>M30S+VE40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE50'>M30S+VE50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE60'>M30S+VE60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE70'>M30S+VE70</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE80'>M30S+VE80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE90'>M30S+VE90</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VE100'>M30S+VE100</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VF20'>M30S+VF20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VF30'>M30S+VF30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M36S_1VG30'>M36S+VG30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VG30'>M30S+VG30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VG40'>M30S+VG40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VG50'>M30S+VG50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VG60'>M30S+VG60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VH10'>M30S+VH10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VH20'>M30S+VH20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VH30'>M30S+VH30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VH40'>M30S+VH40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VH50'>M30S+VH50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_1VH60'>M30S+VH60</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m31s">M31S</a></summary>
|
||||
<summary><a href='../whatsminer/M3X/#M30S_2'>M30S++</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2V10'>M30S++V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2V20'>M30S++V20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VE30'>M30S++VE30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VE40'>M30S++VE40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VE50'>M30S++VE50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VF40'>M30S++VF40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VG30'>M30S++VG30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VG40'>M30S++VG40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VG50'>M30S++VG50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH10'>M30S++VH10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH20'>M30S++VH20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH30'>M30S++VH30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH40'>M30S++VH40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH50'>M30S++VH50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH60'>M30S++VH60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH70'>M30S++VH70</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH80'>M30S++VH80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH90'>M30S++VH90</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VH100'>M30S++VH100</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VJ20'>M30S++VJ20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M30S_2VJ30'>M30S++VJ30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m31s_1">M31S+</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X/#m31sve20">VE20</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m31sv30">V30</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m31sv40">V40</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m31sv60">V60</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m31sv80">V80</a></li>
|
||||
<li><a href="../whatsminer/M3X/#m31sv90">V90</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M3X/#M31'>M31</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M31V10'>M31V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31V20'>M31V20</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m32">M32</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X/#m32v20">V20</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M3X/#M31S'>M31S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M31SV10'>M31SV10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV20'>M31SV20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV30'>M31SV30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV40'>M31SV40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV50'>M31SV50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV60'>M31SV60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV70'>M31SV70</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV80'>M31SV80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SV90'>M31SV90</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SVE10'>M31SVE10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SVE20'>M31SVE20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SVE30'>M31SVE30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m32s">M32S</a></summary>
|
||||
<summary><a href='../whatsminer/M3X/#M31SE'>M31SE</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M31SEV10'>M31SEV10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SEV20'>M31SEV20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31SEV30'>M31SEV30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M3X/#m34s">M34S+</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M3X/#m34sve10">VE10</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M3X/#M31H'>M31H</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M31HV40'>M31HV40</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M31S_1'>M31S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V10'>M31S+V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V20'>M31S+V20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V30'>M31S+V30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V40'>M31S+V40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V50'>M31S+V50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V60'>M31S+V60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V80'>M31S+V80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V90'>M31S+V90</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1V100'>M31S+V100</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE10'>M31S+VE10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE20'>M31S+VE20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE30'>M31S+VE30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE40'>M31S+VE40</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE50'>M31S+VE50</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE60'>M31S+VE60</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VE80'>M31S+VE80</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VF20'>M31S+VF20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VF30'>M31S+VF30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VG20'>M31S+VG20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M31S_1VG30'>M31S+VG30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M32'>M32</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M32V10'>M32V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M32V20'>M32V20</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M33'>M33</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M33V10'>M33V10</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M33V20'>M33V20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M33V30'>M33V30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M33S'>M33S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M33SVG30'>M33SVG30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M33S_1'>M33S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M33S_1VH20'>M33S+VH20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M33S_1VH30'>M33S+VH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M33S_2'>M33S++</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M33S_2VH20'>M33S++VH20</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M33S_2VH30'>M33S++VH30</a></li>
|
||||
<li><a href='../whatsminer/M3X/#M33S_2VG40'>M33S++VG40</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M34S_1'>M34S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M34S_1VE10'>M34S+VE10</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M36S'>M36S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M36SVE10'>M36SVE10</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M36S_1'>M36S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M36S_1VG30'>M36S+VG30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M36S_2'>M36S++</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M36S_2VH30'>M36S++VH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M3X/#M39'>M39</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M3X/#M39V20'>M39V20</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>M2X Series:</summary>
|
||||
<summary>M5X Series:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M2X/#m20">M20</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M2X/#m20v10">V10</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M5X/#M50'>M50</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M50VG30'>M50VG30</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH10'>M50VH10</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH20'>M50VH20</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH30'>M50VH30</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH40'>M50VH40</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH50'>M50VH50</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH60'>M50VH60</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH70'>M50VH70</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VH80'>M50VH80</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VJ10'>M50VJ10</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VJ20'>M50VJ20</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50VJ30'>M50VJ30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M2X/#m20s">M20S</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M2X/#m20sv10">V10</a></li>
|
||||
<li><a href="../whatsminer/M2X/#m20sv20">V20</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M5X/#M50S'>M50S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M50SVJ10'>M50SVJ10</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVJ20'>M50SVJ20</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVJ30'>M50SVJ30</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVH10'>M50SVH10</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVH20'>M50SVH20</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVH30'>M50SVH30</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVH40'>M50SVH40</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50SVH50'>M50SVH50</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M2X/#m20s_1">M20S+</a></summary>
|
||||
<summary><a href='../whatsminer/M5X/#M50S_1'>M50S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M50S_1VH30'>M50S+VH30</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50S_1VH40'>M50S+VH40</a></li>
|
||||
<li><a href='../whatsminer/M5X/#M50S_1VJ30'>M50S+VJ30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M2X/#m21">M21</a></summary>
|
||||
<summary><a href='../whatsminer/M5X/#M53'>M53</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M53VH30'>M53VH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M2X/#m21s">M21S</a></summary>
|
||||
<ul>
|
||||
<li><a href="../whatsminer/M2X/#m21sv20">V20</a></li>
|
||||
<li><a href="../whatsminer/M2X/#m21sv60">V60</a></li>
|
||||
</ul>
|
||||
<summary><a href='../whatsminer/M5X/#M53S'>M53S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M53SVH30'>M53SVH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href="../whatsminer/M2X/#m21s_1">M21S+</a></summary>
|
||||
<summary><a href='../whatsminer/M5X/#M53S_1'>M53S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M53S_1VJ30'>M53S+VJ30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M5X/#M56'>M56</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M56VH30'>M56VH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M5X/#M56S'>M56S</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M56SVH30'>M56SVH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M5X/#M56S_1'>M56S+</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M56S_1VJ30'>M56S+VJ30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary><a href='../whatsminer/M5X/#M59'>M59</a></summary>
|
||||
<ul>
|
||||
<li><a href='../whatsminer/M5X/#M59VH30'>M59VH30</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Stock Firmware Antminers:</summary>
|
||||
<ul>
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
# pyasic
|
||||
## M2X Models
|
||||
|
||||
## M20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20V10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
|
||||
@@ -17,14 +9,6 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20SV10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
|
||||
@@ -41,26 +25,25 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M20S+
|
||||
## M20SV30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlus
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21
|
||||
## M20S+V30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21V10
|
||||
|
||||
## M21S
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21S
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
@@ -82,9 +65,25 @@
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21S+
|
||||
## M21SV70
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlus
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M21S+V20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M29V10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,41 @@
|
||||
# pyasic
|
||||
## M5X Models
|
||||
|
||||
## M50
|
||||
## M50VG30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VH10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VH20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VH40
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
@@ -15,4 +47,196 @@
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
heading_level: 4
|
||||
|
||||
## M50VH60
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH60
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VH70
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH70
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VH80
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH80
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VJ10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VJ20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50VJ30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVJ10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVJ20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVJ30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVH10
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH10
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVH20
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH20
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVH40
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50SVH50
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH50
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50S+VH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50S+VH40
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH40
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M50S+VJ30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M53VH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M53SVH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M53S+VJ30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M56VH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M56SVH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M56S+VJ30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVJ30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
## M59VH30
|
||||
|
||||
::: pyasic.miners.whatsminer.btminer.M5X.M59.BTMinerM59VH30
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
|
||||
@@ -35,6 +35,86 @@ class BaseMinerAPI:
|
||||
raise TypeError(f"Only children of '{cls.__name__}' may be instantiated")
|
||||
return object.__new__(cls)
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.__class__.__name__}: {str(self.ip)}"
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
parameters: Union[str, int, bool] = None,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
) -> dict:
|
||||
"""Send an API command to the miner and return the result.
|
||||
|
||||
Parameters:
|
||||
command: The command to sent to the miner.
|
||||
parameters: Any additional parameters to be sent with the command.
|
||||
ignore_errors: Whether to raise APIError when the command returns an error.
|
||||
allow_warning: Whether to warn if the command fails.
|
||||
|
||||
Returns:
|
||||
The return data from the API command parsed from JSON into a dict.
|
||||
"""
|
||||
logging.debug(
|
||||
f"{self} - (Send Privileged Command) - {command} "
|
||||
+ f"with args {parameters}"
|
||||
if parameters
|
||||
else ""
|
||||
)
|
||||
# create the command
|
||||
cmd = {"command": command}
|
||||
if parameters:
|
||||
cmd["parameter"] = parameters
|
||||
|
||||
# send the command
|
||||
data = await self._send_bytes(json.dumps(cmd).encode("utf-8"))
|
||||
|
||||
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]}"
|
||||
)
|
||||
raise APIError(validation[1])
|
||||
|
||||
logging.debug(f"{self} - (Send Command) - Received data.")
|
||||
return data
|
||||
|
||||
# Privileged command handler, only used by whatsminers, defined here for consistency.
|
||||
async def send_privileged_command(self, *args, **kwargs) -> dict:
|
||||
return await self.send_command(*args, **kwargs)
|
||||
|
||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||
"""Creates and sends multiple commands as one command to the miner.
|
||||
|
||||
Parameters:
|
||||
*commands: The commands to send as a multicommand to the miner.
|
||||
ignore_errors: Whether to raise APIError when the command returns an error.
|
||||
allow_warning: A boolean to supress APIWarnings.
|
||||
|
||||
"""
|
||||
# make sure we can actually run each command, otherwise they will fail
|
||||
commands = self._check_commands(*commands)
|
||||
# standard multicommand format is "command1+command2"
|
||||
# standard format doesn't work for X19
|
||||
command = "+".join(commands)
|
||||
try:
|
||||
data = await self.send_command(command, allow_warning=allow_warning)
|
||||
except APIError:
|
||||
return {command: [{}] for command in commands}
|
||||
logging.debug(f"{self} - (Multicommand) - Received data")
|
||||
return data
|
||||
|
||||
@property
|
||||
def commands(self) -> list:
|
||||
return self.get_commands()
|
||||
|
||||
def get_commands(self) -> list:
|
||||
"""Get a list of command accessible to a specific type of API on the miner.
|
||||
|
||||
@@ -46,6 +126,7 @@ class BaseMinerAPI:
|
||||
for func in
|
||||
# each function in self
|
||||
dir(self)
|
||||
if not func == "commands"
|
||||
if callable(getattr(self, func)) and
|
||||
# no __ or _ methods
|
||||
not func.startswith("__") and not func.startswith("_") and
|
||||
@@ -72,103 +153,51 @@ If you are sure you want to use this command please use API.send_command("{comma
|
||||
)
|
||||
return return_commands
|
||||
|
||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||
"""Creates and sends multiple commands as one command to the miner.
|
||||
|
||||
Parameters:
|
||||
*commands: The commands to send as a multicommand to the miner.
|
||||
allow_warning: A boolean to supress APIWarnings.
|
||||
"""
|
||||
logging.debug(f"{self.ip}: Sending multicommand: {[*commands]}")
|
||||
# make sure we can actually run each command, otherwise they will fail
|
||||
commands = self._check_commands(*commands)
|
||||
# standard multicommand format is "command1+command2"
|
||||
# doesn't work for S19 which uses the backup _x19_multicommand
|
||||
command = "+".join(commands)
|
||||
try:
|
||||
data = await self.send_command(command, allow_warning=allow_warning)
|
||||
except APIError:
|
||||
return {}
|
||||
logging.debug(f"{self.ip}: Received multicommand data.")
|
||||
return data
|
||||
|
||||
async def _send_bytes(self, data: bytes) -> bytes:
|
||||
async def _send_bytes(self, data: bytes, timeout: int = 100) -> bytes:
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Sending")
|
||||
try:
|
||||
# get reader and writer streams
|
||||
reader, writer = await asyncio.open_connection(str(self.ip), self.port)
|
||||
# handle OSError 121
|
||||
except OSError as e:
|
||||
if getattr(e, "winerror") == "121":
|
||||
logging.warning("Semaphore Timeout has Expired.")
|
||||
logging.warning(
|
||||
f"{self} - ([Hidden] Send Bytes) - Semaphore timeout expired."
|
||||
)
|
||||
return b"{}"
|
||||
|
||||
# send the command
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Writing")
|
||||
writer.write(data)
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
|
||||
await writer.drain()
|
||||
|
||||
# instantiate data
|
||||
ret_data = b""
|
||||
|
||||
# loop to receive all the data
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving")
|
||||
try:
|
||||
while True:
|
||||
d = await reader.read(4096)
|
||||
if not d:
|
||||
break
|
||||
ret_data += d
|
||||
try:
|
||||
d = await asyncio.wait_for(reader.read(4096), timeout=timeout)
|
||||
if not d:
|
||||
break
|
||||
ret_data += d
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||
raise e
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
logging.warning(f"{self.ip}: API Command Error: - {e}")
|
||||
logging.warning(f"{self} - ([Hidden] Send Bytes) - API Command Error {e}")
|
||||
|
||||
# close the connection
|
||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Closing")
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
|
||||
return ret_data
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
parameters: Union[str, int, bool] = None,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
) -> dict:
|
||||
"""Send an API command to the miner and return the result.
|
||||
|
||||
Parameters:
|
||||
command: The command to sent to the miner.
|
||||
parameters: Any additional parameters to be sent with the command.
|
||||
ignore_errors: Whether to raise APIError when the command returns an error.
|
||||
allow_warning: Whether to warn if the command fails.
|
||||
|
||||
Returns:
|
||||
The return data from the API command parsed from JSON into a dict.
|
||||
"""
|
||||
# create the command
|
||||
cmd = {"command": command}
|
||||
if parameters:
|
||||
cmd["parameter"] = parameters
|
||||
|
||||
# send the command
|
||||
data = await self._send_bytes(json.dumps(cmd).encode("utf-8"))
|
||||
|
||||
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]}"
|
||||
)
|
||||
raise APIError(validation[1])
|
||||
|
||||
return data
|
||||
|
||||
async def send_privileged_command(self, *args, **kwargs) -> dict:
|
||||
return await self.send_command(*args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _validate_command_output(data: dict) -> tuple:
|
||||
# check if the data returned is correct or an error
|
||||
@@ -198,7 +227,6 @@ If you are sure you want to use this command please use API.send_command("{comma
|
||||
|
||||
@staticmethod
|
||||
def _load_api_data(data: bytes) -> dict:
|
||||
str_data = None
|
||||
# some json from the API returns with a null byte (\x00) on the end
|
||||
if data.endswith(b"\x00"):
|
||||
# handle the null byte
|
||||
@@ -215,7 +243,9 @@ If you are sure you want to use this command please use API.send_command("{comma
|
||||
# fix an error with a bmminer return having a specific comma that breaks json.loads()
|
||||
str_data = str_data.replace("[,{", "[{")
|
||||
# fix an error with Avalonminers returning inf and nan
|
||||
str_data = str_data.replace("info", "1nfo")
|
||||
str_data = str_data.replace("inf", "0")
|
||||
str_data = str_data.replace("1nfo", "info")
|
||||
str_data = str_data.replace("nan", "0")
|
||||
# fix whatever this garbage from avalonminers is `,"id":1}`
|
||||
if str_data.startswith(","):
|
||||
|
||||
@@ -34,13 +34,11 @@ class BMMinerAPI(BaseMinerAPI):
|
||||
port: The port to reference the API on. Default is 4028.
|
||||
"""
|
||||
|
||||
def __init__(self, ip: str, port: int = 4028) -> None:
|
||||
super().__init__(ip, port)
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0", port: int = 4028) -> None:
|
||||
super().__init__(ip, port=port)
|
||||
self.api_ver = api_ver
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: str, allow_warning: bool = True
|
||||
) -> dict:
|
||||
logging.debug(f"{self.ip}: Sending multicommand: {[*commands]}")
|
||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||
# make sure we can actually run each command, otherwise they will fail
|
||||
commands = self._check_commands(*commands)
|
||||
# standard multicommand format is "command1+command2"
|
||||
@@ -49,23 +47,28 @@ class BMMinerAPI(BaseMinerAPI):
|
||||
try:
|
||||
data = await self.send_command(command, allow_warning=allow_warning)
|
||||
except APIError:
|
||||
logging.debug(f"{self.ip}: Handling X19 multicommand.")
|
||||
data = await self._x19_multicommand(*command.split("+"))
|
||||
logging.debug(f"{self.ip}: Received multicommand data.")
|
||||
logging.debug(f"{self} - (Multicommand) - Handling X19 multicommand.")
|
||||
data = await self._x19_multicommand(
|
||||
*command.split("+"), allow_warning=allow_warning
|
||||
)
|
||||
return data
|
||||
|
||||
async def _x19_multicommand(self, *commands):
|
||||
async def _x19_multicommand(self, *commands, allow_warning: bool = True):
|
||||
data = None
|
||||
try:
|
||||
data = {}
|
||||
# send all commands individually
|
||||
for cmd in commands:
|
||||
data[cmd] = []
|
||||
data[cmd].append(await self.send_command(cmd, allow_warning=True))
|
||||
except APIError as e:
|
||||
raise APIError(e)
|
||||
data[cmd].append(
|
||||
await self.send_command(cmd, allow_warning=allow_warning)
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.warning(f"{self.ip}: API Multicommand Error: {e}")
|
||||
logging.warning(
|
||||
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
|
||||
)
|
||||
return data
|
||||
|
||||
async def version(self) -> dict:
|
||||
|
||||
@@ -33,8 +33,9 @@ class BOSMinerAPI(BaseMinerAPI):
|
||||
port: The port to reference the API on. Default is 4028.
|
||||
"""
|
||||
|
||||
def __init__(self, ip: str, port: int = 4028):
|
||||
super().__init__(ip, port)
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0", port: int = 4028) -> None:
|
||||
super().__init__(ip, port=port)
|
||||
self.api_ver = api_ver
|
||||
|
||||
async def asccount(self) -> dict:
|
||||
"""Get data on the number of ASC devices and their info.
|
||||
@@ -166,7 +167,7 @@ class BOSMinerAPI(BaseMinerAPI):
|
||||
return await self.send_command("estats")
|
||||
|
||||
async def check(self, command: str) -> dict:
|
||||
"""Check if the command command exists in BOSMiner.
|
||||
"""Check if the command `command` exists in BOSMiner.
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import asyncio
|
||||
import base64
|
||||
import binascii
|
||||
import datetime
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
@@ -26,6 +27,7 @@ from passlib.handlers.md5_crypt import md5_crypt
|
||||
|
||||
from pyasic.API import BaseMinerAPI
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.misc import api_min_version
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
### IMPORTANT ###
|
||||
@@ -33,7 +35,7 @@ from pyasic.settings import PyasicSettings
|
||||
# tool, then you can set them back to admin with this tool, but they
|
||||
# must be changed to something else and set back to admin with this
|
||||
# or the privileged API will not work using admin as the password. If
|
||||
# you change the password, you can pass that to the this class as pwd,
|
||||
# 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.
|
||||
|
||||
|
||||
@@ -82,7 +84,7 @@ def _add_to_16(string: str) -> bytes:
|
||||
def parse_btminer_priviledge_data(token_data: dict, data: dict):
|
||||
"""Parses data returned from the BTMiner privileged API.
|
||||
|
||||
Parses data from the BTMiner privileged API using the the token
|
||||
Parses data from the BTMiner privileged API using the token
|
||||
from the API in an AES format.
|
||||
|
||||
Parameters:
|
||||
@@ -124,6 +126,7 @@ def create_privileged_cmd(token_data: dict, command: dict) -> bytes:
|
||||
Returns:
|
||||
The encrypted privileged command to be sent to the miner.
|
||||
"""
|
||||
logging.debug(f"(Create Prilileged Command) - Creating Privileged Command")
|
||||
# add token to command
|
||||
command["token"] = token_data["host_sign"]
|
||||
# encode host_passwd data and get hexdigest
|
||||
@@ -179,25 +182,89 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
def __init__(
|
||||
self,
|
||||
ip: str,
|
||||
api_ver: str = "0.0.0",
|
||||
port: int = 4028,
|
||||
pwd: str = PyasicSettings().global_whatsminer_password,
|
||||
):
|
||||
super().__init__(ip, port)
|
||||
self.pwd = pwd
|
||||
self.current_token = None
|
||||
self.api_ver = api_ver
|
||||
|
||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||
"""Creates and sends multiple commands as one command to the miner.
|
||||
|
||||
Parameters:
|
||||
*commands: The commands to send as a multicommand to the miner.
|
||||
allow_warning: A boolean to supress APIWarnings.
|
||||
"""
|
||||
# make sure we can actually run each command, otherwise they will fail
|
||||
commands = self._check_commands(*commands)
|
||||
# standard multicommand format is "command1+command2"
|
||||
# commands starting with "get_" aren't supported, but we can fake that
|
||||
get_commands_data = {}
|
||||
for command in list(commands):
|
||||
if command.startswith("get_"):
|
||||
commands.remove(command)
|
||||
# send seperately and append later
|
||||
try:
|
||||
get_commands_data[command] = [
|
||||
await self.send_command(command, allow_warning=allow_warning)
|
||||
]
|
||||
except APIError:
|
||||
get_commands_data[command] = [{}]
|
||||
|
||||
command = "+".join(commands)
|
||||
try:
|
||||
main_data = await self.send_command(command, allow_warning=allow_warning)
|
||||
except APIError:
|
||||
main_data = {command: [{}] for command in commands}
|
||||
logging.debug(f"{self} - (Multicommand) - Received data")
|
||||
|
||||
data = dict(**main_data, **get_commands_data)
|
||||
return data
|
||||
|
||||
async def send_privileged_command(
|
||||
self, command: Union[str, bytes], ignore_errors: bool = False, **kwargs
|
||||
self,
|
||||
command: Union[str, bytes],
|
||||
ignore_errors: bool = False,
|
||||
timeout: int = 10,
|
||||
**kwargs,
|
||||
) -> dict:
|
||||
command = {"cmd": command}
|
||||
for kwarg in kwargs:
|
||||
if kwargs[kwarg]:
|
||||
command[kwarg] = kwargs[kwarg]
|
||||
logging.debug(
|
||||
f"{self} - (Send Privileged Command) - {command} " + f"with args {kwargs}"
|
||||
if len(kwargs) > 0
|
||||
else ""
|
||||
)
|
||||
command = {"cmd": command, **kwargs}
|
||||
|
||||
token_data = await self.get_token()
|
||||
enc_command = create_privileged_cmd(token_data, command)
|
||||
|
||||
data = await self._send_bytes(enc_command)
|
||||
logging.debug(f"{self} - (Send Privileged Command) - Sending")
|
||||
try:
|
||||
data = await self._send_bytes(enc_command, timeout)
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||
if command["cmd"] in ["reboot", "restart_btminer", "power_on", "power_off"]:
|
||||
logging.info(
|
||||
f"{self} - (reboot/restart_btminer/power_on/power_off) - Whatsminers currently break this. "
|
||||
f"Ignoring exception. Command probably worked."
|
||||
)
|
||||
# FAKING IT HERE
|
||||
data = (
|
||||
b'{"STATUS": "S", "When": 1670966423, "Code": 131, "Msg": "API command OK", "Description": "'
|
||||
+ command["cmd"].encode("utf-8")
|
||||
+ b'"}'
|
||||
)
|
||||
else:
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError("No data was returned from the API.")
|
||||
|
||||
if not data:
|
||||
if ignore_errors:
|
||||
return {}
|
||||
raise APIError("No data was returned from the API.")
|
||||
data = self._load_api_data(data)
|
||||
|
||||
try:
|
||||
@@ -223,6 +290,13 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
An encoded token and md5 password, which are used for the privileged API.
|
||||
</details>
|
||||
"""
|
||||
logging.debug(f"{self} - (Get Token) - Getting token")
|
||||
if self.current_token:
|
||||
if self.current_token[
|
||||
"timestamp"
|
||||
] > datetime.datetime.now() - datetime.timedelta(minutes=30):
|
||||
return self.current_token
|
||||
|
||||
# get the token
|
||||
data = await self.send_command("get_token")
|
||||
|
||||
@@ -244,7 +318,11 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
self.current_token = {
|
||||
"host_sign": host_sign,
|
||||
"host_passwd_md5": host_passwd_md5,
|
||||
"timestamp": datetime.datetime.now(),
|
||||
}
|
||||
logging.debug(
|
||||
f"{self} - (Get Token) - Gathered token data: {self.current_token}"
|
||||
)
|
||||
return self.current_token
|
||||
|
||||
#### PRIVILEGED COMMANDS ####
|
||||
@@ -340,7 +418,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
the password of the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of powering on.
|
||||
</details>
|
||||
"""
|
||||
@@ -355,7 +432,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of resetting the LED.
|
||||
</details>
|
||||
"""
|
||||
@@ -383,12 +459,11 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
duration: LED on time in the cycle in ms.
|
||||
start: LED on time offset in the cycle in ms.
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of setting the LED.
|
||||
</details>
|
||||
"""
|
||||
if auto:
|
||||
return await self.send_privileged_command("set_led", param=auto)
|
||||
return await self.send_privileged_command("set_led", param="auto")
|
||||
return await self.send_privileged_command(
|
||||
"set_led", color=color, period=period, duration=duration, start=start
|
||||
)
|
||||
@@ -402,7 +477,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of setting low power mode.
|
||||
</details>
|
||||
"""
|
||||
@@ -414,17 +488,23 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
# requires a file stream in bytes
|
||||
return NotImplementedError
|
||||
|
||||
async def reboot(self) -> dict:
|
||||
async def reboot(self, timeout: int = 10) -> dict:
|
||||
"""Reboot the miner using the API.
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of the reboot.
|
||||
</details>
|
||||
"""
|
||||
return await self.send_privileged_command("reboot")
|
||||
try:
|
||||
d = await asyncio.wait_for(
|
||||
self.send_privileged_command("reboot"), timeout=timeout
|
||||
)
|
||||
except (asyncio.CancelledError, asyncio.TimeoutError):
|
||||
return {}
|
||||
else:
|
||||
return d
|
||||
|
||||
async def factory_reset(self) -> dict:
|
||||
"""Reset the miner to factory defaults.
|
||||
@@ -432,7 +512,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of the reset.
|
||||
</details>
|
||||
"""
|
||||
@@ -453,7 +532,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
old_pwd: The old admin password.
|
||||
new_pwd: The new password to set.
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of setting the password.
|
||||
"""
|
||||
self.pwd = old_pwd
|
||||
@@ -485,7 +563,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
Parameters:
|
||||
percent: The frequency % to set.
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of setting the frequency.
|
||||
</details>
|
||||
"""
|
||||
@@ -509,7 +586,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of enabling fast boot.
|
||||
</details>
|
||||
"""
|
||||
@@ -525,7 +601,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of disabling fast boot.
|
||||
</details>
|
||||
"""
|
||||
@@ -541,7 +616,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
password of the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of enabling web pools.
|
||||
</details>
|
||||
"""
|
||||
@@ -557,7 +631,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
password of the miner using the Whatsminer tool.
|
||||
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of disabling web pools.
|
||||
</details>
|
||||
"""
|
||||
@@ -575,14 +648,13 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
Parameters:
|
||||
hostname: The new hostname to use.
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of setting the hostname.
|
||||
</details>
|
||||
"""
|
||||
return await self.send_privileged_command("set_hostname", hostname=hostname)
|
||||
|
||||
async def set_power_pct(self, percent: int) -> dict:
|
||||
"""Set the power percentage of the miner.
|
||||
"""Set the power percentage of the miner based on current power. Used for temporary adjustment.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
@@ -593,7 +665,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
Parameters:
|
||||
percent: The power percentage to set.
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of setting the power percentage.
|
||||
</details>
|
||||
"""
|
||||
@@ -622,7 +693,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
* `adjust complete`
|
||||
* `adjust continue`
|
||||
Returns:
|
||||
|
||||
A reply informing of the status of pre power on.
|
||||
</details>
|
||||
"""
|
||||
@@ -642,6 +712,127 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
"pre_power_on", complete="false", msg=msg
|
||||
)
|
||||
|
||||
### ADDED IN V2.0.5 Whatsminer API ###
|
||||
@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.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Set the offset of miner hash board target temperature, only works after
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
|
||||
Parameters:
|
||||
temp_offset: Target temperature offset.
|
||||
Returns:
|
||||
A reply informing of the status of setting temp offset.
|
||||
</details>
|
||||
|
||||
"""
|
||||
if not -30 < temp_offset < 0:
|
||||
raise APIError(
|
||||
f"Temp offset is outside of the allowed "
|
||||
f"range. Please set a number between -30 and "
|
||||
f"0."
|
||||
)
|
||||
|
||||
return await self.send_privileged_command(
|
||||
"set_temp_offset", temp_offset=temp_offset
|
||||
)
|
||||
|
||||
@api_min_version("2.0.5")
|
||||
async def adjust_power_limit(self, power_limit: int) -> dict:
|
||||
"""Set the upper limit of the miner's power. Cannot be higher than the ordinary power of the machine.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Set the upper limit of the miner's power, only works after
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
The miner will reboot after this is set.
|
||||
|
||||
Parameters:
|
||||
power_limit: New power limit.
|
||||
Returns:
|
||||
A reply informing of the status of setting power limit.
|
||||
</details>
|
||||
|
||||
"""
|
||||
return await self.send_privileged_command(
|
||||
"adjust_power_limit", power_limit=power_limit
|
||||
)
|
||||
|
||||
@api_min_version("2.0.5")
|
||||
async def adjust_upfreq_speed(self, upfreq_speed: int) -> dict:
|
||||
"""Set the upfreq speed, 0 is the normal speed, 9 is the fastest speed.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Set the upfreq speed, 0 is the normal speed, 9 is the fastest speed, only works after
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
The faster the speed, the greater the final hash rate and power deviation, and the stability
|
||||
may be impacted. Fast boot mode cannot be used at the same time.
|
||||
|
||||
Parameters:
|
||||
upfreq_speed: New upfreq speed.
|
||||
Returns:
|
||||
A reply informing of the status of setting upfreq speed.
|
||||
</details>
|
||||
"""
|
||||
if not 0 < upfreq_speed < 9:
|
||||
raise APIError(
|
||||
f"Upfreq speed is outside of the allowed "
|
||||
f"range. Please set a number between 0 (Normal) and "
|
||||
f"9 (Fastest)."
|
||||
)
|
||||
return await self.send_privileged_command(
|
||||
"adjust_upfreq_speed", upfreq_speed=upfreq_speed
|
||||
)
|
||||
|
||||
@api_min_version("2.0.5")
|
||||
async def set_poweroff_cool(self, poweroff_cool: bool) -> dict:
|
||||
"""Set whether to cool the machine when mining is stopped.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Set whether to cool the machine when mining is stopped, only works after
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
|
||||
Parameters:
|
||||
poweroff_cool: Whether to cool the miner during power off mode.
|
||||
Returns:
|
||||
A reply informing of the status of setting power off cooling mode.
|
||||
</details>
|
||||
"""
|
||||
|
||||
return await self.send_privileged_command(
|
||||
"set_poweroff_cool", poweroff_cool=int(poweroff_cool)
|
||||
)
|
||||
|
||||
@api_min_version("2.0.5")
|
||||
async def set_fan_zero_speed(self, fan_zero_speed: bool) -> dict:
|
||||
"""Sets whether the fan speed supports the lowest 0 speed.
|
||||
|
||||
<details>
|
||||
<summary>Expand</summary>
|
||||
|
||||
Sets whether the fan speed supports the lowest 0 speed, only works after
|
||||
changing the password of the miner using the Whatsminer tool.
|
||||
|
||||
Parameters:
|
||||
fan_zero_speed: Whether the fan is allowed to support 0 speed.
|
||||
Returns:
|
||||
A reply informing of the status of setting fan minimum speed.
|
||||
</details>
|
||||
|
||||
"""
|
||||
return await self.send_privileged_command(
|
||||
"set_fan_zero_speed", fan_zero_speed=int(fan_zero_speed)
|
||||
)
|
||||
|
||||
#### END privileged COMMANDS ####
|
||||
|
||||
async def summary(self) -> dict:
|
||||
@@ -650,7 +841,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Summary status of the miner.
|
||||
</details>
|
||||
"""
|
||||
@@ -662,7 +852,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Pool status of the miner.
|
||||
</details>
|
||||
"""
|
||||
@@ -674,7 +863,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Data on each PGA/ASC with their details.
|
||||
</details>
|
||||
"""
|
||||
@@ -686,7 +874,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Data on each PGA/ASC with their details.
|
||||
</details>
|
||||
"""
|
||||
@@ -698,7 +885,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Data on all devices with their static details.
|
||||
</details>
|
||||
"""
|
||||
@@ -710,7 +896,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Data on the PSU and power information.
|
||||
</details>
|
||||
"""
|
||||
@@ -726,7 +911,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
with the other miner APIs.
|
||||
|
||||
Returns:
|
||||
|
||||
Version data for the miner.
|
||||
</details>
|
||||
"""
|
||||
@@ -738,7 +922,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
Version data for the miner.
|
||||
</details>
|
||||
"""
|
||||
@@ -750,7 +933,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
BTMiner status and firmware version.
|
||||
</details>
|
||||
"""
|
||||
@@ -762,12 +944,12 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
<summary>Expand</summary>
|
||||
|
||||
Returns:
|
||||
|
||||
General miner info.
|
||||
</details>
|
||||
"""
|
||||
return await self.send_command("get_miner_info", allow_warning=False)
|
||||
|
||||
@api_min_version("2.0.1")
|
||||
async def get_error_code(self) -> dict:
|
||||
"""Get a list of error codes from the miner.
|
||||
|
||||
@@ -776,7 +958,6 @@ class BTMinerAPI(BaseMinerAPI):
|
||||
Get a list of error codes from the miner. Replaced `summary` as the location of error codes with API version 2.0.4.
|
||||
|
||||
Returns:
|
||||
|
||||
A list of error codes on the miner.
|
||||
</details>
|
||||
"""
|
||||
|
||||
@@ -35,24 +35,21 @@ class CGMinerAPI(BaseMinerAPI):
|
||||
port: The port to reference the API on. Default is 4028.
|
||||
"""
|
||||
|
||||
def __init__(self, ip: str, port: int = 4028):
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0", port: int = 4028):
|
||||
super().__init__(ip, port)
|
||||
self.api_ver = api_ver
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: str, allow_warning: bool = True
|
||||
) -> dict:
|
||||
logging.debug(f"{self.ip}: Sending multicommand: {[*commands]}")
|
||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||
# make sure we can actually run each command, otherwise they will fail
|
||||
commands = self._check_commands(*commands)
|
||||
# standard multicommand format is "command1+command2"
|
||||
# doesnt work for S19 which uses the backup _x19_multicommand
|
||||
# doesn't work for S19 which uses the backup _x19_multicommand
|
||||
command = "+".join(commands)
|
||||
try:
|
||||
data = await self.send_command(command, allow_warning=allow_warning)
|
||||
except APIError:
|
||||
logging.debug(f"{self.ip}: Handling X19 multicommand.")
|
||||
logging.debug(f"{self} - (Multicommand) - Handling X19 multicommand.")
|
||||
data = await self._x19_multicommand(*command.split("+"))
|
||||
logging.debug(f"{self.ip}: Received multicommand data.")
|
||||
return data
|
||||
|
||||
async def _x19_multicommand(self, *commands):
|
||||
@@ -64,9 +61,11 @@ class CGMinerAPI(BaseMinerAPI):
|
||||
data[cmd] = []
|
||||
data[cmd].append(await self.send_command(cmd, allow_warning=True))
|
||||
except APIError as e:
|
||||
raise APIError(e)
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.warning(f"{self.ip}: API Multicommand Error: {e}")
|
||||
logging.warning(
|
||||
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
|
||||
)
|
||||
return data
|
||||
|
||||
async def version(self) -> dict:
|
||||
|
||||
@@ -18,13 +18,14 @@ from pyasic.API import BaseMinerAPI
|
||||
class UnknownAPI(BaseMinerAPI):
|
||||
"""An abstraction of an API for a miner which is unknown.
|
||||
|
||||
This class is designed to try to be a intersection of as many miner APIs
|
||||
This class is designed to try to be an intersection of as many miner APIs
|
||||
and API commands as possible (API ⋂ API), to ensure that it can be used
|
||||
with as many APIs as possible.
|
||||
"""
|
||||
|
||||
def __init__(self, ip, port=4028):
|
||||
def __init__(self, ip, api_ver: str = "0.0.0", port: int = 4028):
|
||||
super().__init__(ip, port)
|
||||
self.api_ver = api_ver
|
||||
|
||||
async def asccount(self) -> dict:
|
||||
return await self.send_command("asccount")
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
import time
|
||||
from dataclasses import asdict, dataclass, fields
|
||||
from typing import List, Literal
|
||||
from typing import Dict, List, Literal
|
||||
|
||||
import toml
|
||||
import yaml
|
||||
@@ -193,7 +194,7 @@ class _PoolGroup:
|
||||
return pools
|
||||
|
||||
def as_wm(self, user_suffix: str = None) -> List[dict]:
|
||||
"""Convert the data in this class to a list usable by an Whatsminer device.
|
||||
"""Convert the data in this class to a list usable by a Whatsminer device.
|
||||
|
||||
Parameters:
|
||||
user_suffix: The suffix to append to username.
|
||||
@@ -277,6 +278,7 @@ class MinerConfig:
|
||||
|
||||
def as_dict(self) -> dict:
|
||||
"""Convert the data in this class to a dict."""
|
||||
logging.debug(f"MinerConfig - (To Dict) - Dumping Dict config")
|
||||
data_dict = asdict(self)
|
||||
for key in asdict(self).keys():
|
||||
if data_dict[key] is None:
|
||||
@@ -285,10 +287,12 @@ class MinerConfig:
|
||||
|
||||
def as_toml(self) -> str:
|
||||
"""Convert the data in this class to toml."""
|
||||
logging.debug(f"MinerConfig - (To TOML) - Dumping TOML config")
|
||||
return toml.dumps(self.as_dict())
|
||||
|
||||
def as_yaml(self) -> str:
|
||||
"""Convert the data in this class to yaml."""
|
||||
logging.debug(f"MinerConfig - (To YAML) - Dumping YAML config")
|
||||
return yaml.dump(self.as_dict(), sort_keys=False)
|
||||
|
||||
def from_raw(self, data: dict):
|
||||
@@ -298,6 +302,7 @@ class MinerConfig:
|
||||
Parameters:
|
||||
data: The raw config data to convert.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (From Raw) - Loading raw config")
|
||||
pool_groups = []
|
||||
for key in data.keys():
|
||||
if key == "pools":
|
||||
@@ -360,6 +365,12 @@ class MinerConfig:
|
||||
return self
|
||||
|
||||
def from_api(self, pools: list):
|
||||
"""Convert list output from the `AnyMiner.api.pools()` command into a usable data and save it to this class.
|
||||
|
||||
Parameters:
|
||||
pools: The list of pool data to convert.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (From API) - Loading API config")
|
||||
_pools = []
|
||||
for pool in pools:
|
||||
url = pool.get("URL")
|
||||
@@ -374,6 +385,7 @@ class MinerConfig:
|
||||
Parameters:
|
||||
data: The dict config data to convert.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (From Dict) - Loading Dict config")
|
||||
pool_groups = []
|
||||
for group in data["pool_groups"]:
|
||||
pool_groups.append(_PoolGroup().from_dict(group))
|
||||
@@ -389,6 +401,7 @@ class MinerConfig:
|
||||
Parameters:
|
||||
data: The toml config data to convert.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (From TOML) - Loading TOML config")
|
||||
return self.from_dict(toml.loads(data))
|
||||
|
||||
def from_yaml(self, data: str):
|
||||
@@ -397,15 +410,20 @@ class MinerConfig:
|
||||
Parameters:
|
||||
data: The yaml config data to convert.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (From YAML) - Loading YAML config")
|
||||
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
|
||||
|
||||
def as_wm(self, user_suffix: str = None) -> List[dict]:
|
||||
"""Convert the data in this class to a config usable by an Whatsminer device.
|
||||
def as_wm(self, user_suffix: str = None) -> Dict[str, int]:
|
||||
"""Convert the data in this class to a config usable by a Whatsminer device.
|
||||
|
||||
Parameters:
|
||||
user_suffix: The suffix to append to username.
|
||||
"""
|
||||
return self.pool_groups[0].as_wm(user_suffix=user_suffix)
|
||||
logging.debug(f"MinerConfig - (As Whatsminer) - Generating Whatsminer config")
|
||||
return {
|
||||
"pools": self.pool_groups[0].as_wm(user_suffix=user_suffix),
|
||||
"wattage": self.autotuning_wattage,
|
||||
}
|
||||
|
||||
def as_inno(self, user_suffix: str = None) -> dict:
|
||||
"""Convert the data in this class to a config usable by an Innosilicon device.
|
||||
@@ -413,6 +431,7 @@ class MinerConfig:
|
||||
Parameters:
|
||||
user_suffix: The suffix to append to username.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (As Inno) - Generating Innosilicon config")
|
||||
return self.pool_groups[0].as_inno(user_suffix=user_suffix)
|
||||
|
||||
def as_x19(self, user_suffix: str = None) -> str:
|
||||
@@ -421,6 +440,7 @@ class MinerConfig:
|
||||
Parameters:
|
||||
user_suffix: The suffix to append to username.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (As X19) - Generating X19 config")
|
||||
cfg = {
|
||||
"pools": self.pool_groups[0].as_x19(user_suffix=user_suffix),
|
||||
"bitmain-fan-ctrl": False,
|
||||
@@ -444,6 +464,7 @@ class MinerConfig:
|
||||
Parameters:
|
||||
user_suffix: The suffix to append to username.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (As Avalon) - Generating AvalonMiner config")
|
||||
cfg = self.pool_groups[0].as_avalon(user_suffix=user_suffix)
|
||||
return cfg
|
||||
|
||||
@@ -454,6 +475,7 @@ class MinerConfig:
|
||||
model: The model of the miner to be used in the format portion of the config.
|
||||
user_suffix: The suffix to append to username.
|
||||
"""
|
||||
logging.debug(f"MinerConfig - (As BOS) - Generating BOSMiner config")
|
||||
cfg = {
|
||||
"format": {
|
||||
"version": "1.2+",
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from dataclasses import asdict, dataclass, field, fields
|
||||
from datetime import datetime, timezone
|
||||
from functools import reduce
|
||||
from typing import List, Union
|
||||
|
||||
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
||||
@@ -25,6 +25,18 @@ from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19E
|
||||
|
||||
@dataclass
|
||||
class HashBoard:
|
||||
"""A Dataclass to standardize hashboard data.
|
||||
|
||||
Attributes:
|
||||
slot: The slot of the board as an int.
|
||||
hashrate: The hashrate of the board in TH/s as a float.
|
||||
temp: The temperature of the PCB as an int.
|
||||
chip_temp: The temperature of the chips as an int.
|
||||
chips: The chip count of the board as an int.
|
||||
expected_chips: The ideal chip count of the board as an int.
|
||||
missing: Whether the board is returned from the miners data as a bool.
|
||||
"""
|
||||
|
||||
slot: int = 0
|
||||
hashrate: float = 0.0
|
||||
temp: int = -1
|
||||
@@ -34,6 +46,17 @@ class HashBoard:
|
||||
missing: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class Fan:
|
||||
"""A Dataclass to standardize fan data.
|
||||
|
||||
Attributes:
|
||||
speed: The speed of the fan.
|
||||
"""
|
||||
|
||||
speed: int = -1
|
||||
|
||||
|
||||
@dataclass
|
||||
class MinerData:
|
||||
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
|
||||
@@ -42,8 +65,12 @@ class MinerData:
|
||||
ip: The IP of the miner as a str.
|
||||
datetime: The time and date this data was generated.
|
||||
model: The model of the miner as a str.
|
||||
make: The make of the miner as a str.
|
||||
api_ver: The current api version on the miner as a str.
|
||||
fw_ver: The current firmware version on the miner as a str.
|
||||
hostname: The network hostname of the miner as a str.
|
||||
hashrate: The hashrate of the miner in TH/s as a float.
|
||||
nominal_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
|
||||
left_board_hashrate: The hashrate of the left board of the miner in TH/s as a float.
|
||||
center_board_hashrate: The hashrate of the center board of the miner in TH/s as a float.
|
||||
right_board_hashrate: The hashrate of the right board of the miner in TH/s as a float.
|
||||
@@ -83,8 +110,12 @@ class MinerData:
|
||||
datetime: datetime = None
|
||||
mac: str = "00:00:00:00:00:00"
|
||||
model: str = "Unknown"
|
||||
make: str = "Unknown"
|
||||
api_ver: str = "Unknown"
|
||||
fw_ver: str = "Unknown"
|
||||
hostname: str = "Unknown"
|
||||
hashrate: float = 0
|
||||
nominal_hashrate: float = 0
|
||||
hashboards: List[HashBoard] = field(default_factory=list)
|
||||
ideal_hashboards: int = 1
|
||||
left_board_hashrate: float = field(init=False)
|
||||
@@ -100,10 +131,11 @@ class MinerData:
|
||||
right_board_chip_temp: int = field(init=False)
|
||||
wattage: int = -1
|
||||
wattage_limit: int = -1
|
||||
fan_1: int = -1
|
||||
fan_2: int = -1
|
||||
fan_3: int = -1
|
||||
fan_4: int = -1
|
||||
fans: List[Fan] = field(default_factory=list)
|
||||
fan_1: int = field(init=False)
|
||||
fan_2: int = field(init=False)
|
||||
fan_3: int = field(init=False)
|
||||
fan_4: int = field(init=False)
|
||||
fan_psu: int = -1
|
||||
left_chips: int = field(init=False)
|
||||
center_chips: int = field(init=False)
|
||||
@@ -127,7 +159,6 @@ class MinerData:
|
||||
def fields(cls):
|
||||
return [f.name for f in fields(cls)]
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
self.datetime = datetime.now(timezone.utc).astimezone()
|
||||
|
||||
@@ -180,6 +211,42 @@ class MinerData:
|
||||
setattr(cp, key, item & other_item)
|
||||
return cp
|
||||
|
||||
@property
|
||||
def fan_1(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 0:
|
||||
return self.fans[0].speed
|
||||
|
||||
@fan_1.setter
|
||||
def fan_1(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def fan_2(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 1:
|
||||
return self.fans[1].speed
|
||||
|
||||
@fan_2.setter
|
||||
def fan_2(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def fan_3(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 2:
|
||||
return self.fans[2].speed
|
||||
|
||||
@fan_3.setter
|
||||
def fan_3(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def fan_4(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 3:
|
||||
return self.fans[3].speed
|
||||
|
||||
@fan_4.setter
|
||||
def fan_4(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def total_chips(self): # noqa - Skip PyCharm inspection
|
||||
return sum([hb.chips for hb in self.hashboards])
|
||||
@@ -372,6 +439,7 @@ class MinerData:
|
||||
Returns:
|
||||
A dictionary version of this class.
|
||||
"""
|
||||
logging.debug(f"MinerData - (To Dict) - Dumping Dict data")
|
||||
return asdict(self)
|
||||
|
||||
def as_json(self) -> str:
|
||||
@@ -380,6 +448,7 @@ class MinerData:
|
||||
Returns:
|
||||
A JSON version of this class.
|
||||
"""
|
||||
logging.debug(f"MinerData - (To JSON) - Dumping JSON data")
|
||||
data = self.asdict()
|
||||
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
||||
return json.dumps(data)
|
||||
@@ -390,6 +459,7 @@ class MinerData:
|
||||
Returns:
|
||||
A CSV version of this class with no headers.
|
||||
"""
|
||||
logging.debug(f"MinerData - (To CSV) - Dumping CSV data")
|
||||
data = self.asdict()
|
||||
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
||||
errs = []
|
||||
@@ -408,6 +478,7 @@ class MinerData:
|
||||
Returns:
|
||||
A influxdb line protocol version of this class.
|
||||
"""
|
||||
logging.debug(f"MinerData - (To InfluxDB) - Dumping InfluxDB data")
|
||||
tag_data = [measurement_name]
|
||||
field_data = []
|
||||
|
||||
|
||||
@@ -31,6 +31,5 @@ class BraiinsOSError:
|
||||
def fields(cls):
|
||||
return fields(cls)
|
||||
|
||||
|
||||
def asdict(self):
|
||||
return asdict(self)
|
||||
|
||||
@@ -31,7 +31,6 @@ class InnosiliconError:
|
||||
def fields(cls):
|
||||
return fields(cls)
|
||||
|
||||
|
||||
@property
|
||||
def error_message(self): # noqa - Skip PyCharm inspection
|
||||
if self.error_code in ERROR_CODES:
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
from dataclasses import asdict, dataclass, field, fields
|
||||
|
||||
C_N_CODES = ["52", "53", "54", "55"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class WhatsminerError:
|
||||
@@ -33,15 +35,42 @@ class WhatsminerError:
|
||||
|
||||
@property
|
||||
def error_message(self): # noqa - Skip PyCharm inspection
|
||||
err_type = int(str(self.error_code)[:-2])
|
||||
err_subtype = int(str(self.error_code)[-2:-1])
|
||||
err_value = int(str(self.error_code)[-1:])
|
||||
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
|
||||
err_subtype = int(str(self.error_code)[2:3])
|
||||
err_value = int(str(self.error_code)[3:])
|
||||
else:
|
||||
err_type = int(str(self.error_code)[:-2])
|
||||
err_subtype = int(str(self.error_code)[-2:-1])
|
||||
err_value = int(str(self.error_code)[-1:])
|
||||
try:
|
||||
select_err_subtype = ERROR_CODES[err_type][err_subtype]
|
||||
if err_value in select_err_subtype:
|
||||
return select_err_subtype[err_value]
|
||||
elif "n" in select_err_subtype:
|
||||
return select_err_subtype["n"].replace("{n}", str(err_value)) # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
||||
select_err_type = ERROR_CODES[err_type]
|
||||
if err_subtype in select_err_type:
|
||||
select_err_subtype = select_err_type[err_subtype]
|
||||
if err_value in select_err_subtype:
|
||||
return select_err_subtype[err_value]
|
||||
elif "n" in select_err_subtype:
|
||||
return select_err_subtype[
|
||||
"n" # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
||||
].replace("{n}", str(err_value))
|
||||
else:
|
||||
return "Unknown error type."
|
||||
elif "n" in select_err_type:
|
||||
select_err_subtype = select_err_type[
|
||||
"n" # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
||||
]
|
||||
if err_value in select_err_subtype:
|
||||
return select_err_subtype[err_value]
|
||||
elif "c" in select_err_subtype:
|
||||
return (
|
||||
select_err_subtype["c"]
|
||||
.replace( # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
||||
"{n}", str(err_subtype)
|
||||
)
|
||||
.replace("{c}", str(err_value))
|
||||
)
|
||||
else:
|
||||
return "Unknown error type."
|
||||
except KeyError:
|
||||
@@ -57,6 +86,7 @@ class WhatsminerError:
|
||||
|
||||
ERROR_CODES = {
|
||||
1: { # Fan error
|
||||
0: {0: "Fan unknown."},
|
||||
1: { # Fan speed error of 1000+
|
||||
0: "Intake fan speed error.",
|
||||
1: "Exhaust fan speed error.",
|
||||
@@ -81,10 +111,16 @@ ERROR_CODES = {
|
||||
5: "Power current error.",
|
||||
6: "Power input low voltage error.",
|
||||
7: "Power input current protecting due to bad power input.",
|
||||
8: "Power power error.",
|
||||
9: "Power voltage offset error.",
|
||||
},
|
||||
1: {
|
||||
0: "Power error.",
|
||||
1: "Power iout error, please reboot.",
|
||||
2: "Power vout error, reach vout border. Border: [1150, 1500]",
|
||||
3: "Power input voltage and current do not match power output.",
|
||||
4: "Power pin did not change.",
|
||||
5: "Power vout set error.",
|
||||
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.",
|
||||
@@ -144,8 +180,15 @@ ERROR_CODES = {
|
||||
},
|
||||
5: {"n": "Slot {n} temperature protecting."}, # temperature protection
|
||||
6: {0: "Hashboard high temperature error."}, # high temp
|
||||
8: {
|
||||
0: "Humidity sensor not found.",
|
||||
1: "Humidity sensor read error.",
|
||||
2: "Humidity sensor read error.",
|
||||
3: "Humidity sensor protecting.",
|
||||
},
|
||||
},
|
||||
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
|
||||
@@ -153,12 +196,16 @@ ERROR_CODES = {
|
||||
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
|
||||
},
|
||||
6: { # env temp error
|
||||
0: {0: "Environment temperature is too high."}, # normal env temp error
|
||||
@@ -167,9 +214,10 @@ ERROR_CODES = {
|
||||
},
|
||||
},
|
||||
7: { # control board error
|
||||
0: {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.",
|
||||
},
|
||||
},
|
||||
@@ -180,6 +228,7 @@ ERROR_CODES = {
|
||||
2: "Remote daemon checksum error.",
|
||||
}
|
||||
},
|
||||
9: {0: {1: "Power rate 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
|
||||
@@ -188,26 +237,99 @@ ERROR_CODES = {
|
||||
0: "The pool does not support asicboost mode."
|
||||
},
|
||||
},
|
||||
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."},
|
||||
},
|
||||
50: { # water velocity error
|
||||
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."},
|
||||
},
|
||||
51: { # frequency error
|
||||
1: {"n": "Slot {n} frequency up timeout."}, # frequency up timeout
|
||||
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."}},
|
||||
80: {
|
||||
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."},
|
||||
},
|
||||
82: {
|
||||
0: {0: "Power version error."},
|
||||
1: {0: "Miner type error."},
|
||||
2: {0: "Version info error."},
|
||||
},
|
||||
83: {
|
||||
0: {0: "Empty level error."},
|
||||
},
|
||||
84: {
|
||||
0: {0: "Old firmware."},
|
||||
1: {0: "Software version error."},
|
||||
},
|
||||
85: {
|
||||
"n": {
|
||||
0: "Hashrate substandard L{n}.",
|
||||
1: "Power consumption substandard L{n}.",
|
||||
2: "Fan speed substandard L{n}.",
|
||||
3: "Fan speed substandard L{n}.",
|
||||
4: "Voltage substandard L{n}.",
|
||||
},
|
||||
},
|
||||
86: {
|
||||
0: {0: "Missing product serial #."},
|
||||
1: {0: "Missing product type."},
|
||||
2: {
|
||||
0: "Missing miner serial #.",
|
||||
1: "Wrong miner serial # length.",
|
||||
},
|
||||
3: {
|
||||
0: "Missing power serial #.",
|
||||
1: "Wrong power serial #.",
|
||||
2: "Fault miner serial #.",
|
||||
},
|
||||
4: {
|
||||
0: "Missing power model.",
|
||||
1: "Wrong power model name.",
|
||||
2: "Wrong power model vout.",
|
||||
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 #."},
|
||||
},
|
||||
87: {0: {0: "Miner power mismatch."}},
|
||||
99: {9: {9: "Miner unknown error."}},
|
||||
1000: {
|
||||
0: {
|
||||
0: "Security library error, please upgrade firmware",
|
||||
1: "/antiv/signature illegal.",
|
||||
2: "/antiv/dig/init.d illegal.",
|
||||
3: "/antiv/dig/pf_partial.dig illegal.",
|
||||
},
|
||||
},
|
||||
1001: {0: {0: "Security BTMiner removed, please upgrade firmware."}},
|
||||
1100: {
|
||||
0: {
|
||||
0: "Security illegal file, please upgrade firmware.",
|
||||
1: "Security virus 0001 is removed, please upgrade firmware.",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@ from .bmminer import BMMiner
|
||||
from .bosminer import BOSMiner
|
||||
from .btminer import BTMiner
|
||||
from .cgminer import CGMiner
|
||||
from .cgminer_avalon import CGMinerAvalon
|
||||
from .hiveon import Hiveon
|
||||
|
||||
@@ -14,12 +14,16 @@
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import List, Union
|
||||
from collections import namedtuple
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import asyncssh
|
||||
|
||||
from pyasic.API.bmminer import BMMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
@@ -27,82 +31,25 @@ from pyasic.settings import PyasicSettings
|
||||
class BMMiner(BaseMiner):
|
||||
"""Base handler for BMMiner based miners."""
|
||||
|
||||
def __init__(self, ip: str) -> None:
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
self.api = BMMinerAPI(ip)
|
||||
self.api = BMMinerAPI(ip, api_ver)
|
||||
self.api_type = "BMMiner"
|
||||
self.api_ver = api_ver
|
||||
self.uname = "root"
|
||||
self.pwd = "admin"
|
||||
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
# check if model is cached
|
||||
if self.model:
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
|
||||
# get devdetails data
|
||||
version_data = await self.api.devdetails()
|
||||
|
||||
# if we get data back, parse it for model
|
||||
if version_data:
|
||||
# handle Antminer BMMiner as a base
|
||||
self.model = version_data["DEVDETAILS"][0]["Model"].replace("Antminer ", "")
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
|
||||
# if we don't get devdetails, log a failed attempt
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_hostname(self) -> str:
|
||||
"""Get miner hostname.
|
||||
|
||||
Returns:
|
||||
The hostname of the miner as a string or "?"
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
try:
|
||||
# open an ssh connection
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
# if we get the connection, check hostname
|
||||
if conn is not None:
|
||||
# get output of the hostname file
|
||||
data = await conn.run("cat /proc/sys/kernel/hostname")
|
||||
host = data.stdout.strip()
|
||||
|
||||
# return hostname data
|
||||
logging.debug(f"Found hostname for {self.ip}: {host}")
|
||||
self.hostname = host
|
||||
return self.hostname
|
||||
else:
|
||||
# return ? if we fail to get hostname with no ssh connection
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
return "?"
|
||||
except Exception:
|
||||
# return ? if we fail to get hostname with an exception
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
return "?"
|
||||
|
||||
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||
"""Send a command to the miner over ssh.
|
||||
|
||||
Parameters:
|
||||
cmd: The command to run.
|
||||
|
||||
Returns:
|
||||
Result of the command or None.
|
||||
"""
|
||||
async def send_ssh_command(self, cmd: str) -> Optional[str]:
|
||||
result = None
|
||||
|
||||
try:
|
||||
conn = await self._get_ssh_connection()
|
||||
except (asyncssh.Error, OSError):
|
||||
return None
|
||||
|
||||
# open an ssh connection
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
async with conn:
|
||||
# 3 retries
|
||||
for i in range(3):
|
||||
try:
|
||||
@@ -121,31 +68,17 @@ class BMMiner(BaseMiner):
|
||||
# return the result, either command output or None
|
||||
return result
|
||||
|
||||
async def get_config(self) -> Union[list, None]:
|
||||
"""Get the pool configuration of the miner.
|
||||
|
||||
Returns:
|
||||
Pool config data or None.
|
||||
"""
|
||||
async def get_config(self) -> MinerConfig:
|
||||
# get pool data
|
||||
pools = await self.api.pools()
|
||||
pool_data = []
|
||||
try:
|
||||
pools = await self.api.pools()
|
||||
except APIError:
|
||||
return self.config
|
||||
|
||||
# ensure we got pool data
|
||||
if not pools:
|
||||
return
|
||||
|
||||
# parse all the pools
|
||||
for pool in pools["POOLS"]:
|
||||
pool_data.append({"url": pool["URL"], "user": pool["User"], "pwd": "123"})
|
||||
return pool_data
|
||||
self.config = MinerConfig().from_api(pools["POOLS"])
|
||||
return self.config
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
"""Reboot the miner.
|
||||
|
||||
Returns:
|
||||
The result of rebooting the miner.
|
||||
"""
|
||||
logging.debug(f"{self}: Sending reboot command.")
|
||||
_ret = await self.send_ssh_command("reboot")
|
||||
logging.debug(f"{self}: Reboot command completed.")
|
||||
@@ -156,23 +89,12 @@ class BMMiner(BaseMiner):
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
return None
|
||||
|
||||
async def check_light(self) -> bool:
|
||||
if not self.light:
|
||||
self.light = False
|
||||
return self.light
|
||||
|
||||
async def fault_light_off(self) -> bool:
|
||||
return False
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
return False
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_mac(self) -> str:
|
||||
return "00:00:00:00:00:00"
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
return False
|
||||
|
||||
@@ -182,67 +104,124 @@ class BMMiner(BaseMiner):
|
||||
async def resume_mining(self) -> bool:
|
||||
return False
|
||||
|
||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
return False
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(
|
||||
ip=str(self.ip),
|
||||
ideal_chips=self.nominal_chips * self.ideal_hashboards,
|
||||
ideal_hashboards=self.ideal_hashboards,
|
||||
##################################################
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
async def get_mac(self) -> str:
|
||||
return "00:00:00:00:00:00"
|
||||
|
||||
async def get_model(self, api_devdetails: dict = None) -> Optional[str]:
|
||||
if self.model:
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
|
||||
if not api_devdetails:
|
||||
try:
|
||||
api_devdetails = await self.api.devdetails()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_devdetails:
|
||||
try:
|
||||
self.model = api_devdetails["DEVDETAILS"][0]["Model"].replace(
|
||||
"Antminer ", ""
|
||||
)
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
except (TypeError, IndexError, KeyError):
|
||||
pass
|
||||
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.api_ver:
|
||||
return self.api_ver
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_version:
|
||||
try:
|
||||
self.api_ver = api_version["VERSION"][0]["API"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.fw_ver:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_version:
|
||||
try:
|
||||
self.fw_ver = api_version["VERSION"][0]["CompileTime"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return self.fw_ver
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
# check if version is cached
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
return miner_version(
|
||||
api_ver=await self.get_api_ver(api_version),
|
||||
fw_ver=await self.get_fw_ver(api_version=api_version),
|
||||
)
|
||||
|
||||
board_offset = -1
|
||||
fan_offset = -1
|
||||
async def get_fan_psu(self):
|
||||
return None
|
||||
|
||||
model = await self.get_model()
|
||||
hostname = await self.get_hostname()
|
||||
mac = await self.get_mac()
|
||||
errors = await self.get_errors()
|
||||
async def get_hostname(self) -> Optional[str]:
|
||||
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
|
||||
if hn:
|
||||
self.hostname = hn
|
||||
return self.hostname
|
||||
|
||||
if model:
|
||||
data.model = model
|
||||
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||
# get hr from API
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
if api_summary:
|
||||
try:
|
||||
return round(float(api_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
hashboards = []
|
||||
|
||||
if errors:
|
||||
for error in errors:
|
||||
data.errors.append(error)
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
data.fault_light = await self.check_light()
|
||||
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary", "pools", "stats", allow_warning=allow_warning
|
||||
)
|
||||
if miner_data:
|
||||
break
|
||||
|
||||
if not miner_data:
|
||||
return data
|
||||
|
||||
summary = miner_data.get("summary")[0]
|
||||
pools = miner_data.get("pools")[0]
|
||||
stats = miner_data.get("stats")[0]
|
||||
|
||||
if summary:
|
||||
hr = summary.get("SUMMARY")
|
||||
if hr:
|
||||
if len(hr) > 0:
|
||||
hr = hr[0].get("GHS av")
|
||||
if hr:
|
||||
data.hashrate = round(hr / 1000, 2)
|
||||
|
||||
if stats:
|
||||
boards = stats.get("STATS")
|
||||
if boards:
|
||||
if api_stats:
|
||||
try:
|
||||
board_offset = -1
|
||||
boards = api_stats["STATS"]
|
||||
if len(boards) > 1:
|
||||
for board_num in range(1, 16, 5):
|
||||
for _b_num in range(5):
|
||||
@@ -253,7 +232,6 @@ class BMMiner(BaseMiner):
|
||||
if board_offset == -1:
|
||||
board_offset = 1
|
||||
|
||||
env_temp_list = []
|
||||
for i in range(board_offset, board_offset + self.ideal_hashboards):
|
||||
hashboard = HashBoard(
|
||||
slot=i - board_offset, expected_chips=self.nominal_chips
|
||||
@@ -277,75 +255,101 @@ class BMMiner(BaseMiner):
|
||||
hashboard.missing = False
|
||||
if (not chips) or (not chips > 0):
|
||||
hashboard.missing = True
|
||||
data.hashboards.append(hashboard)
|
||||
hashboards.append(hashboard)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if f"temp_pcb{i}" in boards[1].keys():
|
||||
env_temp = boards[1][f"temp_pcb{i}"].split("-")[0]
|
||||
if not env_temp == 0:
|
||||
env_temp_list.append(int(env_temp))
|
||||
if not env_temp_list == []:
|
||||
data.env_temp = round(sum(env_temp_list) / len(env_temp_list))
|
||||
return hashboards
|
||||
|
||||
if stats:
|
||||
temp = stats.get("STATS")
|
||||
if temp:
|
||||
if len(temp) > 1:
|
||||
for fan_num in range(1, 8, 4):
|
||||
for _f_num in range(4):
|
||||
f = temp[1].get(f"fan{fan_num + _f_num}")
|
||||
if f and not f == 0 and fan_offset == -1:
|
||||
fan_offset = fan_num
|
||||
if fan_offset == -1:
|
||||
fan_offset = 1
|
||||
for fan in range(self.fan_count):
|
||||
setattr(
|
||||
data, f"fan_{fan + 1}", temp[1].get(f"fan{fan_offset+fan}")
|
||||
)
|
||||
async def get_env_temp(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
if pools:
|
||||
pool_1 = None
|
||||
pool_2 = None
|
||||
pool_1_user = None
|
||||
pool_2_user = None
|
||||
pool_1_quota = 1
|
||||
pool_2_quota = 1
|
||||
quota = 0
|
||||
for pool in pools.get("POOLS"):
|
||||
if not pool_1_user:
|
||||
pool_1_user = pool.get("User")
|
||||
pool_1 = pool["URL"]
|
||||
pool_1_quota = pool["Quota"]
|
||||
elif not pool_2_user:
|
||||
pool_2_user = pool.get("User")
|
||||
pool_2 = pool["URL"]
|
||||
pool_2_quota = pool["Quota"]
|
||||
if not pool.get("User") == pool_1_user:
|
||||
if not pool_2_user == pool.get("User"):
|
||||
pool_2_user = pool.get("User")
|
||||
pool_2 = pool["URL"]
|
||||
pool_2_quota = pool["Quota"]
|
||||
if pool_2_user and not pool_2_user == pool_1_user:
|
||||
quota = f"{pool_1_quota}/{pool_2_quota}"
|
||||
async def get_wattage(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
if pool_1:
|
||||
pool_1 = pool_1.replace("stratum+tcp://", "").replace(
|
||||
"stratum2+tcp://", ""
|
||||
)
|
||||
data.pool_1_url = pool_1
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
if pool_1_user:
|
||||
data.pool_1_user = pool_1_user
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if pool_2:
|
||||
pool_2 = pool_2.replace("stratum+tcp://", "").replace(
|
||||
"stratum2+tcp://", ""
|
||||
)
|
||||
data.pool_2_url = pool_2
|
||||
fans_data = [None, None, None, None]
|
||||
if api_stats:
|
||||
try:
|
||||
fan_offset = -1
|
||||
|
||||
if pool_2_user:
|
||||
data.pool_2_user = pool_2_user
|
||||
for fan_num in range(1, 8, 4):
|
||||
for _f_num in range(4):
|
||||
f = api_stats["STATS"][1].get(f"fan{fan_num + _f_num}")
|
||||
if f and not f == 0 and fan_offset == -1:
|
||||
fan_offset = fan_num
|
||||
if fan_offset == -1:
|
||||
fan_offset = 1
|
||||
|
||||
if quota:
|
||||
data.pool_split = str(quota)
|
||||
for fan in range(self.fan_count):
|
||||
fans_data[fan] = api_stats["STATS"][1].get(f"fan{fan_offset+fan}")
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
fans = [Fan(speed=d) if d else Fan() for d in fans_data]
|
||||
|
||||
return data
|
||||
return fans
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
|
||||
if not api_pools:
|
||||
try:
|
||||
api_pools = await self.api.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_pools:
|
||||
try:
|
||||
pools = {}
|
||||
for i, pool in enumerate(api_pools["POOLS"]):
|
||||
pools[f"pool_{i + 1}_url"] = (
|
||||
pool["URL"]
|
||||
.replace("stratum+tcp://", "")
|
||||
.replace("stratum2+tcp://", "")
|
||||
)
|
||||
pools[f"pool_{i + 1}_user"] = pool["User"]
|
||||
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
|
||||
|
||||
groups.append(pools)
|
||||
except KeyError:
|
||||
pass
|
||||
return groups
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_fault_light(self) -> bool:
|
||||
return False
|
||||
|
||||
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
|
||||
# X19 method, not sure compatibility
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_stats:
|
||||
try:
|
||||
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
|
||||
try:
|
||||
rate_unit = api_stats["STATS"][1]["rate_unit"]
|
||||
except KeyError:
|
||||
rate_unit = "GH"
|
||||
if rate_unit == "GH":
|
||||
return round(ideal_rate / 1000, 2)
|
||||
if rate_unit == "MH":
|
||||
return round(ideal_rate / 1000000, 2)
|
||||
else:
|
||||
return round(ideal_rate, 2)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,43 +14,46 @@
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import List, Union
|
||||
from collections import namedtuple
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import asyncssh
|
||||
|
||||
from pyasic.API.bosminer import BOSMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
|
||||
|
||||
class BOSMinerOld(BaseMiner):
|
||||
def __init__(self, ip: str) -> None:
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
self.api = BOSMinerAPI(ip)
|
||||
self.api = BOSMinerAPI(ip, api_ver)
|
||||
self.api_type = "BOSMiner"
|
||||
self.uname = "root"
|
||||
self.pwd = "admin"
|
||||
|
||||
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||
"""Send a command to the miner over ssh.
|
||||
|
||||
:return: Result of the command or None.
|
||||
"""
|
||||
async def send_ssh_command(self, cmd: str) -> Optional[str]:
|
||||
result = None
|
||||
|
||||
try:
|
||||
conn = await self._get_ssh_connection()
|
||||
except (asyncssh.Error, OSError):
|
||||
return None
|
||||
|
||||
# open an ssh connection
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
async with conn:
|
||||
# 3 retries
|
||||
for i in range(3):
|
||||
try:
|
||||
# run the command and get the result
|
||||
result = await conn.run(cmd)
|
||||
if result.stdout:
|
||||
result = result.stdout
|
||||
result = result.stdout
|
||||
|
||||
except Exception as e:
|
||||
if e == "SSH connection closed":
|
||||
return "Update completed."
|
||||
# if the command fails, log it
|
||||
logging.warning(f"{self} command {cmd} error: {e}")
|
||||
|
||||
@@ -59,7 +62,7 @@ class BOSMinerOld(BaseMiner):
|
||||
return
|
||||
continue
|
||||
# return the result, either command output or None
|
||||
return str(result)
|
||||
return result
|
||||
|
||||
async def update_to_plus(self):
|
||||
result = await self.send_ssh_command("opkg update && opkg install bos_plus")
|
||||
@@ -77,18 +80,6 @@ class BOSMinerOld(BaseMiner):
|
||||
async def get_config(self) -> None:
|
||||
return None
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_hostname(self) -> str:
|
||||
return "?"
|
||||
|
||||
async def get_mac(self) -> str:
|
||||
return "00:00:00:00:00:00"
|
||||
|
||||
async def get_model(self) -> str:
|
||||
return "S9"
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
return False
|
||||
|
||||
@@ -104,5 +95,88 @@ class BOSMinerOld(BaseMiner):
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
return None
|
||||
|
||||
async def get_data(self) -> MinerData:
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
return False
|
||||
|
||||
##################################################
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
async def get_mac(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_model(self) -> str:
|
||||
return "S9"
|
||||
|
||||
async def get_version(self) -> Tuple[Optional[str], Optional[str]]:
|
||||
return None, None
|
||||
|
||||
async def get_hostname(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_hashrate(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def get_hashboards(self) -> List[HashBoard]:
|
||||
return []
|
||||
|
||||
async def get_env_temp(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def get_wattage(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_fans(
|
||||
self,
|
||||
) -> List[Fan]:
|
||||
return [Fan(), Fan(), Fan(), Fan()]
|
||||
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_api_ver(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_fw_ver(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
|
||||
if not api_pools:
|
||||
try:
|
||||
api_pools = await self.api.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_pools:
|
||||
try:
|
||||
pools = {}
|
||||
for i, pool in enumerate(api_pools["POOLS"]):
|
||||
pools[f"pool_{i + 1}_url"] = (
|
||||
pool["URL"]
|
||||
.replace("stratum+tcp://", "")
|
||||
.replace("stratum2+tcp://", "")
|
||||
)
|
||||
pools[f"pool_{i + 1}_user"] = pool["User"]
|
||||
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
|
||||
|
||||
groups.append(pools)
|
||||
except KeyError:
|
||||
pass
|
||||
return groups
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_fault_light(self) -> bool:
|
||||
return False
|
||||
|
||||
async def get_nominal_hashrate(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||
return MinerData(ip=str(self.ip))
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import List, Union
|
||||
from collections import namedtuple
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
from pyasic.API.btminer import BTMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -26,76 +27,12 @@ from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class BTMiner(BaseMiner):
|
||||
def __init__(self, ip: str) -> None:
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
self.api = BTMinerAPI(ip)
|
||||
self.api = BTMinerAPI(ip, api_ver)
|
||||
self.api_type = "BTMiner"
|
||||
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
if self.model:
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
version_data = await self.api.devdetails()
|
||||
if version_data:
|
||||
self.model = version_data["DEVDETAILS"][0]["Model"].split("V")[0]
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_hostname(self) -> Union[str, None]:
|
||||
"""Get miner hostname.
|
||||
|
||||
Returns:
|
||||
The hostname of the miner as a string or None.
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
try:
|
||||
host_data = await self.api.get_miner_info()
|
||||
if host_data:
|
||||
host = host_data["Msg"]["hostname"]
|
||||
logging.debug(f"Found hostname for {self.ip}: {host}")
|
||||
self.hostname = host
|
||||
return self.hostname
|
||||
except APIError:
|
||||
logging.info(f"Failed to get hostname for miner: {self}")
|
||||
return None
|
||||
except Exception:
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_mac(self) -> str:
|
||||
"""Get the mac address of the miner.
|
||||
|
||||
Returns:
|
||||
The mac address of the miner as a string.
|
||||
"""
|
||||
mac = ""
|
||||
data = await self.api.summary()
|
||||
if data:
|
||||
if data.get("SUMMARY"):
|
||||
if len(data["SUMMARY"]) > 0:
|
||||
_mac = data["SUMMARY"][0].get("MAC")
|
||||
if _mac:
|
||||
mac = _mac
|
||||
if mac == "":
|
||||
try:
|
||||
data = await self.api.get_miner_info()
|
||||
if data:
|
||||
if "Msg" in data.keys():
|
||||
if "mac" in data["Msg"].keys():
|
||||
mac = data["Msg"]["mac"]
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
return str(mac).upper()
|
||||
self.api_ver = api_ver
|
||||
|
||||
async def _reset_api_pwd_to_admin(self, pwd: str):
|
||||
try:
|
||||
@@ -108,23 +45,6 @@ class BTMiner(BaseMiner):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def check_light(self) -> bool:
|
||||
data = None
|
||||
|
||||
try:
|
||||
data = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
if not self.light:
|
||||
self.light = False
|
||||
if data:
|
||||
if "Msg" in data.keys():
|
||||
if "ledstat" in data["Msg"].keys():
|
||||
if not data["Msg"]["ledstat"] == "auto":
|
||||
self.light = True
|
||||
if data["Msg"]["ledstat"] == "auto":
|
||||
self.light = False
|
||||
return self.light
|
||||
|
||||
async def fault_light_off(self) -> bool:
|
||||
try:
|
||||
data = await self.api.set_led(auto=True)
|
||||
@@ -152,58 +72,41 @@ class BTMiner(BaseMiner):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
data = []
|
||||
try:
|
||||
err_data = await self.api.get_error_code()
|
||||
if err_data:
|
||||
if err_data.get("Msg"):
|
||||
if err_data["Msg"].get("error_code"):
|
||||
for err in err_data["Msg"]["error_code"]:
|
||||
if isinstance(err, dict):
|
||||
for code in err:
|
||||
data.append(WhatsminerError(error_code=int(code)))
|
||||
else:
|
||||
data.append(WhatsminerError(error_code=int(err)))
|
||||
except APIError:
|
||||
summary_data = await self.api.summary()
|
||||
if summary_data.get("SUMMARY"):
|
||||
summary_data = summary_data["SUMMARY"]
|
||||
if summary_data[0].get("Error Code Count"):
|
||||
for i in range(summary_data[0]["Error Code Count"]):
|
||||
if summary_data[0].get(f"Error Code {i}"):
|
||||
if not summary_data[0][f"Error Code {i}"] == "":
|
||||
data.append(
|
||||
WhatsminerError(
|
||||
error_code=summary_data[0][f"Error Code {i}"]
|
||||
)
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
data = await self.api.reboot()
|
||||
try:
|
||||
data = await self.api.reboot()
|
||||
except APIError:
|
||||
return False
|
||||
if data.get("Msg"):
|
||||
if data["Msg"] == "API command OK":
|
||||
return True
|
||||
return False
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
data = await self.api.restart()
|
||||
try:
|
||||
data = await self.api.restart()
|
||||
except APIError:
|
||||
return False
|
||||
if data.get("Msg"):
|
||||
if data["Msg"] == "API command OK":
|
||||
return True
|
||||
return False
|
||||
|
||||
async def stop_mining(self) -> bool:
|
||||
data = await self.api.power_off(respbefore=True)
|
||||
try:
|
||||
data = await self.api.power_off(respbefore=True)
|
||||
except APIError:
|
||||
return False
|
||||
if data.get("Msg"):
|
||||
if data["Msg"] == "API command OK":
|
||||
return True
|
||||
return False
|
||||
|
||||
async def resume_mining(self) -> bool:
|
||||
data = await self.api.power_on()
|
||||
try:
|
||||
data = await self.api.power_on()
|
||||
except APIError:
|
||||
return False
|
||||
if data.get("Msg"):
|
||||
if data["Msg"] == "API command OK":
|
||||
return True
|
||||
@@ -211,224 +114,437 @@ class BTMiner(BaseMiner):
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
conf = config.as_wm(user_suffix=user_suffix)
|
||||
pools_conf = conf["pools"]
|
||||
|
||||
await self.api.update_pools(
|
||||
conf[0]["url"],
|
||||
conf[0]["user"],
|
||||
conf[0]["pass"],
|
||||
conf[1]["url"],
|
||||
conf[1]["user"],
|
||||
conf[1]["pass"],
|
||||
conf[2]["url"],
|
||||
conf[2]["user"],
|
||||
conf[2]["pass"],
|
||||
)
|
||||
try:
|
||||
await self.api.update_pools(
|
||||
pools_conf[0]["url"],
|
||||
pools_conf[0]["user"],
|
||||
pools_conf[0]["pass"],
|
||||
pools_conf[1]["url"],
|
||||
pools_conf[1]["user"],
|
||||
pools_conf[1]["pass"],
|
||||
pools_conf[2]["url"],
|
||||
pools_conf[2]["user"],
|
||||
pools_conf[2]["pass"],
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
try:
|
||||
await self.api.adjust_power_limit(conf["wattage"])
|
||||
except APIError:
|
||||
# cannot set wattage
|
||||
pass
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
pools = None
|
||||
summary = None
|
||||
cfg = MinerConfig()
|
||||
|
||||
try:
|
||||
pools = await self.api.pools()
|
||||
data = await self.api.multicommand("pools", "summary")
|
||||
pools = data["pools"][0]
|
||||
summary = data["summary"][0]
|
||||
except APIError as e:
|
||||
logging.warning(e)
|
||||
|
||||
if pools:
|
||||
if "POOLS" in pools.keys():
|
||||
if "POOLS" in pools:
|
||||
cfg = cfg.from_api(pools["POOLS"])
|
||||
if summary:
|
||||
if "SUMMARY" in summary:
|
||||
if wattage := summary["SUMMARY"][0].get("Power Limit"):
|
||||
cfg.autotuning_wattage = wattage
|
||||
|
||||
return cfg
|
||||
|
||||
async def get_data(self, allow_warning: bool = True) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(
|
||||
ip=str(self.ip),
|
||||
ideal_chips=self.nominal_chips * self.ideal_hashboards,
|
||||
ideal_hashboards=self.ideal_hashboards,
|
||||
)
|
||||
|
||||
mac = None
|
||||
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
try:
|
||||
model = await self.get_model()
|
||||
except APIError:
|
||||
logging.info(f"Failed to get model: {self}")
|
||||
model = None
|
||||
data.model = "Whatsminer"
|
||||
await self.api.adjust_power_limit(wattage)
|
||||
except Exception as e:
|
||||
logging.warning(f"{self} set_power_limit: {e}")
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
try:
|
||||
hostname = await self.get_hostname()
|
||||
except APIError:
|
||||
logging.info(f"Failed to get hostname: {self}")
|
||||
hostname = None
|
||||
data.hostname = "Whatsminer"
|
||||
##################################################
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
if model:
|
||||
data.model = model
|
||||
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
|
||||
data.fault_light = await self.check_light()
|
||||
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
async def get_mac(
|
||||
self, api_summary: dict = None, api_get_miner_info: dict = None
|
||||
) -> Optional[str]:
|
||||
if not api_get_miner_info:
|
||||
try:
|
||||
miner_data = await self.api.multicommand("summary", "devs", "pools", allow_warning=allow_warning)
|
||||
if miner_data:
|
||||
break
|
||||
api_get_miner_info = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
pass
|
||||
if not miner_data:
|
||||
return data
|
||||
|
||||
summary = miner_data.get("summary")[0]
|
||||
devs = miner_data.get("devs")[0]
|
||||
pools = miner_data.get("pools")[0]
|
||||
try:
|
||||
psu_data = await self.api.get_psu()
|
||||
except APIError:
|
||||
psu_data = None
|
||||
try:
|
||||
err_data = await self.api.get_error_code()
|
||||
except APIError:
|
||||
err_data = None
|
||||
|
||||
if summary:
|
||||
summary_data = summary.get("SUMMARY")
|
||||
if summary_data:
|
||||
if len(summary_data) > 0:
|
||||
wattage_limit = None
|
||||
if summary_data[0].get("MAC"):
|
||||
mac = summary_data[0]["MAC"]
|
||||
|
||||
if summary_data[0].get("Env Temp"):
|
||||
data.env_temp = summary_data[0]["Env Temp"]
|
||||
|
||||
if summary_data[0].get("Power Limit"):
|
||||
wattage_limit = summary_data[0]["Power Limit"]
|
||||
|
||||
if summary_data[0].get("Power Fanspeed"):
|
||||
data.fan_psu = summary_data[0]["Power Fanspeed"]
|
||||
|
||||
data.fan_1 = summary_data[0]["Fan Speed In"]
|
||||
data.fan_2 = summary_data[0]["Fan Speed Out"]
|
||||
|
||||
hr = summary_data[0].get("MHS 1m")
|
||||
if hr:
|
||||
data.hashrate = round(hr / 1000000, 2)
|
||||
|
||||
wattage = summary_data[0].get("Power")
|
||||
if wattage:
|
||||
data.wattage = round(wattage)
|
||||
|
||||
if not wattage_limit:
|
||||
wattage_limit = round(wattage)
|
||||
|
||||
data.wattage_limit = wattage_limit
|
||||
|
||||
if summary_data[0].get("Error Code Count"):
|
||||
for i in range(summary_data[0]["Error Code Count"]):
|
||||
if summary_data[0].get(f"Error Code {i}"):
|
||||
if not summary_data[0][f"Error Code {i}"] == "":
|
||||
data.errors.append(
|
||||
WhatsminerError(
|
||||
error_code=summary_data[0][
|
||||
f"Error Code {i}"
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
if psu_data:
|
||||
psu = psu_data.get("Msg")
|
||||
if psu:
|
||||
if psu.get("fan_speed"):
|
||||
data.fan_psu = psu["fan_speed"]
|
||||
|
||||
if err_data:
|
||||
if err_data.get("Msg"):
|
||||
if err_data["Msg"].get("error_code"):
|
||||
for err in err_data["Msg"]["error_code"]:
|
||||
if isinstance(err, dict):
|
||||
for code in err:
|
||||
data.errors.append(
|
||||
WhatsminerError(error_code=int(code))
|
||||
)
|
||||
else:
|
||||
data.errors.append(WhatsminerError(error_code=int(err)))
|
||||
|
||||
if devs:
|
||||
dev_data = devs.get("DEVS")
|
||||
if dev_data:
|
||||
for board in dev_data:
|
||||
temp_board = HashBoard(
|
||||
slot=board["ASC"],
|
||||
chip_temp=round(board["Chip Temp Avg"]),
|
||||
temp=round(board["Temperature"]),
|
||||
hashrate=round(board["MHS 1m"] / 1000000, 2),
|
||||
chips=board["Effective Chips"],
|
||||
missing=False if board["Effective Chips"] > 0 else True,
|
||||
expected_chips=self.nominal_chips,
|
||||
)
|
||||
data.hashboards.append(temp_board)
|
||||
|
||||
if pools:
|
||||
pool_1 = None
|
||||
pool_2 = None
|
||||
pool_1_user = None
|
||||
pool_2_user = None
|
||||
pool_1_quota = 1
|
||||
pool_2_quota = 1
|
||||
quota = 0
|
||||
for pool in pools.get("POOLS"):
|
||||
if not pool_1_user:
|
||||
pool_1_user = pool.get("User")
|
||||
pool_1 = pool["URL"]
|
||||
pool_1_quota = pool["Quota"]
|
||||
elif not pool_2_user:
|
||||
pool_2_user = pool.get("User")
|
||||
pool_2 = pool["URL"]
|
||||
pool_2_quota = pool["Quota"]
|
||||
if not pool.get("User") == pool_1_user:
|
||||
if not pool_2_user == pool.get("User"):
|
||||
pool_2_user = pool.get("User")
|
||||
pool_2 = pool["URL"]
|
||||
pool_2_quota = pool["Quota"]
|
||||
if pool_2_user and not pool_2_user == pool_1_user:
|
||||
quota = f"{pool_1_quota}/{pool_2_quota}"
|
||||
|
||||
if pool_1:
|
||||
pool_1 = pool_1.replace("stratum+tcp://", "").replace(
|
||||
"stratum2+tcp://", ""
|
||||
)
|
||||
data.pool_1_url = pool_1
|
||||
|
||||
if pool_1_user:
|
||||
data.pool_1_user = pool_1_user
|
||||
|
||||
if pool_2:
|
||||
pool_2 = pool_2.replace("stratum+tcp://", "").replace(
|
||||
"stratum2+tcp://", ""
|
||||
)
|
||||
data.pool_2_url = pool_2
|
||||
|
||||
if pool_2_user:
|
||||
data.pool_2_user = pool_2_user
|
||||
|
||||
if quota:
|
||||
data.pool_split = str(quota)
|
||||
|
||||
if not mac:
|
||||
if api_get_miner_info:
|
||||
try:
|
||||
mac = await self.get_mac()
|
||||
mac = api_get_miner_info["Msg"]["mac"]
|
||||
return str(mac).upper()
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
logging.info(f"Failed to get mac: {self}")
|
||||
mac = None
|
||||
pass
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
if api_summary:
|
||||
try:
|
||||
mac = api_summary["SUMMARY"][0]["MAC"]
|
||||
return str(mac).upper()
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return data
|
||||
async def get_model(self, api_devdetails: dict = None) -> Optional[str]:
|
||||
if self.model:
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
|
||||
if not api_devdetails:
|
||||
try:
|
||||
api_devdetails = await self.api.devdetails()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_devdetails:
|
||||
try:
|
||||
self.model = api_devdetails["DEVDETAILS"][0]["Model"].split("V")[0]
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
except (TypeError, IndexError, KeyError):
|
||||
pass
|
||||
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_version(
|
||||
self, api_get_version: dict = None, api_summary: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
api_ver = await self.get_api_ver(api_get_version=api_get_version)
|
||||
fw_ver = await self.get_fw_ver(
|
||||
api_get_version=api_get_version, api_summary=api_summary
|
||||
)
|
||||
return miner_version(api_ver, fw_ver)
|
||||
|
||||
async def get_api_ver(self, api_get_version: dict = None) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.api_ver:
|
||||
return self.api_ver
|
||||
|
||||
if not api_get_version:
|
||||
try:
|
||||
api_get_version = await self.api.get_version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_get_version:
|
||||
if "Code" in api_get_version.keys():
|
||||
if api_get_version["Code"] == 131:
|
||||
try:
|
||||
api_ver = api_get_version["Msg"]
|
||||
if not isinstance(api_ver, str):
|
||||
api_ver = api_ver["api_ver"]
|
||||
self.api_ver = api_ver.replace("whatsminer v", "")
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
self.api.api_ver = self.api_ver
|
||||
return self.api_ver
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(
|
||||
self, api_get_version: dict = None, api_summary: dict = None
|
||||
) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.fw_ver:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_get_version:
|
||||
try:
|
||||
api_get_version = await self.api.get_version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_get_version:
|
||||
if "Code" in api_get_version.keys():
|
||||
if api_get_version["Code"] == 131:
|
||||
try:
|
||||
self.fw_ver = api_get_version["Msg"]["fw_ver"]
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
self.fw_ver = api_summary["SUMMARY"][0]["Firmware Version"].replace(
|
||||
"'", ""
|
||||
)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return self.fw_ver
|
||||
|
||||
async def get_hostname(self, api_get_miner_info: dict = None) -> Optional[str]:
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
|
||||
if not api_get_miner_info:
|
||||
try:
|
||||
api_get_miner_info = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
return None # only one way to get this
|
||||
|
||||
if api_get_miner_info:
|
||||
try:
|
||||
self.hostname = api_get_miner_info["Msg"]["hostname"]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
return self.hostname
|
||||
|
||||
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||
# get hr from API
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(self, api_devs: dict = None) -> List[HashBoard]:
|
||||
|
||||
hashboards = [
|
||||
HashBoard(slot=i, expected_chips=self.nominal_chips)
|
||||
for i in range(self.ideal_hashboards)
|
||||
]
|
||||
|
||||
if not api_devs:
|
||||
try:
|
||||
api_devs = await self.api.devs()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_devs:
|
||||
try:
|
||||
for board in api_devs["DEVS"]:
|
||||
if len(hashboards) < board["ASC"] + 1:
|
||||
hashboards.append(
|
||||
HashBoard(
|
||||
slot=board["ASC"], expected_chips=self.nominal_chips
|
||||
)
|
||||
)
|
||||
self.ideal_hashboards += 1
|
||||
hashboards[board["ASC"]].chip_temp = round(board["Chip Temp Avg"])
|
||||
hashboards[board["ASC"]].temp = round(board["Temperature"])
|
||||
hashboards[board["ASC"]].hashrate = round(
|
||||
float(board["MHS 1m"] / 1000000), 2
|
||||
)
|
||||
hashboards[board["ASC"]].chips = board["Effective Chips"]
|
||||
hashboards[board["ASC"]].missing = False
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_env_temp(self, api_summary: dict = None) -> Optional[float]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return api_summary["SUMMARY"][0]["Env Temp"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_wattage(self, api_summary: dict = None) -> Optional[int]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return api_summary["SUMMARY"][0]["Power"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_wattage_limit(self, api_summary: dict = None) -> Optional[int]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return api_summary["SUMMARY"][0]["Power Limit"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_fans(
|
||||
self, api_summary: dict = None, api_get_psu: dict = None
|
||||
) -> List[Fan]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
fans = [Fan(), Fan(), Fan(), Fan()]
|
||||
if api_summary:
|
||||
try:
|
||||
if self.fan_count > 0:
|
||||
fans = [
|
||||
Fan(api_summary["SUMMARY"][0]["Fan Speed In"]),
|
||||
Fan(api_summary["SUMMARY"][0]["Fan Speed Out"]),
|
||||
Fan(),
|
||||
Fan(),
|
||||
]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return fans
|
||||
|
||||
async def get_fan_psu(
|
||||
self, api_summary: dict = None, api_get_psu: dict = None
|
||||
) -> Optional[int]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return int(api_summary["SUMMARY"][0]["Power Fanspeed"])
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
if not api_get_psu:
|
||||
try:
|
||||
api_get_psu = await self.api.get_psu()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_get_psu:
|
||||
try:
|
||||
return int(api_get_psu["Msg"]["fan_speed"])
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
|
||||
if not api_pools:
|
||||
try:
|
||||
api_pools = await self.api.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_pools:
|
||||
try:
|
||||
pools = {}
|
||||
for i, pool in enumerate(api_pools["POOLS"]):
|
||||
pools[f"pool_{i + 1}_url"] = (
|
||||
pool["URL"]
|
||||
.replace("stratum+tcp://", "")
|
||||
.replace("stratum2+tcp://", "")
|
||||
)
|
||||
pools[f"pool_{i + 1}_user"] = pool["User"]
|
||||
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
|
||||
|
||||
groups.append(pools)
|
||||
except KeyError:
|
||||
pass
|
||||
return groups
|
||||
|
||||
async def get_errors(
|
||||
self, api_summary: dict = None, api_get_error_code: dict = None
|
||||
) -> List[MinerErrorData]:
|
||||
errors = []
|
||||
if not api_summary and not api_get_error_code:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
for i in range(api_summary["SUMMARY"][0]["Error Code Count"]):
|
||||
err = api_summary["SUMMARY"][0].get(f"Error Code {i}")
|
||||
if err:
|
||||
errors.append(WhatsminerError(error_code=err))
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if not api_get_error_code:
|
||||
try:
|
||||
api_get_error_code = await self.api.get_error_code()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_get_error_code:
|
||||
for err in api_get_error_code["Msg"]["error_code"]:
|
||||
if isinstance(err, dict):
|
||||
for code in err:
|
||||
errors.append(WhatsminerError(error_code=int(code)))
|
||||
else:
|
||||
errors.append(WhatsminerError(error_code=int(err)))
|
||||
|
||||
return errors
|
||||
|
||||
async def get_nominal_hashrate(self, api_summary: dict = None):
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
nominal_hashrate = api_summary["SUMMARY"][0]["Factory GHS"]
|
||||
if nominal_hashrate:
|
||||
return round(nominal_hashrate / 1000, 2)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_fault_light(self, api_get_miner_info: dict = None) -> bool:
|
||||
data = None
|
||||
|
||||
if not api_get_miner_info:
|
||||
try:
|
||||
api_get_miner_info = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
if not self.light:
|
||||
self.light = False
|
||||
|
||||
if api_get_miner_info:
|
||||
try:
|
||||
self.light = not (api_get_miner_info["Msg"]["ledstat"] == "auto")
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return self.light if self.light else False
|
||||
|
||||
@@ -14,11 +14,14 @@
|
||||
|
||||
import ipaddress
|
||||
import logging
|
||||
from typing import List, Union
|
||||
from collections import namedtuple
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
import asyncssh
|
||||
|
||||
from pyasic.API.cgminer import CGMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -26,72 +29,42 @@ from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class CGMiner(BaseMiner):
|
||||
def __init__(self, ip: str) -> None:
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
self.api = CGMinerAPI(ip)
|
||||
self.api = CGMinerAPI(ip, api_ver)
|
||||
self.api_ver = api_ver
|
||||
self.api_type = "CGMiner"
|
||||
self.uname = "root"
|
||||
self.pwd = "admin"
|
||||
self.config = None
|
||||
|
||||
async def get_model(self) -> Union[str, None]:
|
||||
"""Get miner model.
|
||||
|
||||
Returns:
|
||||
Miner model or None.
|
||||
"""
|
||||
if self.model:
|
||||
return self.model
|
||||
try:
|
||||
version_data = await self.api.devdetails()
|
||||
except APIError:
|
||||
return None
|
||||
if version_data:
|
||||
self.model = version_data["DEVDETAILS"][0]["Model"].replace("Antminer ", "")
|
||||
return self.model
|
||||
return None
|
||||
|
||||
async def get_hostname(self) -> Union[str, None]:
|
||||
"""Get miner hostname.
|
||||
|
||||
Returns:
|
||||
The hostname of the miner as a string or "?"
|
||||
"""
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
try:
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
if conn is not None:
|
||||
data = await conn.run("cat /proc/sys/kernel/hostname")
|
||||
host = data.stdout.strip()
|
||||
self.hostname = host
|
||||
return self.hostname
|
||||
else:
|
||||
return None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||
"""Send a command to the miner over ssh.
|
||||
|
||||
Parameters:
|
||||
cmd: The command to run.
|
||||
|
||||
Returns:
|
||||
Result of the command or None.
|
||||
"""
|
||||
async def send_ssh_command(self, cmd: str) -> Optional[str]:
|
||||
result = None
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
|
||||
try:
|
||||
conn = await self._get_ssh_connection()
|
||||
except (asyncssh.Error, OSError):
|
||||
return None
|
||||
|
||||
# open an ssh connection
|
||||
async with conn:
|
||||
# 3 retries
|
||||
for i in range(3):
|
||||
try:
|
||||
# run the command and get the result
|
||||
result = await conn.run(cmd)
|
||||
result = result.stdout
|
||||
|
||||
except Exception as e:
|
||||
print(f"{cmd} error: {e}")
|
||||
# if the command fails, log it
|
||||
logging.warning(f"{self} command {cmd} error: {e}")
|
||||
|
||||
# on the 3rd retry, return None
|
||||
if i == 3:
|
||||
return
|
||||
continue
|
||||
# return the result, either command output or None
|
||||
return result
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
@@ -102,131 +75,196 @@ class CGMiner(BaseMiner):
|
||||
"""Restart cgminer hashing process."""
|
||||
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
|
||||
commands = ";".join(commands)
|
||||
_ret = await self.send_ssh_command(commands)
|
||||
if isinstance(_ret, str):
|
||||
return True
|
||||
try:
|
||||
_ret = await self.send_ssh_command(commands)
|
||||
except (asyncssh.Error, OSError):
|
||||
return False
|
||||
else:
|
||||
if isinstance(_ret, str):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
"""Reboots power to the physical miner."""
|
||||
logging.debug(f"{self}: Sending reboot command.")
|
||||
_ret = await self.send_ssh_command("reboot")
|
||||
logging.debug(f"{self}: Reboot command completed.")
|
||||
if isinstance(_ret, str):
|
||||
return True
|
||||
try:
|
||||
_ret = await self.send_ssh_command("reboot")
|
||||
except (asyncssh.Error, OSError):
|
||||
return False
|
||||
else:
|
||||
logging.debug(f"{self}: Reboot command completed.")
|
||||
if isinstance(_ret, str):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def resume_mining(self) -> bool:
|
||||
commands = [
|
||||
"mkdir -p /etc/tmp/",
|
||||
'echo "*/3 * * * * /usr/bin/cgminer-monitor" > /etc/tmp/root',
|
||||
"crontab -u root /etc/tmp/root",
|
||||
"/usr/bin/cgminer-monitor >/dev/null 2>&1",
|
||||
]
|
||||
commands = ";".join(commands)
|
||||
await self.send_ssh_command(commands)
|
||||
return True
|
||||
try:
|
||||
commands = [
|
||||
"mkdir -p /etc/tmp/",
|
||||
'echo "*/3 * * * * /usr/bin/cgminer-monitor" > /etc/tmp/root',
|
||||
"crontab -u root /etc/tmp/root",
|
||||
"/usr/bin/cgminer-monitor >/dev/null 2>&1",
|
||||
]
|
||||
commands = ";".join(commands)
|
||||
await self.send_ssh_command(commands)
|
||||
except (asyncssh.Error, OSError):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
async def stop_mining(self) -> bool:
|
||||
commands = [
|
||||
"mkdir -p /etc/tmp/",
|
||||
'echo "" > /etc/tmp/root',
|
||||
"crontab -u root /etc/tmp/root",
|
||||
"killall cgminer",
|
||||
]
|
||||
commands = ";".join(commands)
|
||||
await self.send_ssh_command(commands)
|
||||
return True
|
||||
try:
|
||||
commands = [
|
||||
"mkdir -p /etc/tmp/",
|
||||
'echo "" > /etc/tmp/root',
|
||||
"crontab -u root /etc/tmp/root",
|
||||
"killall cgminer",
|
||||
]
|
||||
commands = ";".join(commands)
|
||||
await self.send_ssh_command(commands)
|
||||
except (asyncssh.Error, OSError):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
async def get_config(self) -> str:
|
||||
"""Gets the config for the miner and sets it as `self.config`.
|
||||
async def get_config(self, api_pools: dict = None) -> MinerConfig:
|
||||
# get pool data
|
||||
try:
|
||||
api_pools = await self.api.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
Returns:
|
||||
The config from `self.config`.
|
||||
"""
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
command = "cat /etc/config/cgminer"
|
||||
result = await conn.run(command, check=True)
|
||||
self.config = result.stdout
|
||||
if api_pools:
|
||||
self.config = MinerConfig().from_api(api_pools["POOLS"])
|
||||
return self.config
|
||||
|
||||
async def check_light(self) -> bool:
|
||||
if not self.light:
|
||||
self.light = False
|
||||
return self.light
|
||||
|
||||
async def fault_light_off(self) -> bool:
|
||||
return False
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
return False
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
return None
|
||||
|
||||
async def get_mac(self) -> str:
|
||||
return "00:00:00:00:00:00"
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
return False
|
||||
|
||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||
"""Get data from the miner.
|
||||
##################################################
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
|
||||
"""
|
||||
data = MinerData(
|
||||
ip=str(self.ip),
|
||||
ideal_chips=self.nominal_chips * self.ideal_hashboards,
|
||||
ideal_hashboards=self.ideal_hashboards,
|
||||
async def get_mac(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_model(self, api_devdetails: dict = None) -> Optional[str]:
|
||||
if self.model:
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
|
||||
if not api_devdetails:
|
||||
try:
|
||||
api_devdetails = await self.api.devdetails()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_devdetails:
|
||||
try:
|
||||
self.model = api_devdetails["DEVDETAILS"][0]["Model"].replace(
|
||||
"Antminer ", ""
|
||||
)
|
||||
logging.debug(f"Found model for {self.ip}: {self.model}")
|
||||
return self.model
|
||||
except (TypeError, IndexError, KeyError):
|
||||
pass
|
||||
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
return miner_version(
|
||||
api_ver=await self.get_api_ver(api_version=api_version),
|
||||
fw_ver=await self.get_fw_ver(api_version=api_version),
|
||||
)
|
||||
|
||||
board_offset = -1
|
||||
fan_offset = -1
|
||||
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if self.api_ver:
|
||||
return self.api_ver
|
||||
|
||||
model = await self.get_model()
|
||||
hostname = await self.get_hostname()
|
||||
mac = await self.get_mac()
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if model:
|
||||
data.model = model
|
||||
if api_version:
|
||||
try:
|
||||
self.api_ver = api_version["VERSION"][0]["API"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
return self.api_ver
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if self.fw_ver:
|
||||
return self.fw_ver
|
||||
|
||||
data.fault_light = await self.check_light()
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary", "pools", "stats", allow_warning=allow_warning
|
||||
)
|
||||
if miner_data:
|
||||
break
|
||||
if api_version:
|
||||
try:
|
||||
self.fw_ver = api_version["VERSION"][0]["CGMiner"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
if not miner_data:
|
||||
return data
|
||||
return self.fw_ver
|
||||
|
||||
summary = miner_data.get("summary")[0]
|
||||
pools = miner_data.get("pools")[0]
|
||||
stats = miner_data.get("stats")[0]
|
||||
async def get_hostname(self) -> Optional[str]:
|
||||
try:
|
||||
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
|
||||
except (asyncssh.Error, OSError):
|
||||
return None
|
||||
if hn:
|
||||
self.hostname = hn
|
||||
return self.hostname
|
||||
|
||||
if summary:
|
||||
hr = summary.get("SUMMARY")
|
||||
if hr:
|
||||
if len(hr) > 0:
|
||||
hr = hr[0].get("GHS av")
|
||||
if hr:
|
||||
data.hashrate = round(hr / 1000, 2)
|
||||
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||
# get hr from API
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if stats:
|
||||
boards = stats.get("STATS")
|
||||
if boards:
|
||||
if len(boards) > 0:
|
||||
if api_summary:
|
||||
try:
|
||||
return round(
|
||||
float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2
|
||||
)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
hashboards = []
|
||||
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_stats:
|
||||
try:
|
||||
board_offset = -1
|
||||
boards = api_stats["STATS"]
|
||||
if len(boards) > 1:
|
||||
for board_num in range(1, 16, 5):
|
||||
for _b_num in range(5):
|
||||
b = boards[1].get(f"chain_acn{board_num + _b_num}")
|
||||
@@ -236,7 +274,6 @@ class CGMiner(BaseMiner):
|
||||
if board_offset == -1:
|
||||
board_offset = 1
|
||||
|
||||
env_temp_list = []
|
||||
for i in range(board_offset, board_offset + self.ideal_hashboards):
|
||||
hashboard = HashBoard(
|
||||
slot=i - board_offset, expected_chips=self.nominal_chips
|
||||
@@ -260,78 +297,104 @@ class CGMiner(BaseMiner):
|
||||
hashboard.missing = False
|
||||
if (not chips) or (not chips > 0):
|
||||
hashboard.missing = True
|
||||
data.hashboards.append(hashboard)
|
||||
hashboards.append(hashboard)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if f"temp_pcb{i}" in boards[1].keys():
|
||||
env_temp = boards[1][f"temp_pcb{i}"].split("-")[0]
|
||||
if not env_temp == 0:
|
||||
env_temp_list.append(int(env_temp))
|
||||
if not env_temp_list == []:
|
||||
data.env_temp = round(sum(env_temp_list) / len(env_temp_list))
|
||||
return hashboards
|
||||
|
||||
if stats:
|
||||
temp = stats.get("STATS")
|
||||
if temp:
|
||||
if len(temp) > 1:
|
||||
for fan_num in range(1, 8, 4):
|
||||
for _f_num in range(4):
|
||||
f = temp[1].get(f"fan{fan_num + _f_num}")
|
||||
if f and not f == 0 and fan_offset == -1:
|
||||
fan_offset = fan_num
|
||||
if fan_offset == -1:
|
||||
fan_offset = 1
|
||||
for fan in range(self.fan_count):
|
||||
setattr(
|
||||
data, f"fan_{fan + 1}", temp[1].get(f"fan{fan_offset+fan}")
|
||||
)
|
||||
async def get_env_temp(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
if pools:
|
||||
pool_1 = None
|
||||
pool_2 = None
|
||||
pool_1_user = None
|
||||
pool_2_user = None
|
||||
pool_1_quota = 1
|
||||
pool_2_quota = 1
|
||||
quota = 0
|
||||
for pool in pools.get("POOLS"):
|
||||
if not pool_1_user:
|
||||
pool_1_user = pool.get("User")
|
||||
pool_1 = pool["URL"]
|
||||
if pool.get("Quota"):
|
||||
pool_2_quota = pool.get("Quota")
|
||||
elif not pool_2_user:
|
||||
pool_2_user = pool.get("User")
|
||||
pool_2 = pool["URL"]
|
||||
if pool.get("Quota"):
|
||||
pool_2_quota = pool.get("Quota")
|
||||
if not pool.get("User") == pool_1_user:
|
||||
if not pool_2_user == pool.get("User"):
|
||||
pool_2_user = pool.get("User")
|
||||
pool_2 = pool["URL"]
|
||||
if pool.get("Quota"):
|
||||
pool_2_quota = pool.get("Quota")
|
||||
if pool_2_user and not pool_2_user == pool_1_user:
|
||||
quota = f"{pool_1_quota}/{pool_2_quota}"
|
||||
async def get_wattage(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
if pool_1:
|
||||
pool_1 = pool_1.replace("stratum+tcp://", "").replace(
|
||||
"stratum2+tcp://", ""
|
||||
)
|
||||
data.pool_1_url = pool_1
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
if pool_1_user:
|
||||
data.pool_1_user = pool_1_user
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if pool_2:
|
||||
pool_2 = pool_2.replace("stratum+tcp://", "").replace(
|
||||
"stratum2+tcp://", ""
|
||||
)
|
||||
data.pool_2_url = pool_2
|
||||
fans_data = [Fan(), Fan(), Fan(), Fan()]
|
||||
if api_stats:
|
||||
try:
|
||||
fan_offset = -1
|
||||
|
||||
if pool_2_user:
|
||||
data.pool_2_user = pool_2_user
|
||||
for fan_num in range(1, 8, 4):
|
||||
for _f_num in range(4):
|
||||
f = api_stats["STATS"][1].get(f"fan{fan_num + _f_num}")
|
||||
if f and not f == 0 and fan_offset == -1:
|
||||
fan_offset = fan_num
|
||||
if fan_offset == -1:
|
||||
fan_offset = 1
|
||||
|
||||
if quota:
|
||||
data.pool_split = str(quota)
|
||||
for fan in range(self.fan_count):
|
||||
fans_data[fan] = Fan(
|
||||
api_stats["STATS"][1].get(f"fan{fan_offset+fan}")
|
||||
)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
return fans_data
|
||||
|
||||
return data
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
|
||||
if not api_pools:
|
||||
try:
|
||||
api_pools = await self.api.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_pools:
|
||||
try:
|
||||
pools = {}
|
||||
for i, pool in enumerate(api_pools["POOLS"]):
|
||||
pools[f"pool_{i + 1}_url"] = (
|
||||
pool["URL"]
|
||||
.replace("stratum+tcp://", "")
|
||||
.replace("stratum2+tcp://", "")
|
||||
)
|
||||
pools[f"pool_{i + 1}_user"] = pool["User"]
|
||||
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
|
||||
|
||||
groups.append(pools)
|
||||
except KeyError:
|
||||
pass
|
||||
return groups
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_fault_light(self) -> bool:
|
||||
return False
|
||||
|
||||
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
|
||||
# X19 method, not sure compatibility
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_stats:
|
||||
try:
|
||||
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
|
||||
try:
|
||||
rate_unit = api_stats["STATS"][1]["rate_unit"]
|
||||
except KeyError:
|
||||
rate_unit = "GH"
|
||||
if rate_unit == "GH":
|
||||
return round(ideal_rate / 1000, 2)
|
||||
if rate_unit == "MH":
|
||||
return round(ideal_rate / 1000000, 2)
|
||||
else:
|
||||
return round(ideal_rate, 2)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
269
pyasic/miners/_backends/cgminer_avalon.py
Normal file
269
pyasic/miners/_backends/cgminer_avalon.py
Normal file
@@ -0,0 +1,269 @@
|
||||
# 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
|
||||
import logging
|
||||
import re
|
||||
from collections import namedtuple
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
from pyasic.API.cgminer import CGMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners._backends import CGMiner
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.settings import PyasicSettings
|
||||
|
||||
|
||||
class CGMinerAvalon(CGMiner):
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ip
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
try:
|
||||
data = await self.api.ascset(0, "led", "1-1")
|
||||
except APIError:
|
||||
return False
|
||||
if data["STATUS"][0]["Msg"] == "ASC 0 set OK":
|
||||
return True
|
||||
return False
|
||||
|
||||
async def fault_light_off(self) -> bool:
|
||||
try:
|
||||
data = await self.api.ascset(0, "led", "1-0")
|
||||
except APIError:
|
||||
return False
|
||||
if data["STATUS"][0]["Msg"] == "ASC 0 set OK":
|
||||
return True
|
||||
return False
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
try:
|
||||
data = await self.api.restart()
|
||||
except APIError:
|
||||
return False
|
||||
|
||||
try:
|
||||
if data["STATUS"] == "RESTART":
|
||||
return True
|
||||
except KeyError:
|
||||
return False
|
||||
return False
|
||||
|
||||
async def stop_mining(self) -> bool:
|
||||
return False
|
||||
|
||||
async def resume_mining(self) -> bool:
|
||||
return False
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
"""Configures miner with yaml config."""
|
||||
return None
|
||||
logging.debug(f"{self}: Sending config.") # noqa - This doesnt work...
|
||||
conf = config.as_avalon(user_suffix=user_suffix)
|
||||
try:
|
||||
data = await self.api.ascset(
|
||||
0, "setpool", f"root,root,{conf}"
|
||||
) # this should work but doesn't
|
||||
except APIError:
|
||||
pass
|
||||
# return data
|
||||
|
||||
@staticmethod
|
||||
def parse_stats(stats):
|
||||
_stats_items = re.findall(".+?\[*?]", stats)
|
||||
stats_items = []
|
||||
stats_dict = {}
|
||||
for item in _stats_items:
|
||||
if ":" in item:
|
||||
data = item.replace("]", "").split("[")
|
||||
data_list = [i.split(": ") for i in data[1].strip().split(", ")]
|
||||
data_dict = {}
|
||||
for key, val in [tuple(item) for item in data_list]:
|
||||
data_dict[key] = val
|
||||
raw_data = [data[0].strip(), data_dict]
|
||||
else:
|
||||
raw_data = [
|
||||
value
|
||||
for value in item.replace("[", " ")
|
||||
.replace("]", " ")
|
||||
.split(" ")[:-1]
|
||||
if value != ""
|
||||
]
|
||||
if len(raw_data) == 1:
|
||||
raw_data.append("")
|
||||
if raw_data[0] == "":
|
||||
raw_data = raw_data[1:]
|
||||
|
||||
if len(raw_data) == 2:
|
||||
stats_dict[raw_data[0]] = raw_data[1]
|
||||
else:
|
||||
stats_dict[raw_data[0]] = raw_data[1:]
|
||||
stats_items.append(raw_data)
|
||||
|
||||
return stats_dict
|
||||
|
||||
##################################################
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
async def get_mac(self, api_version: dict = None) -> Optional[str]:
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_version:
|
||||
try:
|
||||
base_mac = api_version["VERSION"][0]["MAC"]
|
||||
base_mac = base_mac.upper()
|
||||
mac = ":".join(
|
||||
[base_mac[i : (i + 2)] for i in range(0, len(base_mac), 2)]
|
||||
)
|
||||
return mac
|
||||
except (KeyError, ValueError):
|
||||
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:]}"
|
||||
|
||||
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2)
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
hashboards = [
|
||||
HashBoard(slot=i, expected_chips=self.nominal_chips)
|
||||
for i in range(self.ideal_hashboards)
|
||||
]
|
||||
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_stats:
|
||||
try:
|
||||
stats_data = api_stats[0].get("STATS")
|
||||
if stats_data:
|
||||
for key in stats_data[0].keys():
|
||||
if key.startswith("MM ID"):
|
||||
raw_data = self.parse_stats(stats_data[0][key])
|
||||
for board in range(self.ideal_hashboards):
|
||||
chip_temp = raw_data.get("MTmax")
|
||||
if chip_temp:
|
||||
hashboards[board].chip_temp = chip_temp[board]
|
||||
|
||||
temp = raw_data.get("MTavg")
|
||||
if temp:
|
||||
hashboards[board].temp = temp[board]
|
||||
|
||||
chips = raw_data.get(f"PVT_T{board}")
|
||||
if chips:
|
||||
hashboards[board].chips = len(
|
||||
[item for item in chips if not item == "0"]
|
||||
)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_env_temp(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def get_wattage(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
fans_data = [Fan(), Fan(), Fan(), Fan()]
|
||||
if api_stats:
|
||||
try:
|
||||
stats_data = api_stats[0].get("STATS")
|
||||
if stats_data:
|
||||
for key in stats_data[0].keys():
|
||||
if key.startswith("MM ID"):
|
||||
raw_data = self.parse_stats(stats_data[0][key])
|
||||
for fan in range(self.fan_count):
|
||||
fans_data[fan] = Fan(int(raw_data[f"Fan{fan + 1}"]))
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
return fans_data
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
|
||||
if not api_pools:
|
||||
try:
|
||||
api_pools = await self.api.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_pools:
|
||||
try:
|
||||
pools = {}
|
||||
for i, pool in enumerate(api_pools["POOLS"]):
|
||||
pools[f"pool_{i + 1}_url"] = (
|
||||
pool["URL"]
|
||||
.replace("stratum+tcp://", "")
|
||||
.replace("stratum2+tcp://", "")
|
||||
)
|
||||
pools[f"pool_{i + 1}_user"] = pool["User"]
|
||||
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
|
||||
|
||||
groups.append(pools)
|
||||
except KeyError:
|
||||
pass
|
||||
return groups
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_fault_light(self) -> bool:
|
||||
if self.light:
|
||||
return self.light
|
||||
try:
|
||||
data = await self.api.ascset(0, "led", "1-255")
|
||||
except APIError:
|
||||
return False
|
||||
if data["STATUS"][0]["Msg"] == "ASC 0 set info: LED[1]":
|
||||
return True
|
||||
return False
|
||||
@@ -18,46 +18,9 @@ from pyasic.miners._backends import BMMiner
|
||||
|
||||
|
||||
class Hiveon(BMMiner):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||
super().__init__(ip, api_ver)
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
self.api_type = "Hiveon"
|
||||
self.uname = "root"
|
||||
self.pwd = "admin"
|
||||
|
||||
async def get_board_info(self) -> dict:
|
||||
"""Gets data on each board and chain in the miner."""
|
||||
board_stats = await self.api.stats()
|
||||
stats = board_stats["STATS"][1]
|
||||
boards = {}
|
||||
board_chains = {0: [2, 9, 10], 1: [3, 11, 12], 2: [4, 13, 14]}
|
||||
for idx, board in enumerate(board_chains):
|
||||
boards[board] = []
|
||||
for chain in board_chains[board]:
|
||||
count = stats[f"chain_acn{chain}"]
|
||||
chips = stats[f"chain_acs{chain}"].replace(" ", "")
|
||||
if not count == 18 or "x" in chips:
|
||||
nominal = False
|
||||
else:
|
||||
nominal = True
|
||||
boards[board].append(
|
||||
{
|
||||
"chain": chain,
|
||||
"chip_count": count,
|
||||
"chip_status": chips,
|
||||
"nominal": nominal,
|
||||
}
|
||||
)
|
||||
return boards
|
||||
|
||||
async def get_bad_boards(self) -> dict:
|
||||
"""Checks for and provides list of non working boards."""
|
||||
boards = await self.get_board_info()
|
||||
bad_boards = {}
|
||||
for board in boards.keys():
|
||||
for chain in boards[board]:
|
||||
if not chain["chip_count"] == 18 or "x" in chain["chip_status"]:
|
||||
if board not in bad_boards.keys():
|
||||
bad_boards[board] = []
|
||||
bad_boards[board].append(chain)
|
||||
return bad_boards
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S17(BaseMiner):
|
||||
class S17(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S17Plus(BaseMiner):
|
||||
class S17Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S17Pro(BaseMiner):
|
||||
class S17Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S17e(BaseMiner):
|
||||
class S17e(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class T17(BaseMiner):
|
||||
class T17(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class T17Plus(BaseMiner):
|
||||
class T17Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class T17e(BaseMiner):
|
||||
class T17e(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19(BaseMiner):
|
||||
class S19(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19Pro(BaseMiner):
|
||||
class S19Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19XP(BaseMiner):
|
||||
class S19XP(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19a(BaseMiner):
|
||||
class S19a(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
24
pyasic/miners/_types/antminer/X19/S19a_Pro.py
Normal file
24
pyasic/miners/_types/antminer/X19/S19a_Pro.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Copyright 2022 Upstream Data Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19aPro(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "S19a Pro"
|
||||
self.nominal_chips = 100
|
||||
self.fan_count = 4
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19j(BaseMiner):
|
||||
class S19j(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S19jPro(BaseMiner):
|
||||
class S19jPro(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class T19(BaseMiner):
|
||||
class T19(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -16,6 +16,7 @@ from .S19 import S19
|
||||
from .S19_Pro import S19Pro
|
||||
from .S19_XP import S19XP
|
||||
from .S19a import S19a
|
||||
from .S19a_Pro import S19aPro
|
||||
from .S19j import S19j
|
||||
from .S19j_Pro import S19jPro
|
||||
from .T19 import T19
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S9(BaseMiner):
|
||||
class S9(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class S9i(BaseMiner):
|
||||
class S9i(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AntMiner
|
||||
|
||||
|
||||
class T9(BaseMiner):
|
||||
class T9(AntMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon1026(BaseMiner):
|
||||
class Avalon1026(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon1047(BaseMiner):
|
||||
class Avalon1047(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon1066(BaseMiner):
|
||||
class Avalon1066(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon721(BaseMiner):
|
||||
class Avalon721(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 721"
|
||||
self.chip_count = 18 # This miner has 4 boards totaling 72
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 18
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon741(BaseMiner):
|
||||
class Avalon741(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 741"
|
||||
self.chip_count = 22 # This miner has 4 boards totaling 88
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 22
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon761(BaseMiner):
|
||||
class Avalon761(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 761"
|
||||
self.chip_count = 18 # This miner has 4 boards totaling 72
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 18
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon821(BaseMiner):
|
||||
class Avalon821(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 821"
|
||||
self.chip_count = 26 # This miner has 4 boards totaling 104
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 26
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon841(BaseMiner):
|
||||
class Avalon841(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 841"
|
||||
self.chip_count = 26 # This miner has 4 boards totaling 104
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 26
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon851(BaseMiner):
|
||||
class Avalon851(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 851"
|
||||
self.chip_count = 26 # This miner has 4 boards totaling 104
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 26
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import AvalonMiner
|
||||
|
||||
|
||||
class Avalon921(BaseMiner):
|
||||
class Avalon921(AvalonMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "Avalon 921"
|
||||
self.chip_count = 26 # This miner has 4 boards totaling 104
|
||||
self.fan_count = 1 # also only 1 fan
|
||||
self.ideal_hashboards = 4
|
||||
self.chip_count = 26
|
||||
self.fan_count = 1
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import InnosiliconMiner
|
||||
|
||||
|
||||
class InnosiliconT3HPlus(BaseMiner):
|
||||
class InnosiliconT3HPlus(InnosiliconMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
39
pyasic/miners/_types/makes.py
Normal file
39
pyasic/miners/_types/makes.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# 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.base import BaseMiner
|
||||
|
||||
|
||||
class WhatsMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.make = "WhatsMiner"
|
||||
|
||||
|
||||
class AntMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.make = "AntMiner"
|
||||
|
||||
|
||||
class AvalonMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.make = "AvalonMiner"
|
||||
|
||||
|
||||
class InnosiliconMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.make = "Innosilicon"
|
||||
@@ -12,19 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M20"
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20V10(BaseMiner):
|
||||
class M20V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,19 +12,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M20S(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M20S"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20SV10(BaseMiner):
|
||||
class M20SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -33,10 +26,22 @@ class M20SV10(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20SV20(BaseMiner):
|
||||
class M20SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M20S V20"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M20SV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
@@ -12,13 +12,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M20SPlus(BaseMiner):
|
||||
class M20SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M20S+"
|
||||
self.nominal_chips = 66
|
||||
self.model = "M20S+ V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M20S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -12,13 +12,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M21(BaseMiner):
|
||||
class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21"
|
||||
self.nominal_chips = 105
|
||||
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.fan_count = 2
|
||||
|
||||
@@ -12,19 +12,21 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M21S(BaseMiner):
|
||||
class M21SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21S"
|
||||
self.model = "M21S V20"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M21SV60(BaseMiner):
|
||||
class M21SV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -33,10 +35,13 @@ class M21SV60(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M21SV20(BaseMiner):
|
||||
class M21SV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21S V20"
|
||||
self.nominal_chips = 66
|
||||
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.fan_count = 2
|
||||
|
||||
@@ -12,13 +12,18 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M21SPlus(BaseMiner):
|
||||
class M21SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21S+"
|
||||
self.nominal_chips = 105
|
||||
self.model = "M21S+ V20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M21S+ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
29
pyasic/miners/_types/whatsminer/M2X/M29.py
Normal file
29
pyasic/miners/_types/whatsminer/M2X/M29.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M29V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
@@ -12,9 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .M20 import M20, M20V10
|
||||
from .M20S import M20S, M20SV10, M20SV20
|
||||
from .M20S_Plus import M20SPlus
|
||||
from .M21 import M21
|
||||
from .M21S import M21S, M21SV20, M21SV60
|
||||
from .M21S_Plus import M21SPlus
|
||||
from .M20 import M20V10
|
||||
from .M20S import M20SV10, M20SV20, M20SV30
|
||||
from .M20S_Plus import M20SPlusV30
|
||||
from .M21 import M21V10
|
||||
from .M21S import M21SV20, M21SV60, M21SV70
|
||||
from .M21S_Plus import M21SPlusV20
|
||||
from .M29 import M29V10
|
||||
|
||||
41
pyasic/miners/_types/whatsminer/M3X/M30.py
Normal file
41
pyasic/miners/_types/whatsminer/M3X/M30.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
@@ -12,19 +12,60 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M30S(BaseMiner):
|
||||
class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S"
|
||||
self.nominal_chips = 148
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SV50(BaseMiner):
|
||||
class M30SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -33,16 +74,52 @@ class M30SV50(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVG20(BaseMiner):
|
||||
class M30SV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VG20"
|
||||
self.nominal_chips = 70
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE20(BaseMiner):
|
||||
class M30SV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S V70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VE10"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -51,10 +128,223 @@ class M30SVE20(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE10(BaseMiner):
|
||||
class M30SVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VE10"
|
||||
self.nominal_chips = 105
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VE60"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VE70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVF10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVF20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVF30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVG10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VG10"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVG20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VG20"
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SVH60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VH60"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30SVH60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVI20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
@@ -12,28 +12,144 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M30SPlus(BaseMiner):
|
||||
class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+"
|
||||
self.nominal_chips = 156
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG60(BaseMiner):
|
||||
class M30SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VG60"
|
||||
self.nominal_chips = 86
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE40(BaseMiner):
|
||||
class M30SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -42,10 +158,214 @@ class M30SPlusVE40(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVF20(BaseMiner):
|
||||
class M30SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE90"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE100(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE100"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VF20"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VG40"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VG60"
|
||||
self.nominal_chips = 86
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VH30"
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VH60"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -12,19 +12,84 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M30SPlusPlus(BaseMiner):
|
||||
class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++"
|
||||
self.nominal_chips = 111
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVG30(BaseMiner):
|
||||
class M30SPlusPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVF40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -33,7 +98,7 @@ class M30SPlusPlusVG30(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVG40(BaseMiner):
|
||||
class M30SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -42,10 +107,154 @@ class M30SPlusPlusVG40(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH60(BaseMiner):
|
||||
class M30SPlusPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH60"
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVH100(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VH100"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VH100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVJ20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VJ20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++ VJ30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S++ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
41
pyasic/miners/_types/whatsminer/M3X/M31.py
Normal file
41
pyasic/miners/_types/whatsminer/M3X/M31.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
29
pyasic/miners/_types/whatsminer/M3X/M31H.py
Normal file
29
pyasic/miners/_types/whatsminer/M3X/M31H.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
@@ -12,13 +12,138 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M31S(BaseMiner):
|
||||
class M31SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S"
|
||||
# TODO: Add chip count for this miner (per board) - self.nominal_chips
|
||||
self.model = "M31S V10"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S V20"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S V60"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S V70"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S V80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SV80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SV90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S VE10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SVE10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SVE20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S VE30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M31SVE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
53
pyasic/miners/_types/whatsminer/M3X/M31SE.py
Normal file
53
pyasic/miners/_types/whatsminer/M3X/M31SE.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M31SEV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SEV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SEV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
@@ -12,28 +12,36 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M31SPlus(BaseMiner):
|
||||
class M31SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+"
|
||||
self.nominal_chips = 78
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE20(BaseMiner):
|
||||
class M31SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE20"
|
||||
self.nominal_chips = 78
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV30(BaseMiner):
|
||||
class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -42,7 +50,7 @@ class M31SPlusV30(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV40(BaseMiner):
|
||||
class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -51,7 +59,19 @@ class M31SPlusV40(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV60(BaseMiner):
|
||||
class M31SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -60,7 +80,7 @@ class M31SPlusV60(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV80(BaseMiner):
|
||||
class M31SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
@@ -69,10 +89,166 @@ class M31SPlusV80(BaseMiner):
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV90(BaseMiner):
|
||||
class M31SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V90"
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V100"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE20"
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ 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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ 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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V30"
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V40"
|
||||
self.nominal_chips = 123
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -12,19 +12,24 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M32(BaseMiner):
|
||||
class M32V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M32"
|
||||
self.nominal_chips = 74
|
||||
self.model = "M32 V10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M32V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M32V20(BaseMiner):
|
||||
class M32V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M32S(BaseMiner):
|
||||
class M32S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
53
pyasic/miners/_types/whatsminer/M3X/M33.py
Normal file
53
pyasic/miners/_types/whatsminer/M3X/M33.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M33V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
|
||||
|
||||
class M33V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
|
||||
|
||||
class M33V30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M3X/M33S.py
Normal file
29
pyasic/miners/_types/whatsminer/M3X/M33S.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M33SVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
41
pyasic/miners/_types/whatsminer/M3X/M33S_Plus.py
Normal file
41
pyasic/miners/_types/whatsminer/M3X/M33S_Plus.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M33SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
|
||||
|
||||
class M33SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M33S+ 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.fan_count = 0
|
||||
53
pyasic/miners/_types/whatsminer/M3X/M33S_Plus_Plus.py
Normal file
53
pyasic/miners/_types/whatsminer/M3X/M33S_Plus_Plus.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M33SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
|
||||
|
||||
class M33SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M33S++ 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.fan_count = 0
|
||||
|
||||
|
||||
class M33SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M33S++ VG40"
|
||||
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.fan_count = 0
|
||||
@@ -12,20 +12,10 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M34SPlus(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M34S+"
|
||||
self.ideal_hashboards = 4
|
||||
self.nominal_chips = 116
|
||||
self.fan_count = 0
|
||||
|
||||
|
||||
class M34SPlusVE10(BaseMiner):
|
||||
class M34SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
|
||||
29
pyasic/miners/_types/whatsminer/M3X/M36S.py
Normal file
29
pyasic/miners/_types/whatsminer/M3X/M36S.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M36SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M3X/M36S_Plus.py
Normal file
29
pyasic/miners/_types/whatsminer/M3X/M36S_Plus.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M3X/M36S_Plus_Plus.py
Normal file
29
pyasic/miners/_types/whatsminer/M3X/M36S_Plus_Plus.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M36SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M3X/M39.py
Normal file
29
pyasic/miners/_types/whatsminer/M3X/M39.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M39V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
@@ -12,24 +12,140 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .M30S import M30S, M30SV50, M30SVE10, M30SVE20, M30SVG20
|
||||
from .M30S_Plus import M30SPlus, M30SPlusVE40, M30SPlusVF20, M30SPlusVG60
|
||||
from .M30 import M30V10, M30V20
|
||||
from .M30S import (
|
||||
M30SV10,
|
||||
M30SV20,
|
||||
M30SV30,
|
||||
M30SV40,
|
||||
M30SV50,
|
||||
M30SV60,
|
||||
M30SV70,
|
||||
M30SV80,
|
||||
M30SVE10,
|
||||
M30SVE20,
|
||||
M30SVE30,
|
||||
M30SVE40,
|
||||
M30SVE50,
|
||||
M30SVE60,
|
||||
M30SVE70,
|
||||
M30SVF10,
|
||||
M30SVF20,
|
||||
M30SVF30,
|
||||
M30SVG10,
|
||||
M30SVG20,
|
||||
M30SVG30,
|
||||
M30SVG40,
|
||||
M30SVH10,
|
||||
M30SVH20,
|
||||
M30SVH30,
|
||||
M30SVH40,
|
||||
M30SVH50,
|
||||
M30SVH60,
|
||||
M30SVI20,
|
||||
)
|
||||
from .M30S_Plus import (
|
||||
M30SPlusV10,
|
||||
M30SPlusV20,
|
||||
M30SPlusV30,
|
||||
M30SPlusV40,
|
||||
M30SPlusV50,
|
||||
M30SPlusV60,
|
||||
M30SPlusV70,
|
||||
M30SPlusV80,
|
||||
M30SPlusV90,
|
||||
M30SPlusV100,
|
||||
M30SPlusVE30,
|
||||
M30SPlusVE40,
|
||||
M30SPlusVE50,
|
||||
M30SPlusVE60,
|
||||
M30SPlusVE70,
|
||||
M30SPlusVE80,
|
||||
M30SPlusVE90,
|
||||
M30SPlusVE100,
|
||||
M30SPlusVF20,
|
||||
M30SPlusVF30,
|
||||
M30SPlusVG30,
|
||||
M30SPlusVG40,
|
||||
M30SPlusVG50,
|
||||
M30SPlusVG60,
|
||||
M30SPlusVH10,
|
||||
M30SPlusVH20,
|
||||
M30SPlusVH30,
|
||||
M30SPlusVH40,
|
||||
M30SPlusVH50,
|
||||
M30SPlusVH60,
|
||||
M36SPlusVG30,
|
||||
)
|
||||
from .M30S_Plus_Plus import (
|
||||
M30SPlusPlus,
|
||||
M30SPlusPlusV10,
|
||||
M30SPlusPlusV20,
|
||||
M30SPlusPlusVE30,
|
||||
M30SPlusPlusVE40,
|
||||
M30SPlusPlusVE50,
|
||||
M30SPlusPlusVF40,
|
||||
M30SPlusPlusVG30,
|
||||
M30SPlusPlusVG40,
|
||||
M30SPlusPlusVG50,
|
||||
M30SPlusPlusVH10,
|
||||
M30SPlusPlusVH20,
|
||||
M30SPlusPlusVH30,
|
||||
M30SPlusPlusVH40,
|
||||
M30SPlusPlusVH50,
|
||||
M30SPlusPlusVH60,
|
||||
M30SPlusPlusVH70,
|
||||
M30SPlusPlusVH80,
|
||||
M30SPlusPlusVH90,
|
||||
M30SPlusPlusVH100,
|
||||
M30SPlusPlusVJ20,
|
||||
M30SPlusPlusVJ30,
|
||||
)
|
||||
from .M31 import M31V10, M31V20
|
||||
from .M31H import M31HV40
|
||||
from .M31S import (
|
||||
M31SV10,
|
||||
M31SV20,
|
||||
M31SV30,
|
||||
M31SV40,
|
||||
M31SV50,
|
||||
M31SV60,
|
||||
M31SV70,
|
||||
M31SV80,
|
||||
M31SV90,
|
||||
M31SVE10,
|
||||
M31SVE20,
|
||||
M31SVE30,
|
||||
)
|
||||
from .M31S import M31S
|
||||
from .M31S_Plus import (
|
||||
M31SPlus,
|
||||
M31SPlusV10,
|
||||
M31SPlusV20,
|
||||
M31SPlusV30,
|
||||
M31SPlusV40,
|
||||
M31SPlusV50,
|
||||
M31SPlusV60,
|
||||
M31SPlusV80,
|
||||
M31SPlusV90,
|
||||
M31SPlusV100,
|
||||
M31SPlusVE10,
|
||||
M31SPlusVE20,
|
||||
M31SPlusVE30,
|
||||
M31SPlusVE40,
|
||||
M31SPlusVE50,
|
||||
M31SPlusVE60,
|
||||
M31SPlusVE80,
|
||||
M31SPlusVF20,
|
||||
M31SPlusVF30,
|
||||
M31SPlusVG20,
|
||||
M31SPlusVG30,
|
||||
)
|
||||
from .M32 import M32, M32V20
|
||||
from .M32S import M32S
|
||||
from .M34S_Plus import M34SPlus, M34SPlusVE10
|
||||
from .M31SE import M31SEV10, M31SEV20, M31SEV30
|
||||
from .M32 import M32V10, M32V20
|
||||
from .M33 import M33V10, M33V20, M33V30
|
||||
from .M33S import M33SVG30
|
||||
from .M33S_Plus import 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
|
||||
|
||||
@@ -12,22 +12,147 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from pyasic.miners.base import BaseMiner
|
||||
import warnings
|
||||
|
||||
from pyasic.miners._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M50(BaseMiner):
|
||||
class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50"
|
||||
self.nominal_chips = 105
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M50VH50(BaseMiner):
|
||||
class M50VH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M50VH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VH20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M50VH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VH40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VH50"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VH60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VH60"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VH70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VH70"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VH80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VH80"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VH80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VJ10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VJ10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VJ10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VJ20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VJ20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50VJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50 VJ30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50 VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
113
pyasic/miners/_types/whatsminer/M5X/M50S.py
Normal file
113
pyasic/miners/_types/whatsminer/M5X/M50S.py
Normal file
@@ -0,0 +1,113 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M50SVJ10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S VJ10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VJ10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50SVJ20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S VJ20"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50SVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S VJ30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50SVH10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S VH10"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50SVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M50SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M50SVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S VH40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50SVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S VH50"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S VH50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
53
pyasic/miners/_types/whatsminer/M5X/M50S_Plus.py
Normal file
53
pyasic/miners/_types/whatsminer/M5X/M50S_Plus.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# 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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M50SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 2
|
||||
|
||||
|
||||
class M50SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S+ VH40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S+ VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M50SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M50S+ VJ30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M50S+ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 2
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M53.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M53.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M53VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M53S.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M53S.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M53SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M53S VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M53S VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M53S_Plus.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M53S_Plus.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M53SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M53S+ VJ30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M53S+ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M56.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M56.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M56VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
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.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M56S.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M56S.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M56SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M56S VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M56S VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M56S_Plus.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M56S_Plus.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M56SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M56S+ VJ30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M56S+ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 0
|
||||
29
pyasic/miners/_types/whatsminer/M5X/M59.py
Normal file
29
pyasic/miners/_types/whatsminer/M5X/M59.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._types.makes import WhatsMiner
|
||||
|
||||
|
||||
class M59VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M59 VH30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M59 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.fan_count = 0
|
||||
@@ -12,4 +12,35 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from .M50 import M50, M50VH50
|
||||
from .M50 import (
|
||||
M50VG30,
|
||||
M50VH10,
|
||||
M50VH20,
|
||||
M50VH30,
|
||||
M50VH40,
|
||||
M50VH50,
|
||||
M50VH60,
|
||||
M50VH70,
|
||||
M50VH80,
|
||||
M50VJ10,
|
||||
M50VJ20,
|
||||
M50VJ30,
|
||||
)
|
||||
from .M50S import (
|
||||
M50SVH10,
|
||||
M50SVH20,
|
||||
M50SVH30,
|
||||
M50SVH40,
|
||||
M50SVH50,
|
||||
M50SVJ10,
|
||||
M50SVJ20,
|
||||
M50SVJ30,
|
||||
)
|
||||
from .M50S_Plus import M50SPlusVH30, M50SPlusVH40, M50SPlusVJ30
|
||||
from .M53 import M53VH30
|
||||
from .M53S import M53SVH30
|
||||
from .M53S_Plus import M53SPlusVJ30
|
||||
from .M56 import M56VH30
|
||||
from .M56S import M56SVH30
|
||||
from .M56S_Plus import M56SPlusVJ30
|
||||
from .M59 import M59VH30
|
||||
|
||||
@@ -18,6 +18,4 @@ from .X17 import BMMinerX17
|
||||
|
||||
|
||||
class BMMinerS17(BMMinerX17, S17):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
pass
|
||||
|
||||
@@ -18,6 +18,4 @@ from .X17 import BMMinerX17
|
||||
|
||||
|
||||
class BMMinerS17Plus(BMMinerX17, S17Plus):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
pass
|
||||
|
||||
@@ -18,6 +18,4 @@ from .X17 import BMMinerX17
|
||||
|
||||
|
||||
class BMMinerS17Pro(BMMinerX17, S17Pro):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
pass
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user