Compare commits

..

9 Commits

Author SHA1 Message Date
UpstreamData
5be3187eec version: bump version number. 2023-06-29 16:51:29 -06:00
UpstreamData
be1e9127b0 bug: fix weird pool info when multiple groups are defined. 2023-06-29 16:51:15 -06:00
UpstreamData
13572c4770 version: bump version number. 2023-06-29 16:48:09 -06:00
UpstreamData
08fa3961fe bug: fix bosminer on X19 not reporting pause mode correctly. 2023-06-29 16:47:27 -06:00
UpstreamData
b5d2809e9c format: remove random print statements. 2023-06-29 15:53:53 -06:00
UpstreamData
aa538d3079 docs: add miner docs generation file. 2023-06-28 11:21:19 -06:00
UpstreamData
e1500bb75c docs: update docs. 2023-06-28 11:20:22 -06:00
UpstreamData
7f00a65598 version: bump version number. 2023-06-27 16:23:46 -06:00
UpstreamData
64c473a7d4 bug: fix improper stats key when getting uptime. 2023-06-27 16:23:21 -06:00
14 changed files with 233 additions and 43 deletions

163
docs/generate_miners.py Normal file
View File

@@ -0,0 +1,163 @@
import asyncio
import importlib
import os
import warnings
from pyasic.miners.miner_factory import MINER_CLASSES, MinerTypes
warnings.filterwarnings("ignore")
def path(cls):
module = importlib.import_module(cls.__module__)
return module.__name__ + "." + cls.__name__
def make(cls):
p = path(cls)
return p.split(".")[2]
def model_type(cls):
p = path(cls)
return p.split(".")[4]
def backend_str(backend: MinerTypes) -> str:
match backend:
case MinerTypes.ANTMINER:
return "Stock Firmware Antminers"
case MinerTypes.AVALONMINER:
return "Stock Firmware Avalonminers"
case MinerTypes.VNISH:
return "Vnish Firmware Miners"
case MinerTypes.BRAIINS_OS:
return "BOS+ Firmware Miners"
case MinerTypes.HIVEON:
return "HiveOS Firmware Miners"
case MinerTypes.INNOSILICON:
return "Stock Firmware Innosilicons"
case MinerTypes.WHATSMINER:
return "Stock Firmware Whatsminers"
case MinerTypes.GOLDSHELL:
return "Stock Firmware Goldshells"
case MinerTypes.LUX_OS:
return "LuxOS Firmware Miners"
def create_url_str(mtype: str):
return (
mtype.lower()
.replace(" ", "-")
.replace("(", "")
.replace(")", "")
.replace("+", "_1")
)
HEADER_FORMAT = "# pyasic\n## {} Models\n\n"
MINER_HEADER_FORMAT = "## {}\n"
DATA_FORMAT = """::: {}
handler: python
options:
show_root_heading: false
heading_level: 4
"""
SUPPORTED_TYPES_HEADER = """# pyasic
## Supported Miners
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
##### pyasic currently supports the following miners and subtypes:
<style>
details {
margin:0px;
padding-top:0px;
padding-bottom:0px;
}
</style>
"""
BACKEND_TYPE_HEADER = """
<details>
<summary>{}:</summary>
<ul>"""
MINER_TYPE_HEADER = """
<details>
<summary>{} Series:</summary>
<ul>"""
MINER_DETAILS = """
<li><a href="../{}/{}#{}">{}</a></li>"""
MINER_TYPE_CLOSER = """
</ul>
</details>"""
BACKEND_TYPE_CLOSER = """
</ul>
</details>"""
m_data = {}
for m in MINER_CLASSES:
for t in MINER_CLASSES[m]:
if t is not None:
miner = MINER_CLASSES[m][t]
if make(miner) not in m_data:
m_data[make(miner)] = {}
if model_type(miner) not in m_data[make(miner)]:
m_data[make(miner)][model_type(miner)] = []
m_data[make(miner)][model_type(miner)].append(miner)
async def create_directory_structure(directory, data):
if not os.path.exists(directory):
os.makedirs(directory)
for key, value in data.items():
subdirectory = os.path.join(directory, key)
if isinstance(value, dict):
await create_directory_structure(subdirectory, value)
elif isinstance(value, list):
file_path = os.path.join(subdirectory + ".md")
with open(file_path, "w") as file:
file.write(HEADER_FORMAT.format(key))
for item in value:
header = await item("1.1.1.1").get_model()
file.write(MINER_HEADER_FORMAT.format(header))
file.write(DATA_FORMAT.format(path(item)))
async def create_supported_types(directory):
with open(os.path.join(directory, "supported_types.md"), "w") as file:
file.write(SUPPORTED_TYPES_HEADER)
for mback in MINER_CLASSES:
backend_types = {}
file.write(BACKEND_TYPE_HEADER.format(backend_str(mback)))
for mtype in MINER_CLASSES[mback]:
if mtype is None:
continue
m = MINER_CLASSES[mback][mtype]
if model_type(m) not in backend_types:
backend_types[model_type(m)] = []
backend_types[model_type(m)].append(m)
for mtype in backend_types:
file.write(MINER_TYPE_HEADER.format(mtype))
for minstance in backend_types[mtype]:
model = await minstance("1.1.1.1").get_model()
file.write(
MINER_DETAILS.format(
make(minstance), mtype, create_url_str(model), model
)
)
file.write(MINER_TYPE_CLOSER)
file.write(BACKEND_TYPE_CLOSER)
root_directory = os.path.join(os.getcwd(), "miners")
asyncio.run(create_directory_structure(root_directory, m_data))
asyncio.run(create_supported_types(root_directory))

