Compare commits

..

18 Commits

Author SHA1 Message Date
UpstreamData
3a81844898 bump version number 2022-08-05 12:12:25 -06:00
UpstreamData
0ac80fb205 fix a bug with vnish miner identification 2022-08-05 12:12:10 -06:00
UpstreamData
9494018c12 bump version number 2022-08-05 12:08:05 -06:00
UpstreamData
0bc86c98c5 add support for some X19 models running vnish to be able to get miner type from them 2022-08-05 12:07:33 -06:00
UpstreamData
f0d69c9ca7 bump version number 2022-08-05 10:23:22 -06:00
UpstreamData
b81590bd2e add support for X19 miner errors codes shown on their dashboard 2022-08-05 10:23:03 -06:00
UpstreamData
a53e01df6f bump version number 2022-08-02 08:19:03 -06:00
UpstreamData
f63e063954 fix a bug with not capitalizing BITMAIN for a model check 2022-08-02 08:18:46 -06:00
upstreamdata
9cbaf7076a bump version number 2022-07-28 12:29:24 -06:00
upstreamdata
daa5ac5870 fixed a bug with capitalization of "Pro" in antminer models 2022-07-28 12:28:55 -06:00
upstreamdata
0b8c08016b bump version number. 2022-07-27 23:48:42 -06:00
upstreamdata
8c768d351b fix a bug with braiinsOS+ miners which return with a capital letter in their model instead of a lowercase letter. 2022-07-27 23:44:19 -06:00
UpstreamData
c9e7fa2629 bump version number 2022-07-22 13:05:13 -06:00
UpstreamData
9d3f2b5968 add support for M20 versions and update docs 2022-07-22 13:04:47 -06:00
UpstreamData
283e3d5e11 bump version number 2022-07-21 08:43:30 -06:00
UpstreamData
add4b575c2 update shields and improve typing and handling of fault light checks 2022-07-21 08:42:35 -06:00
UpstreamData
af2f1e9ad5 misc docs changes 2022-07-20 14:54:59 -06:00
UpstreamData
8258320a7b fix a bug with avalonminer imports and bump version number 2022-07-20 14:42:33 -06:00
21 changed files with 206 additions and 42 deletions

View File

