reformatted, added a bunch of comments to improve readability, and added a whatsminer admin password in settings
This commit is contained in:
@@ -45,11 +45,16 @@ class MinerFactory:
|
||||
Parameters:
|
||||
ips: a list of ip addresses to get miners for.
|
||||
"""
|
||||
# get the event loop
|
||||
loop = asyncio.get_event_loop()
|
||||
# create a list of tasks
|
||||
scan_tasks = []
|
||||
# for each miner IP that was passed in, add a task to get its class
|
||||
for miner in ips:
|
||||
scan_tasks.append(loop.create_task(self.get_miner(miner)))
|
||||
# asynchronously run the tasks and return them as they complete
|
||||
scanned = asyncio.as_completed(scan_tasks)
|
||||
# loop through and yield the miners as they complete
|
||||
for miner in scanned:
|
||||
yield await miner
|
||||
|
||||
@@ -58,40 +63,69 @@ class MinerFactory:
|
||||
# check if the miner already exists in cache
|
||||
if ip in self.miners:
|
||||
return self.miners[ip]
|
||||
# if everything fails, the miner is already set to unknown
|
||||
miner = UnknownMiner(str(ip))
|
||||
api = None
|
||||
model = None
|
||||
|
||||
# try to get the API multiple times based on retries
|
||||
for i in range(GET_VERSION_RETRIES):
|
||||
# get the API type, should be BOSMiner, CGMiner, BMMiner, BTMiner, or None
|
||||
api = await self._get_api_type(ip)
|
||||
# if we find the API type, dont need to loop anymore
|
||||
if api:
|
||||
break
|
||||
model = None
|
||||
|
||||
# try to get the model multiple times based on retries
|
||||
for i in range(GET_VERSION_RETRIES):
|
||||
# get the model, should return some miner model type, e.g. Antminer S9
|
||||
model = await self._get_miner_model(ip)
|
||||
# if we find the model type, dont need to loop anymore
|
||||
if model:
|
||||
break
|
||||
|
||||
# make sure we have model information
|
||||
if model:
|
||||
|
||||
# check if the miner is an Antminer
|
||||
if "Antminer" in model:
|
||||
|
||||
# S9 logic
|
||||
if "Antminer S9" in model:
|
||||
|
||||
# handle the different API types
|
||||
if "BOSMiner" in api:
|
||||
miner = BOSMinerS9(str(ip))
|
||||
elif "CGMiner" in api:
|
||||
miner = CGMinerS9(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMinerS9(str(ip))
|
||||
|
||||
# X17 model logic
|
||||
elif "17" in model:
|
||||
|
||||
# handle the different API types
|
||||
if "BOSMiner" in api:
|
||||
miner = BOSMinerX17(str(ip))
|
||||
elif "CGMiner" in api:
|
||||
miner = CGMinerX17(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMinerX17(str(ip))
|
||||
|
||||
# X19 logic
|
||||
elif "19" in model:
|
||||
|
||||
# handle the different API types
|
||||
if "CGMiner" in api:
|
||||
miner = CGMinerX19(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMinerX19(str(ip))
|
||||
|
||||
# Avalonminer V8
|
||||
elif "avalon" in model:
|
||||
miner = CGMinerAvalon(str(ip))
|
||||
|
||||
# Whatsminers
|
||||
elif "M20" in model:
|
||||
miner = BTMinerM20(str(ip))
|
||||
elif "M21" in model:
|
||||
@@ -102,7 +136,11 @@ class MinerFactory:
|
||||
miner = BTMinerM31(str(ip))
|
||||
elif "M32" in model:
|
||||
miner = BTMinerM32(str(ip))
|
||||
|
||||
# if we cant find a model, check if we found the API
|
||||
else:
|
||||
|
||||
# return the miner base class with some API if we found it
|
||||
if api:
|
||||
if "BOSMiner" in api:
|
||||
miner = BOSMiner(str(ip))
|
||||
@@ -110,37 +148,69 @@ class MinerFactory:
|
||||
miner = CGMiner(str(ip))
|
||||
elif "BMMiner" in api:
|
||||
miner = BMMiner(str(ip))
|
||||
|
||||
# save the miner to the cache at its IP
|
||||
self.miners[ip] = miner
|
||||
|
||||
# return the miner
|
||||
return miner
|
||||
|
||||
def clear_cached_miners(self):
|
||||
"""Clear the miner factory cache."""
|
||||
# empty out self.miners
|
||||
self.miners = {}
|
||||
|
||||
async def _get_miner_model(self, ip: ipaddress.ip_address or str) -> dict or None:
|
||||
async def _get_miner_model(self, ip: ipaddress.ip_address or str) -> str or None:
|
||||
# instantiate model as being nothing if getting it fails
|
||||
model = None
|
||||
|
||||
# try block in case of APIError or OSError 121 (Semaphore timeout)
|
||||
try:
|
||||
|
||||
# send the devdetails command to the miner (will fail with no boards/devices)
|
||||
data = await self._send_api_command(str(ip), "devdetails")
|
||||
|
||||
# sometimes data is b'', check for that
|
||||
if data:
|
||||
# status check, make sure the command succeeded
|
||||
if data.get("STATUS"):
|
||||
if not isinstance(data["STATUS"], str):
|
||||
# if status is E, its an error
|
||||
if data["STATUS"][0].get("STATUS") not in ["I", "S"]:
|
||||
|
||||
# try an alternate method if devdetails fails
|
||||
data = await self._send_api_command(str(ip), "version")
|
||||
|
||||
# make sure we have data
|
||||
if data:
|
||||
# check the keys are there to get the version
|
||||
if data.get("VERSION"):
|
||||
if data["VERSION"][0].get("Type"):
|
||||
# save the model to be returned later
|
||||
model = data["VERSION"][0]["Type"]
|
||||
else:
|
||||
# make sure devdetails actually contains data, if its empty, there are no devices
|
||||
if "DEVDETAILS" in data.keys() and not data["DEVDETAILS"] == []:
|
||||
|
||||
# check for model, for most miners
|
||||
if not data["DEVDETAILS"][0]["Model"] == "":
|
||||
# model of most miners
|
||||
model = data["DEVDETAILS"][0]["Model"]
|
||||
|
||||
# if model fails, try driver
|
||||
else:
|
||||
# some avalonminers have model in driver
|
||||
model = data["DEVDETAILS"][0]["Driver"]
|
||||
else:
|
||||
# if all that fails, try just version
|
||||
data = await self._send_api_command(str(ip), "version")
|
||||
model = data["VERSION"][0]["Type"]
|
||||
|
||||
# if we have a model, return it
|
||||
if model:
|
||||
return model
|
||||
|
||||
# if there are errors, we just return None
|
||||
except APIError as e:
|
||||
return None
|
||||
except OSError as e:
|
||||
@@ -210,22 +280,41 @@ class MinerFactory:
|
||||
|
||||
async def _get_api_type(self, ip: ipaddress.ip_address or str) -> dict or None:
|
||||
"""Get data on the version of the miner to return the right miner."""
|
||||
# instantiate API as None in case something fails
|
||||
api = None
|
||||
|
||||
# try block to handle OSError 121 (Semaphore timeout)
|
||||
try:
|
||||
# try the version command,works on most miners
|
||||
data = await self._send_api_command(str(ip), "version")
|
||||
|
||||
# if we got data back, try to parse it
|
||||
if data:
|
||||
# make sure the command succeeded
|
||||
if data.get("STATUS") and not data.get("STATUS") == "E":
|
||||
if data["STATUS"][0].get("STATUS") in ["I", "S"]:
|
||||
|
||||
# check if there are any BMMiner strings in any of the dict keys
|
||||
if any("BMMiner" in string for string in data["VERSION"][0].keys()):
|
||||
api = "BMMiner"
|
||||
|
||||
# check if there are any CGMiner strings in any of the dict keys
|
||||
elif any("CGMiner" in string for string in data["VERSION"][0].keys()):
|
||||
api = "CGMiner"
|
||||
|
||||
# check if there are any BOSMiner strings in any of the dict keys
|
||||
elif any("BOSminer" in string for string in data["VERSION"][0].keys()):
|
||||
api = "BOSMiner"
|
||||
|
||||
# if all that fails, check the Description to see if it is a whatsminer
|
||||
elif data.get("Description") and "whatsminer" in data.get("Description"):
|
||||
api = "BTMiner"
|
||||
|
||||
# return the API if we found it
|
||||
if api:
|
||||
return api
|
||||
|
||||
# if there are errors, return None
|
||||
except OSError as e:
|
||||
if e.winerror == 121:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user