Compare commits

...

26 Commits

Author SHA1 Message Date
UpstreamData
d8b938cd5b version: bump version number. 2023-11-27 10:39:10 -07:00
UpstreamData
47d76e325a docs: update docs to include new information. 2023-11-27 10:37:38 -07:00
UpstreamData
7ee7868094 feature: change so_linger_time to socket_linger_time. 2023-11-27 10:19:30 -07:00
UpstreamData
3f1183a4f9 feature: add so_linger option to settings. 2023-11-27 09:24:28 -07:00
Upstream Data
2b443497ea bug: improve handling of whatsminers in get_miner. 2023-11-25 12:48:45 -07:00
Upstream Data
c3972f9524 feature: add default ssl ctx to all httpx clients to speed up initialization. 2023-11-25 01:08:04 -07:00
Upstream Data
92bbb21c11 bug: handle OSError as ConnectionError, and handle Vnish Msg bug because of missing id key. 2023-11-25 00:05:03 -07:00
Upstream Data
1980ff2563 bug: do additional checks on refused connection when scanning. 2023-11-24 23:32:22 -07:00
Upstream Data
93d09a1483 bug: handle unhandled error in pings. 2023-11-24 23:28:16 -07:00
Upstream Data
690d0d99df feature: added new setting for api command timeouts. 2023-11-24 23:19:14 -07:00
Upstream Data
78f689eb2c feature: update scan method to use port 80 when possible, and add .scan() method. 2023-11-24 23:13:56 -07:00
Upstream Data
e68f188e8f bug: fix timeout references in MinerFactory and fix MinerNetwork instantiation. 2023-11-24 23:00:06 -07:00
Upstream Data
7eda611fe9 bug: fix scanning size being too large. 2023-11-24 22:50:43 -07:00
Upstream Data
1d12817942 feature: improve network parsing and implementation. 2023-11-24 22:38:43 -07:00
UpstreamData
b24efd4c69 bug: fix miner network not working with lists. 2023-11-24 13:27:09 -07:00
UpstreamData
5533135b05 docs: update supported miners. 2023-11-23 11:54:01 -07:00
UpstreamData
475054fbe0 feature: finish support for most whatsminer subtypes. 2023-11-23 11:52:44 -07:00
Daniel Sokil
06bad1bbe0 Add More Whatsminer Models, and Additional Config For Existing Models (#70) 2023-11-23 11:35:18 -07:00
UpstreamData
f3746ff756 version: bump version number. 2023-11-20 11:19:45 -07:00
UpstreamData
9f16d37c8b feature: hide GRPC and GQL if BOSer is not found. 2023-11-20 11:19:13 -07:00
UpstreamData
8a13c7940a docs: update pyproject.toml description. 2023-11-20 10:33:25 -07:00
UpstreamData
8bea76ff67 feature: add chip count for M30S+VG50. 2023-11-20 10:32:51 -07:00
Upstream Data
1504bd744c version: bump version number. 2023-11-18 22:45:38 -07:00
Upstream Data
6449f10615 feature: implement GPRC set commands properly. 2023-11-18 22:45:09 -07:00
UpstreamData
d79509bda7 version: bump version number. Pin httpx to 0.25.0 min. 2023-11-12 18:36:45 -07:00
UpstreamData
630b847466 version: bump version number. Pin httpx to 0.25.0 min. 2023-11-12 18:35:52 -07:00
59 changed files with 860 additions and 738 deletions

View File

@@ -19,7 +19,7 @@ Getting started with pyasic is easy. First, find your miner (or miners) on the
## Scanning for miners
To scan for miners in pyasic, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
The command [`MinerNetwork().scan_network_for_miners()`][pyasic.network.MinerNetwork.scan_network_for_miners] returns a list that contains any miners found.
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
```python
import asyncio # asyncio for handling the async part
from pyasic.network import MinerNetwork # miner network handles the scanning
@@ -28,7 +28,7 @@ from pyasic.network import MinerNetwork # miner network handles the scanning
async def scan_miners(): # define async scan function to allow awaiting
# create a miner network
# you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
network = MinerNetwork("192.168.1.50") # this uses the 192.168.1.0-255 network
network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
# scan for miners asynchronously
# this will return the correct type of miners if they are supported with all functionality.

View File

@@ -29,6 +29,20 @@
show_root_heading: false
heading_level: 4
## S19i
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python
options:
show_root_heading: false
heading_level: 4
## S19+
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j No PIC
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python
@@ -113,6 +127,13 @@
show_root_heading: false
heading_level: 4
## S19j Pro (BOS)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro
handler: python
options:
show_root_heading: false
heading_level: 4
## T19 (BOS)
::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19
handler: python

View File

@@ -10,9 +10,6 @@ details {
padding-top:0px;
padding-bottom:0px;
}
ul {
margin:0px;
}
</style>
<details>
@@ -73,6 +70,8 @@ ul {
<li><a href="../antminer/X19#s19l">S19L</a></li>
<li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
<li><a href="../antminer/X19#s19j">S19j</a></li>
<li><a href="../antminer/X19#s19i">S19i</a></li>
<li><a href="../antminer/X19#s19_1">S19+</a></li>
<li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
<li><a href="../antminer/X19#s19-pro_1">S19 Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
@@ -94,6 +93,8 @@ ul {
<li><a href="../whatsminer/M2X#m20s-v10">M20S V10</a></li>
<li><a href="../whatsminer/M2X#m20s-v20">M20S V20</a></li>
<li><a href="../whatsminer/M2X#m20s-v30">M20S V30</a></li>
<li><a href="../whatsminer/M2X#m20p-v10">M20P V10</a></li>
<li><a href="../whatsminer/M2X#m20p-v30">M20P V30</a></li>
<li><a href="../whatsminer/M2X#m20s_1-v30">M20S+ V30</a></li>
<li><a href="../whatsminer/M2X#m21-v10">M21 V10</a></li>
<li><a href="../whatsminer/M2X#m21s-v20">M21S V20</a></li>
@@ -108,6 +109,8 @@ ul {
<ul>
<li><a href="../whatsminer/M3X#m30-v10">M30 V10</a></li>
<li><a href="../whatsminer/M3X#m30-v20">M30 V20</a></li>
<li><a href="../whatsminer/M3X#m30k-v10">M30K V10</a></li>
<li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
<li><a href="../whatsminer/M3X#m30s-v10">M30S V10</a></li>
<li><a href="../whatsminer/M3X#m30s-v20">M30S V20</a></li>
<li><a href="../whatsminer/M3X#m30s-v30">M30S V30</a></li>
@@ -157,6 +160,7 @@ ul {
<li><a href="../whatsminer/M3X#m30s_1-ve100">M30S+ VE100</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf20">M30S+ VF20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf30">M30S+ VF30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg20">M30S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg30">M30S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg40">M30S+ VG40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg50">M30S+ VG50</a></li>
@@ -190,6 +194,9 @@ ul {
<li><a href="../whatsminer/M3X#m30s_1_1-vj30">M30S++ VJ30</a></li>
<li><a href="../whatsminer/M3X#m31-v10">M31 V10</a></li>
<li><a href="../whatsminer/M3X#m31-v20">M31 V20</a></li>
<li><a href="../whatsminer/M3X#m31h-v10">M31H V10</a></li>
<li><a href="../whatsminer/M3X#m31h-v40">M31H V40</a></li>
<li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
<li><a href="../whatsminer/M3X#m31s-v10">M31S V10</a></li>
<li><a href="../whatsminer/M3X#m31s-v20">M31S V20</a></li>
<li><a href="../whatsminer/M3X#m31s-v30">M31S V30</a></li>
@@ -205,7 +212,6 @@ ul {
<li><a href="../whatsminer/M3X#m31se-v10">M31SE V10</a></li>
<li><a href="../whatsminer/M3X#m31se-v20">M31SE V20</a></li>
<li><a href="../whatsminer/M3X#m31se-v30">M31SE V30</a></li>
<li><a href="../whatsminer/M3X#m31h-v40">M31H V40</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v10">M31S+ V10</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v20">M31S+ V20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v30">M31S+ V30</a></li>
@@ -232,6 +238,7 @@ ul {
<li><a href="../whatsminer/M3X#m33-v20">M33 V20</a></li>
<li><a href="../whatsminer/M3X#m33-v30">M33 V30</a></li>
<li><a href="../whatsminer/M3X#m33s-vg30">M33S VG30</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vg20">M33S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh20">M33S+ VH20</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh30">M33S+ VH30</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh20">M33S++ VH20</a></li>
@@ -241,12 +248,15 @@ ul {
<li><a href="../whatsminer/M3X#m36s-ve10">M36S VE10</a></li>
<li><a href="../whatsminer/M3X#m36s_1-vg30">M36S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m36s_1_1-vh30">M36S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m39-v10">M39 V10</a></li>
<li><a href="../whatsminer/M3X#m39-v20">M39 V20</a></li>
<li><a href="../whatsminer/M3X#m39-v30">M39 V30</a></li>
</ul>
</details>
<details>
<summary>M5X Series:</summary>
<ul>
<li><a href="../whatsminer/M5X#m50-ve30">M50 VE30</a></li>
<li><a href="../whatsminer/M5X#m50-vg30">M50 VG30</a></li>
<li><a href="../whatsminer/M5X#m50-vh10">M50 VH10</a></li>
<li><a href="../whatsminer/M5X#m50-vh20">M50 VH20</a></li>
@@ -397,6 +407,7 @@ ul {
<li><a href="../antminer/X19#s19j-bos">S19j (BOS)</a></li>
<li><a href="../antminer/X19#s19j-no-pic-bos">S19j No PIC (BOS)</a></li>
<li><a href="../antminer/X19#s19j-pro-bos">S19j Pro (BOS)</a></li>
<li><a href="../antminer/X19#s19j-pro-bos">S19j Pro (BOS)</a></li>
<li><a href="../antminer/X19#t19-bos">T19 (BOS)</a></li>
</ul>
</details>
@@ -454,4 +465,4 @@ ul {
</ul>
</details>
</ul>
</details>
</details>

View File

@@ -29,6 +29,20 @@
show_root_heading: false
heading_level: 4
## M20P V10
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M20P V30
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
handler: python
options:
show_root_heading: false
heading_level: 4
## M20S+ V30
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
handler: python

View File

@@ -15,6 +15,20 @@
show_root_heading: false
heading_level: 4
## M30K V10
::: pyasic.miners.whatsminer.btminer.M3X.M30K.BTMinerM30KV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M30L V10
::: pyasic.miners.whatsminer.btminer.M3X.M30L.BTMinerM30LV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M30S V10
::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV10
handler: python
@@ -358,6 +372,13 @@
show_root_heading: false
heading_level: 4
## M30S+ VG20
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG20
handler: python
options:
show_root_heading: false
heading_level: 4
## M30S+ VG30
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG30
handler: python
@@ -589,6 +610,27 @@
show_root_heading: false
heading_level: 4
## M31H V10
::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M31H V40
::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV40
handler: python
options:
show_root_heading: false
heading_level: 4
## M30L V10
::: pyasic.miners.whatsminer.btminer.M3X.M31L.BTMinerM31LV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M31S V10
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV10
handler: python
@@ -694,13 +736,6 @@
show_root_heading: false
heading_level: 4
## M31H V40
::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV40
handler: python
options:
show_root_heading: false
heading_level: 4
## M31S+ V10
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV10
handler: python
@@ -883,6 +918,13 @@
show_root_heading: false
heading_level: 4
## M33S+ VG20
::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVG20
handler: python
options:
show_root_heading: false
heading_level: 4
## M33S+ VH20
::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVH20
handler: python
@@ -946,6 +988,13 @@
show_root_heading: false
heading_level: 4
## M39 V10
::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V10
handler: python
options:
show_root_heading: false
heading_level: 4
## M39 V20
::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V20
handler: python
@@ -953,3 +1002,10 @@
show_root_heading: false
heading_level: 4
## M39 V30
::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V30
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,6 +1,13 @@
# pyasic
## M5X Models
## M50 VE30
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VG30
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30
handler: python

View File

@@ -1,12 +0,0 @@
# pyasic
## Miner Network Range
[`MinerNetworkRange`][pyasic.network.net_range.MinerNetworkRange] is a class used by [`MinerNetwork`][pyasic.network.MinerNetwork] to handle any constructor stings.
The goal is to emulate what is produced by `ipaddress.ip_network` by allowing [`MinerNetwork`][pyasic.network.MinerNetwork] to get a list of hosts.
This allows this class to be the [`MinerNetwork.network`][pyasic.network.MinerNetwork] and hence be used for scanning.
::: pyasic.network.net_range.MinerNetworkRange
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -257,6 +257,12 @@ If you are sure you want to use this command please use API.send_command("{comma
# this is an error
return False, f"{key}: " + data[key][0]["STATUS"][0]["Msg"]
elif "id" not in data.keys():
if isinstance(data["STATUS"], list):
if data["STATUS"][0].get("STATUS", None) in ["S", "I"]:
return True, None
else:
return False, data["STATUS"][0]["Msg"]
if data["STATUS"] not in ["S", "I"]:
return False, data["Msg"]
else:

View File

@@ -184,11 +184,11 @@ BOSMINER_DATA_LOC = {
class BOSMiner(BaseMiner):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
def __init__(self, ip: str, api_ver: str = "0.0.0", boser: bool = None) -> None:
super().__init__(ip)
# interfaces
self.api = BOSMinerAPI(ip, api_ver)
self.web = BOSMinerWebAPI(ip)
self.web = BOSMinerWebAPI(ip, boser=boser)
# static data
self.api_type = "BOSMiner"

View File

@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import asyncio
import enum
import ipaddress
@@ -25,6 +23,7 @@ from typing import AsyncGenerator, Callable, List, Optional, Tuple, Union
import anyio
import httpx
from pyasic import settings
from pyasic.logger import logger
from pyasic.miners.antminer import *
from pyasic.miners.avalonminer import *
@@ -45,9 +44,6 @@ from pyasic.miners.innosilicon import *
from pyasic.miners.unknown import UnknownMiner
from pyasic.miners.whatsminer import *
TIMEOUT = 20
RETRIES = 3
class MinerTypes(enum.Enum):
ANTMINER = 0
@@ -113,6 +109,8 @@ MINER_CLASSES = {
"M29V10": BTMinerM29V10,
"M30V10": BTMinerM30V10,
"M30V20": BTMinerM30V20,
"M30KV10": BTMinerM30KV10,
"M30LV10": BTMinerM30LV10,
"M30SV10": BTMinerM30SV10,
"M30SV20": BTMinerM30SV20,
"M30SV30": BTMinerM30SV30,
@@ -162,6 +160,7 @@ MINER_CLASSES = {
"M30S+VE100": BTMinerM30SPlusVE100,
"M30S+VF20": BTMinerM30SPlusVF20,
"M30S+VF30": BTMinerM30SPlusVF30,
"M30S+VG20": BTMinerM30SPlusVG20,
"M30S+VG30": BTMinerM30SPlusVG30,
"M30S+VG40": BTMinerM30SPlusVG40,
"M30S+VG50": BTMinerM30SPlusVG50,
@@ -195,6 +194,9 @@ MINER_CLASSES = {
"M30S++VJ30": BTMinerM30SPlusPlusVJ30,
"M31V10": BTMinerM31V10,
"M31V20": BTMinerM31V20,
"M31HV10": BTMinerM31HV10,
"M31HV40": BTMinerM31HV40,
"M31LV10": BTMinerM31LV10,
"M31SV10": BTMinerM31SV10,
"M31SV20": BTMinerM31SV20,
"M31SV30": BTMinerM31SV30,
@@ -210,7 +212,6 @@ MINER_CLASSES = {
"M31SEV10": BTMinerM31SEV10,
"M31SEV20": BTMinerM31SEV20,
"M31SEV30": BTMinerM31SEV30,
"M31HV40": BTMinerM31HV40,
"M31S+V10": BTMinerM31SPlusV10,
"M31S+V20": BTMinerM31SPlusV20,
"M31S+V30": BTMinerM31SPlusV30,
@@ -237,6 +238,7 @@ MINER_CLASSES = {
"M33V20": BTMinerM33V20,
"M33V30": BTMinerM33V30,
"M33SVG30": BTMinerM33SVG30,
"M33S+VG20": BTMinerM33SPlusVG20,
"M33S+VH20": BTMinerM33SPlusVH20,
"M33S+VH30": BTMinerM33SPlusVH30,
"M33S++VH20": BTMinerM33SPlusPlusVH20,
@@ -246,7 +248,10 @@ MINER_CLASSES = {
"M36SVE10": BTMinerM36SVE10,
"M36S+VG30": BTMinerM36SPlusVG30,
"M36S++VH30": BTMinerM36SPlusPlusVH30,
"M39V10": BTMinerM39V10,
"M39V20": BTMinerM39V20,
"M39V30": BTMinerM39V30,
"M50VE30": BTMinerM50VE30,
"M50VG30": BTMinerM50VG30,
"M50VH10": BTMinerM50VH10,
"M50VH20": BTMinerM50VH20,
@@ -410,10 +415,12 @@ class MinerFactory:
miner_type = None
for _ in range(RETRIES):
for _ in range(settings.get("factory_get_retries", 1)):
task = asyncio.create_task(self._get_miner_type(ip))
try:
miner_type = await asyncio.wait_for(task, timeout=TIMEOUT)
miner_type = await asyncio.wait_for(
task, timeout=settings.get("factory_get_timeout", 3)
)
except asyncio.TimeoutError:
task.cancel()
else:
@@ -438,12 +445,21 @@ class MinerFactory:
if fn is not None:
task = asyncio.create_task(fn(ip))
try:
miner_model = await asyncio.wait_for(task, timeout=30)
miner_model = await asyncio.wait_for(
task, timeout=settings.get("factory_get_timeout", 3)
)
except asyncio.TimeoutError:
task.cancel()
boser_enabled = None
if miner_type == MinerTypes.BRAIINS_OS:
boser_enabled = await self.get_boser_braiins_os(ip)
miner = self._select_miner_from_classes(
ip, miner_type=miner_type, miner_model=miner_model
ip,
miner_type=miner_type,
miner_model=miner_model,
boser_enabled=boser_enabled,
)
if miner is not None and not isinstance(miner, UnknownMiner):
@@ -453,18 +469,22 @@ class MinerFactory:
async def _get_miner_type(self, ip: str):
tasks = [
asyncio.create_task(self._get_miner_web(ip)),
asyncio.create_task(self._get_miner_socket(ip)),
# asyncio.create_task(self._get_miner_socket(ip)),
]
return await concurrent_get_first_result(tasks, lambda x: x is not None)
async def _get_miner_web(self, ip: str):
urls = [f"http://{ip}/", f"https://{ip}/"]
async with httpx.AsyncClient(verify=False) as session:
async with httpx.AsyncClient(
transport=settings.transport(verify=False)
) as session:
tasks = [asyncio.create_task(self._web_ping(session, url)) for url in urls]
text, resp = await concurrent_get_first_result(
tasks, lambda x: x[0] is not None
tasks,
lambda x: x[0] is not None
and self._parse_web_type(x[0], x[1]) is not None,
)
if text is not None:
return self._parse_web_type(text, resp)
@@ -474,9 +494,14 @@ class MinerFactory:
session: httpx.AsyncClient, url: str
) -> Tuple[Optional[str], Optional[httpx.Response]]:
try:
resp = await session.get(url, follow_redirects=False)
resp = await session.get(url, follow_redirects=True)
return resp.text, resp
except (httpx.HTTPError, asyncio.TimeoutError, anyio.EndOfStream, anyio.ClosedResourceError):
except (
httpx.HTTPError,
asyncio.TimeoutError,
anyio.EndOfStream,
anyio.ClosedResourceError,
):
pass
return None, None
@@ -486,10 +511,14 @@ class MinerFactory:
"www-authenticate", ""
):
return MinerTypes.ANTMINER
if web_resp.status_code == 307 and "https://" in web_resp.headers.get(
"location", ""
):
return MinerTypes.WHATSMINER
if len(web_resp.history) > 0:
history_resp = web_resp.history[0]
if (
"/cgi-bin/luci" in web_text
and history_resp.status_code == 307
and "https://" in history_resp.headers.get("location", "")
):
return MinerTypes.WHATSMINER
if "Braiins OS" in web_text:
return MinerTypes.BRAIINS_OS
if "cloud-box" in web_text:
@@ -517,7 +546,8 @@ class MinerFactory:
data = b""
try:
reader, writer = await asyncio.wait_for(
asyncio.open_connection(str(ip), 4028), timeout=30
asyncio.open_connection(str(ip), 4028),
timeout=settings.get("factory_get_timeout", 3),
)
except (ConnectionError, OSError, asyncio.TimeoutError):
return
@@ -583,12 +613,12 @@ class MinerFactory:
location: str,
auth: Optional[httpx.DigestAuth] = None,
) -> Optional[dict]:
async with httpx.AsyncClient(verify=False) as session:
async with httpx.AsyncClient(transport=settings.transport()) as session:
try:
data = await session.get(
f"http://{str(ip)}{location}",
auth=auth,
timeout=30,
timeout=settings.get("factory_get_timeout", 3),
)
except (httpx.HTTPError, asyncio.TimeoutError):
logger.info(f"{ip}: Web command timeout.")
@@ -687,9 +717,13 @@ class MinerFactory:
ip: ipaddress.ip_address,
miner_model: Union[str, None],
miner_type: Union[MinerTypes, None],
boser_enabled: bool = None,
) -> AnyMiner:
kwargs = {}
if boser_enabled is not None:
kwargs["boser"] = boser_enabled
try:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip, **kwargs)
except LookupError:
if miner_type in MINER_CLASSES:
return MINER_CLASSES[miner_type][None](ip)
@@ -775,7 +809,7 @@ class MinerFactory:
async def get_miner_model_innosilicon(self, ip: str) -> Optional[str]:
try:
async with httpx.AsyncClient(verify=False) as session:
async with httpx.AsyncClient(transport=settings.transport()) as session:
auth_req = await session.post(
f"http://{ip}/api/auth",
data={"username": "admin", "password": "admin"},
@@ -805,7 +839,7 @@ class MinerFactory:
pass
try:
async with httpx.AsyncClient(verify=False) as session:
async with httpx.AsyncClient(transport=settings.transport()) as session:
d = await session.post(
f"http://{ip}/graphql",
json={"query": "{bosminer {info{modelName}}}"},
@@ -817,6 +851,15 @@ class MinerFactory:
except (httpx.HTTPError, LookupError):
pass
async def get_boser_braiins_os(self, ip: str):
# TODO: refine this check
try:
sock_json_data = await self.send_api_command(ip, "version")
return sock_json_data["STATUS"][0]["Msg"].split(" ")[0].upper() == "BOSER"
except LookupError:
# let the bosminer class decide
return None
async def get_miner_model_vnish(self, ip: str) -> Optional[str]:
sock_json_data = await self.send_api_command(ip, "stats")
try:

View File

@@ -24,8 +24,5 @@ class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21 V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M21V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 33
self.fan_count = 2

View File

@@ -42,8 +42,5 @@ class M21SV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21S V70"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M21SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 111
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30 V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 105
self.fan_count = 2
@@ -36,8 +33,5 @@ class M30V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30 V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 111
self.fan_count = 2

View File

@@ -0,0 +1,29 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner
class M30KV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30K V10"
self.ideal_hashboards = 4
self.nominal_chips = 240
self.fan_count = 2

View File

@@ -0,0 +1,29 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner
class M30LV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30L V10"
self.board_num = 4
self.nominal_chips = 144
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 148
self.fan_count = 2
@@ -36,10 +33,7 @@ class M30SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 156
self.fan_count = 2
@@ -48,10 +42,7 @@ class M30SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 164
self.fan_count = 2
@@ -60,10 +51,7 @@ class M30SV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 172
self.fan_count = 2
@@ -81,10 +69,7 @@ class M30SV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V60"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 164
self.fan_count = 2
@@ -105,10 +90,7 @@ class M30SV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V80"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 129
self.fan_count = 2
@@ -135,10 +117,7 @@ class M30SVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 117
self.fan_count = 2
@@ -147,10 +126,7 @@ class M30SVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 123
self.fan_count = 2
@@ -159,10 +135,7 @@ class M30SVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 129
self.fan_count = 2
@@ -195,10 +168,7 @@ class M30SVF10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVF10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 70
self.fan_count = 2
@@ -207,10 +177,7 @@ class M30SVF20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVF20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 74
self.fan_count = 2
@@ -219,10 +186,7 @@ class M30SVF30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVF30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2
@@ -249,10 +213,7 @@ class M30SVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 74
self.fan_count = 2
@@ -261,10 +222,7 @@ class M30SVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVG40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2
@@ -273,10 +231,7 @@ class M30SVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 64
self.fan_count = 2
@@ -285,10 +240,7 @@ class M30SVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 66
self.fan_count = 2
@@ -309,10 +261,7 @@ class M30SVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 64
self.fan_count = 2
@@ -321,10 +270,7 @@ class M30SVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 66
self.fan_count = 2
@@ -345,8 +291,5 @@ class M30SVI20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VI20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVI20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 70
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 215
self.fan_count = 2
@@ -36,10 +33,7 @@ class M30SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 255
self.fan_count = 2
@@ -60,10 +54,7 @@ class M30SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 235
self.fan_count = 2
@@ -72,10 +63,7 @@ class M30SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 225
self.fan_count = 2
@@ -84,10 +72,7 @@ class M30SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V60"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 245
self.fan_count = 2
@@ -96,10 +81,7 @@ class M30SPlusV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V70"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 235
self.fan_count = 2
@@ -108,10 +90,7 @@ class M30SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V80"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 245
self.fan_count = 2
@@ -120,10 +99,7 @@ class M30SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V90"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 225
self.fan_count = 2
@@ -132,10 +108,7 @@ class M30SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V100"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 215
self.fan_count = 2
@@ -144,10 +117,7 @@ class M30SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 148
self.fan_count = 2
@@ -174,10 +144,7 @@ class M30SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE60"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 172
self.fan_count = 2
@@ -243,34 +210,23 @@ class M30SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VF30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VF30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 117
self.fan_count = 2
class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S+ VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M36SPlusVG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.model = "M30S+ VG20"
self.nominal_chips = 82
self.fan_count = 2
class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2
@@ -288,10 +244,7 @@ class M30SPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VG50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 111
self.fan_count = 2
@@ -309,10 +262,7 @@ class M30SPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 64
self.fan_count = 2
@@ -321,10 +271,7 @@ class M30SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 66
self.fan_count = 2
@@ -342,10 +289,7 @@ class M30SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 74
self.fan_count = 2
@@ -354,10 +298,7 @@ class M30SPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VH50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 64
self.fan_count = 2

View File

@@ -24,10 +24,8 @@ class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 255
self.fan_count = 2
@@ -36,10 +34,8 @@ class M30SPlusPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 255
self.fan_count = 2
@@ -48,10 +44,7 @@ class M30SPlusPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 215
self.fan_count = 2
@@ -60,10 +53,7 @@ class M30SPlusPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VE40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 225
self.fan_count = 2
@@ -72,10 +62,7 @@ class M30SPlusPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 235
self.fan_count = 2
@@ -84,10 +71,7 @@ class M30SPlusPlusVF40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VF40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VF40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 156
self.fan_count = 2
@@ -126,10 +110,7 @@ class M30SPlusPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 82
self.fan_count = 2
@@ -138,10 +119,7 @@ class M30SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 86
self.fan_count = 2
@@ -150,10 +128,7 @@ class M30SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 111
self.fan_count = 2
@@ -162,10 +137,7 @@ class M30SPlusPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 70
self.fan_count = 2
@@ -201,10 +173,7 @@ class M30SPlusPlusVH80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH80"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 74
self.fan_count = 2
@@ -213,10 +182,7 @@ class M30SPlusPlusVH90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH90"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31 V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 70
self.fan_count = 2
@@ -36,8 +33,5 @@ class M31V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31 V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 74
self.fan_count = 2

View File

@@ -18,14 +18,19 @@ import warnings
from pyasic.miners.makes import WhatsMiner
class M31HV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31H V10"
self.nominal_chips = 114
self.fan_count = 0
class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31H V40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31HV40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 136
self.fan_count = 0

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner
class M31LV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31L V10"
self.nominal_chips = 114
self.fan_count = 2

View File

@@ -42,10 +42,7 @@ class M31SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 117
self.fan_count = 2
@@ -54,10 +51,7 @@ class M31SV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SV40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 123
self.fan_count = 2
@@ -66,10 +60,7 @@ class M31SV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SV50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2
@@ -108,10 +99,7 @@ class M31SV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V90"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SV90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 117
self.fan_count = 2
@@ -129,10 +117,7 @@ class M31SVE20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S VE20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SVE20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 74
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M31SEV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31SE V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SEV10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 82
self.fan_count = 2
@@ -36,10 +33,7 @@ class M31SEV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31SE V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SEV20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2
@@ -48,8 +42,5 @@ class M31SEV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31SE V30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SEV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 78
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M31SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 105
self.fan_count = 2
@@ -36,10 +33,7 @@ class M31SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 111
self.fan_count = 2
@@ -66,10 +60,7 @@ class M31SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 148
self.fan_count = 2
@@ -114,10 +105,7 @@ class M31SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 82
self.fan_count = 2
@@ -135,10 +123,7 @@ class M31SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 105
self.fan_count = 2
@@ -147,10 +132,7 @@ class M31SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE40"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 111
self.fan_count = 2
@@ -159,10 +141,7 @@ class M31SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE50"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 117
self.fan_count = 2
@@ -195,10 +174,7 @@ class M31SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VF20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VF20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 66
self.fan_count = 2
@@ -219,10 +195,7 @@ class M31SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VG20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VG20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 66
self.fan_count = 2
@@ -231,10 +204,7 @@ class M31SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 70
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M32V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M32 V10"
self.nominal_chips = 74
self.nominal_chips = 78
self.fan_count = 2

View File

@@ -24,10 +24,7 @@ class M33V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33 V10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M33V10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 33
self.fan_count = 0
@@ -36,10 +33,7 @@ class M33V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33 V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M33V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 62
self.fan_count = 0
@@ -48,8 +42,5 @@ class M33V30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33 V30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M33V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 66
self.fan_count = 0

View File

@@ -24,8 +24,6 @@ class M33SVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M33SVG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 116
self.fan_count = 0

View File

@@ -18,16 +18,23 @@ import warnings
from pyasic.miners.makes import WhatsMiner
class M33SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S+ VG20"
self.ideal_hashboards = 4
self.nominal_chips = 112
self.fan_count = 0
class M33SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S+ VH20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 100
self.fan_count = 0
@@ -36,7 +43,8 @@ class M33SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S+ VH30"
self.nominal_chips = 0
self.ideal_hashboards = 4
self.nominal_chips = 0 # slot1 116, slot2 106, slot3 116, slot4 106
warnings.warn(
"Unknown chip count for miner type M30S+ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -48,8 +48,5 @@ class M33SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
self.ip = ip
self.model = "M33S++ VG40"
self.ideal_hashboards = 4
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VG40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 174
self.fan_count = 0

View File

@@ -24,8 +24,6 @@ class M36SVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S VE10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M36SVE10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 114
self.fan_count = 0

View File

@@ -24,8 +24,6 @@ class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S+ VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M36S+ VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 108
self.fan_count = 0

View File

@@ -24,8 +24,6 @@ class M36SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S++ VH30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M36S++ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 80
self.fan_count = 0

View File

@@ -18,14 +18,28 @@ import warnings
from pyasic.miners.makes import WhatsMiner
class M39V10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M39 V10"
self.nominal_chips = 50
self.fan_count = 0
class M39V20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M39 V20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M39 V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 54
self.fan_count = 0
class M39V30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M39 V30"
self.nominal_chips = 68
self.fan_count = 0

View File

@@ -15,6 +15,8 @@
# ------------------------------------------------------------------------------
from .M30 import M30V10, M30V20
from .M30K import M30KV10
from .M30L import M30LV10
from .M30S import (
M30SV10,
M30SV20,
@@ -67,6 +69,7 @@ from .M30S_Plus import (
M30SPlusVE100,
M30SPlusVF20,
M30SPlusVF30,
M30SPlusVG20,
M30SPlusVG30,
M30SPlusVG40,
M30SPlusVG50,
@@ -77,7 +80,6 @@ from .M30S_Plus import (
M30SPlusVH40,
M30SPlusVH50,
M30SPlusVH60,
M36SPlusVG30,
)
from .M30S_Plus_Plus import (
M30SPlusPlusV10,
@@ -103,7 +105,8 @@ from .M30S_Plus_Plus import (
M30SPlusPlusVJ30,
)
from .M31 import M31V10, M31V20
from .M31H import M31HV40
from .M31H import M31HV10, M31HV40
from .M31L import M31LV10
from .M31S import (
M31SV10,
M31SV20,
@@ -145,10 +148,10 @@ from .M32 import M32V10, M32V20
from .M32S import M32S
from .M33 import M33V10, M33V20, M33V30
from .M33S import M33SVG30
from .M33S_Plus import M33SPlusVH20, M33SPlusVH30
from .M33S_Plus import M33SPlusVG20, M33SPlusVH20, M33SPlusVH30
from .M33S_Plus_Plus import M33SPlusPlusVG40, M33SPlusPlusVH20, M33SPlusPlusVH30
from .M34S_Plus import M34SPlusVE10
from .M36S import M36SVE10
from .M36S_Plus import M36SPlusVG30
from .M36S_Plus_Plus import M36SPlusPlusVH30
from .M39 import M39V20
from .M39 import M39V10, M39V20, M39V30

View File

@@ -18,16 +18,21 @@ import warnings
from pyasic.miners.makes import WhatsMiner
class M50VE30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VE30"
self.ideal_hashboards = 4
self.nominal_chips = 255
self.fan_count = 2
class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VG30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VG30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 156
self.fan_count = 2
@@ -36,10 +41,7 @@ class M50VH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH10"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VH10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 86
self.fan_count = 2
@@ -57,10 +59,7 @@ class M50VH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 117
self.fan_count = 2

View File

@@ -72,10 +72,7 @@ class M50SVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50S VH20"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M50S VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 135
self.fan_count = 2
@@ -84,10 +81,7 @@ class M50SVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50S VH30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M50S VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 156
self.fan_count = 2

View File

@@ -24,8 +24,6 @@ class M53VH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M53 VH30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M53 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 128
self.fan_count = 0

View File

@@ -24,8 +24,6 @@ class M56VH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M56 VH30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M56 VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.ideal_hashboards = 4
self.nominal_chips = 108
self.fan_count = 0

View File

@@ -15,6 +15,7 @@
# ------------------------------------------------------------------------------
from .M50 import (
M50VE30,
M50VG30,
M50VH10,
M50VH20,

View File

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

View File

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

View File

@@ -36,6 +36,7 @@ from pyasic.miners.types import (
M30SPlusVE100,
M30SPlusVF20,
M30SPlusVF30,
M30SPlusVG20,
M30SPlusVG30,
M30SPlusVG40,
M30SPlusVG50,
@@ -46,7 +47,6 @@ from pyasic.miners.types import (
M30SPlusVH40,
M30SPlusVH50,
M30SPlusVH60,
M36SPlusVG30,
)
@@ -130,7 +130,7 @@ class BTMinerM30SPlusVF30(M3X, M30SPlusVF30):
pass
class BTMinerM36SPlusVG30(M3X, M36SPlusVG30):
class BTMinerM30SPlusVG20(M3X, M30SPlusVG20):
pass

View File

@@ -15,7 +15,11 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import M3X
from pyasic.miners.types import M31HV40
from pyasic.miners.types import M31HV10, M31HV40
class BTMinerM31HV10(M3X, M31HV10):
pass
class BTMinerM31HV40(M3X, M31HV40):

View File

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

View File

@@ -15,7 +15,11 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import M3X
from pyasic.miners.types import M33SPlusVH20, M33SPlusVH30
from pyasic.miners.types import M33SPlusVG20, M33SPlusVH20, M33SPlusVH30
class BTMinerM33SPlusVG20(M3X, M33SPlusVG20):
pass
class BTMinerM33SPlusVH20(M3X, M33SPlusVH20):

View File

@@ -15,8 +15,16 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import M3X
from pyasic.miners.types import M39V20
from pyasic.miners.types import M39V10, M39V20, M39V30
class BTMinerM39V10(M3X, M39V10):
pass
class BTMinerM39V20(M3X, M39V20):
pass
class BTMinerM39V30(M3X, M39V30):
pass

View File

@@ -15,6 +15,8 @@
# ------------------------------------------------------------------------------
from .M30 import BTMinerM30V10, BTMinerM30V20
from .M30K import BTMinerM30KV10
from .M30L import BTMinerM30LV10
from .M30S import (
BTMinerM30SV10,
BTMinerM30SV20,
@@ -67,6 +69,7 @@ from .M30S_Plus import (
BTMinerM30SPlusVE100,
BTMinerM30SPlusVF20,
BTMinerM30SPlusVF30,
BTMinerM30SPlusVG20,
BTMinerM30SPlusVG30,
BTMinerM30SPlusVG40,
BTMinerM30SPlusVG50,
@@ -77,7 +80,6 @@ from .M30S_Plus import (
BTMinerM30SPlusVH40,
BTMinerM30SPlusVH50,
BTMinerM30SPlusVH60,
BTMinerM36SPlusVG30,
)
from .M30S_Plus_Plus import (
BTMinerM30SPlusPlusV10,
@@ -103,7 +105,8 @@ from .M30S_Plus_Plus import (
BTMinerM30SPlusPlusVJ30,
)
from .M31 import BTMinerM31V10, BTMinerM31V20
from .M31H import BTMinerM31HV40
from .M31H import BTMinerM31HV10, BTMinerM31HV40
from .M31L import BTMinerM31LV10
from .M31S import (
BTMinerM31SV10,
BTMinerM31SV20,
@@ -145,7 +148,7 @@ from .M32 import BTMinerM32V10, BTMinerM32V20
from .M32S import BTMinerM32S
from .M33 import BTMinerM33V10, BTMinerM33V20, BTMinerM33V30
from .M33S import BTMinerM33SVG30
from .M33S_Plus import BTMinerM33SPlusVH20, BTMinerM33SPlusVH30
from .M33S_Plus import BTMinerM33SPlusVG20, BTMinerM33SPlusVH20, BTMinerM33SPlusVH30
from .M33S_Plus_Plus import (
BTMinerM33SPlusPlusVG40,
BTMinerM33SPlusPlusVH20,
@@ -155,4 +158,4 @@ from .M34S_Plus import BTMinerM34SPlusVE10
from .M36S import BTMinerM36SVE10
from .M36S_Plus import BTMinerM36SPlusVG30
from .M36S_Plus_Plus import BTMinerM36SPlusPlusVH30
from .M39 import BTMinerM39V20
from .M39 import BTMinerM39V10, BTMinerM39V20, BTMinerM39V30

View File

@@ -16,6 +16,7 @@
from pyasic.miners.backends import M5X
from pyasic.miners.types import (
M50VE30,
M50VG30,
M50VH10,
M50VH20,
@@ -31,6 +32,10 @@ from pyasic.miners.types import (
)
class BTMinerM50VE30(M5X, M50VE30):
pass
class BTMinerM50VG30(M5X, M50VG30):
pass

View File

@@ -15,6 +15,7 @@
# ------------------------------------------------------------------------------
from .M50 import (
BTMinerM50VE30,
BTMinerM50VG30,
BTMinerM50VH10,
BTMinerM50VH20,

View File

@@ -21,88 +21,103 @@ from typing import AsyncIterator, List, Union
from pyasic import settings
from pyasic.miners.miner_factory import AnyMiner, miner_factory
from pyasic.network.net_range import MinerNetworkRange
class MinerNetwork:
"""A class to handle a network containing miners. Handles scanning and gets miners via [`MinerFactory`][pyasic.miners.miner_factory.MinerFactory].
Parameters:
ip_addr: ### An IP address, range of IP addresses, or a list of IPs
* Takes a single IP address as an `ipadddress.ipaddress()` or a string
* Takes a string formatted as:
```f"{ip_range_1_start}-{ip_range_1_end}, {ip_address_1}, {ip_range_2_start}-{ip_range_2_end}, {ip_address_2}..."```
* Also takes a list of strings or `ipaddress.ipaddress` formatted as:
```[{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]```
mask: A subnet mask to use when constructing the network. Only used if `ip_addr` is a single IP.
Defaults to /24 (255.255.255.0 or 0.0.0.255)
hosts: A list of `ipaddress.IPv4Address` to be used when scanning.
"""
def __init__(
self,
ip_addr: Union[str, List[str], None] = None,
mask: Union[str, int, None] = None,
) -> None:
self.network = None
self.ip_addr = ip_addr
self.connected_miners = {}
if isinstance(mask, str):
if mask.startswith("/"):
mask = mask.replace("/", "")
self.mask = mask
self.network = self.get_network()
def __init__(self, hosts: List[ipaddress.IPv4Address]):
self.hosts = hosts
def __len__(self):
return len([item for item in self.get_network().hosts()])
return len(self.hosts)
def __repr__(self):
return str(self.network)
@classmethod
def from_list(cls, addresses: list) -> "MinerNetwork":
"""Parse a list of address constructors into a MinerNetwork.
def hosts(self):
for x in self.network.hosts():
yield x
def get_network(self) -> ipaddress.ip_network:
"""Get the network using the information passed to the MinerNetwork or from cache.
Returns:
The proper network to be able to scan.
Parameters:
addresses: A list of address constructors, such as `["10.1-2.1.1-50", "10.4.1-2.1-50"]`.
"""
# if we have a network cached already, use that
if self.network:
return self.network
hosts = []
for address in addresses:
hosts = [*hosts, *cls.from_address(address).hosts]
return cls(sorted(list(set(hosts))))
# if there is no IP address passed, default to 192.168.1.0
if not self.ip_addr:
self.ip_addr = "192.168.1.0"
if "-" in self.ip_addr:
self.network = MinerNetworkRange(self.ip_addr)
elif isinstance(self.ip_addr, list):
self.network = MinerNetworkRange(self.ip_addr)
else:
# if there is no subnet mask passed, default to /24
if not self.mask:
subnet_mask = "24"
# if we do have a mask passed, use that
else:
subnet_mask = str(self.mask)
@classmethod
def from_address(cls, address: str) -> "MinerNetwork":
"""Parse an address constructor into a MinerNetwork.
# save the network and return it
self.network = ipaddress.ip_network(
f"{self.ip_addr}/{subnet_mask}", strict=False
)
Parameters:
address: An address constructor, such as `"10.1-2.1.1-50"`.
"""
octets = address.split(".")
if len(octets) > 4:
raise ValueError("Too many octets in IP constructor.")
if len(octets) < 4:
raise ValueError("Too few octets in IP constructor.")
return cls.from_octets(*octets)
logging.debug(f"{self} - (Get Network) - Found network")
return self.network
@classmethod
def from_octets(
cls, oct_1: str, oct_2: str, oct_3: str, oct_4: str
) -> "MinerNetwork":
"""Parse 4 octet constructors into a MinerNetwork.
async def scan_network_for_miners(self) -> List[AnyMiner]:
"""Scan the network for miners, and return found miners as a list.
Parameters:
oct_1: An octet constructor, such as `"10"`.
oct_2: An octet constructor, such as `"1-2"`.
oct_3: An octet constructor, such as `"1"`.
oct_4: An octet constructor, such as `"1-50"`.
"""
hosts = []
oct_1_val_start, oct_1_start, oct_1_end = compute_oct_range(oct_1)
for oct_1_idx in range((abs(oct_1_end - oct_1_start)) + 1):
oct_1_val = str(oct_1_idx + oct_1_start)
oct_2_val_start, oct_2_start, oct_2_end = compute_oct_range(oct_2)
for oct_2_idx in range((abs(oct_2_end - oct_2_start)) + 1):
oct_2_val = str(oct_2_idx + oct_2_start)
oct_3_val_start, oct_3_start, oct_3_end = compute_oct_range(oct_3)
for oct_3_idx in range((abs(oct_3_end - oct_3_start)) + 1):
oct_3_val = str(oct_3_idx + oct_3_start)
oct_4_val_start, oct_4_start, oct_4_end = compute_oct_range(oct_4)
for oct_4_idx in range((abs(oct_4_end - oct_4_start)) + 1):
oct_4_val = str(oct_4_idx + oct_4_start)
hosts.append(
ipaddress.ip_address(
".".join([oct_1_val, oct_2_val, oct_3_val, oct_4_val])
)
)
return cls(sorted(hosts))
@classmethod
def from_subnet(cls, subnet: str) -> "MinerNetwork":
"""Parse a subnet into a MinerNetwork.
Parameters:
subnet: A subnet string, such as `"10.0.0.1/24"`.
"""
return cls(list(ipaddress.ip_network(subnet, strict=False).hosts()))
async def scan(self) -> List[AnyMiner]:
"""Scan the network for miners.
Returns:
A list of found miners.
"""
# get the network
local_network = self.get_network()
return await self.scan_network_for_miners()
async def scan_network_for_miners(self) -> List[AnyMiner]:
logging.debug(f"{self} - (Scan Network For Miners) - Scanning")
# clear cached miners
@@ -110,7 +125,7 @@ class MinerNetwork:
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
miners = await asyncio.gather(
*[self.ping_and_get_miner(host, limit) for host in local_network.hosts()]
*[self.ping_and_get_miner(host, limit) for host in self.hosts]
)
# remove all None from the miner list
@@ -132,15 +147,12 @@ class MinerNetwork:
# get the current event loop
loop = asyncio.get_event_loop()
# get the network
local_network = self.get_network()
# create a list of scan tasks
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
miners = asyncio.as_completed(
[
loop.create_task(self.ping_and_get_miner(host, limit))
for host in local_network.hosts()
for host in self.hosts
]
)
for miner in miners:
@@ -149,76 +161,26 @@ class MinerNetwork:
except TimeoutError:
yield None
@staticmethod
async def ping_miner(
ip: ipaddress.ip_address, semaphore: asyncio.Semaphore
) -> Union[None, ipaddress.ip_address]:
async with semaphore:
try:
miner = await ping_miner(ip)
if miner:
return miner
except ConnectionRefusedError:
tasks = [ping_miner(ip, port=port) for port in [4029, 8889]]
for miner in asyncio.as_completed(tasks):
try:
miner = await miner
if miner:
return miner
except ConnectionRefusedError:
pass
@staticmethod
async def ping_and_get_miner(
ip: ipaddress.ip_address, semaphore: asyncio.Semaphore
) -> Union[None, AnyMiner]:
async with semaphore:
try:
miner = await ping_and_get_miner(ip)
if miner:
return miner
return await ping_and_get_miner(ip)
except ConnectionRefusedError:
tasks = [ping_and_get_miner(ip, port=port) for port in [4029, 8889]]
tasks = [
ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]
]
for miner in asyncio.as_completed(tasks):
try:
miner = await miner
if miner:
return miner
return await miner
except ConnectionRefusedError:
pass
async def ping_miner(
ip: ipaddress.ip_address, port=4028
) -> Union[None, ipaddress.ip_address]:
for i in range(settings.get("network_ping_retries", 1)):
try:
connection_fut = asyncio.open_connection(str(ip), port)
# get the read and write streams from the connection
reader, writer = await asyncio.wait_for(
connection_fut, timeout=settings.get("network_ping_timeout", 3)
)
# immediately close connection, we know connection happened
writer.close()
# make sure the writer is closed
await writer.wait_closed()
# ping was successful
return ip
except asyncio.exceptions.TimeoutError:
# ping failed if we time out
continue
except (ConnectionRefusedError, OSError):
# handle for other connection errors
logging.debug(f"{str(ip)}: Connection Refused.")
raise ConnectionRefusedError
except Exception as e:
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
raise ConnectionRefusedError
return
async def ping_and_get_miner(
ip: ipaddress.ip_address, port=4028
ip: ipaddress.ip_address, port=80
) -> Union[None, AnyMiner]:
for i in range(settings.get("network_ping_retries", 1)):
try:
@@ -236,11 +198,25 @@ async def ping_and_get_miner(
except asyncio.exceptions.TimeoutError:
# ping failed if we time out
continue
except (ConnectionRefusedError, OSError):
# handle for other connection errors
logging.debug(f"{str(ip)}: Connection Refused.")
raise ConnectionRefusedError
except OSError as e:
raise ConnectionRefusedError from e
except Exception as e:
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
raise ConnectionRefusedError
logging.warning(f"{str(ip)}: Unhandled ping exception: {e}")
return
return
def compute_oct_range(octet: str) -> tuple:
octet_split = octet.split("-")
octet_start = int(octet_split[0])
octet_end = None
try:
octet_end = int(octet_split[1])
except IndexError:
pass
if octet_end is None:
octet_end = int(octet_start)
octet_val_start = min([octet_start, octet_end])
return octet_val_start, octet_start, octet_end

View File

@@ -1,56 +0,0 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import ipaddress
from typing import Union
class MinerNetworkRange:
"""A MinerNetwork that takes a range of IP addresses.
Parameters:
ip_range: ## A range of IP addresses to put in the network, or a list of IPs
* Takes a string formatted as:
```f"{ip_range_1_start}-{ip_range_1_end}, {ip_address_1}, {ip_range_2_start}-{ip_range_2_end}, {ip_address_2}..."```
* Also takes a list of strings or `ipaddress.ipaddress` formatted as:
```[{ip_address_1}, {ip_address_2}, {ip_address_3}, ...]```
"""
def __init__(self, ip_range: Union[str, list]):
self.host_ips = []
if isinstance(ip_range, str):
ip_ranges = ip_range.replace(" ", "").split(",")
for item in ip_ranges:
if "-" in item:
start, end = item.split("-")
start_ip = ipaddress.ip_address(start)
end_ip = ipaddress.ip_address(end)
networks = ipaddress.summarize_address_range(start_ip, end_ip)
for network in networks:
self.host_ips.append(network.network_address)
for host in network.hosts():
if host not in self.host_ips:
self.host_ips.append(host)
if network.broadcast_address not in self.host_ips:
self.host_ips.append(network.broadcast_address)
else:
self.host_ips.append(ipaddress.ip_address(item))
elif isinstance(ip_range, list):
self.host_ips = [ipaddress.ip_address(ip_str) for ip_str in ip_range]
def hosts(self):
for x in self.host_ips:
yield x

View File

@@ -13,24 +13,44 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import socket
import struct
from ssl import SSLContext
from typing import Any, Union
from typing import Any
import httpx
from httpx import AsyncHTTPTransport
_settings = { # defaults
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"factory_get_retries": 1,
"factory_get_timeout": 3,
"get_data_retries": 1,
"api_function_timeout": 5,
"default_whatsminer_password": "admin",
"default_innosilicon_password": "admin",
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
"default_goldshell_password": "123456789",
"socket_linger_time": 1000,
}
ssl_cxt = httpx.create_ssl_context()
def transport(verify: Union[str, bool, SSLContext] = ssl_cxt):
l_onoff = 1
l_linger = get("so_linger_time", 1000)
opts = [(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", l_onoff, l_linger))]
return AsyncHTTPTransport(socket_options=opts, verify=verify)
def get(key: str, other: Any = None) -> Any:
return _settings.get(key, other)

View File

@@ -38,10 +38,13 @@ class AntminerModernWebAPI(BaseWebAPI):
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
auth = httpx.DigestAuth(self.username, self.pwd)
try:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
if parameters:
data = await client.post(
url, data=json.dumps(parameters), auth=auth, timeout=15 # noqa
url,
data=json.dumps(parameters),
auth=auth,
timeout=settings.get("api_function_timeout", 3), # noqa
)
else:
data = await client.get(url, auth=auth)
@@ -57,7 +60,7 @@ class AntminerModernWebAPI(BaseWebAPI):
async def multicommand(
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
) -> dict:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
tasks = [
asyncio.create_task(self._handle_multicommand(client, command))
for command in commands
@@ -149,10 +152,13 @@ class AntminerOldWebAPI(BaseWebAPI):
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
auth = httpx.DigestAuth(self.username, self.pwd)
try:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
if parameters:
data = await client.post(
url, data=parameters, auth=auth, timeout=15
url,
data=parameters,
auth=auth,
timeout=settings.get("api_function_timeout", 3),
)
else:
data = await client.get(url, auth=auth)
@@ -170,7 +176,7 @@ class AntminerOldWebAPI(BaseWebAPI):
) -> dict:
data = {k: None for k in commands}
auth = httpx.DigestAuth(self.username, self.pwd)
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
for command in commands:
try:
url = f"http://{self.ip}/cgi-bin/{command}.cgi"

View File

@@ -15,11 +15,11 @@
# ------------------------------------------------------------------------------
import json
from datetime import datetime, timedelta
from enum import Enum
from typing import List, Union
import grpc_requests
import httpx
from google.protobuf.message import Message
from grpc import RpcError
from pyasic import APIError, settings
@@ -28,22 +28,53 @@ from pyasic.web.bosminer.proto import (
get_auth_service_descriptors,
get_service_descriptors,
)
from pyasic.web.bosminer.proto.bos.v1.actions import SaveAction
from pyasic.web.bosminer.proto.bos.v1.performance import (
ManualPerformanceMode,
PerformanceMode,
from pyasic.web.bosminer.proto.bos.v1.actions_pb2 import ( # noqa: this will be defined
SetLocateDeviceStatusRequest,
)
from pyasic.web.bosminer.proto.bos.v1.authentication_pb2 import ( # noqa: this will be defined
SetPasswordRequest,
)
from pyasic.web.bosminer.proto.bos.v1.common_pb2 import ( # noqa: this will be defined
SaveAction,
)
from pyasic.web.bosminer.proto.bos.v1.cooling_pb2 import ( # noqa: this will be defined
SetImmersionModeRequest,
)
from pyasic.web.bosminer.proto.bos.v1.miner_pb2 import ( # noqa: this will be defined
DisableHashboardsRequest,
EnableHashboardsRequest,
)
from pyasic.web.bosminer.proto.bos.v1.performance_pb2 import ( # noqa: this will be defined
DecrementHashrateTargetRequest,
DecrementPowerTargetRequest,
IncrementHashrateTargetRequest,
IncrementPowerTargetRequest,
SetDefaultHashrateTargetRequest,
SetDefaultPowerTargetRequest,
SetHashrateTargetRequest,
SetPowerTargetRequest,
)
class BOSMinerWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
self.gql = BOSMinerGQLAPI(ip, settings.get("default_bosminer_password", "root"))
def __init__(self, ip: str, boser: bool = None) -> None:
if boser is None:
boser = True
if boser:
self.gql = BOSMinerGQLAPI(
ip, settings.get("default_bosminer_password", "root")
)
self.grpc = BOSMinerGRPCAPI(
ip, settings.get("default_bosminer_password", "root")
)
else:
self.gql = None
self.grpc = None
self.luci = BOSMinerLuCIAPI(
ip, settings.get("default_bosminer_password", "root")
)
self.grpc = BOSMinerGRPCAPI(
ip, settings.get("default_bosminer_password", "root")
)
self._pwd = settings.get("default_bosminer_password", "root")
super().__init__(ip)
@@ -55,7 +86,10 @@ class BOSMinerWebAPI(BaseWebAPI):
def pwd(self, other: str):
self._pwd = other
self.luci.pwd = other
self.gql.pwd = other
if self.gql is not None:
self.gql.pwd = other
if self.grpc is not None:
self.grpc.pwd = other
async def send_command(
self,
@@ -65,30 +99,46 @@ class BOSMinerWebAPI(BaseWebAPI):
**parameters: Union[str, int, bool],
) -> dict:
if isinstance(command, dict):
return await self.gql.send_command(command)
if self.gql is not None:
return await self.gql.send_command(command)
elif command.startswith("/cgi-bin/luci"):
return await self.gql.send_command(command)
else:
if self.grpc is not None:
return await self.grpc.send_command(command)
async def multicommand(
self, *commands: Union[dict, str], allow_warning: bool = True
) -> dict:
luci_commands = []
gql_commands = []
grpc_commands = []
for cmd in commands:
if isinstance(cmd, dict):
gql_commands.append(cmd)
elif cmd.startswith("/cgi-bin/luci"):
luci_commands.append(cmd)
else:
grpc_commands.append(cmd)
luci_data = await self.luci.multicommand(*luci_commands)
gql_data = await self.gql.multicommand(*gql_commands)
if self.gql is not None:
gql_data = await self.gql.multicommand(*gql_commands)
else:
gql_data = None
if self.grpc is not None:
grpc_data = await self.grpc.multicommand(*grpc_commands)
else:
grpc_data = None
if gql_data is None:
gql_data = {}
if luci_data is None:
luci_data = {}
if grpc_data is None:
grpc_data = {}
data = dict(**luci_data, **gql_data)
data = dict(**luci_data, **gql_data, **grpc_data)
return data
@@ -136,7 +186,7 @@ class BOSMinerGQLAPI:
if command.get("query") is None:
query = {"query": self.parse_command(command)}
try:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
await self.auth(client)
data = await client.post(url, json=query)
except httpx.HTTPError:
@@ -189,7 +239,7 @@ class BOSMinerLuCIAPI:
async def send_command(self, path: str, ignore_errors: bool = False) -> dict:
try:
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
await self.auth(client)
data = await client.get(
f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"}
@@ -265,7 +315,11 @@ class BOSMinerGRPCAPI:
pass
async def send_command(
self, command: str, ignore_errors: bool = False, auth: bool = True, **parameters
self,
command: str,
message: Message = None,
ignore_errors: bool = False,
auth: bool = True,
) -> dict:
service, method = command.split("/")
metadata = []
@@ -279,7 +333,7 @@ class BOSMinerGRPCAPI:
return await client.request(
service,
method,
request=parameters,
request=message,
metadata=metadata,
)
except RpcError as e:
@@ -339,8 +393,10 @@ class BOSMinerGRPCAPI:
return await self.send_command("braiins.bos.v1.ActionsService/Reboot")
async def set_locate_device_status(self, enable: bool):
message = SetLocateDeviceStatusRequest()
message.enable = enable
return await self.send_command(
"braiins.bos.v1.ActionsService/SetLocateDeviceStatus", enable=enable
"braiins.bos.v1.ActionsService/SetLocateDeviceStatus", message=message
)
async def get_locate_device_status(self):
@@ -349,23 +405,26 @@ class BOSMinerGRPCAPI:
)
async def set_password(self, password: str = None):
kwargs = {}
message = SetPasswordRequest()
if password:
kwargs["password"] = password
message.password = password
return await self.send_command(
"braiins.bos.v1.AuthenticationService/SetPassword", **kwargs
"braiins.bos.v1.AuthenticationService/SetPassword", message=message
)
async def get_cooling_state(self):
return await self.send_command("braiins.bos.v1.CoolingService/GetCoolingState")
async def set_immersion_mode(
self, enable: bool, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
self,
enable: bool,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = SetImmersionModeRequest()
message.enable = enable
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.CoolingService/SetImmersionMode",
save_action=save_action,
enable_immersion_mode=enable,
"braiins.bos.v1.CoolingService/SetImmersionMode", message=message
)
async def get_tuner_state(self):
@@ -379,101 +438,117 @@ class BOSMinerGRPCAPI:
)
async def set_default_power_target(
self, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
):
message = SetDefaultPowerTargetRequest()
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/SetDefaultPowerTarget",
save_action=save_action,
"braiins.bos.v1.PerformanceService/SetDefaultPowerTarget", message=message
)
async def set_power_target(
self, power_target: int, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
self,
power_target: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = SetPowerTargetRequest()
message.power_target.watt = power_target
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/SetPowerTarget",
save_action=save_action,
power_target=power_target,
"braiins.bos.v1.PerformanceService/SetPowerTarget", message=message
)
async def increment_power_target(
self,
power_target_increment: int,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = IncrementPowerTargetRequest()
message.power_target_increment.watt = power_target_increment
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/IncrementPowerTarget",
save_action=save_action,
power_target_increment=power_target_increment,
"braiins.bos.v1.PerformanceService/IncrementPowerTarget", message=message
)
async def decrement_power_target(
self,
power_target_decrement: int,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = DecrementPowerTargetRequest()
message.power_target_decrement.watt = power_target_decrement
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/DecrementPowerTarget",
save_action=save_action,
power_target_decrement=power_target_decrement,
message=message,
)
async def set_default_hashrate_target(
self, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
):
message = SetDefaultHashrateTargetRequest()
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget",
save_action=save_action,
message=message,
)
async def set_hashrate_target(
self, hashrate_target: int, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
self,
hashrate_target: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = SetHashrateTargetRequest()
message.hashrate_target.terahash_per_second = hashrate_target
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/SetHashrateTarget",
save_action=save_action,
hashrate_target=hashrate_target,
"braiins.bos.v1.PerformanceService/SetHashrateTarget", message=message
)
async def increment_hashrate_target(
self,
hashrate_target_increment: int,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = IncrementHashrateTargetRequest()
message.hashrate_target_increment.terahash_per_second = (
hashrate_target_increment
)
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/IncrementHashrateTarget",
save_action=save_action,
hashrate_target_increment=hashrate_target_increment,
message=message,
)
async def decrement_hashrate_target(
self,
hashrate_target_decrement: int,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = DecrementHashrateTargetRequest()
message.hashrate_target_decrement.terahash_per_second = (
hashrate_target_decrement
)
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.PerformanceService/DecrementHashrateTarget",
save_action=save_action,
hashrate_target_decrement=hashrate_target_decrement,
message=message,
)
async def set_dps(self):
raise NotImplementedError
return await self.send_command("braiins.bos.v1.PerformanceService/SetDPS")
async def set_performance_mode(
self,
power_target: int = None,
hashrate_target: float = None,
manual_config: ManualPerformanceMode = None,
):
config = PerformanceMode.create(
power_target=power_target,
hashrate_target=hashrate_target,
manual_configuration=manual_config,
)
async def set_performance_mode(self):
raise NotImplementedError
return await self.send_command(
"braiins.bos.v1.PerformanceService/SetPerformanceMode", **config
"braiins.bos.v1.PerformanceService/SetPerformanceMode"
)
async def get_active_performance_mode(self):
@@ -527,21 +602,25 @@ class BOSMinerGRPCAPI:
async def enable_hashboards(
self,
hashboard_ids: List[str],
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = EnableHashboardsRequest()
message.hashboard_ids[:] = hashboard_ids
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.MinerService/EnableHashboards",
hashboard_ids=hashboard_ids,
save_action=save_action,
"braiins.bos.v1.MinerService/EnableHashboards", message=message
)
async def disable_hashboards(
self,
hashboard_ids: List[str],
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
):
message = DisableHashboardsRequest()
message.hashboard_ids[:] = hashboard_ids
message.save_action = save_action
return await self.send_command(
"braiins.bos.v1.MinerService/DisableHashboards",
hashboard_ids=hashboard_ids,
save_action=save_action,
"braiins.bos.v1.MinerService/DisableHashboards", message=message
)

View File

@@ -31,7 +31,7 @@ class GoldshellWebAPI(BaseWebAPI):
self.jwt = None
async def auth(self):
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
try:
await client.get(f"http://{self.ip}/user/logout")
auth = (
@@ -71,21 +71,21 @@ class GoldshellWebAPI(BaseWebAPI):
parameters.pop("pool_pwd")
if not self.jwt:
await self.auth()
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
for i in range(settings.get("get_data_retries", 1)):
try:
if parameters:
response = await client.put(
f"http://{self.ip}/mcb/{command}",
headers={"Authorization": "Bearer " + self.jwt},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
json=parameters,
)
else:
response = await client.get(
f"http://{self.ip}/mcb/{command}",
headers={"Authorization": "Bearer " + self.jwt},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
)
json_data = response.json()
return json_data
@@ -102,13 +102,13 @@ class GoldshellWebAPI(BaseWebAPI):
data = {k: None for k in commands}
data["multicommand"] = True
await self.auth()
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
for command in commands:
try:
response = await client.get(
f"http://{self.ip}/mcb/{command}",
headers={"Authorization": "Bearer " + self.jwt},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
)
json_data = response.json()
data[command] = json_data

View File

@@ -32,7 +32,7 @@ class InnosiliconWebAPI(BaseWebAPI):
self.jwt = None
async def auth(self):
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
try:
auth = await client.post(
f"http://{self.ip}/api/auth",
@@ -54,13 +54,13 @@ class InnosiliconWebAPI(BaseWebAPI):
) -> dict:
if not self.jwt:
await self.auth()
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
for i in range(settings.get("get_data_retries", 1)):
try:
response = await client.post(
f"http://{self.ip}/api/{command}",
headers={"Authorization": "Bearer " + self.jwt},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
json=parameters,
)
json_data = response.json()
@@ -90,13 +90,13 @@ class InnosiliconWebAPI(BaseWebAPI):
data = {k: None for k in commands}
data["multicommand"] = True
await self.auth()
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
for command in commands:
try:
response = await client.post(
f"http://{self.ip}/api/{command}",
headers={"Authorization": "Bearer " + self.jwt},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
)
json_data = response.json()
data[command] = json_data

View File

@@ -31,7 +31,7 @@ class VNishWebAPI(BaseWebAPI):
self.token = None
async def auth(self):
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
try:
auth = await client.post(
f"http://{self.ip}/api/v1/unlock",
@@ -58,7 +58,7 @@ class VNishWebAPI(BaseWebAPI):
) -> dict:
if not self.token:
await self.auth()
async with httpx.AsyncClient() as client:
async with httpx.AsyncClient(transport=settings.transport()) as client:
for i in range(settings.get("get_data_retries", 1)):
try:
auth = self.token
@@ -70,21 +70,21 @@ class VNishWebAPI(BaseWebAPI):
response = await client.post(
f"http://{self.ip}/api/v1/{command}",
headers={"Authorization": auth},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
json=parameters,
)
elif not parameters == {}:
response = await client.post(
f"http://{self.ip}/api/v1/{command}",
headers={"Authorization": auth},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
json=parameters,
)
else:
response = await client.get(
f"http://{self.ip}/api/v1/{command}",
headers={"Authorization": auth},
timeout=5,
timeout=settings.get("api_function_timeout", 5),
)
if not response.status_code == 200:
# refresh the token, retry

View File

@@ -1,7 +1,7 @@
[tool.poetry]
name = "pyasic"
version = "0.40.2"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
version = "0.41.0"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"
documentation = "https://pyasic.readthedocs.io/en/latest/"
@@ -9,12 +9,12 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
asyncssh = "^2.13.1"
httpx = "^0.24.0"
httpx = "^0.25.2"
asyncssh = "^2.14.1"
grpc-requests = "^0.1.12"
passlib = "^1.7.4"
pyaml = "^23.5.9"
pyaml = "^23.9.7"
toml = "^0.10.2"
grpc-requests = "^0.1.11"
[tool.poetry.group.dev]
optional = true

View File

@@ -22,7 +22,7 @@ from pyasic.network import MinerNetwork
class NetworkTest(unittest.TestCase):
def test_net_range(self):
net_range_str = "192.168.1.29, 192.168.1.40-192.168.1.43, 192.168.1.60"
net_range_str = ["192.168.1.29", "192.168.1.40-43", "192.168.1.60"]
net_range_list = [
"192.168.1.29",
"192.168.1.40",
@@ -32,8 +32,8 @@ class NetworkTest(unittest.TestCase):
"192.168.1.60",
]
net_1 = list(MinerNetwork(net_range_str).get_network().hosts())
net_2 = list(MinerNetwork(net_range_list).get_network().hosts())
net_1 = list(MinerNetwork.from_list(net_range_list).hosts)
net_2 = list(MinerNetwork.from_list(net_range_str).hosts)
correct_net = [
ipaddress.IPv4Address("192.168.1.29"),
@@ -51,11 +51,9 @@ class NetworkTest(unittest.TestCase):
net_1_str = "192.168.1.0"
net_1_mask = "/29"
net_1 = list(MinerNetwork(net_1_str, mask=net_1_mask).get_network().hosts())
net_1 = list(MinerNetwork.from_subnet(net_1_str + net_1_mask).hosts)
net_2 = list(
MinerNetwork("192.168.1.1-192.168.1.5, 192.168.1.6").get_network().hosts()
)
net_2 = list(MinerNetwork.from_list(["192.168.1.1-5", "192.168.1.6"]).hosts)
correct_net = [
ipaddress.IPv4Address("192.168.1.1"),
@@ -70,11 +68,10 @@ class NetworkTest(unittest.TestCase):
self.assertTrue(net_2 == correct_net)
def test_net_defaults(self):
net = MinerNetwork()
net_obj = net.get_network()
self.assertEqual(net_obj, MinerNetwork("192.168.1.0", mask=24).get_network())
self.assertEqual(net_obj, net.get_network())
net = MinerNetwork.from_subnet("192.168.1.1/24")
self.assertEqual(
net.hosts, list(ipaddress.ip_network("192.168.1.0/24").hosts())
)
if __name__ == "__main__":