@@ -5,8 +5,8 @@
[![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/) [![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![python](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/) [![python](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/) [![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/)
![GitHub](https://img.shields.io/github/license/UpstreamData/pyasic) [![GitHub](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic) [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic)
## Documentation ## Documentation
Documentation is located on Read the Docs as [pyasic](https://pyasic.readthedocs.io/en/latest/) Documentation is located on Read the Docs as [pyasic](https://pyasic.readthedocs.io/en/latest/)

View File

@@ -5,6 +5,8 @@
[![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/) [![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![python](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/) [![python](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/) [![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/)
[![GitHub](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic)
## Intro ## Intro
Welcome to pyasic! Pyasic uses an asynchronous method of communicating with asic miners on your network, which makes it super fast. Welcome to pyasic! Pyasic uses an asynchronous method of communicating with asic miners on your network, which makes it super fast.

View File

@@ -27,6 +27,8 @@ Supported miner types are here on this list. If your miner (or miner version) i
* [VE20][pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE20] * [VE20][pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE20]
* [M32S][pyasic.miners.whatsminer.btminer.M3X.M32S.BTMinerM32S] * [M32S][pyasic.miners.whatsminer.btminer.M3X.M32S.BTMinerM32S]
* M2X Series: * M2X Series:
* [M20][pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20]:
* [V10][pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10]
* [M20S][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S]: * [M20S][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S]:
* [V10][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10] * [V10][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10]
* [V20][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20] * [V20][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20]

View File

@@ -1,6 +1,22 @@
# pyasic # pyasic
## M2X Models ## M2X Models
## M20
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20
handler: python
options:
show_root_heading: false
heading_level: 4
## M20V10
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
handler: python
options:
show_root_heading: false
heading_level: 4
## M20S ## M20S
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S

View File

@@ -57,6 +57,7 @@ class MinerData:
pool_2_url: The second pool url on the miner as a str. pool_2_url: The second pool url on the miner as a str.
pool_2_user: The second pool user on the miner as a str. pool_2_user: The second pool user on the miner as a str.
errors: A list of errors on the miner. errors: A list of errors on the miner.
fault_light: Whether or not the fault light is on as a boolean.
""" """
ip: str ip: str

View File

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

View File

@@ -14,3 +14,4 @@
from .whatsminer import WhatsminerError from .whatsminer import WhatsminerError
from .bos import BraiinsOSError from .bos import BraiinsOSError
from .X19 import X19Error

View File

@@ -112,5 +112,8 @@ class BaseMiner:
async def get_mac(self): async def get_mac(self):
return None return None
async def get_errors(self):
return None
async def get_data(self) -> MinerData: async def get_data(self) -> MinerData:
return MinerData(ip=str(self.ip)) return MinerData(ip=str(self.ip))

View File

@@ -168,6 +168,7 @@ class BMMiner(BaseMiner):
model = await self.get_model() model = await self.get_model()
hostname = await self.get_hostname() hostname = await self.get_hostname()
mac = await self.get_mac() mac = await self.get_mac()
errors = await self.get_errors()
if model: if model:
data.model = model data.model = model
@@ -178,6 +179,10 @@ class BMMiner(BaseMiner):
if mac: if mac:
data.mac = mac data.mac = mac
if errors:
for error in errors:
data.errors.append(error)
data.fault_light = await self.check_light() data.fault_light = await self.check_light()
miner_data = None miner_data = None

View File

@@ -0,0 +1,33 @@
# 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 import BaseMiner
class M20(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M20"
self.nominal_chips = 70
self.fan_count = 2
class M20V10(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M20 V10"
self.nominal_chips = 70
self.fan_count = 2

View File

@@ -28,7 +28,7 @@ class M20SV10(BaseMiner):
def __init__(self, ip: str): def __init__(self, ip: str):
super().__init__() super().__init__()
self.ip = ip self.ip = ip
self.model = "M20S" self.model = "M20S V10"
self.nominal_chips = 105 self.nominal_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -37,6 +37,6 @@ class M20SV20(BaseMiner):
def __init__(self, ip: str): def __init__(self, ip: str):
super().__init__() super().__init__()
self.ip = ip self.ip = ip
self.model = "M20S" self.model = "M20S V20"
self.nominal_chips = 111 self.nominal_chips = 111
self.fan_count = 2 self.fan_count = 2

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from .M20 import M20, M20V10
from .M20S import M20S, M20SV10, M20SV20 from .M20S import M20S, M20SV10, M20SV20
from .M20S_Plus import M20SPlus from .M20S_Plus import M20SPlus

View File

@@ -36,7 +36,7 @@ class BMMinerX17(BMMiner):
hostname = data["hostname"] hostname = data["hostname"]
return hostname return hostname
async def get_mac(self): async def get_mac(self) -> Union[str, None]:
mac = None mac = None
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi" url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
auth = httpx.DigestAuth("root", "root") auth = httpx.DigestAuth("root", "root")
@@ -62,6 +62,7 @@ class BMMinerX17(BMMiner):
if data.status_code == 200: if data.status_code == 200:
data = data.json() data = data.json()
if data["isBlinking"]: if data["isBlinking"]:
self.light = True
return True return True
return False return False
@@ -74,10 +75,13 @@ class BMMinerX17(BMMiner):
if data.status_code == 200: if data.status_code == 200:
data = data.json() data = data.json()
if not data["isBlinking"]: if not data["isBlinking"]:
self.light = False
return True return True
return False return False
async def check_light(self): async def check_light(self) -> Union[bool, None]:
if self.light:
return self.light
url = f"http://{self.ip}/cgi-bin/blink.cgi" url = f"http://{self.ip}/cgi-bin/blink.cgi"
auth = httpx.DigestAuth("root", "root") auth = httpx.DigestAuth("root", "root")
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
@@ -85,8 +89,12 @@ class BMMinerX17(BMMiner):
if data.status_code == 200: if data.status_code == 200:
data = data.json() data = data.json()
if data["isBlinking"]: if data["isBlinking"]:
self.light = True
return True return True
return False else:
self.light = False
return False
return None
async def reboot(self) -> bool: async def reboot(self) -> bool:
url = f"http://{self.ip}/cgi-bin/reboot.cgi" url = f"http://{self.ip}/cgi-bin/reboot.cgi"

View File

@@ -15,10 +15,12 @@
from pyasic.miners._backends import BMMiner # noqa - Ignore access to _module from pyasic.miners._backends import BMMiner # noqa - Ignore access to _module
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data.error_codes import X19Error
import httpx import httpx
import json import json
import asyncio import asyncio
from typing import Union, List
class BMMinerX19(BMMiner): class BMMinerX19(BMMiner):
@@ -26,7 +28,7 @@ class BMMinerX19(BMMiner):
super().__init__(ip) super().__init__(ip)
self.ip = ip self.ip = ip
async def check_light(self) -> bool: async def check_light(self) -> Union[bool, None]:
if self.light: if self.light:
return self.light return self.light
url = f"http://{self.ip}/cgi-bin/get_blink_status.cgi" url = f"http://{self.ip}/cgi-bin/get_blink_status.cgi"
@@ -36,8 +38,9 @@ class BMMinerX19(BMMiner):
if data.status_code == 200: if data.status_code == 200:
data = data.json() data = data.json()
light = data["blink"] light = data["blink"]
self.light = light
return light return light
return False return None
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi" url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
@@ -69,7 +72,7 @@ class BMMinerX19(BMMiner):
break break
await asyncio.sleep(1) await asyncio.sleep(1)
async def get_hostname(self) -> str or None: async def get_hostname(self) -> Union[str, None]:
hostname = None hostname = None
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi" url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
auth = httpx.DigestAuth("root", "root") auth = httpx.DigestAuth("root", "root")
@@ -82,7 +85,7 @@ class BMMinerX19(BMMiner):
hostname = data["hostname"] hostname = data["hostname"]
return hostname return hostname
async def get_mac(self): async def get_mac(self) -> Union[str, None]:
mac = None mac = None
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi" url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
auth = httpx.DigestAuth("root", "root") auth = httpx.DigestAuth("root", "root")
@@ -129,3 +132,18 @@ class BMMinerX19(BMMiner):
if data.status_code == 200: if data.status_code == 200:
return True return True
return False return False
async def get_errors(self) -> List[X19Error]:
errors = []
url = f"http://{self.ip}/cgi-bin/summary.cgi"
auth = httpx.DigestAuth("root", "root")
async with httpx.AsyncClient() as client:
data = await client.get(url, auth=auth)
if data:
data = data.json()
if "SUMMARY" in data.keys():
if "status" in data["SUMMARY"][0].keys():
for item in data["SUMMARY"][0]["status"]:
if not item["status"] == "s":
errors.append(X19Error(item["msg"]))
return errors

View File

@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from A8X import CGMinerA8X # noqa - Ignore access to _module from .A8X import CGMinerA8X # noqa - Ignore access to _module
from pyasic.miners._types import Avalon821 # noqa - Ignore access to _module from pyasic.miners._types import Avalon821 # noqa - Ignore access to _module

View File

@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from A8X import CGMinerA8X # noqa - Ignore access to _module from .A8X import CGMinerA8X # noqa - Ignore access to _module
from pyasic.miners._types import Avalon841 # noqa - Ignore access to _module from pyasic.miners._types import Avalon841 # noqa - Ignore access to _module

View File

@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from A8X import CGMinerA8X # noqa - Ignore access to _module from .A8X import CGMinerA8X # noqa - Ignore access to _module
from pyasic.miners._types import Avalon851 # noqa - Ignore access to _module from pyasic.miners._types import Avalon851 # noqa - Ignore access to _module

View File

@@ -45,93 +45,98 @@ import asyncssh
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner) AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)
MINER_CLASSES = { MINER_CLASSES = {
"Antminer S9": { "ANTMINER S9": {
"Default": BOSMinerS9, "Default": BOSMinerS9,
"BOSMiner": BOSMinerOld, "BOSMiner": BOSMinerOld,
"BOSMiner+": BOSMinerS9, "BOSMiner+": BOSMinerS9,
"BMMiner": BMMinerS9, "BMMiner": BMMinerS9,
"CGMiner": CGMinerS9, "CGMiner": CGMinerS9,
}, },
"Antminer S9i": { "ANTMINER S9I": {
"Default": BMMinerS9i, "Default": BMMinerS9i,
"BMMiner": BMMinerS9i, "BMMiner": BMMinerS9i,
}, },
"Antminer S17": { "ANTMINER S17": {
"Default": BMMinerS17, "Default": BMMinerS17,
"BOSMiner+": BOSMinerS17, "BOSMiner+": BOSMinerS17,
"BMMiner": BMMinerS17, "BMMiner": BMMinerS17,
"CGMiner": CGMinerS17, "CGMiner": CGMinerS17,
}, },
"Antminer S17+": { "ANTMINER S17+": {
"Default": BMMinerS17Plus, "Default": BMMinerS17Plus,
"BOSMiner+": BOSMinerS17Plus, "BOSMiner+": BOSMinerS17Plus,
"BMMiner": BMMinerS17Plus, "BMMiner": BMMinerS17Plus,
"CGMiner": CGMinerS17Plus, "CGMiner": CGMinerS17Plus,
}, },
"Antminer S17 Pro": { "ANTMINER S17 PRO": {
"Default": BMMinerS17Pro, "Default": BMMinerS17Pro,
"BOSMiner+": BOSMinerS17Pro, "BOSMiner+": BOSMinerS17Pro,
"BMMiner": BMMinerS17Pro, "BMMiner": BMMinerS17Pro,
"CGMiner": CGMinerS17Pro, "CGMiner": CGMinerS17Pro,
}, },
"Antminer S17e": { "ANTMINER S17E": {
"Default": BMMinerS17e, "Default": BMMinerS17e,
"BOSMiner+": BOSMinerS17e, "BOSMiner+": BOSMinerS17e,
"BMMiner": BMMinerS17e, "BMMiner": BMMinerS17e,
"CGMiner": CGMinerS17e, "CGMiner": CGMinerS17e,
}, },
"Antminer T17": { "ANTMINER T17": {
"Default": BMMinerT17, "Default": BMMinerT17,
"BOSMiner+": BOSMinerT17, "BOSMiner+": BOSMinerT17,
"BMMiner": BMMinerT17, "BMMiner": BMMinerT17,
"CGMiner": CGMinerT17, "CGMiner": CGMinerT17,
}, },
"Antminer T17+": { "ANTMINER T17+": {
"Default": BMMinerT17Plus, "Default": BMMinerT17Plus,
"BOSMiner+": BOSMinerT17Plus, "BOSMiner+": BOSMinerT17Plus,
"BMMiner": BMMinerT17Plus, "BMMiner": BMMinerT17Plus,
"CGMiner": CGMinerT17Plus, "CGMiner": CGMinerT17Plus,
}, },
"Antminer T17e": { "ANTMINER T17E": {
"Default": BMMinerT17e, "Default": BMMinerT17e,
"BOSMiner+": BOSMinerT17e, "BOSMiner+": BOSMinerT17e,
"BMMiner": BMMinerT17e, "BMMiner": BMMinerT17e,
"CGMiner": CGMinerT17e, "CGMiner": CGMinerT17e,
}, },
"Antminer S19": { "ANTMINER S19": {
"Default": BMMinerS19, "Default": BMMinerS19,
"BOSMiner+": BOSMinerS19, "BOSMiner+": BOSMinerS19,
"BMMiner": BMMinerS19, "BMMiner": BMMinerS19,
"CGMiner": CGMinerS19, "CGMiner": CGMinerS19,
}, },
"Antminer S19 Pro": { "ANTMINER S19 PRO": {
"Default": BMMinerS19Pro, "Default": BMMinerS19Pro,
"BOSMiner+": BOSMinerS19Pro, "BOSMiner+": BOSMinerS19Pro,
"BMMiner": BMMinerS19Pro, "BMMiner": BMMinerS19Pro,
"CGMiner": CGMinerS19Pro, "CGMiner": CGMinerS19Pro,
}, },
"Antminer S19j": { "ANTMINER S19J": {
"Default": BMMinerS19j, "Default": BMMinerS19j,
"BOSMiner+": BOSMinerS19j, "BOSMiner+": BOSMinerS19j,
"BMMiner": BMMinerS19j, "BMMiner": BMMinerS19j,
"CGMiner": CGMinerS19j, "CGMiner": CGMinerS19j,
}, },
"Antminer S19j Pro": { "ANTMINER S19J PRO": {
"Default": BMMinerS19jPro, "Default": BMMinerS19jPro,
"BOSMiner+": BOSMinerS19jPro, "BOSMiner+": BOSMinerS19jPro,
"BMMiner": BMMinerS19jPro, "BMMiner": BMMinerS19jPro,
"CGMiner": CGMinerS19jPro, "CGMiner": CGMinerS19jPro,
}, },
"Antminer S19a": { "ANTMINER S19A": {
"Default": BMMinerS19a, "Default": BMMinerS19a,
"BMMiner": BMMinerS19a, "BMMiner": BMMinerS19a,
}, },
"Antminer T19": { "ANTMINER T19": {
"Default": BMMinerT19, "Default": BMMinerT19,
"BOSMiner+": BOSMinerT19, "BOSMiner+": BOSMinerT19,
"BMMiner": BMMinerT19, "BMMiner": BMMinerT19,
"CGMiner": CGMinerT19, "CGMiner": CGMinerT19,
}, },
"M20": {
"Default": BTMinerM20,
"BTMiner": BTMinerM20,
"10": BTMinerM20V10,
},
"M20S": { "M20S": {
"Default": BTMinerM20S, "Default": BTMinerM20S,
"BTMiner": BTMinerM20S, "BTMiner": BTMinerM20S,
@@ -426,9 +431,9 @@ class MinerFactory(metaclass=Singleton):
if board_name: if board_name:
if board_name == "am1-s9": if board_name == "am1-s9":
model = "Antminer S9" model = "ANTMINER S9"
if board_name == "am2-s17": if board_name == "am2-s17":
model = "Antminer S17" model = "ANTMINER S17"
api = "BOSMiner+" api = "BOSMiner+"
return model, api, None return model, api, None
@@ -441,7 +446,7 @@ class MinerFactory(metaclass=Singleton):
if data.status_code == 200: if data.status_code == 200:
data = data.json() data = data.json()
if "minertype" in data.keys(): if "minertype" in data.keys():
model = data["minertype"] model = data["minertype"].upper()
if "bmminer" in "\t".join(data.keys()): if "bmminer" in "\t".join(data.keys()):
api = "BMMiner" api = "BMMiner"
except Exception as e: except Exception as e:
@@ -454,15 +459,15 @@ class MinerFactory(metaclass=Singleton):
# check for model, for most miners # check for model, for most miners
if not devdetails["DEVDETAILS"][0]["Model"] == "": if not devdetails["DEVDETAILS"][0]["Model"] == "":
# model of most miners # model of most miners
model = devdetails["DEVDETAILS"][0]["Model"] model = devdetails["DEVDETAILS"][0]["Model"].upper()
# if model fails, try driver # if model fails, try driver
else: else:
# some avalonminers have model in driver # some avalonminers have model in driver
model = devdetails["DEVDETAILS"][0]["Driver"] model = devdetails["DEVDETAILS"][0]["Driver"].upper()
else: else:
if "s9" in devdetails["STATUS"][0]["Description"]: if "s9" in devdetails["STATUS"][0]["Description"]:
model = "Antminer S9" model = "ANTMINER S9"
# if we have version we can get API type from here # if we have version we can get API type from here
if version: if version:
@@ -497,7 +502,7 @@ class MinerFactory(metaclass=Singleton):
# check for avalonminers # check for avalonminers
if version["VERSION"][0].get("PROD"): if version["VERSION"][0].get("PROD"):
_data = version["VERSION"][0]["PROD"].split("-") _data = version["VERSION"][0]["PROD"].split("-")
model = _data[0] model = _data[0].upper()
if len(data) > 1: if len(data) > 1:
ver = _data[1] ver = _data[1]
elif version["VERSION"][0].get("MODEL"): elif version["VERSION"][0].get("MODEL"):
@@ -522,11 +527,23 @@ class MinerFactory(metaclass=Singleton):
): ):
# try to get "Type" which is model # try to get "Type" which is model
if version["VERSION"][0].get("Type"): if version["VERSION"][0].get("Type"):
model = version["VERSION"][0]["Type"] model = version["VERSION"][0]["Type"].upper()
# braiins OS bug check just in case # braiins OS bug check just in case
elif "am2-s17" in version["STATUS"][0]["Description"]: elif "am2-s17" in version["STATUS"][0]["Description"]:
model = "Antminer S17" model = "ANTMINER S17"
if not model:
stats = await self._send_api_command(str(ip), "stats")
if stats:
if stats["STATS"][0].get("Type"):
_model = stats["STATS"][0]["Type"].upper()
if " BB" in _model:
_model = _model.split(" BB")[0]
if " XILINX" in _model:
_model = _model.split(" XILINX")[0]
if "PRO" in _model and not " PRO" in _model:
model = _model.replace("PRO", " PRO")
if model: if model:
# whatsminer have a V in their version string (M20SV41), remove everything after it # whatsminer have a V in their version string (M20SV41), remove everything after it
@@ -535,9 +552,9 @@ class MinerFactory(metaclass=Singleton):
if len(_ver) > 1: if len(_ver) > 1:
ver = model.split("V")[1] ver = model.split("V")[1]
model = model.split("V")[0] model = model.split("V")[0]
# don't need "Bitmain", just "Antminer XX" as model # don't need "Bitmain", just "ANTMINER XX" as model
if "Bitmain " in model: if "BITMAIN " in model:
model = model.replace("Bitmain ", "") model = model.replace("BITMAIN ", "")
return model, api, ver return model, api, ver
@staticmethod @staticmethod

View File

@@ -0,0 +1,31 @@
# 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 BTMiner # noqa - Ignore access to _module
from pyasic.miners._types import ( # noqa - Ignore access to _module
M20,
M20V10,
)
class BTMinerM20(BTMiner, M20):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip
class BTMinerM20V10(BTMiner, M20V10):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from .M20 import BTMinerM20, BTMinerM20V10
from .M20S import BTMinerM20S, BTMinerM20SV10, BTMinerM20SV20 from .M20S import BTMinerM20S, BTMinerM20SV10, BTMinerM20SV20
from .M20S_Plus import BTMinerM20SPlus from .M20S_Plus import BTMinerM20SPlus

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.13.0" version = "0.14.6"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH." description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
authors = ["UpstreamData <brett@upstreamdata.ca>"] authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic" repository = "https://github.com/UpstreamData/pyasic"