Compare commits

...

29 Commits

Author SHA1 Message Date
UpstreamData
2d4bf4e847 version: bump version number 2022-11-14 11:20:54 -07:00
UpstreamData
774e3d1a62 bug: fix wattage not setting correctly for some BOS+ miners with issues. 2022-11-14 11:20:27 -07:00
UpstreamData
ade3cd6fee version: bump version number 2022-11-14 09:56:18 -07:00
UpstreamData
28dc3ccb84 version: bump version number 2022-11-14 09:51:34 -07:00
UpstreamData
6ca8582ec3 bug: improve robustness of identifying new versions of bosminer running BOSer. 2022-11-14 09:49:19 -07:00
UpstreamData
74b4aeb44a version: bump version number 2022-11-14 09:14:28 -07:00
UpstreamData
9c7ab5ac57 bug: fix support for new miners that return a BOSer string in version data. 2022-11-14 09:10:18 -07:00
Upstream Data
65ecf1fea2 version: bump version number 2022-11-13 19:36:25 -07:00
Upstream Data
44142c658b format: remove print statements 2022-11-13 19:36:07 -07:00
Upstream Data
25a205ce6c version: bump version number 2022-11-13 19:27:55 -07:00
Upstream Data
25094084cf bug: clarify uppercasing data from __get_model_from_graphql() 2022-11-13 19:27:32 -07:00
Upstream Data
4eac601153 version: bump version number 2022-11-13 19:17:43 -07:00
Upstream Data
f0d8d66b9b bug: fix not handling errors in send_graphql_query 2022-11-13 19:16:48 -07:00
Upstream Data
cfa550f8c0 version: bump version number 2022-11-13 19:07:56 -07:00
Arceris
91f6a5bf41 bug: catch TypeError on temps 2022-11-13 19:06:52 -07:00
Upstream Data
464bd6be65 version: bump version number. 2022-11-13 18:58:31 -07:00
Upstream Data
031d7e2186 bug: fix missing catching IndexError 2022-11-13 18:58:05 -07:00
Upstream Data
126b0d124c version: bump version number 2022-11-13 18:54:46 -07:00
Upstream Data
81c84a3e8f bug: fix a bug with no try except blocks on pools. 2022-11-13 18:54:26 -07:00
Upstream Data
406d5bd549 version: bump version number 2022-11-13 18:21:18 -07:00
Upstream Data
cd5fe09fd9 bug: fix wrong formatting for multiple error handling 2022-11-13 18:20:15 -07:00
Upstream Data
766fc4efed bug: fix check light not responding properly from graphQL. 2022-11-13 17:06:35 -07:00
Upstream Data
b70fed40c8 bug: add try except statements to get_data to fix bugs with getting data from graphql. 2022-11-13 17:03:41 -07:00
Upstream Data
255d98fd08 bug: fix some issues with validating data from graphql properly 2022-11-13 16:48:22 -07:00
Upstream Data
78304631f7 bug: fix errors not registering properly on braiins OS miners 2022-11-13 16:34:26 -07:00
Upstream Data
ab230844fc bug: fix an issue with chip count not receiving properly in some cases. 2022-11-13 16:31:41 -07:00
Upstream Data
8a58cb9fd3 format: remove extra print 2022-11-13 16:28:36 -07:00
Upstream Data
70b6ed73dc bug: fix manual return from __get_devdetails_and_version 2022-11-13 16:27:42 -07:00
Upstream Data
d2400bf44e bug: fix identifying stock as bosminer. 2022-11-13 16:22:57 -07:00
4 changed files with 163 additions and 76 deletions

View File