View File

@@ -127,6 +127,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19 No PIC (VNish)
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro (VNish) ## S19 Pro (VNish)
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19Pro ::: pyasic.miners.antminer.vnish.X19.S19.VNishS19Pro
handler: python handler: python

View File

@@ -50,3 +50,10 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S9 (LuxOS)
::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -10,6 +10,9 @@ details {
padding-top:0px; padding-top:0px;
padding-bottom:0px; padding-bottom:0px;
} }
ul {
margin:0px;
}
</style> </style>
<details> <details>
@@ -419,6 +422,7 @@ details {
<summary>X19 Series:</summary> <summary>X19 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X19#s19-vnish">S19 (VNish)</a></li> <li><a href="../antminer/X19#s19-vnish">S19 (VNish)</a></li>
<li><a href="../antminer/X19#s19-no-pic-vnish">S19 No PIC (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li> <li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li> <li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li> <li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
@@ -439,4 +443,15 @@ details {
</ul> </ul>
</details> </details>
</ul> </ul>
</details> </details>
<details>
<summary>LuxOS Firmware Miners:</summary>
<ul>
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="../antminer/X9#s9-luxos">S9 (LuxOS)</a></li>
</ul>
</details>
</ul>
</details>

View File

@@ -257,7 +257,7 @@ class AntminerModern(BMMiner):
if api_stats: if api_stats:
try: try:
return int(api_stats["STATS"][0]["Elapsed"]) return int(api_stats["STATS"][1]["Elapsed"])
except LookupError: except LookupError:
pass pass
@@ -502,6 +502,6 @@ class AntminerOld(CGMiner):
if api_stats: if api_stats:
try: try:
return int(api_stats["STATS"][0]["Elapsed"]) return int(api_stats["STATS"][1]["Elapsed"])
except LookupError: except LookupError:
pass pass

View File

@@ -18,6 +18,7 @@ from typing import List, Optional
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import HashBoard from pyasic.data import HashBoard
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends import BFGMiner from pyasic.miners.backends import BFGMiner
from pyasic.web.goldshell import GoldshellWebAPI from pyasic.web.goldshell import GoldshellWebAPI
@@ -138,7 +139,7 @@ class BFGMinerGoldshell(BFGMiner):
except KeyError: except KeyError:
pass pass
else: else:
print(self, api_devs) logger.error(self, api_devs)
if not api_devdetails: if not api_devdetails:
try: try:
@@ -156,7 +157,7 @@ class BFGMinerGoldshell(BFGMiner):
except KeyError: except KeyError:
pass pass
else: else:
print(self, api_devdetails) logger.error(self, api_devdetails)
return hashboards return hashboards

View File

@@ -370,6 +370,6 @@ class BMMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
return int(api_stats["STATS"][0]["Elapsed"]) return int(api_stats["STATS"][1]["Elapsed"])
except LookupError: except LookupError:
pass pass

View File

@@ -174,7 +174,7 @@ BOSMINER_DATA_LOC = {
}, },
"is_mining": { "is_mining": {
"cmd": "is_mining", "cmd": "is_mining",
"kwargs": {"api_tunerstatus": {"api": "tunerstatus"}}, "kwargs": {"api_devdetails": {"api": "devdetails"}},
}, },
"uptime": { "uptime": {
"cmd": "get_uptime", "cmd": "get_uptime",
@@ -1072,22 +1072,16 @@ class BOSMiner(BaseMiner):
except (IndexError, KeyError): except (IndexError, KeyError):
pass pass
async def is_mining(self, api_tunerstatus: dict = None) -> Optional[bool]: async def is_mining(self, api_devdetails: dict = None) -> Optional[bool]:
if not api_tunerstatus: if not api_devdetails:
try: try:
api_tunerstatus = await self.api.tunerstatus() api_devdetails = await self.api.send_command("devdetails", ignore_errors=True, allow_warning=False)
except APIError: except APIError:
pass pass
if api_tunerstatus: if api_devdetails:
try: try:
running = any( return not api_devdetails["STATUS"][0]["Msg"] == "Unavailable"
[
d["TunerRunning"]
for d in api_tunerstatus["TUNERSTATUS"][0]["TunerChainStatus"]
]
)
return running
except LookupError: except LookupError:
pass pass

View File

@@ -393,6 +393,6 @@ class CGMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
return int(api_stats["STATS"][0]["Elapsed"]) return int(api_stats["STATS"][1]["Elapsed"])
except LookupError: except LookupError:
pass pass

View File

@@ -442,6 +442,6 @@ class LUXMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
return int(api_stats["STATS"][0]["Elapsed"]) return int(api_stats["STATS"][1]["Elapsed"])
except LookupError: except LookupError:
pass pass

View File

@@ -17,6 +17,7 @@
from typing import Optional from typing import Optional
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.bmminer import BMMiner
from pyasic.web.vnish import VNishWebAPI from pyasic.web.vnish import VNishWebAPI
@@ -144,7 +145,7 @@ class VNish(BMMiner):
float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2 float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2
) )
except (IndexError, KeyError, ValueError, TypeError) as e: except (IndexError, KeyError, ValueError, TypeError) as e:
print(e) logger.error(e)
pass pass
async def get_wattage_limit(self, web_settings: dict = None) -> Optional[int]: async def get_wattage_limit(self, web_settings: dict = None) -> Optional[int]:

View File

@@ -24,6 +24,7 @@ import asyncssh
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard, MinerData from pyasic.data import Fan, HashBoard, MinerData
from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.logger import logger
class BaseMiner(ABC): class BaseMiner(ABC):
@@ -397,7 +398,7 @@ class BaseMiner(ABC):
if fn_args[arg_name].get("web"): if fn_args[arg_name].get("web"):
web_multicommand.append(fn_args[arg_name]["web"]) web_multicommand.append(fn_args[arg_name]["web"])
except KeyError as e: except KeyError as e:
print(e, data_name) logger.error(e, data_name)
continue continue
api_multicommand = list(set(api_multicommand)) api_multicommand = list(set(api_multicommand))
@@ -426,23 +427,26 @@ class BaseMiner(ABC):
fn_args = self.data_locations[data_name]["kwargs"] fn_args = self.data_locations[data_name]["kwargs"]
args_to_send = {k: None for k in fn_args} args_to_send = {k: None for k in fn_args}
for arg_name in fn_args: for arg_name in fn_args:
if fn_args[arg_name].get("api"): try:
if api_command_data.get("multicommand"): if fn_args[arg_name].get("api"):
args_to_send[arg_name] = api_command_data[ if api_command_data.get("multicommand"):
fn_args[arg_name]["api"] args_to_send[arg_name] = api_command_data[
][0] fn_args[arg_name]["api"]
else: ][0]
args_to_send[arg_name] = api_command_data
if fn_args[arg_name].get("web"):
if web_command_data is not None:
if web_command_data.get("multicommand"):
args_to_send[arg_name] = web_command_data[
fn_args[arg_name]["web"]
]
else: else:
if not web_command_data == {"multicommand": False}: args_to_send[arg_name] = api_command_data
args_to_send[arg_name] = web_command_data if fn_args[arg_name].get("web"):
except (KeyError, IndexError): if web_command_data is not None:
if web_command_data.get("multicommand"):
args_to_send[arg_name] = web_command_data[
fn_args[arg_name]["web"]
]
else:
if not web_command_data == {"multicommand": False}:
args_to_send[arg_name] = web_command_data
except LookupError:
args_to_send[arg_name] = None
except LookupError as e:
continue continue
function = getattr(self, self.data_locations[data_name]["cmd"]) function = getattr(self, self.data_locations[data_name]["cmd"])
@@ -457,8 +461,8 @@ class BaseMiner(ABC):
except KeyError: except KeyError:
pass pass
if len(pools_data) > 1: if len(pools_data) > 1:
miner_data["pool_2_url"] = pools_data[1]["pool_2_url"] miner_data["pool_2_url"] = pools_data[1]["pool_1_url"]
miner_data["pool_2_user"] = pools_data[1]["pool_2_user"] miner_data["pool_2_user"] = pools_data[1]["pool_1_user"]
miner_data[ miner_data[
"pool_split" "pool_split"
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}" ] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"

View File

@@ -146,8 +146,6 @@ class BOSMinerWebAPI(BaseWebAPI):
data = await client.get( data = await client.get(
f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"} f"http://{self.ip}{path}", headers={"User-Agent": "BTC Tools v0.1"}
) )
print(data.status_code)
print(data.text)
if data.status_code == 200: if data.status_code == 200:
return data.json() return data.json()
if ignore_errors: if ignore_errors:

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.36.3" version = "0.36.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"