@@ -68,20 +68,23 @@ class BOSMiner(BaseMiner):
async def send_graphql_query(self, query) -> Union[dict, None]:
url = f"http://{self.ip}/graphql"
async with httpx.AsyncClient() as client:
_auth = await client.post(
url,
json={
"query": 'mutation{auth{login(username:"'
+ self.uname
+ '", password:"'
+ self.pwd
+ '"){__typename}}}'
},
)
d = await client.post(url, json={"query": query})
if d.status_code == 200:
return d.json()
try:
async with httpx.AsyncClient() as client:
_auth = await client.post(
url,
json={
"query": 'mutation{auth{login(username:"'
+ self.uname
+ '", password:"'
+ self.pwd
+ '"){__typename}}}'
},
)
d = await client.post(url, json={"query": query})
if d.status_code == 200:
return d.json()
except httpx.ReadError:
return None
return None
async def fault_light_on(self) -> bool:
@@ -276,8 +279,11 @@ class BOSMiner(BaseMiner):
return self.light
# get light through GraphQL
if data := await self.send_graphql_query("{bos {faultLight}}"):
self.light = data["data"]["bos"]["faultLight"]
return self.light
try:
self.light = data["data"]["bos"]["faultLight"]
return self.light
except (TypeError, KeyError, ValueError, IndexError):
pass
# get light via ssh if that fails (10x slower)
data = (
@@ -478,7 +484,7 @@ class BOSMiner(BaseMiner):
wattage_limit = tuner[0].get("PowerLimit")
if wattage_limit:
data.wattage_limit = wattage_limit
if wattage:
if wattage is not None:
data.wattage = wattage
chain_status = tuner[0].get("TunerChainStatus")
@@ -548,57 +554,116 @@ class BOSMiner(BaseMiner):
data.mac = await self.get_mac()
data.model = await self.get_model()
data.hostname = query_data["bos"]["hostname"]
data.hashrate = round(
query_data["bosminer"]["info"]["workSolver"]["realHashrate"]["mhs1M"]
/ 1000000,
2,
)
if query_data.get("bos"):
if query_data["bos"].get("hostname"):
data.hostname = query_data["bos"]["hostname"]
boards = query_data["bosminer"]["info"]["workSolver"]["childSolvers"]
offset = 6 if int(boards[0]["name"]) in [6, 7, 8] else int(boards[0]["name"])
for hb in boards:
_id = int(hb["name"]) - offset
board = data.hashboards[_id]
board.hashrate = round(hb["realHashrate"]["mhs1M"] / 1000000, 2)
temps = hb["temperatures"]
if len(temps) > 0:
board.temp = round(hb["temperatures"][0]["degreesC"])
if len(temps) > 1:
board.chip_temp = round(hb["temperatures"][1]["degreesC"])
board.chips = hb["hwDetails"]["chips"]
board.missing = False
if hb["tuner"]["statusMessages"][0] not in [
"Stable",
"Testing performance profile",
"Tuning individual chips"
]:
data.errors.append(
BraiinsOSError(f"Slot {_id} {hb['tuner']['statusMessages'][0]}")
try:
if query_data["bosminer"]["info"]["workSolver"]["realHashrate"].get("mhs1M"):
data.hashrate = round(
query_data["bosminer"]["info"]["workSolver"]["realHashrate"]["mhs1M"]
/ 1000000,
2,
)
except (TypeError, KeyError, ValueError, IndexError):
pass
boards = None
if query_data.get("bosminer"):
if query_data["bosminer"].get("info"):
if query_data["bosminer"]["info"].get("workSolver"):
boards = query_data["bosminer"]["info"]["workSolver"].get("childSolvers")
if boards:
offset = 6 if int(boards[0]["name"]) in [6, 7, 8] else int(boards[0]["name"])
for hb in boards:
_id = int(hb["name"]) - offset
board = data.hashboards[_id]
board.hashrate = round(hb["realHashrate"]["mhs1M"] / 1000000, 2)
temps = hb["temperatures"]
try:
if len(temps) > 0:
board.temp = round(hb["temperatures"][0]["degreesC"])
if len(temps) > 1:
board.chip_temp = round(hb["temperatures"][1]["degreesC"])
except (TypeError, KeyError, ValueError, IndexError):
pass
details = hb.get("hwDetails")
if details:
if chips := details["chips"]:
board.chips = chips
board.missing = False
tuner = hb.get("tuner")
if tuner:
if msg := tuner.get("statusMessages"):
if len(msg) > 0:
if hb["tuner"]["statusMessages"][0] not in [
"Stable",
"Testing performance profile",
"Tuning individual chips"
]:
data.errors.append(
BraiinsOSError(f"Slot {_id} {hb['tuner']['statusMessages'][0]}")
)
try:
data.wattage = query_data["bosminer"]["info"]["workSolver"]["power"]["approxConsumptionW"]
except (TypeError, KeyError, ValueError, IndexError):
data.wattage = 0
try:
data.wattage_limit = query_data["bosminer"]["info"]["workSolver"]["power"]["limitW"]
except (TypeError, KeyError, ValueError, IndexError):
pass
data.wattage = query_data["bosminer"]["info"]["workSolver"]["power"]["approxConsumptionW"]
data.wattage_limit = query_data["bosminer"]["info"]["workSolver"]["power"]["limitW"]
for n in range(self.fan_count):
setattr(data, f"fan_{n - 1}", query_data["bosminer"]["info"]["fans"][n]["rpm"])
try:
setattr(data, f"fan_{n + 1}", query_data["bosminer"]["info"]["fans"][n]["rpm"])
except (TypeError, KeyError, ValueError, IndexError):
pass
groups = query_data["bosminer"]["config"]["groups"]
if len(groups) == 1:
data.pool_1_user = groups[0]["pools"][0]["user"]
data.pool_1_url = groups[0]["pools"][0]["url"]
data.pool_2_user = groups[0]["pools"][1]["user"]
data.pool_2_url = groups[0]["pools"][1]["url"]
data.quota = 0
else:
data.pool_1_user = groups[0]["pools"][0]["user"]
data.pool_1_url = groups[0]["pools"][0]["url"]
data.pool_2_user = groups[1]["pools"][0]["user"]
data.pool_2_url = groups[1]["pools"][0]["url"]
if groups[0]["strategy"].get("quota"):
data.quota = groups[0]["strategy"]["quota"] + "/" + groups[1]["strategy"]["quota"]
groups = None
if query_data.get("bosminer"):
if query_data["bosminer"].get("config"):
groups = query_data["bosminer"]["config"].get("groups")
if groups:
if len(groups) == 1:
try:
data.pool_1_user = groups[0]["pools"][0]["user"]
except (TypeError, KeyError, ValueError, IndexError):
pass
try:
data.pool_1_url = groups[0]["pools"][0]["url"]
except (TypeError, KeyError, ValueError, IndexError):
pass
try:
data.pool_2_user = groups[0]["pools"][1]["user"]
except (TypeError, KeyError, ValueError, IndexError):
pass
try:
data.pool_2_url = groups[0]["pools"][1]["url"]
except (TypeError, KeyError, ValueError, IndexError):
pass
data.quota = 0
else:
try:
data.pool_1_user = groups[0]["pools"][0]["user"]
except (TypeError, KeyError, ValueError, IndexError):
pass
try:
data.pool_1_url = groups[0]["pools"][0]["url"]
except (TypeError, KeyError, ValueError, IndexError):
pass
try:
data.pool_2_user = groups[1]["pools"][0]["user"]
except (TypeError, KeyError, ValueError, IndexError):
pass
try:
data.pool_2_url = groups[1]["pools"][0]["url"]
except (TypeError, KeyError, ValueError, IndexError):
pass
if groups[0]["strategy"].get("quota"):
data.quota = groups[0]["strategy"]["quota"] + "/" + groups[1]["strategy"]["quota"]
data.fault_light = await self.check_light()

View File

@@ -52,7 +52,7 @@ class BaseMiner(ABC):
return object.__new__(cls)
def __repr__(self):
return f"{'' if not self.api_type else self.api_type} {'' if not self.model else self.model}: {str(self.ip)}"
return f"{'' if not self.api_type else self.api_type}{'' if not self.model else ' ' + self.model}: {str(self.ip)}"
def __lt__(self, other):
return ipaddress.ip_address(self.ip) < ipaddress.ip_address(other.ip)

View File

@@ -337,7 +337,6 @@ class MinerFactory(metaclass=Singleton):
break
except asyncio.TimeoutError:
logging.warning(f"{ip}: Get Miner Timed Out")
miner = self._select_miner_from_classes(ip, model, api, ver)
# save the miner to the cache at its IP if its not unknown
@@ -412,7 +411,9 @@ class MinerFactory(metaclass=Singleton):
# miner refused connection on API port, we wont be able to get data this way
# try ssh
try:
_model = await self.__get_model_from_ssh(ip)
_model = await self.__get_model_from_graphql(ip)
if not _model:
_model = await self.__get_model_from_ssh(ip)
if _model:
model = _model
api = "BOSMiner+"
@@ -433,11 +434,6 @@ class MinerFactory(metaclass=Singleton):
# if we have devdetails, we can get model data from there
if devdetails:
if devdetails == {"Msg": "Disconnected"}:
model = await self.__get_model_from_graphql(ip)
api = "BOSMiner+"
return model, api, ver
for _devdetails_key in ["Model", "Driver"]:
try:
model = devdetails["DEVDETAILS"][0][_devdetails_key].upper()
@@ -445,15 +441,42 @@ class MinerFactory(metaclass=Singleton):
break
except KeyError:
continue
try:
if devdetails[0]["STATUS"][0]["Msg"]:
model = await self.__get_model_from_graphql(ip)
if model:
api = "BOSMiner+"
except (KeyError, TypeError, ValueError, IndexError):
pass
if not model:
# braiins OS bug check just in case
if "s9" in devdetails["STATUS"][0]["Description"]:
model = "ANTMINER S9"
if "s17" in version["STATUS"][0]["Description"]:
model = "ANTMINER S17"
if not api:
if "boser" in version["STATUS"][0]["Description"]:
api = "BOSMiner+"
else:
try:
_model = await self.__get_model_from_graphql(ip)
if _model:
model = _model
api = "BOSMiner+"
except (KeyError, TypeError, ValueError, IndexError):
pass
# if we have version we can get API type from here
if version:
try:
if version[0]["STATUS"][0]["Msg"]:
model = await self.__get_model_from_graphql(ip)
if model:
api = "BOSMiner+"
return model, api, ver
except (KeyError, TypeError, ValueError, IndexError):
pass
if "VERSION" in version:
api_types = ["BMMiner", "CGMiner", "BTMiner"]
# check basic API types, BOSMiner needs a special check
@@ -469,6 +492,8 @@ class MinerFactory(metaclass=Singleton):
api = "BOSMiner+"
if "BOSminer+" in version["VERSION"][0]:
api = "BOSMiner+"
if any("BOSer" in string for string in version["VERSION"][0]):
api = "BOSMiner+"
# check for avalonminers
for _version_key in ["PROD", "MODEL"]:
@@ -532,14 +557,11 @@ class MinerFactory(metaclass=Singleton):
# don't need "Bitmain", just "ANTMINER XX" as model
if "BITMAIN " in model:
model = model.replace("BITMAIN ", "")
return model, api, ver
async def __get_devdetails_and_version(
self, ip
) -> Tuple[Union[dict, None], Union[dict, None]]:
return {"Msg": "Disconnected"}, None
version = None
try:
# get device details and version data
@@ -549,7 +571,7 @@ class MinerFactory(metaclass=Singleton):
if not validation[0]:
try:
if data["version"][0]["STATUS"][0]["Msg"] == "Disconnected":
return {"Msg": "Disconnected"}, None
return data["devdetails"], data["version"]
except KeyError:
pass
raise APIError(validation[1])
@@ -606,7 +628,7 @@ class MinerFactory(metaclass=Singleton):
async with httpx.AsyncClient() as client:
d = await client.post(url, json={"query": "{bosminer {info{modelName}}}"})
if d.status_code == 200:
model = d.json()["data"]["bosminer"]["info"]["modelName"].upper()
model = (d.json()["data"]["bosminer"]["info"]["modelName"]).upper()
return model
@staticmethod

View File

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