From 4adb7dc92c13f102dd2e214fe0da031bdbf2961b Mon Sep 17 00:00:00 2001 From: 1e9abhi1e10 <2311abhiptdr@gmail.com> Date: Sat, 22 Jun 2024 08:06:44 +0530 Subject: [PATCH 01/27] feat: Add update firmware for ePIC miner --- pyasic/miners/backends/epic.py | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index a3c60fdc..bfbf73a7 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -14,6 +14,11 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +import logging +import aiofiles +import hashlib +import aiohttp +from pathlib import Path from typing import List, Optional from pyasic.config import MinerConfig @@ -450,3 +455,70 @@ class ePIC(ePICFirmware): return pool_data except LookupError: pass + + async def upgrade_firmware(self, file: Path, keepsettings: bool, password: str): + + """ + Upgrade the firmware of the ePIC miner device. + + Args: + file (Path): The local file path of the firmware to be uploaded. + keepsettings (bool): Whether to keep the current settings after the update. + password (str): The password for authentication. + + Returns: + str: Confirmation message after upgrading the firmware. + """ + try: + logging.info("Starting firmware upgrade process for ePIC miner.") + + if not file: + raise ValueError("File location must be provided for firmware upgrade.") + + # calculate the SHA256 checksum of the firmware file + sha256_hash = hashlib.sha256() + async with aiofiles.open(file, "rb") as f: + while chunk := await f.read(8192): + sha256_hash.update(chunk) + checksum = sha256_hash.hexdigest() + + # prepare the multipart/form-data request + form_data = aiohttp.FormData() + form_data.add_field('checksum', checksum) + form_data.add_field('keepsettings', str(keepsettings).lower()) + form_data.add_field('password', password) + form_data.add_field('update.zip', open(file, 'rb'), filename='update.zip') + + # Send the POST request to the ePIC miner device + async with aiohttp.ClientSession() as session: + async with session.post(f"http://{self.ip}:{self.port}/systemupdate", data=form_data) as response: + if response.status == 200: + result = await response.json() + if result.get("result"): + logging.info("Firmware upgrade process completed successfully for ePIC miner.") + return "Firmware upgrade completed successfully." + else: + error = result.get("error", "Unknown error") + logging.error(f"Firmware upgrade failed: {error}") + raise Exception(f"Firmware upgrade failed: {error}") + else: + logging.error(f"Firmware upgrade failed with status code: {response.status}") + raise Exception(f"Firmware upgrade failed with status code: {response.status}") + + except FileNotFoundError as e: + logging.error(f"File not found during the firmware upgrade process: {e}") + raise + except ValueError as e: + logging.error( + f"Validation error occurred during the firmware upgrade process: {e}" + ) + raise + except OSError as e: + logging.error(f"OS error occurred during the firmware upgrade process: {e}") + raise + except Exception as e: + logging.error( + f"An unexpected error occurred during the firmware upgrade process: {e}", + exc_info=True, + ) + raise \ No newline at end of file From 7803fa60f28e7ffe06e221b904f85739cef8f6bd Mon Sep 17 00:00:00 2001 From: 1e9abhi1e10 <2311abhiptdr@gmail.com> Date: Sat, 22 Jun 2024 08:12:03 +0530 Subject: [PATCH 02/27] removed trailing whitespace --- pyasic/miners/backends/epic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index bfbf73a7..03ee6673 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -16,8 +16,8 @@ import logging import aiofiles -import hashlib -import aiohttp +import hashlib +import aiohttp from pathlib import Path from typing import List, Optional From bd9592c19cc2653d7ffc7d22a7e68968d81153a9 Mon Sep 17 00:00:00 2001 From: 1e9abhi1e10 <2311abhiptdr@gmail.com> Date: Sun, 23 Jun 2024 08:38:35 +0530 Subject: [PATCH 03/27] Use web attribute --- pyasic/miners/backends/epic.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index 03ee6673..126bd2bd 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -490,20 +490,19 @@ class ePIC(ePICFirmware): form_data.add_field('update.zip', open(file, 'rb'), filename='update.zip') # Send the POST request to the ePIC miner device - async with aiohttp.ClientSession() as session: - async with session.post(f"http://{self.ip}:{self.port}/systemupdate", data=form_data) as response: - if response.status == 200: - result = await response.json() - if result.get("result"): - logging.info("Firmware upgrade process completed successfully for ePIC miner.") - return "Firmware upgrade completed successfully." - else: - error = result.get("error", "Unknown error") - logging.error(f"Firmware upgrade failed: {error}") - raise Exception(f"Firmware upgrade failed: {error}") + async with self.web.post(f"http://{self.ip}:{self.port}/systemupdate", data=form_data) as response: + if response.status == 200: + result = await response.json() + if result.get("result"): + logging.info("Firmware upgrade process completed successfully for ePIC miner.") + return "Firmware upgrade completed successfully." else: - logging.error(f"Firmware upgrade failed with status code: {response.status}") - raise Exception(f"Firmware upgrade failed with status code: {response.status}") + error = result.get("error", "Unknown error") + logging.error(f"Firmware upgrade failed: {error}") + raise Exception(f"Firmware upgrade failed: {error}") + else: + logging.error(f"Firmware upgrade failed with status code: {response.status}") + raise Exception(f"Firmware upgrade failed with status code: {response.status}") except FileNotFoundError as e: logging.error(f"File not found during the firmware upgrade process: {e}") From 7a3c9a34600f3fcc98b49f87f4e30d7aad950b79 Mon Sep 17 00:00:00 2001 From: Brett Rowan <121075405+b-rowan@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:35:27 -0600 Subject: [PATCH 04/27] refactor: fix hashrate return typing. --- pyasic/miners/backends/antminer.py | 19 ++++++++++--------- pyasic/miners/backends/auradine.py | 2 +- pyasic/miners/backends/avalonminer.py | 6 ++++-- pyasic/miners/backends/bfgminer.py | 6 ++++-- pyasic/miners/backends/bmminer.py | 6 ++++-- pyasic/miners/backends/braiins_os.py | 14 +++++++------- pyasic/miners/backends/btminer.py | 24 +++++++++++++++++------- pyasic/miners/backends/cgminer.py | 2 +- pyasic/miners/backends/epic.py | 8 +++++--- pyasic/miners/backends/innosilicon.py | 2 +- pyasic/miners/backends/luxminer.py | 6 ++++-- pyasic/miners/backends/marathon.py | 8 +++++--- pyasic/miners/backends/unknown.py | 6 +++--- pyasic/miners/backends/vnish.py | 2 +- pyasic/miners/base.py | 12 ++++++------ tests/miners_tests/__init__.py | 1 - 16 files changed, 73 insertions(+), 51 deletions(-) diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index 26b803c4..55a0b391 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -19,6 +19,8 @@ from typing import List, Optional, Union from pyasic.config import MinerConfig, MiningModeConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData, X19Error +from pyasic.data.pools import PoolMetrics +from pyasic.errors import APIError from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.cgminer import CGMiner from pyasic.miners.data import ( @@ -31,8 +33,6 @@ from pyasic.miners.data import ( from pyasic.rpc.antminer import AntminerRPCAPI from pyasic.ssh.antminer import AntminerModernSSH from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI -from pyasic.data.pools import PoolMetrics -from pyasic.errors import APIError ANTMINER_MODERN_DATA_LOC = DataLocations( **{ @@ -95,7 +95,7 @@ class AntminerModern(BMMiner): web: AntminerModernWebAPI _rpc_cls = AntminerRPCAPI - web: AntminerRPCAPI + rpc: AntminerRPCAPI _ssh_cls = AntminerModernSSH ssh: AntminerModernSSH @@ -156,7 +156,7 @@ class AntminerModern(BMMiner): await self.send_config(cfg) return True - async def _get_hostname(self, web_get_system_info: dict = None) -> Union[str, None]: + async def _get_hostname(self, web_get_system_info: dict = None) -> Optional[str]: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -169,7 +169,7 @@ class AntminerModern(BMMiner): except KeyError: pass - async def _get_mac(self, web_get_system_info: dict = None) -> Union[str, None]: + async def _get_mac(self, web_get_system_info: dict = None) -> Optional[str]: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -264,7 +264,9 @@ class AntminerModern(BMMiner): pass return self.light - async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_stats: dict = None + ) -> Optional[AlgoHashRate]: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -377,8 +379,7 @@ class AntminerModern(BMMiner): alive=pool_info.get("Status") == "Alive", url=pool_info.get("URL"), user=pool_info.get("User"), - index=pool_info.get("POOL") - + index=pool_info.get("POOL"), ) pools_data.append(pool_data) except LookupError: @@ -446,7 +447,7 @@ class AntminerOld(CGMiner): self.config = config await self.web.set_miner_conf(config.as_am_old(user_suffix=user_suffix)) - async def _get_mac(self) -> Union[str, None]: + async def _get_mac(self) -> Optional[str]: try: data = await self.web.get_system_info() if data: diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 37290048..b472855c 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -236,7 +236,7 @@ class Auradine(StockFirmware): except LookupError: pass - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() diff --git a/pyasic/miners/backends/avalonminer.py b/pyasic/miners/backends/avalonminer.py index da552e61..a831ce41 100644 --- a/pyasic/miners/backends/avalonminer.py +++ b/pyasic/miners/backends/avalonminer.py @@ -173,7 +173,7 @@ class AvalonMiner(CGMiner): except (KeyError, ValueError): pass - async def _get_hashrate(self, rpc_devs: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_devs: dict = None) -> Optional[AlgoHashRate]: if rpc_devs is None: try: rpc_devs = await self.rpc.devs() @@ -238,7 +238,9 @@ class AvalonMiner(CGMiner): return hashboards - async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_stats: dict = None + ) -> Optional[AlgoHashRate]: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() diff --git a/pyasic/miners/backends/bfgminer.py b/pyasic/miners/backends/bfgminer.py index e2b3cdea..dda132ea 100644 --- a/pyasic/miners/backends/bfgminer.py +++ b/pyasic/miners/backends/bfgminer.py @@ -105,7 +105,7 @@ class BFGMiner(StockFirmware): return self.fw_ver - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: # get hr from API if rpc_summary is None: try: @@ -207,7 +207,9 @@ class BFGMiner(StockFirmware): return fans - async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_stats: dict = None + ) -> Optional[AlgoHashRate]: # X19 method, not sure compatibility if rpc_stats is None: try: diff --git a/pyasic/miners/backends/bmminer.py b/pyasic/miners/backends/bmminer.py index 963ef61e..a04462dc 100644 --- a/pyasic/miners/backends/bmminer.py +++ b/pyasic/miners/backends/bmminer.py @@ -109,7 +109,7 @@ class BMMiner(StockFirmware): return self.fw_ver - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: # get hr from API if rpc_summary is None: try: @@ -223,7 +223,9 @@ class BMMiner(StockFirmware): return fans - async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_stats: dict = None + ) -> Optional[AlgoHashRate]: # X19 method, not sure compatibility if rpc_stats is None: try: diff --git a/pyasic/miners/backends/braiins_os.py b/pyasic/miners/backends/braiins_os.py index ab0c75ea..7b27909c 100644 --- a/pyasic/miners/backends/braiins_os.py +++ b/pyasic/miners/backends/braiins_os.py @@ -26,6 +26,7 @@ from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePowerTune from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import BraiinsOSError, MinerErrorData +from pyasic.data.pools import PoolMetrics from pyasic.errors import APIError from pyasic.miners.data import ( DataFunction, @@ -39,7 +40,6 @@ from pyasic.rpc.bosminer import BOSMinerRPCAPI from pyasic.ssh.braiins_os import BOSMinerSSH from pyasic.web.braiins_os import BOSerWebAPI, BOSMinerWebAPI from pyasic.web.braiins_os.proto.braiins.bos.v1 import SaveAction -from pyasic.data.pools import PoolMetrics BOSMINER_DATA_LOC = DataLocations( **{ @@ -349,7 +349,7 @@ class BOSMiner(BraiinsOSFirmware): return None return hostname - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -525,7 +525,9 @@ class BOSMiner(BraiinsOSFirmware): except (TypeError, AttributeError): return self.light - async def _get_expected_hashrate(self, rpc_devs: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_devs: dict = None + ) -> Optional[AlgoHashRate]: if rpc_devs is None: try: rpc_devs = await self.rpc.devs() @@ -600,14 +602,12 @@ class BOSMiner(BraiinsOSFirmware): url=pool_info.get("URL"), user=pool_info.get("User"), index=pool_info.get("POOL"), - ) pools_data.append(pool_data) except LookupError: pass return pools_data - async def upgrade_firmware(self, file: Path): """ Upgrade the firmware of the BOSMiner device. @@ -866,7 +866,7 @@ class BOSer(BraiinsOSFirmware): except LookupError: pass - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -883,7 +883,7 @@ class BOSer(BraiinsOSFirmware): async def _get_expected_hashrate( self, grpc_miner_details: dict = None - ) -> Optional[float]: + ) -> Optional[AlgoHashRate]: if grpc_miner_details is None: try: grpc_miner_details = await self.web.get_miner_details() diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index 7e6b6852..8427bee9 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -15,9 +15,10 @@ # ------------------------------------------------------------------------------ import logging -from typing import List, Optional -import aiofiles from pathlib import Path +from typing import List, Optional + +import aiofiles from pyasic.config import MinerConfig, MiningModeConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit @@ -388,7 +389,7 @@ class BTMiner(StockFirmware): return hostname - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -564,7 +565,9 @@ class BTMiner(StockFirmware): pass return errors - async def _get_expected_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_summary: dict = None + ) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -675,17 +678,24 @@ class BTMiner(StockFirmware): result = await self.rpc.update_firmware(upgrade_contents) - logging.info("Firmware upgrade process completed successfully for Whatsminer.") + logging.info( + "Firmware upgrade process completed successfully for Whatsminer." + ) return result except FileNotFoundError as e: logging.error(f"File not found during the firmware upgrade process: {e}") raise except ValueError as e: - logging.error(f"Validation error occurred during the firmware upgrade process: {e}") + logging.error( + f"Validation error occurred during the firmware upgrade process: {e}" + ) raise except OSError as e: logging.error(f"OS error occurred during the firmware upgrade process: {e}") raise except Exception as e: - logging.error(f"An unexpected error occurred during the firmware upgrade process: {e}", exc_info=True) + logging.error( + f"An unexpected error occurred during the firmware upgrade process: {e}", + exc_info=True, + ) raise diff --git a/pyasic/miners/backends/cgminer.py b/pyasic/miners/backends/cgminer.py index 53322aed..bb1d1ca4 100644 --- a/pyasic/miners/backends/cgminer.py +++ b/pyasic/miners/backends/cgminer.py @@ -109,7 +109,7 @@ class CGMiner(StockFirmware): return self.fw_ver - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index a3c60fdc..d303d226 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -19,9 +19,9 @@ from typing import List, Optional from pyasic.config import MinerConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData, X19Error +from pyasic.data.pools import PoolMetrics from pyasic.errors import APIError from pyasic.logger import logger -from pyasic.data.pools import PoolMetrics from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.miners.device.firmware import ePICFirmware from pyasic.web.epic import ePICWebAPI @@ -220,7 +220,7 @@ class ePIC(ePICFirmware): except KeyError: pass - async def _get_hashrate(self, web_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, web_summary: dict = None) -> Optional[AlgoHashRate]: if web_summary is None: try: web_summary = await self.web.summary() @@ -239,7 +239,9 @@ class ePIC(ePICFirmware): except (LookupError, ValueError, TypeError): pass - async def _get_expected_hashrate(self, web_summary: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, web_summary: dict = None + ) -> Optional[AlgoHashRate]: if web_summary is None: try: web_summary = await self.web.summary() diff --git a/pyasic/miners/backends/innosilicon.py b/pyasic/miners/backends/innosilicon.py index 8efe28f2..b6cc88b1 100644 --- a/pyasic/miners/backends/innosilicon.py +++ b/pyasic/miners/backends/innosilicon.py @@ -169,7 +169,7 @@ class Innosilicon(CGMiner): async def _get_hashrate( self, rpc_summary: dict = None, web_get_all: dict = None - ) -> Optional[float]: + ) -> Optional[AlgoHashRate]: if web_get_all: web_get_all = web_get_all["all"] diff --git a/pyasic/miners/backends/luxminer.py b/pyasic/miners/backends/luxminer.py index 0a6a4e38..8886e7b3 100644 --- a/pyasic/miners/backends/luxminer.py +++ b/pyasic/miners/backends/luxminer.py @@ -162,7 +162,7 @@ class LUXMiner(LuxOSFirmware): return mac - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -263,7 +263,9 @@ class LUXMiner(LuxOSFirmware): fans.append(Fan()) return fans - async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, rpc_stats: dict = None + ) -> Optional[AlgoHashRate]: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() diff --git a/pyasic/miners/backends/marathon.py b/pyasic/miners/backends/marathon.py index f9edee32..0c8b1c5f 100644 --- a/pyasic/miners/backends/marathon.py +++ b/pyasic/miners/backends/marathon.py @@ -225,7 +225,7 @@ class MaraMiner(MaraFirmware): except LookupError: pass - async def _get_hashrate(self, web_brief: dict = None) -> Optional[float]: + async def _get_hashrate(self, web_brief: dict = None) -> Optional[AlgoHashRate]: if web_brief is None: try: web_brief = await self.web.brief() @@ -271,7 +271,9 @@ class MaraMiner(MaraFirmware): pass return False - async def _get_expected_hashrate(self, web_brief: dict = None) -> Optional[float]: + async def _get_expected_hashrate( + self, web_brief: dict = None + ) -> Optional[AlgoHashRate]: if web_brief is None: try: web_brief = await self.web.brief() @@ -288,7 +290,7 @@ class MaraMiner(MaraFirmware): async def _get_wattage_limit( self, web_miner_config: dict = None - ) -> Optional[float]: + ) -> Optional[AlgoHashRate]: if web_miner_config is None: try: web_miner_config = await self.web.get_miner_config() diff --git a/pyasic/miners/backends/unknown.py b/pyasic/miners/backends/unknown.py index 5eccae04..a5ecdd3f 100644 --- a/pyasic/miners/backends/unknown.py +++ b/pyasic/miners/backends/unknown.py @@ -15,7 +15,7 @@ from typing import List, Optional, Tuple from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard from pyasic.data.error_codes import MinerErrorData from pyasic.miners.base import BaseMiner from pyasic.rpc.unknown import UnknownRPCAPI @@ -80,7 +80,7 @@ class UnknownMiner(BaseMiner): async def _get_hostname(self) -> Optional[str]: return None - async def _get_hashrate(self) -> Optional[float]: + async def _get_hashrate(self) -> Optional[AlgoHashRate]: return None async def _get_hashboards(self) -> List[HashBoard]: @@ -113,7 +113,7 @@ class UnknownMiner(BaseMiner): async def _get_fault_light(self) -> bool: return False - async def _get_expected_hashrate(self) -> Optional[float]: + async def _get_expected_hashrate(self) -> Optional[AlgoHashRate]: return None async def _is_mining(self, *args, **kwargs) -> Optional[bool]: diff --git a/pyasic/miners/backends/vnish.py b/pyasic/miners/backends/vnish.py index 4a560288..b8adc30f 100644 --- a/pyasic/miners/backends/vnish.py +++ b/pyasic/miners/backends/vnish.py @@ -193,7 +193,7 @@ class VNish(VNishFirmware, BMMiner): except KeyError: pass - async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]: + async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: # get hr from API if rpc_summary is None: try: diff --git a/pyasic/miners/base.py b/pyasic/miners/base.py index c66dc3b3..c0fd96fa 100644 --- a/pyasic/miners/base.py +++ b/pyasic/miners/base.py @@ -19,7 +19,7 @@ import warnings from typing import List, Optional, Protocol, Tuple, Type, TypeVar, Union from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard, MinerData +from pyasic.data import AlgoHashRate, Fan, HashBoard, MinerData from pyasic.data.device import DeviceInfo from pyasic.data.error_codes import MinerErrorData from pyasic.data.pools import PoolMetrics @@ -238,7 +238,7 @@ class MinerProtocol(Protocol): """ return await self._get_hostname() - async def get_hashrate(self) -> Optional[float]: + async def get_hashrate(self) -> Optional[AlgoHashRate]: """Get the hashrate of the miner and return it as a float in TH/s. Returns: @@ -318,7 +318,7 @@ class MinerProtocol(Protocol): """ return await self._get_fault_light() - async def get_expected_hashrate(self) -> Optional[float]: + async def get_expected_hashrate(self) -> Optional[AlgoHashRate]: """Get the nominal hashrate from factory if available. Returns: @@ -362,7 +362,7 @@ class MinerProtocol(Protocol): async def _get_hostname(self) -> Optional[str]: pass - async def _get_hashrate(self) -> Optional[float]: + async def _get_hashrate(self) -> Optional[AlgoHashRate]: pass async def _get_hashboards(self) -> List[HashBoard]: @@ -392,7 +392,7 @@ class MinerProtocol(Protocol): async def _get_fault_light(self) -> Optional[bool]: pass - async def _get_expected_hashrate(self) -> Optional[float]: + async def _get_expected_hashrate(self) -> Optional[AlgoHashRate]: pass async def _is_mining(self) -> Optional[bool]: @@ -561,4 +561,4 @@ class BaseMiner(MinerProtocol): self.ssh = self._ssh_cls(ip) -AnyMiner = TypeVar("AnyMiner", bound=BaseMiner) \ No newline at end of file +AnyMiner = TypeVar("AnyMiner", bound=BaseMiner) diff --git a/tests/miners_tests/__init__.py b/tests/miners_tests/__init__.py index 6beb5c49..523271a2 100644 --- a/tests/miners_tests/__init__.py +++ b/tests/miners_tests/__init__.py @@ -55,7 +55,6 @@ class MinersTest(unittest.TestCase): "expected_hashrate", "uptime", "wattage", - "voltage", "wattage_limit", "pools", ] From 923e96336916d43abc85defd93b53772ff7a4f0e Mon Sep 17 00:00:00 2001 From: Brett Rowan <121075405+b-rowan@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:09:42 -0600 Subject: [PATCH 05/27] feature: basic bitaxe support. --- pyasic/device/makes.py | 1 + pyasic/device/models.py | 10 ++ pyasic/miners/backends/bitaxe.py | 129 ++++++++++++++++++ pyasic/miners/backends/whatsminer.py | 2 +- pyasic/miners/bitaxe/__init__.py | 1 + pyasic/miners/bitaxe/espminer/BM/BM1366.py | 6 + pyasic/miners/bitaxe/espminer/BM/BM1368.py | 6 + pyasic/miners/bitaxe/espminer/BM/BM1397.py | 6 + pyasic/miners/bitaxe/espminer/BM/__init__.py | 3 + pyasic/miners/bitaxe/espminer/__init__.py | 1 + pyasic/miners/device/makes.py | 4 + .../miners/device/models/bitaxe/BM/BM1366.py | 9 ++ .../miners/device/models/bitaxe/BM/BM1368.py | 9 ++ .../miners/device/models/bitaxe/BM/BM1397.py | 9 ++ .../device/models/bitaxe/BM/__init__.py | 3 + .../miners/device/models/bitaxe/__init__.py | 1 + pyasic/miners/factory.py | 24 ++++ pyasic/ssh/braiins_os.py | 2 +- pyasic/web/bitaxe.py | 85 ++++++++++++ 19 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 pyasic/miners/backends/bitaxe.py create mode 100644 pyasic/miners/bitaxe/__init__.py create mode 100644 pyasic/miners/bitaxe/espminer/BM/BM1366.py create mode 100644 pyasic/miners/bitaxe/espminer/BM/BM1368.py create mode 100644 pyasic/miners/bitaxe/espminer/BM/BM1397.py create mode 100644 pyasic/miners/bitaxe/espminer/BM/__init__.py create mode 100644 pyasic/miners/bitaxe/espminer/__init__.py create mode 100644 pyasic/miners/device/models/bitaxe/BM/BM1366.py create mode 100644 pyasic/miners/device/models/bitaxe/BM/BM1368.py create mode 100644 pyasic/miners/device/models/bitaxe/BM/BM1397.py create mode 100644 pyasic/miners/device/models/bitaxe/BM/__init__.py create mode 100644 pyasic/miners/device/models/bitaxe/__init__.py create mode 100644 pyasic/web/bitaxe.py diff --git a/pyasic/device/makes.py b/pyasic/device/makes.py index f31bfdcd..cfb1ecbe 100644 --- a/pyasic/device/makes.py +++ b/pyasic/device/makes.py @@ -25,6 +25,7 @@ class MinerMake(str, Enum): GOLDSHELL = "Goldshell" AURADINE = "Auradine" EPIC = "ePIC" + BITAXE = "BitAxe" def __str__(self): return self.value diff --git a/pyasic/device/models.py b/pyasic/device/models.py index 883da95c..8eab6655 100644 --- a/pyasic/device/models.py +++ b/pyasic/device/models.py @@ -329,6 +329,15 @@ class AuradineModels(str, Enum): return self.value +class BitAxeModels(str, Enum): + BM1366 = "Ultra" + BM1368 = "Supra" + BM1397 = "Max" + + def __str__(self): + return self.value + + class MinerModel: ANTMINER = AntminerModels WHATSMINER = WhatsminerModels @@ -337,3 +346,4 @@ class MinerModel: GOLDSHELL = GoldshellModels AURADINE = AuradineModels EPIC = ePICModels + BITAXE = BitAxeModels diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py new file mode 100644 index 00000000..5c293c42 --- /dev/null +++ b/pyasic/miners/backends/bitaxe.py @@ -0,0 +1,129 @@ +from typing import List, Optional + +from pyasic import APIError +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit +from pyasic.miners.base import BaseMiner +from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand +from pyasic.web.bitaxe import BitAxeWebAPI + +BITAXE_DATA_LOC = DataLocations( + **{ + str(DataOptions.HASHRATE): DataFunction( + "_get_hashrate", + [WebAPICommand("web_system_info", "system_info")], + ), + str(DataOptions.WATTAGE): DataFunction( + "_get_wattage", + [WebAPICommand("web_system_info", "system_info")], + ), + str(DataOptions.UPTIME): DataFunction( + "_get_uptime", + [WebAPICommand("web_system_info", "system_info")], + ), + str(DataOptions.HASHBOARDS): DataFunction( + "_get_hashboards", + [WebAPICommand("web_system_info", "system_info")], + ), + str(DataOptions.FANS): DataFunction( + "_get_fans", + [WebAPICommand("web_system_info", "system_info")], + ), + } +) + + +class BitAxe(BaseMiner): + """Handler for BitAxe""" + + web: BitAxeWebAPI + _web_cls = BitAxeWebAPI + + data_locations = BITAXE_DATA_LOC + + async def reboot(self) -> bool: + await self.web.restart() + return True + + async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return web_system_info["power"] + except KeyError: + pass + + async def _get_hashrate( + self, web_system_info: dict = None + ) -> Optional[AlgoHashRate]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return AlgoHashRate.SHA256( + web_system_info["hashRate"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) + except KeyError: + pass + + async def _get_uptime(self, web_system_info: dict = None) -> Optional[int]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return web_system_info["uptimeSeconds"] + except KeyError: + pass + + async def _get_hashboards(self, web_system_info: dict = None) -> List[HashBoard]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return [ + HashBoard( + hashrate=AlgoHashRate.SHA256( + web_system_info["hashRate"], HashUnit.SHA256.GH + ).into(self.algo.unit.default), + chip_temp=web_system_info["temp"], + temp=web_system_info["vrTemp"], + chips=web_system_info["asicCount"], + expected_chips=self.expected_chips, + missing=False, + active=True, + voltage=web_system_info["voltage"], + ) + ] + except KeyError: + pass + return [] + + async def _get_fans(self, web_system_info: dict = None) -> List[Fan]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return [Fan(speed=web_system_info["fanrpm"])] + except KeyError: + pass + return [] diff --git a/pyasic/miners/backends/whatsminer.py b/pyasic/miners/backends/whatsminer.py index 78d2415b..0654f7fe 100644 --- a/pyasic/miners/backends/whatsminer.py +++ b/pyasic/miners/backends/whatsminer.py @@ -29,4 +29,4 @@ class M3X(BTMiner): class M2X(BTMiner): - pass \ No newline at end of file + pass diff --git a/pyasic/miners/bitaxe/__init__.py b/pyasic/miners/bitaxe/__init__.py new file mode 100644 index 00000000..6068242d --- /dev/null +++ b/pyasic/miners/bitaxe/__init__.py @@ -0,0 +1 @@ +from .espminer import * diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1366.py b/pyasic/miners/bitaxe/espminer/BM/BM1366.py new file mode 100644 index 00000000..d2507541 --- /dev/null +++ b/pyasic/miners/bitaxe/espminer/BM/BM1366.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends.bitaxe import BitAxe +from pyasic.miners.device.models.bitaxe import Ultra + + +class BitAxeUltra(BitAxe, Ultra): + pass diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1368.py b/pyasic/miners/bitaxe/espminer/BM/BM1368.py new file mode 100644 index 00000000..ea2e3b7b --- /dev/null +++ b/pyasic/miners/bitaxe/espminer/BM/BM1368.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends.bitaxe import BitAxe +from pyasic.miners.device.models.bitaxe import Supra + + +class BitAxeSupra(BitAxe, Supra): + pass diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1397.py b/pyasic/miners/bitaxe/espminer/BM/BM1397.py new file mode 100644 index 00000000..722b1d35 --- /dev/null +++ b/pyasic/miners/bitaxe/espminer/BM/BM1397.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends.bitaxe import BitAxe +from pyasic.miners.device.models.bitaxe import Max + + +class BitAxeMax(BitAxe, Max): + pass diff --git a/pyasic/miners/bitaxe/espminer/BM/__init__.py b/pyasic/miners/bitaxe/espminer/BM/__init__.py new file mode 100644 index 00000000..9926a677 --- /dev/null +++ b/pyasic/miners/bitaxe/espminer/BM/__init__.py @@ -0,0 +1,3 @@ +from .BM1366 import BitAxeUltra +from .BM1368 import BitAxeSupra +from .BM1397 import BitAxeMax diff --git a/pyasic/miners/bitaxe/espminer/__init__.py b/pyasic/miners/bitaxe/espminer/__init__.py new file mode 100644 index 00000000..dc06b2e0 --- /dev/null +++ b/pyasic/miners/bitaxe/espminer/__init__.py @@ -0,0 +1 @@ +from .BM import * diff --git a/pyasic/miners/device/makes.py b/pyasic/miners/device/makes.py index 2d8433a4..6bfbe40b 100644 --- a/pyasic/miners/device/makes.py +++ b/pyasic/miners/device/makes.py @@ -44,3 +44,7 @@ class AuradineMake(BaseMiner): class ePICMake(BaseMiner): make = MinerMake.EPIC + + +class BitAxeMake(BaseMiner): + make = MinerMake.BITAXE diff --git a/pyasic/miners/device/models/bitaxe/BM/BM1366.py b/pyasic/miners/device/models/bitaxe/BM/BM1366.py new file mode 100644 index 00000000..820ae024 --- /dev/null +++ b/pyasic/miners/device/models/bitaxe/BM/BM1366.py @@ -0,0 +1,9 @@ +from pyasic.device.models import MinerModel +from pyasic.miners.device.makes import BitAxeMake + + +class Ultra(BitAxeMake): + raw_model = MinerModel.BITAXE.BM1366 + + expected_chips = 1 + expected_fans = 1 diff --git a/pyasic/miners/device/models/bitaxe/BM/BM1368.py b/pyasic/miners/device/models/bitaxe/BM/BM1368.py new file mode 100644 index 00000000..97ea9329 --- /dev/null +++ b/pyasic/miners/device/models/bitaxe/BM/BM1368.py @@ -0,0 +1,9 @@ +from pyasic.device.models import MinerModel +from pyasic.miners.device.makes import BitAxeMake + + +class Supra(BitAxeMake): + raw_model = MinerModel.BITAXE.BM1368 + + expected_chips = 1 + expected_fans = 1 diff --git a/pyasic/miners/device/models/bitaxe/BM/BM1397.py b/pyasic/miners/device/models/bitaxe/BM/BM1397.py new file mode 100644 index 00000000..17a36ee6 --- /dev/null +++ b/pyasic/miners/device/models/bitaxe/BM/BM1397.py @@ -0,0 +1,9 @@ +from pyasic.device.models import MinerModel +from pyasic.miners.device.makes import BitAxeMake + + +class Max(BitAxeMake): + raw_model = MinerModel.BITAXE.BM1397 + + expected_chips = 1 + expected_fans = 1 diff --git a/pyasic/miners/device/models/bitaxe/BM/__init__.py b/pyasic/miners/device/models/bitaxe/BM/__init__.py new file mode 100644 index 00000000..b2c1b2c0 --- /dev/null +++ b/pyasic/miners/device/models/bitaxe/BM/__init__.py @@ -0,0 +1,3 @@ +from .BM1366 import Ultra +from .BM1368 import Supra +from .BM1397 import Max diff --git a/pyasic/miners/device/models/bitaxe/__init__.py b/pyasic/miners/device/models/bitaxe/__init__.py new file mode 100644 index 00000000..dc06b2e0 --- /dev/null +++ b/pyasic/miners/device/models/bitaxe/__init__.py @@ -0,0 +1 @@ +from .BM import * diff --git a/pyasic/miners/factory.py b/pyasic/miners/factory.py index 7a338029..4963eba1 100644 --- a/pyasic/miners/factory.py +++ b/pyasic/miners/factory.py @@ -31,8 +31,10 @@ from pyasic.miners.antminer import * from pyasic.miners.auradine import * from pyasic.miners.avalonminer import * from pyasic.miners.backends import * +from pyasic.miners.backends.bitaxe import BitAxe from pyasic.miners.backends.unknown import UnknownMiner from pyasic.miners.base import AnyMiner +from pyasic.miners.bitaxe import * from pyasic.miners.blockminer import * from pyasic.miners.device.makes import * from pyasic.miners.goldshell import * @@ -53,6 +55,7 @@ class MinerTypes(enum.Enum): EPIC = 9 AURADINE = 10 MARATHON = 11 + BITAXE = 12 MINER_CLASSES = { @@ -438,6 +441,12 @@ MINER_CLASSES = { "ANTMINER S21": MaraS21, "ANTMINER T21": MaraT21, }, + MinerTypes.BITAXE: { + None: BitAxe, + "SUPRA": BitAxeSupra, + "ULTRA": BitAxeUltra, + "MAX": BitAxeMax, + }, } @@ -514,6 +523,7 @@ class MinerFactory: MinerTypes.LUX_OS: self.get_miner_model_luxos, MinerTypes.AURADINE: self.get_miner_model_auradine, MinerTypes.MARATHON: self.get_miner_model_marathon, + MinerTypes.BITAXE: self.get_miner_model_bitaxe, } fn = miner_model_fns.get(miner_type) @@ -595,6 +605,8 @@ class MinerFactory: return MinerTypes.WHATSMINER if "Braiins OS" in web_text: return MinerTypes.BRAIINS_OS + if "AxeOS" in web_text: + return MinerTypes.BITAXE if "cloud-box" in web_text: return MinerTypes.GOLDSHELL if "AnthillOS" in web_text: @@ -1008,6 +1020,18 @@ class MinerFactory: except (TypeError, LookupError): pass + async def get_miner_model_bitaxe(self, ip: str) -> str | None: + web_json_data = await self.send_web_command(ip, "/api/system/info") + + try: + miner_model = web_json_data["devicemodel"] + if miner_model == "": + return None + + return miner_model + except (TypeError, LookupError): + pass + miner_factory = MinerFactory() diff --git a/pyasic/ssh/braiins_os.py b/pyasic/ssh/braiins_os.py index 28ad000d..6dee3502 100644 --- a/pyasic/ssh/braiins_os.py +++ b/pyasic/ssh/braiins_os.py @@ -92,4 +92,4 @@ class BOSMinerSSH(BaseSSH): Returns: str: Status of the LED. """ - return await self.send_command("cat /sys/class/leds/'Red LED'/delay_off") \ No newline at end of file + return await self.send_command("cat /sys/class/leds/'Red LED'/delay_off") diff --git a/pyasic/web/bitaxe.py b/pyasic/web/bitaxe.py new file mode 100644 index 00000000..f95922c4 --- /dev/null +++ b/pyasic/web/bitaxe.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import asyncio +import json +from typing import Any + +import httpx + +from pyasic import APIError, settings +from pyasic.web.base import BaseWebAPI + + +class BitAxeWebAPI(BaseWebAPI): + async def send_command( + self, + command: str | bytes, + ignore_errors: bool = False, + allow_warning: bool = True, + privileged: bool = False, + **parameters: Any, + ) -> dict: + url = f"http://{self.ip}:{self.port}/{command}" + try: + async with httpx.AsyncClient( + transport=settings.transport(), + ) as client: + if parameters.get("post", False): + data = await client.post( + url, + timeout=settings.get("api_function_timeout", 3), + json=parameters, + ) + else: + data = await client.get(url) + except httpx.HTTPError: + pass + else: + if data.status_code == 200: + try: + return data.json() + except json.decoder.JSONDecodeError: + pass + + async def multicommand( + self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True + ) -> dict: + """Execute multiple commands simultaneously on the Auradine miner. + + Args: + *commands (str): Commands to execute. + ignore_errors (bool): Whether to ignore errors during command execution. + allow_warning (bool): Whether to proceed despite warnings. + + Returns: + dict: A dictionary containing responses for all commands executed. + """ + tasks = {} + # send all commands individually + for cmd in commands: + tasks[cmd] = asyncio.create_task( + self.send_command(cmd, allow_warning=allow_warning) + ) + + await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True) + + data = {"multicommand": True} + for cmd in tasks: + try: + result = tasks[cmd].result() + if result is None or result == {}: + result = {} + data[cmd] = result + except APIError: + pass + + return data + + async def system_info(self): + return await self.send_command("api/system/info") + + async def swarm_info(self): + return await self.send_command("api/swarm/info") + + async def restart(self): + return await self.send_command("api/system/restart", post=True) From fca72eb747a955226a92fc3c443bf3624a4a378e Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Fri, 28 Jun 2024 22:43:13 -0600 Subject: [PATCH 06/27] bug: fix some of the issues with bitaxe. --- pyasic/config/mining/algo.py | 2 + pyasic/miners/backends/bitaxe.py | 68 ++++++++++++++++--- .../miners/device/models/bitaxe/BM/BM1366.py | 1 + .../miners/device/models/bitaxe/BM/BM1368.py | 1 + .../miners/device/models/bitaxe/BM/BM1397.py | 1 + pyasic/web/bitaxe.py | 10 +-- 6 files changed, 67 insertions(+), 16 deletions(-) diff --git a/pyasic/config/mining/algo.py b/pyasic/config/mining/algo.py index c8c00e79..96021b8f 100644 --- a/pyasic/config/mining/algo.py +++ b/pyasic/config/mining/algo.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from dataclasses import dataclass, field from pyasic.config.base import MinerConfigOption, MinerConfigValue diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py index 5c293c42..cb82b37a 100644 --- a/pyasic/miners/backends/bitaxe.py +++ b/pyasic/miners/backends/bitaxe.py @@ -10,23 +10,31 @@ BITAXE_DATA_LOC = DataLocations( **{ str(DataOptions.HASHRATE): DataFunction( "_get_hashrate", - [WebAPICommand("web_system_info", "system_info")], + [WebAPICommand("web_system_info", "system/info")], ), str(DataOptions.WATTAGE): DataFunction( "_get_wattage", - [WebAPICommand("web_system_info", "system_info")], + [WebAPICommand("web_system_info", "system/info")], ), str(DataOptions.UPTIME): DataFunction( "_get_uptime", - [WebAPICommand("web_system_info", "system_info")], + [WebAPICommand("web_system_info", "system/info")], ), str(DataOptions.HASHBOARDS): DataFunction( "_get_hashboards", - [WebAPICommand("web_system_info", "system_info")], + [WebAPICommand("web_system_info", "system/info")], ), str(DataOptions.FANS): DataFunction( "_get_fans", - [WebAPICommand("web_system_info", "system_info")], + [WebAPICommand("web_system_info", "system/info")], + ), + str(DataOptions.FW_VERSION): DataFunction( + "_get_fw_ver", + [WebAPICommand("web_system_info", "system/info")], + ), + str(DataOptions.API_VERSION): DataFunction( + "_get_api_ver", + [WebAPICommand("web_system_info", "system/info")], ), } ) @@ -50,10 +58,9 @@ class BitAxe(BaseMiner): web_system_info = await self.web.system_info() except APIError: pass - if web_system_info is not None: try: - return web_system_info["power"] + return round(web_system_info["power"]) except KeyError: pass @@ -101,13 +108,13 @@ class BitAxe(BaseMiner): hashrate=AlgoHashRate.SHA256( web_system_info["hashRate"], HashUnit.SHA256.GH ).into(self.algo.unit.default), - chip_temp=web_system_info["temp"], - temp=web_system_info["vrTemp"], - chips=web_system_info["asicCount"], + chip_temp=web_system_info.get("temp"), + temp=web_system_info.get("vrTemp"), + chips=web_system_info.get("asicCount"), expected_chips=self.expected_chips, missing=False, active=True, - voltage=web_system_info["voltage"], + voltage=web_system_info.get("voltage"), ) ] except KeyError: @@ -127,3 +134,42 @@ class BitAxe(BaseMiner): except KeyError: pass return [] + + async def _get_hostname(self, web_system_info: dict = None) -> Optional[str]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return web_system_info["hostname"] + except KeyError: + pass + + async def _get_api_ver(self, web_system_info: dict = None) -> Optional[str]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return web_system_info["version"] + except KeyError: + pass + + async def _get_fw_ver(self, web_system_info: dict = None) -> Optional[str]: + if web_system_info is None: + try: + web_system_info = await self.web.system_info() + except APIError: + pass + + if web_system_info is not None: + try: + return web_system_info["version"] + except KeyError: + pass diff --git a/pyasic/miners/device/models/bitaxe/BM/BM1366.py b/pyasic/miners/device/models/bitaxe/BM/BM1366.py index 820ae024..6db1ad16 100644 --- a/pyasic/miners/device/models/bitaxe/BM/BM1366.py +++ b/pyasic/miners/device/models/bitaxe/BM/BM1366.py @@ -5,5 +5,6 @@ from pyasic.miners.device.makes import BitAxeMake class Ultra(BitAxeMake): raw_model = MinerModel.BITAXE.BM1366 + expected_hashboards = 1 expected_chips = 1 expected_fans = 1 diff --git a/pyasic/miners/device/models/bitaxe/BM/BM1368.py b/pyasic/miners/device/models/bitaxe/BM/BM1368.py index 97ea9329..76cf63c9 100644 --- a/pyasic/miners/device/models/bitaxe/BM/BM1368.py +++ b/pyasic/miners/device/models/bitaxe/BM/BM1368.py @@ -5,5 +5,6 @@ from pyasic.miners.device.makes import BitAxeMake class Supra(BitAxeMake): raw_model = MinerModel.BITAXE.BM1368 + expected_hashboards = 1 expected_chips = 1 expected_fans = 1 diff --git a/pyasic/miners/device/models/bitaxe/BM/BM1397.py b/pyasic/miners/device/models/bitaxe/BM/BM1397.py index 17a36ee6..97230f17 100644 --- a/pyasic/miners/device/models/bitaxe/BM/BM1397.py +++ b/pyasic/miners/device/models/bitaxe/BM/BM1397.py @@ -5,5 +5,6 @@ from pyasic.miners.device.makes import BitAxeMake class Max(BitAxeMake): raw_model = MinerModel.BITAXE.BM1397 + expected_hashboards = 1 expected_chips = 1 expected_fans = 1 diff --git a/pyasic/web/bitaxe.py b/pyasic/web/bitaxe.py index f95922c4..ff4eb85b 100644 --- a/pyasic/web/bitaxe.py +++ b/pyasic/web/bitaxe.py @@ -19,7 +19,7 @@ class BitAxeWebAPI(BaseWebAPI): privileged: bool = False, **parameters: Any, ) -> dict: - url = f"http://{self.ip}:{self.port}/{command}" + url = f"http://{self.ip}:{self.port}/api/{command}" try: async with httpx.AsyncClient( transport=settings.transport(), @@ -44,7 +44,7 @@ class BitAxeWebAPI(BaseWebAPI): async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True ) -> dict: - """Execute multiple commands simultaneously on the Auradine miner. + """Execute multiple commands simultaneously on the BitAxe miner. Args: *commands (str): Commands to execute. @@ -76,10 +76,10 @@ class BitAxeWebAPI(BaseWebAPI): return data async def system_info(self): - return await self.send_command("api/system/info") + return await self.send_command("system/info") async def swarm_info(self): - return await self.send_command("api/swarm/info") + return await self.send_command("swarm/info") async def restart(self): - return await self.send_command("api/system/restart", post=True) + return await self.send_command("system/restart", post=True) From a751efe7d5fe31d51d55e733a338e4cb07b268dd Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Thu, 27 Jun 2024 09:54:57 +0100 Subject: [PATCH 07/27] dataclass: PoolUrl --- pyasic/data/pools.py | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/pyasic/data/pools.py b/pyasic/data/pools.py index 4ff4e158..7fc915b5 100644 --- a/pyasic/data/pools.py +++ b/pyasic/data/pools.py @@ -1,4 +1,24 @@ from dataclasses import dataclass, field +from enum import Enum +from typing import Optional + + +class Scheme (Enum): + STRATUM_V1 = "stratum+tcp" + STRATUM_V2 = "stratum2+tcp" + +@dataclass +class PoolUrl: + scheme: Scheme + host: str + port: int + pubKey: Optional[str] = None + + def __str__(self) -> str: + if self.scheme == Scheme.STRATUM_V2 and self.pubKey: + return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubKey}" + else: + return f"{self.scheme.value}://{self.host}:{self.port}" @dataclass @@ -19,15 +39,15 @@ class PoolMetrics: pool_stale_percent: Percentage of stale shares by the pool. """ - accepted: int = None - rejected: int = None - get_failures: int = None - remote_failures: int = None - active: bool = None - alive: bool = None - url: str = None - index: int = None - user: str = None + accepted: int + rejected: int + get_failures: int + remote_failures: int + active: bool + alive: bool + url: PoolUrl + index: int + user: str pool_rejected_percent: float = field(init=False) pool_stale_percent: float = field(init=False) From 8e4a547c773fb640fc59299b2cf2838c92d2bbe3 Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Thu, 27 Jun 2024 18:07:42 +0100 Subject: [PATCH 08/27] dataclass: PoolUrl --- pyasic/data/pools.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pyasic/data/pools.py b/pyasic/data/pools.py index 7fc915b5..4d6abfc2 100644 --- a/pyasic/data/pools.py +++ b/pyasic/data/pools.py @@ -12,7 +12,7 @@ class PoolUrl: scheme: Scheme host: str port: int - pubKey: Optional[str] = None + pubkey: Optional[str] = None def __str__(self) -> str: if self.scheme == Scheme.STRATUM_V2 and self.pubKey: @@ -39,17 +39,17 @@ class PoolMetrics: pool_stale_percent: Percentage of stale shares by the pool. """ - accepted: int - rejected: int - get_failures: int - remote_failures: int - active: bool - alive: bool - url: PoolUrl - index: int - user: str pool_rejected_percent: float = field(init=False) pool_stale_percent: float = field(init=False) + url: PoolUrl + accepted: int = None + rejected: int = None + get_failures: int = None + remote_failures: int = None + active: bool = None + alive: bool = None + index: int = None + user: str = None @property def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection From 65cfb57605b88993530da65df36bbbdb76677aef Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Thu, 27 Jun 2024 18:17:55 +0100 Subject: [PATCH 09/27] PEP8: case fix --- pyasic/data/pools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyasic/data/pools.py b/pyasic/data/pools.py index 4d6abfc2..eac1a3ef 100644 --- a/pyasic/data/pools.py +++ b/pyasic/data/pools.py @@ -15,8 +15,8 @@ class PoolUrl: pubkey: Optional[str] = None def __str__(self) -> str: - if self.scheme == Scheme.STRATUM_V2 and self.pubKey: - return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubKey}" + if self.scheme == Scheme.STRATUM_V2 and self.pubkey: + return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}" else: return f"{self.scheme.value}://{self.host}:{self.port}" From 3627194f3410b65810fdf378ea064743a7a65b65 Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Sat, 29 Jun 2024 14:59:27 -0600 Subject: [PATCH 10/27] feature: add config parsing from bitaxe. --- pyasic/config/__init__.py | 8 ++++++++ pyasic/config/fans.py | 7 +++++++ pyasic/config/pools.py | 13 +++++++++++++ pyasic/miners/backends/bitaxe.py | 6 +++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 4b1af0e9..f022dda5 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -235,3 +235,11 @@ class MinerConfig: fan_mode=FanModeConfig.from_mara(web_miner_config), mining_mode=MiningModeConfig.from_mara(web_miner_config), ) + + @classmethod + def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig": + return cls( + pools=PoolConfig.from_bitaxe(web_system_info), + fan_mode=FanModeConfig.from_bitaxe(web_system_info) + + ) \ No newline at end of file diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index ed0ef002..01b33ee5 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -291,3 +291,10 @@ class FanModeConfig(MinerConfigOption): except LookupError: pass return cls.default() + + @classmethod + def from_bitaxe(cls, web_system_info: dict): + if web_system_info["autofanspeed"] == 1: + return cls.normal() + else: + return cls.manual(speed=web_system_info["fanspeed"]) \ No newline at end of file diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 19c88262..67b0b0ca 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -194,6 +194,11 @@ class Pool(MinerConfigValue): password=web_pool["pass"], ) + @classmethod + def from_bitaxe(cls, web_system_info: dict) -> "Pool": + url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}" + return cls(url=url, user=web_system_info["stratumUser"], password=web_system_info.get("stratumPassword", "")) + @dataclass class PoolGroup(MinerConfigValue): @@ -360,6 +365,10 @@ class PoolGroup(MinerConfigValue): def from_mara(cls, web_config_pools: dict) -> "PoolGroup": return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools]) + @classmethod + def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup": + return cls(pools=[Pool.from_bitaxe(web_system_info)]) + @dataclass class PoolConfig(MinerConfigValue): @@ -514,3 +523,7 @@ class PoolConfig(MinerConfigValue): @classmethod def from_mara(cls, web_config: dict) -> "PoolConfig": return cls(groups=[PoolGroup.from_mara(web_config["pools"])]) + + @classmethod + def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig": + return cls(groups=[PoolGroup.from_bitaxe(web_system_info)]) diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py index cb82b37a..6b0ed666 100644 --- a/pyasic/miners/backends/bitaxe.py +++ b/pyasic/miners/backends/bitaxe.py @@ -1,6 +1,6 @@ from typing import List, Optional -from pyasic import APIError +from pyasic import APIError, MinerConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.miners.base import BaseMiner from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand @@ -52,6 +52,10 @@ class BitAxe(BaseMiner): await self.web.restart() return True + async def get_config(self) -> MinerConfig: + web_system_info = await self.web.system_info() + return MinerConfig.from_bitaxe(web_system_info) + async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]: if web_system_info is None: try: From ee1eece181798e9ee47e6f23495532b9b6a4eae5 Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Sat, 29 Jun 2024 15:12:33 -0600 Subject: [PATCH 11/27] feature: add config sending for bitaxe. --- pyasic/config/__init__.py | 13 ++++++++++--- pyasic/config/base.py | 6 ++++++ pyasic/config/fans.py | 8 +++++++- pyasic/config/pools.py | 19 ++++++++++++++++++- pyasic/miners/backends/bitaxe.py | 3 +++ pyasic/web/bitaxe.py | 11 +++++++++++ 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index f022dda5..bacf5c38 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -140,6 +140,14 @@ class MinerConfig: **self.pools.as_mara(user_suffix=user_suffix), } + def as_bitaxe(self, user_suffix: str = None) -> dict: + return { + **self.fan_mode.as_bitaxe(), + **self.temperature.as_bitaxe(), + **self.mining_mode.as_bitaxe(), + **self.pools.as_bitaxe(user_suffix=user_suffix), + } + @classmethod def from_dict(cls, dict_conf: dict) -> "MinerConfig": """Constructs a MinerConfig object from a dictionary.""" @@ -240,6 +248,5 @@ class MinerConfig: def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig": return cls( pools=PoolConfig.from_bitaxe(web_system_info), - fan_mode=FanModeConfig.from_bitaxe(web_system_info) - - ) \ No newline at end of file + fan_mode=FanModeConfig.from_bitaxe(web_system_info), + ) diff --git a/pyasic/config/base.py b/pyasic/config/base.py index 08834068..c8e55f00 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -60,6 +60,9 @@ class MinerConfigOption(Enum): def as_mara(self) -> dict: return self.value.as_mara() + def as_bitaxe(self) -> dict: + return self.value.as_bitaxe() + def __call__(self, *args, **kwargs): return self.value(*args, **kwargs) @@ -119,6 +122,9 @@ class MinerConfigValue: def as_mara(self) -> dict: return {} + def as_bitaxe(self) -> dict: + return {} + def __getitem__(self, item): try: return getattr(self, item) diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index 01b33ee5..6359b2fd 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -80,6 +80,9 @@ class FanModeNormal(MinerConfigValue): }, } + def as_bitaxe(self) -> dict: + return {"autoFanspeed": 1} + @dataclass class FanModeManual(MinerConfigValue): @@ -138,6 +141,9 @@ class FanModeManual(MinerConfigValue): }, } + def as_bitaxe(self) -> dict: + return {"autoFanspeed": 0, "fanspeed": self.speed} + @dataclass class FanModeImmersion(MinerConfigValue): @@ -297,4 +303,4 @@ class FanModeConfig(MinerConfigOption): if web_system_info["autofanspeed"] == 1: return cls.normal() else: - return cls.manual(speed=web_system_info["fanspeed"]) \ No newline at end of file + return cls.manual(speed=web_system_info["fanspeed"]) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 67b0b0ca..e2572cd9 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -127,6 +127,13 @@ class Pool(MinerConfigValue): } return {"url": self.url, "user": self.user, "pass": self.password} + def as_bitaxe(self, user_suffix: str = None) -> dict: + return { + "stratumURL": self.url, + "stratumUser": f"{self.user}{user_suffix}", + "stratumPassword": self.password, + } + @classmethod def from_dict(cls, dict_conf: dict | None) -> "Pool": return cls( @@ -197,7 +204,11 @@ class Pool(MinerConfigValue): @classmethod def from_bitaxe(cls, web_system_info: dict) -> "Pool": url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}" - return cls(url=url, user=web_system_info["stratumUser"], password=web_system_info.get("stratumPassword", "")) + return cls( + url=url, + user=web_system_info["stratumUser"], + password=web_system_info.get("stratumPassword", ""), + ) @dataclass @@ -292,6 +303,9 @@ class PoolGroup(MinerConfigValue): def as_mara(self, user_suffix: str = None) -> list: return [p.as_mara(user_suffix=user_suffix) for p in self.pools] + def as_bitaxe(self, user_suffix: str = None) -> dict: + return self.pools[0].as_bitaxe(user_suffix=user_suffix) + @classmethod def from_dict(cls, dict_conf: dict | None) -> "PoolGroup": cls_conf = {} @@ -465,6 +479,9 @@ class PoolConfig(MinerConfigValue): return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)} return {"pools": []} + def as_bitaxe(self, user_suffix: str = None) -> dict: + return self.groups[0].as_bitaxe(user_suffix=user_suffix) + @classmethod def from_api(cls, api_pools: dict) -> "PoolConfig": try: diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py index 6b0ed666..33d1bbc5 100644 --- a/pyasic/miners/backends/bitaxe.py +++ b/pyasic/miners/backends/bitaxe.py @@ -56,6 +56,9 @@ class BitAxe(BaseMiner): web_system_info = await self.web.system_info() return MinerConfig.from_bitaxe(web_system_info) + async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: + await self.web.update_settings(**config.as_bitaxe()) + async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]: if web_system_info is None: try: diff --git a/pyasic/web/bitaxe.py b/pyasic/web/bitaxe.py index ff4eb85b..6f5c5de7 100644 --- a/pyasic/web/bitaxe.py +++ b/pyasic/web/bitaxe.py @@ -25,11 +25,19 @@ class BitAxeWebAPI(BaseWebAPI): transport=settings.transport(), ) as client: if parameters.get("post", False): + parameters.pop("post") data = await client.post( url, timeout=settings.get("api_function_timeout", 3), json=parameters, ) + elif parameters.get("patch", False): + parameters.pop("patch") + data = await client.patch( + url, + timeout=settings.get("api_function_timeout", 3), + json=parameters, + ) else: data = await client.get(url) except httpx.HTTPError: @@ -83,3 +91,6 @@ class BitAxeWebAPI(BaseWebAPI): async def restart(self): return await self.send_command("system/restart", post=True) + + async def update_settings(self, **config): + return await self.send_command("system", patch=True, **config) From 9b431b020f02644c25258b7ab8d355499fc43272 Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Sun, 30 Jun 2024 18:20:41 +0100 Subject: [PATCH 12/27] method: from_str --- pyasic/data/pools.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pyasic/data/pools.py b/pyasic/data/pools.py index eac1a3ef..c81b9670 100644 --- a/pyasic/data/pools.py +++ b/pyasic/data/pools.py @@ -1,12 +1,14 @@ from dataclasses import dataclass, field from enum import Enum from typing import Optional +from urllib.parse import urlparse class Scheme (Enum): STRATUM_V1 = "stratum+tcp" STRATUM_V2 = "stratum2+tcp" + @dataclass class PoolUrl: scheme: Scheme @@ -20,6 +22,15 @@ class PoolUrl: else: return f"{self.scheme.value}://{self.host}:{self.port}" + @classmethod + def from_str(cls, url: str) -> 'PoolUrl': + parsed_url = urlparse(url) + scheme = Scheme(parsed_url.scheme) + host = parsed_url.hostname + port = parsed_url.port + pubkey = parsed_url.path.lstrip('/') if scheme == Scheme.STRATUM_V2 else None + return cls(scheme=scheme, host=host, port=port, pubkey=pubkey) + @dataclass class PoolMetrics: From 7a9c9237a3688ad3b9bd4c2a3ec3a1acba38691c Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Sun, 30 Jun 2024 22:03:50 -0600 Subject: [PATCH 13/27] bug: fix bitaxe not identifying on some versions, switch to using ASICModel key. --- pyasic/miners/factory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyasic/miners/factory.py b/pyasic/miners/factory.py index 4963eba1..cbd1e6be 100644 --- a/pyasic/miners/factory.py +++ b/pyasic/miners/factory.py @@ -443,9 +443,9 @@ MINER_CLASSES = { }, MinerTypes.BITAXE: { None: BitAxe, - "SUPRA": BitAxeSupra, - "ULTRA": BitAxeUltra, - "MAX": BitAxeMax, + "BM1368": BitAxeSupra, + "BM1366": BitAxeUltra, + "BM1397": BitAxeMax, }, } @@ -1024,7 +1024,7 @@ class MinerFactory: web_json_data = await self.send_web_command(ip, "/api/system/info") try: - miner_model = web_json_data["devicemodel"] + miner_model = web_json_data["ASICModel"] if miner_model == "": return None From 427f91d677e1f346f4c44ed3b649e96c759afca2 Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Mon, 1 Jul 2024 23:08:31 -0600 Subject: [PATCH 14/27] bug: use default chip count for bitaxe when `asicCount` is not set. --- pyasic/miners/backends/bitaxe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py index 33d1bbc5..1500b8f1 100644 --- a/pyasic/miners/backends/bitaxe.py +++ b/pyasic/miners/backends/bitaxe.py @@ -117,7 +117,7 @@ class BitAxe(BaseMiner): ).into(self.algo.unit.default), chip_temp=web_system_info.get("temp"), temp=web_system_info.get("vrTemp"), - chips=web_system_info.get("asicCount"), + chips=web_system_info.get("asicCount", 1), expected_chips=self.expected_chips, missing=False, active=True, From 42d2d975db1fe8f8cee485108d27f2eb031af0c0 Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Mon, 1 Jul 2024 23:11:26 -0600 Subject: [PATCH 15/27] feature: add firmware type to bitaxe. --- pyasic/miners/backends/bitaxe.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py index 1500b8f1..0bdd139f 100644 --- a/pyasic/miners/backends/bitaxe.py +++ b/pyasic/miners/backends/bitaxe.py @@ -2,6 +2,7 @@ from typing import List, Optional from pyasic import APIError, MinerConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit +from pyasic.device import MinerFirmware from pyasic.miners.base import BaseMiner from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.web.bitaxe import BitAxeWebAPI @@ -46,6 +47,8 @@ class BitAxe(BaseMiner): web: BitAxeWebAPI _web_cls = BitAxeWebAPI + firmware = MinerFirmware.STOCK + data_locations = BITAXE_DATA_LOC async def reboot(self) -> bool: From d46908a29838b5c4fd543161e1a38f427c65b933 Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Tue, 2 Jul 2024 15:03:16 -0600 Subject: [PATCH 16/27] docs: update docs. --- docs/generate_miners.py | 2 + docs/miners/antminer/X15.md | 2 +- docs/miners/antminer/X17.md | 14 +- docs/miners/antminer/X19.md | 71 +- docs/miners/antminer/X21.md | 13 +- docs/miners/antminer/X3.md | 6 +- docs/miners/antminer/X5.md | 2 +- docs/miners/antminer/X7.md | 2 +- docs/miners/antminer/X9.md | 12 +- docs/miners/auradine/AD.md | 6 +- docs/miners/auradine/AI.md | 4 +- docs/miners/auradine/AT.md | 4 +- docs/miners/avalonminer/A10X.md | 6 +- docs/miners/avalonminer/A11X.md | 2 +- docs/miners/avalonminer/A12X.md | 2 +- docs/miners/avalonminer/A7X.md | 6 +- docs/miners/avalonminer/A8X.md | 6 +- docs/miners/avalonminer/A9X.md | 2 +- docs/miners/bitaxe/BM.md | 24 + docs/miners/blockminer/blockminer.md | 17 + docs/miners/goldshell/X5.md | 6 +- docs/miners/goldshell/XBox.md | 4 +- docs/miners/goldshell/XMax.md | 2 +- docs/miners/innosilicon/A10X.md | 2 +- docs/miners/innosilicon/T3X.md | 2 +- docs/miners/supported_types.md | 608 +++++++++--------- docs/miners/whatsminer/M2X.md | 26 +- docs/miners/whatsminer/M3X.md | 288 ++++----- docs/miners/whatsminer/M5X.md | 74 +-- docs/miners/whatsminer/M6X.md | 38 +- pyasic/miners/device/models/antminer/X3/L3.py | 2 +- 31 files changed, 666 insertions(+), 589 deletions(-) create mode 100644 docs/miners/bitaxe/BM.md create mode 100644 docs/miners/blockminer/blockminer.md diff --git a/docs/generate_miners.py b/docs/generate_miners.py index cf3ea670..87f23886 100644 --- a/docs/generate_miners.py +++ b/docs/generate_miners.py @@ -49,6 +49,8 @@ def backend_str(backend: MinerTypes) -> str: return "LuxOS Firmware Miners" case MinerTypes.MARATHON: return "Mara Firmware Miners" + case MinerTypes.BITAXE: + return "Stock Firmware BitAxe Miners" def create_url_str(mtype: str): diff --git a/docs/miners/antminer/X15.md b/docs/miners/antminer/X15.md index 8fbb1c26..9c6b9073 100644 --- a/docs/miners/antminer/X15.md +++ b/docs/miners/antminer/X15.md @@ -1,7 +1,7 @@ # pyasic ## X15 Models -## Z15 +## Z15 (Stock) ::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15 handler: python options: diff --git a/docs/miners/antminer/X17.md b/docs/miners/antminer/X17.md index b60db7b8..4dfe6072 100644 --- a/docs/miners/antminer/X17.md +++ b/docs/miners/antminer/X17.md @@ -1,49 +1,49 @@ # pyasic ## X17 Models -## S17 +## S17 (Stock) ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17 handler: python options: show_root_heading: false heading_level: 4 -## S17+ +## S17+ (Stock) ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus handler: python options: show_root_heading: false heading_level: 4 -## S17 Pro +## S17 Pro (Stock) ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro handler: python options: show_root_heading: false heading_level: 4 -## S17e +## S17e (Stock) ::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e handler: python options: show_root_heading: false heading_level: 4 -## T17 +## T17 (Stock) ::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17 handler: python options: show_root_heading: false heading_level: 4 -## T17+ +## T17+ (Stock) ::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus handler: python options: show_root_heading: false heading_level: 4 -## T17e +## T17e (Stock) ::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e handler: python options: diff --git a/docs/miners/antminer/X19.md b/docs/miners/antminer/X19.md index 11c7fbdf..f01ba1ba 100644 --- a/docs/miners/antminer/X19.md +++ b/docs/miners/antminer/X19.md @@ -1,224 +1,231 @@ # pyasic ## X19 Models -## S19 +## S19 (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19 handler: python options: show_root_heading: false heading_level: 4 -## S19L +## S19L (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L handler: python options: show_root_heading: false heading_level: 4 -## S19 Pro +## S19 Pro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Pro handler: python options: show_root_heading: false heading_level: 4 -## S19j +## S19j (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j handler: python options: show_root_heading: false heading_level: 4 -## S19i +## S19i (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i handler: python options: show_root_heading: false heading_level: 4 -## S19+ +## S19+ (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus handler: python options: show_root_heading: false heading_level: 4 -## S19j No PIC +## S19j No PIC (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC handler: python options: show_root_heading: false heading_level: 4 -## S19 Pro+ +## S19 Pro+ (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus handler: python options: show_root_heading: false heading_level: 4 -## S19j Pro +## S19j Pro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro handler: python options: show_root_heading: false heading_level: 4 -## S19 XP +## S19 XP (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP handler: python options: show_root_heading: false heading_level: 4 -## S19a +## S19a (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a handler: python options: show_root_heading: false heading_level: 4 -## S19a Pro +## S19a Pro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro handler: python options: show_root_heading: false heading_level: 4 -## S19 Hydro +## S19 Hydro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro handler: python options: show_root_heading: false heading_level: 4 -## S19 Pro Hydro +## S19 Pro Hydro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProHydro handler: python options: show_root_heading: false heading_level: 4 -## S19 Pro+ Hydro +## S19 Pro+ Hydro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlusHydro handler: python options: show_root_heading: false heading_level: 4 -## S19K Pro +## S19K Pro (Stock) ::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro handler: python options: show_root_heading: false heading_level: 4 -## T19 +## T19 (Stock) ::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19 handler: python options: show_root_heading: false heading_level: 4 -## S19 +## S19 (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19 handler: python options: show_root_heading: false heading_level: 4 -## S19+ +## S19+ (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus handler: python options: show_root_heading: false heading_level: 4 -## S19 Pro +## S19 Pro (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Pro handler: python options: show_root_heading: false heading_level: 4 -## S19a +## S19a (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19a handler: python options: show_root_heading: false heading_level: 4 -## S19a Pro +## S19a Pro (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19aPro handler: python options: show_root_heading: false heading_level: 4 -## S19j +## S19j (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19j handler: python options: show_root_heading: false heading_level: 4 -## S19j No PIC +## S19j No PIC (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC handler: python options: show_root_heading: false heading_level: 4 -## S19j Pro +## S19j Pro (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro handler: python options: show_root_heading: false heading_level: 4 -## S19j Pro No PIC +## S19j Pro No PIC (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProNoPIC handler: python options: show_root_heading: false heading_level: 4 -## S19j Pro+ +## S19j Pro+ (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus handler: python options: show_root_heading: false heading_level: 4 -## S19j Pro+ +## S19j Pro+ (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus handler: python options: show_root_heading: false heading_level: 4 -## S19j Pro+ No PIC +## S19j Pro+ No PIC (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlusNoPIC handler: python options: show_root_heading: false heading_level: 4 -## S19k Pro No PIC +## S19k Pro No PIC (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC handler: python options: show_root_heading: false heading_level: 4 -## S19 XP +## S19k Pro No PIC (BOS+) +::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC + handler: python + options: + show_root_heading: false + heading_level: 4 + +## S19 XP (BOS+) ::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP handler: python options: show_root_heading: false heading_level: 4 -## T19 +## T19 (BOS+) ::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19 handler: python options: diff --git a/docs/miners/antminer/X21.md b/docs/miners/antminer/X21.md index 39fcff04..fc7539d8 100644 --- a/docs/miners/antminer/X21.md +++ b/docs/miners/antminer/X21.md @@ -1,27 +1,34 @@ # pyasic ## X21 Models -## S21 +## S21 (Stock) ::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21 handler: python options: show_root_heading: false heading_level: 4 -## T21 +## T21 (Stock) ::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21 handler: python options: show_root_heading: false heading_level: 4 -## S21 +## S21 (BOS+) ::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21 handler: python options: show_root_heading: false heading_level: 4 +## S21 (VNish) +::: pyasic.miners.antminer.vnish.X21.S21.VNishS21 + handler: python + options: + show_root_heading: false + heading_level: 4 + ## S21 (ePIC) ::: pyasic.miners.antminer.epic.X21.S21.ePICS21 handler: python diff --git a/docs/miners/antminer/X3.md b/docs/miners/antminer/X3.md index b3695dd9..5ca2d80d 100644 --- a/docs/miners/antminer/X3.md +++ b/docs/miners/antminer/X3.md @@ -1,21 +1,21 @@ # pyasic ## X3 Models -## D3 +## D3 (Stock) ::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3 handler: python options: show_root_heading: false heading_level: 4 -## HS3 +## HS3 (Stock) ::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3 handler: python options: show_root_heading: false heading_level: 4 -## L3+ +## L3+ (Stock) ::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus handler: python options: diff --git a/docs/miners/antminer/X5.md b/docs/miners/antminer/X5.md index deb33a89..944f4b05 100644 --- a/docs/miners/antminer/X5.md +++ b/docs/miners/antminer/X5.md @@ -1,7 +1,7 @@ # pyasic ## X5 Models -## DR5 +## DR5 (Stock) ::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5 handler: python options: diff --git a/docs/miners/antminer/X7.md b/docs/miners/antminer/X7.md index ea532304..7bccd093 100644 --- a/docs/miners/antminer/X7.md +++ b/docs/miners/antminer/X7.md @@ -1,7 +1,7 @@ # pyasic ## X7 Models -## L7 +## L7 (Stock) ::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7 handler: python options: diff --git a/docs/miners/antminer/X9.md b/docs/miners/antminer/X9.md index 6a69d0df..0fda0313 100644 --- a/docs/miners/antminer/X9.md +++ b/docs/miners/antminer/X9.md @@ -1,35 +1,35 @@ # pyasic ## X9 Models -## E9Pro +## E9Pro (Stock) ::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro handler: python options: show_root_heading: false heading_level: 4 -## S9 +## S9 (Stock) ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9 handler: python options: show_root_heading: false heading_level: 4 -## S9i +## S9i (Stock) ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i handler: python options: show_root_heading: false heading_level: 4 -## S9j +## S9j (Stock) ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j handler: python options: show_root_heading: false heading_level: 4 -## T9 +## T9 (Stock) ::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9 handler: python options: @@ -43,7 +43,7 @@ show_root_heading: false heading_level: 4 -## T9 (Hive) +## T9 (Stock) ::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9 handler: python options: diff --git a/docs/miners/auradine/AD.md b/docs/miners/auradine/AD.md index 08f9db64..e3ec74a6 100644 --- a/docs/miners/auradine/AD.md +++ b/docs/miners/auradine/AD.md @@ -1,21 +1,21 @@ # pyasic ## AD Models -## AT1500 +## AT1500 (Stock) ::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500 handler: python options: show_root_heading: false heading_level: 4 -## AT2860 +## AT2860 (Stock) ::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860 handler: python options: show_root_heading: false heading_level: 4 -## AT2880 +## AT2880 (Stock) ::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880 handler: python options: diff --git a/docs/miners/auradine/AI.md b/docs/miners/auradine/AI.md index 6a05a788..d237658d 100644 --- a/docs/miners/auradine/AI.md +++ b/docs/miners/auradine/AI.md @@ -1,14 +1,14 @@ # pyasic ## AI Models -## AI2500 +## AI2500 (Stock) ::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500 handler: python options: show_root_heading: false heading_level: 4 -## AI3680 +## AI3680 (Stock) ::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680 handler: python options: diff --git a/docs/miners/auradine/AT.md b/docs/miners/auradine/AT.md index 1081b21f..d49c54ae 100644 --- a/docs/miners/auradine/AT.md +++ b/docs/miners/auradine/AT.md @@ -1,14 +1,14 @@ # pyasic ## AT Models -## AD2500 +## AD2500 (Stock) ::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500 handler: python options: show_root_heading: false heading_level: 4 -## AD3500 +## AD3500 (Stock) ::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500 handler: python options: diff --git a/docs/miners/avalonminer/A10X.md b/docs/miners/avalonminer/A10X.md index 60f9bb81..bdc91f72 100644 --- a/docs/miners/avalonminer/A10X.md +++ b/docs/miners/avalonminer/A10X.md @@ -1,21 +1,21 @@ # pyasic ## A10X Models -## Avalon 1026 +## Avalon 1026 (Stock) ::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026 handler: python options: show_root_heading: false heading_level: 4 -## Avalon 1047 +## Avalon 1047 (Stock) ::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047 handler: python options: show_root_heading: false heading_level: 4 -## Avalon 1066 +## Avalon 1066 (Stock) ::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066 handler: python options: diff --git a/docs/miners/avalonminer/A11X.md b/docs/miners/avalonminer/A11X.md index 4624b2f4..c51ebc87 100644 --- a/docs/miners/avalonminer/A11X.md +++ b/docs/miners/avalonminer/A11X.md @@ -1,7 +1,7 @@ # pyasic ## A11X Models -## Avalon 1166 Pro +## Avalon 1166 Pro (Stock) ::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro handler: python options: diff --git a/docs/miners/avalonminer/A12X.md b/docs/miners/avalonminer/A12X.md index 0a8429a0..644d65e4 100644 --- a/docs/miners/avalonminer/A12X.md +++ b/docs/miners/avalonminer/A12X.md @@ -1,7 +1,7 @@ # pyasic ## A12X Models -## Avalon 1246 +## Avalon 1246 (Stock) ::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246 handler: python options: diff --git a/docs/miners/avalonminer/A7X.md b/docs/miners/avalonminer/A7X.md index b2c8371c..f760417f 100644 --- a/docs/miners/avalonminer/A7X.md +++ b/docs/miners/avalonminer/A7X.md @@ -1,21 +1,21 @@ # pyasic ## A7X Models -## Avalon 721 +## Avalon 721 (Stock) ::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721 handler: python options: show_root_heading: false heading_level: 4 -## Avalon 741 +## Avalon 741 (Stock) ::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741 handler: python options: show_root_heading: false heading_level: 4 -## Avalon 761 +## Avalon 761 (Stock) ::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761 handler: python options: diff --git a/docs/miners/avalonminer/A8X.md b/docs/miners/avalonminer/A8X.md index 7e99dcba..b0a34c3d 100644 --- a/docs/miners/avalonminer/A8X.md +++ b/docs/miners/avalonminer/A8X.md @@ -1,21 +1,21 @@ # pyasic ## A8X Models -## Avalon 821 +## Avalon 821 (Stock) ::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821 handler: python options: show_root_heading: false heading_level: 4 -## Avalon 841 +## Avalon 841 (Stock) ::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841 handler: python options: show_root_heading: false heading_level: 4 -## Avalon 851 +## Avalon 851 (Stock) ::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851 handler: python options: diff --git a/docs/miners/avalonminer/A9X.md b/docs/miners/avalonminer/A9X.md index 1af53274..c2b0acca 100644 --- a/docs/miners/avalonminer/A9X.md +++ b/docs/miners/avalonminer/A9X.md @@ -1,7 +1,7 @@ # pyasic ## A9X Models -## Avalon 921 +## Avalon 921 (Stock) ::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921 handler: python options: diff --git a/docs/miners/bitaxe/BM.md b/docs/miners/bitaxe/BM.md new file mode 100644 index 00000000..fe0bfbf3 --- /dev/null +++ b/docs/miners/bitaxe/BM.md @@ -0,0 +1,24 @@ +# pyasic +## BM Models + +## Supra (Stock) +::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra + handler: python + options: + show_root_heading: false + heading_level: 4 + +## Ultra (Stock) +::: pyasic.miners.bitaxe.espminer.BM.BM1366.BitAxeUltra + handler: python + options: + show_root_heading: false + heading_level: 4 + +## Max (Stock) +::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax + handler: python + options: + show_root_heading: false + heading_level: 4 + diff --git a/docs/miners/blockminer/blockminer.md b/docs/miners/blockminer/blockminer.md new file mode 100644 index 00000000..715be9e2 --- /dev/null +++ b/docs/miners/blockminer/blockminer.md @@ -0,0 +1,17 @@ +# pyasic +## blockminer Models + +## BlockMiner 520i (ePIC) +::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner520i + handler: python + options: + show_root_heading: false + heading_level: 4 + +## BlockMiner 720i (ePIC) +::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner720i + handler: python + options: + show_root_heading: false + heading_level: 4 + diff --git a/docs/miners/goldshell/X5.md b/docs/miners/goldshell/X5.md index 8ca83002..f4d77c6b 100644 --- a/docs/miners/goldshell/X5.md +++ b/docs/miners/goldshell/X5.md @@ -1,21 +1,21 @@ # pyasic ## X5 Models -## CK5 +## CK5 (Stock) ::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5 handler: python options: show_root_heading: false heading_level: 4 -## HS5 +## HS5 (Stock) ::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5 handler: python options: show_root_heading: false heading_level: 4 -## KD5 +## KD5 (Stock) ::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5 handler: python options: diff --git a/docs/miners/goldshell/XBox.md b/docs/miners/goldshell/XBox.md index 263ed930..d483ba8b 100644 --- a/docs/miners/goldshell/XBox.md +++ b/docs/miners/goldshell/XBox.md @@ -1,14 +1,14 @@ # pyasic ## XBox Models -## KD Box II +## KD Box II (Stock) ::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII handler: python options: show_root_heading: false heading_level: 4 -## KD Box Pro +## KD Box Pro (Stock) ::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro handler: python options: diff --git a/docs/miners/goldshell/XMax.md b/docs/miners/goldshell/XMax.md index 0533a633..8b0953f7 100644 --- a/docs/miners/goldshell/XMax.md +++ b/docs/miners/goldshell/XMax.md @@ -1,7 +1,7 @@ # pyasic ## XMax Models -## KD Max +## KD Max (Stock) ::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax handler: python options: diff --git a/docs/miners/innosilicon/A10X.md b/docs/miners/innosilicon/A10X.md index 8319625f..c81ccfeb 100644 --- a/docs/miners/innosilicon/A10X.md +++ b/docs/miners/innosilicon/A10X.md @@ -1,7 +1,7 @@ # pyasic ## A10X Models -## A10X +## A10X (Stock) ::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X handler: python options: diff --git a/docs/miners/innosilicon/T3X.md b/docs/miners/innosilicon/T3X.md index 15e4f8fb..b81af696 100644 --- a/docs/miners/innosilicon/T3X.md +++ b/docs/miners/innosilicon/T3X.md @@ -1,7 +1,7 @@ # pyasic ## T3X Models -## T3H+ +## T3H+ (Stock) ::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus handler: python options: diff --git a/docs/miners/supported_types.md b/docs/miners/supported_types.md index dcd0280e..27cd9120 100644 --- a/docs/miners/supported_types.md +++ b/docs/miners/supported_types.md @@ -18,78 +18,78 @@ details {
X3 Series:
X5 Series:
X7 Series:
X9 Series:
X15 Series:
X17 Series:
X19 Series:
X21 Series:
@@ -100,234 +100,234 @@ details {
M2X Series:
M3X Series:
M5X Series:
M6X Series:
@@ -338,43 +338,43 @@ details {
A7X Series:
A8X Series:
A9X Series:
A10X Series:
A11X Series:
A12X Series:
@@ -385,13 +385,13 @@ details {
T3X Series:
A10X Series:
@@ -402,22 +402,22 @@ details {
X5 Series:
XMax Series:
XBox Series:
@@ -446,27 +446,28 @@ details {
X19 Series:
X21 Series:
@@ -507,6 +508,12 @@ details {
  • T19 (VNish)
  • +
    + X21 Series: + +
    @@ -546,7 +553,7 @@ details {
    X9 Series:
    @@ -586,23 +593,23 @@ details {
    AD Series:
    AI Series:
    AT Series:
    @@ -630,4 +637,17 @@ details {
    + +
    +Stock Firmware BitAxe Miners: +
    \ No newline at end of file diff --git a/docs/miners/whatsminer/M2X.md b/docs/miners/whatsminer/M2X.md index 2cae2ddc..0724c495 100644 --- a/docs/miners/whatsminer/M2X.md +++ b/docs/miners/whatsminer/M2X.md @@ -1,91 +1,91 @@ # pyasic ## M2X Models -## M20 V10 +## M20 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10 handler: python options: show_root_heading: false heading_level: 4 -## M20S V10 +## M20S V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10 handler: python options: show_root_heading: false heading_level: 4 -## M20S V20 +## M20S V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20 handler: python options: show_root_heading: false heading_level: 4 -## M20S V30 +## M20S V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30 handler: python options: show_root_heading: false heading_level: 4 -## M20P V10 +## M20P V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10 handler: python options: show_root_heading: false heading_level: 4 -## M20P V30 +## M20P V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30 handler: python options: show_root_heading: false heading_level: 4 -## M20S+ V30 +## M20S+ V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30 handler: python options: show_root_heading: false heading_level: 4 -## M21 V10 +## M21 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10 handler: python options: show_root_heading: false heading_level: 4 -## M21S V20 +## M21S V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20 handler: python options: show_root_heading: false heading_level: 4 -## M21S V60 +## M21S V60 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60 handler: python options: show_root_heading: false heading_level: 4 -## M21S V70 +## M21S V70 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70 handler: python options: show_root_heading: false heading_level: 4 -## M21S+ V20 +## M21S+ V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20 handler: python options: show_root_heading: false heading_level: 4 -## M29 V10 +## M29 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10 handler: python options: diff --git a/docs/miners/whatsminer/M3X.md b/docs/miners/whatsminer/M3X.md index a060b1f5..01f1e7d7 100644 --- a/docs/miners/whatsminer/M3X.md +++ b/docs/miners/whatsminer/M3X.md @@ -1,1008 +1,1008 @@ # pyasic ## M3X Models -## M30 V10 +## M30 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30.BTMinerM30V10 handler: python options: show_root_heading: false heading_level: 4 -## M30 V20 +## M30 V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30.BTMinerM30V20 handler: python options: show_root_heading: false heading_level: 4 -## M30K V10 +## M30K V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30K.BTMinerM30KV10 handler: python options: show_root_heading: false heading_level: 4 -## M30L V10 +## M30L V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30L.BTMinerM30LV10 handler: python options: show_root_heading: false heading_level: 4 -## M30S V10 +## M30S V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV10 handler: python options: show_root_heading: false heading_level: 4 -## M30S V20 +## M30S V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV20 handler: python options: show_root_heading: false heading_level: 4 -## M30S V30 +## M30S V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV30 handler: python options: show_root_heading: false heading_level: 4 -## M30S V40 +## M30S V40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV40 handler: python options: show_root_heading: false heading_level: 4 -## M30S V50 +## M30S V50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV50 handler: python options: show_root_heading: false heading_level: 4 -## M30S V60 +## M30S V60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV60 handler: python options: show_root_heading: false heading_level: 4 -## M30S V70 +## M30S V70 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV70 handler: python options: show_root_heading: false heading_level: 4 -## M30S V80 +## M30S V80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV80 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE10 +## M30S VE10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE10 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE20 +## M30S VE20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE20 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE30 +## M30S VE30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE30 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE40 +## M30S VE40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE40 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE50 +## M30S VE50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE50 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE60 +## M30S VE60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE60 handler: python options: show_root_heading: false heading_level: 4 -## M30S VE70 +## M30S VE70 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE70 handler: python options: show_root_heading: false heading_level: 4 -## M30S VF10 +## M30S VF10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVF10 handler: python options: show_root_heading: false heading_level: 4 -## M30S VF20 +## M30S VF20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVF20 handler: python options: show_root_heading: false heading_level: 4 -## M30S VF30 +## M30S VF30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVF30 handler: python options: show_root_heading: false heading_level: 4 -## M30S VG10 +## M30S VG10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG10 handler: python options: show_root_heading: false heading_level: 4 -## M30S VG20 +## M30S VG20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG20 handler: python options: show_root_heading: false heading_level: 4 -## M30S VG30 +## M30S VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG30 handler: python options: show_root_heading: false heading_level: 4 -## M30S VG40 +## M30S VG40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG40 handler: python options: show_root_heading: false heading_level: 4 -## M30S VH10 +## M30S VH10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVH10 handler: python options: show_root_heading: false heading_level: 4 -## M30S VH20 +## M30S VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVH20 handler: python options: show_root_heading: false heading_level: 4 -## M30S VH30 +## M30S VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVH30 handler: python options: show_root_heading: false heading_level: 4 -## M30S VH40 +## M30S VH40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVH40 handler: python options: show_root_heading: false heading_level: 4 -## M30S VH50 +## M30S VH50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVH50 handler: python options: show_root_heading: false heading_level: 4 -## M30S VH60 +## M30S VH60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVH60 handler: python options: show_root_heading: false heading_level: 4 -## M30S VI20 +## M30S VI20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVI20 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V10 +## M30S+ V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV10 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V20 +## M30S+ V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV20 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V30 +## M30S+ V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV30 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V40 +## M30S+ V40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV40 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V50 +## M30S+ V50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV50 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V60 +## M30S+ V60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV60 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V70 +## M30S+ V70 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV70 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V80 +## M30S+ V80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV80 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V90 +## M30S+ V90 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV90 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ V100 +## M30S+ V100 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusV100 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE30 +## M30S+ VE30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE30 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE40 +## M30S+ VE40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE40 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE50 +## M30S+ VE50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE50 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE60 +## M30S+ VE60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE60 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE70 +## M30S+ VE70 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE70 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE80 +## M30S+ VE80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE80 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE90 +## M30S+ VE90 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE90 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VE100 +## M30S+ VE100 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE100 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VF20 +## M30S+ VF20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVF20 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VF30 +## M30S+ VF30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVF30 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VG20 +## M30S+ VG20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG20 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VG30 +## M30S+ VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG30 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VG40 +## M30S+ VG40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG40 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VG50 +## M30S+ VG50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG50 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VG60 +## M30S+ VG60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG60 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VH10 +## M30S+ VH10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVH10 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VH20 +## M30S+ VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVH20 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VH30 +## M30S+ VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVH30 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VH40 +## M30S+ VH40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVH40 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VH50 +## M30S+ VH50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVH50 handler: python options: show_root_heading: false heading_level: 4 -## M30S+ VH60 +## M30S+ VH60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVH60 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ V10 +## M30S++ V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusV10 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ V20 +## M30S++ V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusV20 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VE30 +## M30S++ VE30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVE30 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VE40 +## M30S++ VE40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVE40 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VE50 +## M30S++ VE50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVE50 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VF40 +## M30S++ VF40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVF40 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VG30 +## M30S++ VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG30 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VG40 +## M30S++ VG40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG40 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VG50 +## M30S++ VG50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG50 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH10 +## M30S++ VH10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH10 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH20 +## M30S++ VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH20 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH30 +## M30S++ VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH30 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH40 +## M30S++ VH40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH40 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH50 +## M30S++ VH50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH50 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH60 +## M30S++ VH60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH60 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH70 +## M30S++ VH70 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH70 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH80 +## M30S++ VH80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH80 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH90 +## M30S++ VH90 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH90 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VH100 +## M30S++ VH100 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH100 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VJ20 +## M30S++ VJ20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVJ20 handler: python options: show_root_heading: false heading_level: 4 -## M30S++ VJ30 +## M30S++ VJ30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVJ30 handler: python options: show_root_heading: false heading_level: 4 -## M31 V10 +## M31 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31.BTMinerM31V10 handler: python options: show_root_heading: false heading_level: 4 -## M31 V20 +## M31 V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31.BTMinerM31V20 handler: python options: show_root_heading: false heading_level: 4 -## M31H V10 +## M31H V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV10 handler: python options: show_root_heading: false heading_level: 4 -## M31H V40 +## M31H V40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31H.BTMinerM31HV40 handler: python options: show_root_heading: false heading_level: 4 -## M30L V10 +## M30L V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31L.BTMinerM31LV10 handler: python options: show_root_heading: false heading_level: 4 -## M31S V10 +## M31S V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV10 handler: python options: show_root_heading: false heading_level: 4 -## M31S V20 +## M31S V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV20 handler: python options: show_root_heading: false heading_level: 4 -## M31S V30 +## M31S V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV30 handler: python options: show_root_heading: false heading_level: 4 -## M31S V40 +## M31S V40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV40 handler: python options: show_root_heading: false heading_level: 4 -## M31S V50 +## M31S V50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV50 handler: python options: show_root_heading: false heading_level: 4 -## M31S V60 +## M31S V60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV60 handler: python options: show_root_heading: false heading_level: 4 -## M31S V70 +## M31S V70 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV70 handler: python options: show_root_heading: false heading_level: 4 -## M31S V80 +## M31S V80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV80 handler: python options: show_root_heading: false heading_level: 4 -## M31S V90 +## M31S V90 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV90 handler: python options: show_root_heading: false heading_level: 4 -## M31S VE10 +## M31S VE10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SVE10 handler: python options: show_root_heading: false heading_level: 4 -## M31S VE20 +## M31S VE20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SVE20 handler: python options: show_root_heading: false heading_level: 4 -## M31S VE30 +## M31S VE30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SVE30 handler: python options: show_root_heading: false heading_level: 4 -## M31SE V10 +## M31SE V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31SE.BTMinerM31SEV10 handler: python options: show_root_heading: false heading_level: 4 -## M31SE V20 +## M31SE V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31SE.BTMinerM31SEV20 handler: python options: show_root_heading: false heading_level: 4 -## M31SE V30 +## M31SE V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31SE.BTMinerM31SEV30 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V10 +## M31S+ V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV10 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V20 +## M31S+ V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV20 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V30 +## M31S+ V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV30 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V40 +## M31S+ V40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV40 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V50 +## M31S+ V50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV50 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V60 +## M31S+ V60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV60 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V80 +## M31S+ V80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV80 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V90 +## M31S+ V90 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV90 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ V100 +## M31S+ V100 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV100 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE10 +## M31S+ VE10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE10 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE20 +## M31S+ VE20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE20 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE30 +## M31S+ VE30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE30 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE40 +## M31S+ VE40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE40 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE50 +## M31S+ VE50 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE50 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE60 +## M31S+ VE60 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE60 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VE80 +## M31S+ VE80 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE80 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VF20 +## M31S+ VF20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVF20 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VF30 +## M31S+ VF30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVF30 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VG20 +## M31S+ VG20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVG20 handler: python options: show_root_heading: false heading_level: 4 -## M31S+ VG30 +## M31S+ VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVG30 handler: python options: show_root_heading: false heading_level: 4 -## M32 V10 +## M32 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M32.BTMinerM32V10 handler: python options: show_root_heading: false heading_level: 4 -## M32 V20 +## M32 V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M32.BTMinerM32V20 handler: python options: show_root_heading: false heading_level: 4 -## M33 V10 +## M33 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33.BTMinerM33V10 handler: python options: show_root_heading: false heading_level: 4 -## M33 V20 +## M33 V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33.BTMinerM33V20 handler: python options: show_root_heading: false heading_level: 4 -## M33 V30 +## M33 V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33.BTMinerM33V30 handler: python options: show_root_heading: false heading_level: 4 -## M33S VG30 +## M33S VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S.BTMinerM33SVG30 handler: python options: show_root_heading: false heading_level: 4 -## M33S+ VG20 +## M33S+ VG20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVG20 handler: python options: show_root_heading: false heading_level: 4 -## M33S+ VH20 +## M33S+ VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVH20 handler: python options: show_root_heading: false heading_level: 4 -## M33S+ VH30 +## M33S+ VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus.BTMinerM33SPlusVH30 handler: python options: show_root_heading: false heading_level: 4 -## M33S++ VH20 +## M33S++ VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus_Plus.BTMinerM33SPlusPlusVH20 handler: python options: show_root_heading: false heading_level: 4 -## M33S++ VH30 +## M33S++ VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus_Plus.BTMinerM33SPlusPlusVH30 handler: python options: show_root_heading: false heading_level: 4 -## M33S++ VG40 +## M33S++ VG40 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M33S_Plus_Plus.BTMinerM33SPlusPlusVG40 handler: python options: show_root_heading: false heading_level: 4 -## M34S+ VE10 +## M34S+ VE10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M34S_Plus.BTMinerM34SPlusVE10 handler: python options: show_root_heading: false heading_level: 4 -## M36S VE10 +## M36S VE10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M36S.BTMinerM36SVE10 handler: python options: show_root_heading: false heading_level: 4 -## M36S+ VG30 +## M36S+ VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M36S_Plus.BTMinerM36SPlusVG30 handler: python options: show_root_heading: false heading_level: 4 -## M36S++ VH30 +## M36S++ VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M36S_Plus_Plus.BTMinerM36SPlusPlusVH30 handler: python options: show_root_heading: false heading_level: 4 -## M39 V10 +## M39 V10 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V10 handler: python options: show_root_heading: false heading_level: 4 -## M39 V20 +## M39 V20 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V20 handler: python options: show_root_heading: false heading_level: 4 -## M39 V30 +## M39 V30 (Stock) ::: pyasic.miners.whatsminer.btminer.M3X.M39.BTMinerM39V30 handler: python options: diff --git a/docs/miners/whatsminer/M5X.md b/docs/miners/whatsminer/M5X.md index 2ef24281..34249629 100644 --- a/docs/miners/whatsminer/M5X.md +++ b/docs/miners/whatsminer/M5X.md @@ -1,259 +1,259 @@ # pyasic ## M5X Models -## M50 VE30 +## M50 VE30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30 handler: python options: show_root_heading: false heading_level: 4 -## M50 VG30 +## M50 VG30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH10 +## M50 VH10 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH10 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH20 +## M50 VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH20 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH30 +## M50 VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH30 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH40 +## M50 VH40 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH40 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH50 +## M50 VH50 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH50 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH60 +## M50 VH60 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH60 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH70 +## M50 VH70 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH70 handler: python options: show_root_heading: false heading_level: 4 -## M50 VH80 +## M50 VH80 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH80 handler: python options: show_root_heading: false heading_level: 4 -## M50 VJ10 +## M50 VJ10 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10 handler: python options: show_root_heading: false heading_level: 4 -## M50 VJ20 +## M50 VJ20 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ20 handler: python options: show_root_heading: false heading_level: 4 -## M50 VJ30 +## M50 VJ30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ30 handler: python options: show_root_heading: false heading_level: 4 -## M50S VJ10 +## M50S VJ10 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ10 handler: python options: show_root_heading: false heading_level: 4 -## M50S VJ20 +## M50S VJ20 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ20 handler: python options: show_root_heading: false heading_level: 4 -## M50S VJ30 +## M50S VJ30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ30 handler: python options: show_root_heading: false heading_level: 4 -## M50S VH10 +## M50S VH10 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH10 handler: python options: show_root_heading: false heading_level: 4 -## M50S VH20 +## M50S VH20 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH20 handler: python options: show_root_heading: false heading_level: 4 -## M50S VH30 +## M50S VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH30 handler: python options: show_root_heading: false heading_level: 4 -## M50S VH40 +## M50S VH40 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH40 handler: python options: show_root_heading: false heading_level: 4 -## M50S VH50 +## M50S VH50 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH50 handler: python options: show_root_heading: false heading_level: 4 -## M50S+ VH30 +## M50S+ VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH30 handler: python options: show_root_heading: false heading_level: 4 -## M50S+ VH40 +## M50S+ VH40 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH40 handler: python options: show_root_heading: false heading_level: 4 -## M50S+ VJ30 +## M50S+ VJ30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ30 handler: python options: show_root_heading: false heading_level: 4 -## M50S+ VK20 +## M50S+ VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK20 handler: python options: show_root_heading: false heading_level: 4 -## M50S++ VK10 +## M50S++ VK10 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK10 handler: python options: show_root_heading: false heading_level: 4 -## M50S++ VK20 +## M50S++ VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK20 handler: python options: show_root_heading: false heading_level: 4 -## M50S++ VK30 +## M50S++ VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK30 handler: python options: show_root_heading: false heading_level: 4 -## M53 VH30 +## M53 VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH30 handler: python options: show_root_heading: false heading_level: 4 -## M53S VH30 +## M53S VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH30 handler: python options: show_root_heading: false heading_level: 4 -## M53S VJ40 +## M53S VJ40 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ40 handler: python options: show_root_heading: false heading_level: 4 -## M53S+ VJ30 +## M53S+ VJ30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30 handler: python options: show_root_heading: false heading_level: 4 -## M53S++ VK10 +## M53S++ VK10 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK10 handler: python options: show_root_heading: false heading_level: 4 -## M56 VH30 +## M56 VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30 handler: python options: show_root_heading: false heading_level: 4 -## M56S VH30 +## M56S VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVH30 handler: python options: show_root_heading: false heading_level: 4 -## M56S+ VJ30 +## M56S+ VJ30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVJ30 handler: python options: show_root_heading: false heading_level: 4 -## M59 VH30 +## M59 VH30 (Stock) ::: pyasic.miners.whatsminer.btminer.M5X.M59.BTMinerM59VH30 handler: python options: diff --git a/docs/miners/whatsminer/M6X.md b/docs/miners/whatsminer/M6X.md index bf67327d..be87c25b 100644 --- a/docs/miners/whatsminer/M6X.md +++ b/docs/miners/whatsminer/M6X.md @@ -1,133 +1,133 @@ # pyasic ## M6X Models -## M60 VK10 +## M60 VK10 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK10 handler: python options: show_root_heading: false heading_level: 4 -## M60 VK20 +## M60 VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK20 handler: python options: show_root_heading: false heading_level: 4 -## M60 VK30 +## M60 VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK30 handler: python options: show_root_heading: false heading_level: 4 -## M60 VK40 +## M60 VK40 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK40 handler: python options: show_root_heading: false heading_level: 4 -## M60S VK10 +## M60S VK10 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK10 handler: python options: show_root_heading: false heading_level: 4 -## M60S VK20 +## M60S VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK20 handler: python options: show_root_heading: false heading_level: 4 -## M60S VK30 +## M60S VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK30 handler: python options: show_root_heading: false heading_level: 4 -## M60S VK40 +## M60S VK40 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK40 handler: python options: show_root_heading: false heading_level: 4 -## M63 VK10 +## M63 VK10 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK10 handler: python options: show_root_heading: false heading_level: 4 -## M63 VK20 +## M63 VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK20 handler: python options: show_root_heading: false heading_level: 4 -## M63 VK30 +## M63 VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK30 handler: python options: show_root_heading: false heading_level: 4 -## M63S VK10 +## M63S VK10 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK10 handler: python options: show_root_heading: false heading_level: 4 -## M63S VK20 +## M63S VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK20 handler: python options: show_root_heading: false heading_level: 4 -## M63S VK30 +## M63S VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK30 handler: python options: show_root_heading: false heading_level: 4 -## M66 VK20 +## M66 VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK20 handler: python options: show_root_heading: false heading_level: 4 -## M66 VK30 +## M66 VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK30 handler: python options: show_root_heading: false heading_level: 4 -## M66S VK20 +## M66S VK20 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK20 handler: python options: show_root_heading: false heading_level: 4 -## M66S VK30 +## M66S VK30 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK30 handler: python options: show_root_heading: false heading_level: 4 -## M66S VK40 +## M66S VK40 (Stock) ::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK40 handler: python options: diff --git a/pyasic/miners/device/models/antminer/X3/L3.py b/pyasic/miners/device/models/antminer/X3/L3.py index 82bb7172..bd5ba911 100644 --- a/pyasic/miners/device/models/antminer/X3/L3.py +++ b/pyasic/miners/device/models/antminer/X3/L3.py @@ -18,6 +18,6 @@ from pyasic.miners.device.makes import AntMinerMake class L3Plus(AntMinerMake): - raw_model = MinerModel.ANTMINER + raw_model = MinerModel.ANTMINER.L3Plus expected_chips = 72 From bd76966d3a7ee8e3e8d9cb55b92f19648609d521 Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Tue, 2 Jul 2024 15:05:45 -0600 Subject: [PATCH 17/27] docs: update docs nav. --- mkdocs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index 0e99b1b6..7ed5fb21 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,6 +68,8 @@ nav: - Auradine AD: "miners/auradine/AD.md" - Auradine AI: "miners/auradine/AI.md" - Auradine AT: "miners/auradine/AT.md" + - Blockminer: "miners/blockminer/blockminer.md" + - BitAxe BM: "miners/bitaxe/BM.md" - Base Miner: "miners/base_miner.md" - Settings: - Settings: "settings/settings.md" From e5d5cb4dab75e69c00a19bfec7c3a11ecc2ab19d Mon Sep 17 00:00:00 2001 From: upstreamdata Date: Tue, 2 Jul 2024 15:06:03 -0600 Subject: [PATCH 18/27] version: bump version number. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 47937b3d..f90a566d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyasic" -version = "0.57.6" +version = "0.58.0" description = "A simplified and standardized interface for Bitcoin ASICs." authors = ["UpstreamData "] repository = "https://github.com/UpstreamData/pyasic" From 092126bded08618aa02fe40d2bbee07adb324190 Mon Sep 17 00:00:00 2001 From: 1e9abhi1e10 <2311abhiptdr@gmail.com> Date: Wed, 3 Jul 2024 08:43:22 +0530 Subject: [PATCH 19/27] Added checksum validation and command handling --- pyasic/miners/backends/epic.py | 68 +++------------------------------- pyasic/web/epic.py | 31 ++++++++++++++++ 2 files changed, 37 insertions(+), 62 deletions(-) diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index 126bd2bd..ad7e7d3f 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -14,10 +14,6 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -import logging -import aiofiles -import hashlib -import aiohttp from pathlib import Path from typing import List, Optional @@ -455,69 +451,17 @@ class ePIC(ePICFirmware): return pool_data except LookupError: pass - - async def upgrade_firmware(self, file: Path, keepsettings: bool, password: str): + + async def upgrade_firmware(self, file: Path | str, keep_settings: bool = True) -> bool: """ Upgrade the firmware of the ePIC miner device. Args: - file (Path): The local file path of the firmware to be uploaded. - keepsettings (bool): Whether to keep the current settings after the update. - password (str): The password for authentication. + file (Path | str): The local file path of the firmware to be uploaded. + keep_settings (bool): Whether to keep the current settings after the update. Returns: - str: Confirmation message after upgrading the firmware. + bool: Whether the firmware update succeeded. """ - try: - logging.info("Starting firmware upgrade process for ePIC miner.") - - if not file: - raise ValueError("File location must be provided for firmware upgrade.") - - # calculate the SHA256 checksum of the firmware file - sha256_hash = hashlib.sha256() - async with aiofiles.open(file, "rb") as f: - while chunk := await f.read(8192): - sha256_hash.update(chunk) - checksum = sha256_hash.hexdigest() - - # prepare the multipart/form-data request - form_data = aiohttp.FormData() - form_data.add_field('checksum', checksum) - form_data.add_field('keepsettings', str(keepsettings).lower()) - form_data.add_field('password', password) - form_data.add_field('update.zip', open(file, 'rb'), filename='update.zip') - - # Send the POST request to the ePIC miner device - async with self.web.post(f"http://{self.ip}:{self.port}/systemupdate", data=form_data) as response: - if response.status == 200: - result = await response.json() - if result.get("result"): - logging.info("Firmware upgrade process completed successfully for ePIC miner.") - return "Firmware upgrade completed successfully." - else: - error = result.get("error", "Unknown error") - logging.error(f"Firmware upgrade failed: {error}") - raise Exception(f"Firmware upgrade failed: {error}") - else: - logging.error(f"Firmware upgrade failed with status code: {response.status}") - raise Exception(f"Firmware upgrade failed with status code: {response.status}") - - except FileNotFoundError as e: - logging.error(f"File not found during the firmware upgrade process: {e}") - raise - except ValueError as e: - logging.error( - f"Validation error occurred during the firmware upgrade process: {e}" - ) - raise - except OSError as e: - logging.error(f"OS error occurred during the firmware upgrade process: {e}") - raise - except Exception as e: - logging.error( - f"An unexpected error occurred during the firmware upgrade process: {e}", - exc_info=True, - ) - raise \ No newline at end of file + return await self.web.system_update(file=file, keep_settings=keep_settings) \ No newline at end of file diff --git a/pyasic/web/epic.py b/pyasic/web/epic.py index 8f0e1b41..5c19a98e 100644 --- a/pyasic/web/epic.py +++ b/pyasic/web/epic.py @@ -19,6 +19,10 @@ import json from typing import Any import httpx +import aiofiles +import aiohttp +import hashlib +from pathlib import Path from pyasic import settings from pyasic.errors import APIError @@ -46,6 +50,14 @@ class ePICWebAPI(BaseWebAPI): async with httpx.AsyncClient(transport=settings.transport()) as client: for retry_cnt in range(settings.get("get_data_retries", 1)): try: + if parameters.get("form") is not None: + form_data = parameters["form"] + form_data.add_field('password', self.pwd) + response = await client.post( + f"http://{self.ip}:{self.port}/{command}", + timeout=5, + data=form_data, + ) if post: response = await client.post( f"http://{self.ip}:{self.port}/{command}", @@ -135,3 +147,22 @@ class ePICWebAPI(BaseWebAPI): async def capabilities(self) -> dict: return await self.send_command("capabilities") + + async def system_update(self, file: Path | str, keep_settings: bool = True): + """Perform a system update by uploading a firmware file and sending a + command to initiate the update.""" + + # calculate the SHA256 checksum of the firmware file + sha256_hash = hashlib.sha256() + async with aiofiles.open(str(file), "rb") as f: + while chunk := await f.read(8192): + sha256_hash.update(chunk) + checksum = sha256_hash.hexdigest() + + # prepare the multipart/form-data request + form_data = aiohttp.FormData() + form_data.add_field('checksum', checksum) + form_data.add_field('keepsettings', str(keep_settings).lower()) + form_data.add_field('update.zip', open(file, 'rb'), filename='update.zip') + + await self.send_command("systemupdate", form=form_data) \ No newline at end of file From e6f9a33b3c58191073d1aa7aad9b1fd1502656d9 Mon Sep 17 00:00:00 2001 From: 1e9abhi1e10 <2311abhiptdr@gmail.com> Date: Wed, 3 Jul 2024 08:50:50 +0530 Subject: [PATCH 20/27] Removed trailing whitespace --- pyasic/miners/backends/epic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index ad7e7d3f..b357b5a6 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -451,7 +451,7 @@ class ePIC(ePICFirmware): return pool_data except LookupError: pass - + async def upgrade_firmware(self, file: Path | str, keep_settings: bool = True) -> bool: """ From 846bbb9033b13aabb3b8760bc57a170ab4df4905 Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Wed, 3 Jul 2024 04:55:26 +0100 Subject: [PATCH 21/27] Backends: updated BMMiner _get_pools to include PoolUrl dataclass. --- pyasic/miners/backends/antminer.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index 55a0b391..5d162730 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -19,7 +19,7 @@ from typing import List, Optional, Union from pyasic.config import MinerConfig, MiningModeConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData, X19Error -from pyasic.data.pools import PoolMetrics +from pyasic.data.pools import PoolMetrics, PoolUrl from pyasic.errors import APIError from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.cgminer import CGMiner @@ -370,6 +370,8 @@ class AntminerModern(BMMiner): try: pools = rpc_pools.get("POOLS", []) for pool_info in pools: + url = pool_info.get("URL") + pool_url = PoolUrl.from_str(url) if url else None pool_data = PoolMetrics( accepted=pool_info.get("Accepted"), rejected=pool_info.get("Rejected"), @@ -377,7 +379,7 @@ class AntminerModern(BMMiner): remote_failures=pool_info.get("Remote Failures"), active=pool_info.get("Stratum Active"), alive=pool_info.get("Status") == "Alive", - url=pool_info.get("URL"), + url=pool_url, user=pool_info.get("User"), index=pool_info.get("POOL"), ) From dd8d895b503b3546bbe1388893c2af625beae408 Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Wed, 3 Jul 2024 07:30:30 +0100 Subject: [PATCH 22/27] Backends: updated BOSMiner _get_pools to include PoolUrl dataclass. --- pyasic/miners/backends/braiins_os.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pyasic/miners/backends/braiins_os.py b/pyasic/miners/backends/braiins_os.py index 7b27909c..fbbdba37 100644 --- a/pyasic/miners/backends/braiins_os.py +++ b/pyasic/miners/backends/braiins_os.py @@ -26,7 +26,7 @@ from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePowerTune from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import BraiinsOSError, MinerErrorData -from pyasic.data.pools import PoolMetrics +from pyasic.data.pools import PoolMetrics, PoolUrl from pyasic.errors import APIError from pyasic.miners.data import ( DataFunction, @@ -592,6 +592,8 @@ class BOSMiner(BraiinsOSFirmware): try: pools = rpc_pools.get("POOLS", []) for pool_info in pools: + url = pool_info.get("URL") + pool_url = PoolUrl.from_str(url) if url else None pool_data = PoolMetrics( accepted=pool_info.get("Accepted"), rejected=pool_info.get("Rejected"), @@ -599,7 +601,7 @@ class BOSMiner(BraiinsOSFirmware): remote_failures=pool_info.get("Remote Failures"), active=pool_info.get("Stratum Active"), alive=pool_info.get("Status") == "Alive", - url=pool_info.get("URL"), + url=pool_url, user=pool_info.get("User"), index=pool_info.get("POOL"), ) From eea5d5ba2a00d671be76107fe6bfd76329d36faa Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Fri, 5 Jul 2024 08:56:36 -0600 Subject: [PATCH 23/27] bug: fix dataclass ordering issues and bitaxe issues. --- pyasic/data/pools.py | 10 +++++----- pyasic/miners/backends/bitaxe.py | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pyasic/data/pools.py b/pyasic/data/pools.py index c81b9670..f4970fbf 100644 --- a/pyasic/data/pools.py +++ b/pyasic/data/pools.py @@ -4,7 +4,7 @@ from typing import Optional from urllib.parse import urlparse -class Scheme (Enum): +class Scheme(Enum): STRATUM_V1 = "stratum+tcp" STRATUM_V2 = "stratum2+tcp" @@ -23,12 +23,12 @@ class PoolUrl: return f"{self.scheme.value}://{self.host}:{self.port}" @classmethod - def from_str(cls, url: str) -> 'PoolUrl': + def from_str(cls, url: str) -> "PoolUrl": parsed_url = urlparse(url) scheme = Scheme(parsed_url.scheme) host = parsed_url.hostname port = parsed_url.port - pubkey = parsed_url.path.lstrip('/') if scheme == Scheme.STRATUM_V2 else None + pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None return cls(scheme=scheme, host=host, port=port, pubkey=pubkey) @@ -50,8 +50,6 @@ class PoolMetrics: pool_stale_percent: Percentage of stale shares by the pool. """ - pool_rejected_percent: float = field(init=False) - pool_stale_percent: float = field(init=False) url: PoolUrl accepted: int = None rejected: int = None @@ -61,6 +59,8 @@ class PoolMetrics: alive: bool = None index: int = None user: str = None + pool_rejected_percent: float = field(init=False) + pool_stale_percent: float = field(init=False) @property def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection diff --git a/pyasic/miners/backends/bitaxe.py b/pyasic/miners/backends/bitaxe.py index 0bdd139f..ca0d7f06 100644 --- a/pyasic/miners/backends/bitaxe.py +++ b/pyasic/miners/backends/bitaxe.py @@ -25,6 +25,10 @@ BITAXE_DATA_LOC = DataLocations( "_get_hashboards", [WebAPICommand("web_system_info", "system/info")], ), + str(DataOptions.HOSTNAME): DataFunction( + "_get_hostname", + [WebAPICommand("web_system_info", "system/info")], + ), str(DataOptions.FANS): DataFunction( "_get_fans", [WebAPICommand("web_system_info", "system/info")], From db2615a4eb59ee9c5775a058b702c70202e8ec78 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Fri, 5 Jul 2024 08:57:00 -0600 Subject: [PATCH 24/27] version: bump version number. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f90a566d..2de66080 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyasic" -version = "0.58.0" +version = "0.58.1" description = "A simplified and standardized interface for Bitcoin ASICs." authors = ["UpstreamData "] repository = "https://github.com/UpstreamData/pyasic" From fec7a8980783c783f2a738ad6a15ba43c96ec61d Mon Sep 17 00:00:00 2001 From: ytemiloluwa Date: Mon, 8 Jul 2024 15:00:57 +0100 Subject: [PATCH 25/27] backends: add _get_pools method to luxminer --- pyasic/miners/backends/luxminer.py | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pyasic/miners/backends/luxminer.py b/pyasic/miners/backends/luxminer.py index 8886e7b3..857c0412 100644 --- a/pyasic/miners/backends/luxminer.py +++ b/pyasic/miners/backends/luxminer.py @@ -21,6 +21,7 @@ from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import LuxOSFirmware from pyasic.rpc.luxminer import LUXMinerRPCAPI +from pyasic.data.pools import PoolMetrics, PoolUrl LUXMINER_DATA_LOC = DataLocations( **{ @@ -51,6 +52,9 @@ LUXMINER_DATA_LOC = DataLocations( str(DataOptions.UPTIME): DataFunction( "_get_uptime", [RPCAPICommand("rpc_stats", "stats")] ), + str(DataOptions.POOLS): DataFunction( + "get_pools", [RPCAPICommand("rpc_pools", "pools")] + ), } ) @@ -297,3 +301,33 @@ class LUXMiner(LuxOSFirmware): return int(rpc_stats["STATS"][1]["Elapsed"]) except LookupError: pass + + async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]: + if rpc_pools is None: + try: + rpc_pools = await self.rpc.pools() + except APIError: + pass + + pools_data = [] + if rpc_pools is not None: + try: + pools = rpc_pools.get("POOLS", []) + for pool_info in pools: + url = pool_info.get("URL") + pool_url = PoolUrl.from_str(url) if url else None + pool_data = PoolMetrics( + accepted=pool_info.get("Accepted"), + rejected=pool_info.get("Rejected"), + get_failures=pool_info.get("Get Failures"), + remote_failures=pool_info.get("Remote Failures"), + active=pool_info.get("Stratum Active"), + alive=pool_info.get("Status") == "Alive", + url=pool_url, + user=pool_info.get("User"), + index=pool_info.get("POOL"), + ) + pools_data.append(pool_data) + except LookupError: + pass + return pools_data From ca5db726bd43d79400b21e926eaab62f95ba8555 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Wed, 10 Jul 2024 07:56:03 +0000 Subject: [PATCH 26/27] fix: docs/requirements.txt to reduce vulnerabilities The following vulnerabilities are fixed by pinning transitive dependencies: - https://snyk.io/vuln/SNYK-PYTHON-ZIPP-7430899 --- docs/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 55112534..29721bb6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ jinja2<3.1.4 mkdocs mkdocstrings[python] +zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability From 3ca75729b977511f2cf5096ccecd90a95dafd972 Mon Sep 17 00:00:00 2001 From: Brett Rowan <121075405+b-rowan@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:59:03 -0600 Subject: [PATCH 27/27] feature: add support for Vnish S19 Pro Hydro. --- pyasic/miners/antminer/vnish/X19/S19.py | 5 +++++ pyasic/miners/antminer/vnish/X19/__init__.py | 1 + pyasic/miners/backends/luxminer.py | 4 ++-- pyasic/miners/factory.py | 1 + pyasic/web/epic.py | 19 +++++++++---------- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/pyasic/miners/antminer/vnish/X19/S19.py b/pyasic/miners/antminer/vnish/X19/S19.py index 032d5db9..7dd9dd11 100644 --- a/pyasic/miners/antminer/vnish/X19/S19.py +++ b/pyasic/miners/antminer/vnish/X19/S19.py @@ -24,6 +24,7 @@ from pyasic.miners.device.models import ( S19jPro, S19NoPIC, S19Pro, + S19ProHydro, ) @@ -57,3 +58,7 @@ class VNishS19j(VNish, S19j): class VNishS19jPro(VNish, S19jPro): pass + + +class VNishS19ProHydro(VNish, S19ProHydro): + pass diff --git a/pyasic/miners/antminer/vnish/X19/__init__.py b/pyasic/miners/antminer/vnish/X19/__init__.py index f69c45e2..b7f679c0 100644 --- a/pyasic/miners/antminer/vnish/X19/__init__.py +++ b/pyasic/miners/antminer/vnish/X19/__init__.py @@ -22,6 +22,7 @@ from .S19 import ( VNishS19jPro, VNishS19NoPIC, VNishS19Pro, + VNishS19ProHydro, VNishS19XP, ) from .T19 import VNishT19 diff --git a/pyasic/miners/backends/luxminer.py b/pyasic/miners/backends/luxminer.py index 857c0412..abaa3664 100644 --- a/pyasic/miners/backends/luxminer.py +++ b/pyasic/miners/backends/luxminer.py @@ -17,11 +17,11 @@ from typing import List, Optional from pyasic.config import MinerConfig from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit +from pyasic.data.pools import PoolMetrics, PoolUrl from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import LuxOSFirmware from pyasic.rpc.luxminer import LUXMinerRPCAPI -from pyasic.data.pools import PoolMetrics, PoolUrl LUXMINER_DATA_LOC = DataLocations( **{ @@ -53,7 +53,7 @@ LUXMINER_DATA_LOC = DataLocations( "_get_uptime", [RPCAPICommand("rpc_stats", "stats")] ), str(DataOptions.POOLS): DataFunction( - "get_pools", [RPCAPICommand("rpc_pools", "pools")] + "_get_pools", [RPCAPICommand("rpc_pools", "pools")] ), } ) diff --git a/pyasic/miners/factory.py b/pyasic/miners/factory.py index cbd1e6be..bff39771 100644 --- a/pyasic/miners/factory.py +++ b/pyasic/miners/factory.py @@ -386,6 +386,7 @@ MINER_CLASSES = { "ANTMINER S19J PRO": VNishS19jPro, "ANTMINER S19A": VNishS19a, "ANTMINER S19A PRO": VNishS19aPro, + "ANTMINER S19 PRO HYD.": VNishS19ProHydro, "ANTMINER T19": VNishT19, "ANTMINER S21": VNishS21, }, diff --git a/pyasic/web/epic.py b/pyasic/web/epic.py index 5c19a98e..4a72ec93 100644 --- a/pyasic/web/epic.py +++ b/pyasic/web/epic.py @@ -15,14 +15,13 @@ # ------------------------------------------------------------------------------ from __future__ import annotations +import hashlib import json +from pathlib import Path from typing import Any -import httpx import aiofiles -import aiohttp -import hashlib -from pathlib import Path +import httpx from pyasic import settings from pyasic.errors import APIError @@ -52,7 +51,7 @@ class ePICWebAPI(BaseWebAPI): try: if parameters.get("form") is not None: form_data = parameters["form"] - form_data.add_field('password', self.pwd) + form_data.add_field("password", self.pwd) response = await client.post( f"http://{self.ip}:{self.port}/{command}", timeout=5, @@ -149,7 +148,7 @@ class ePICWebAPI(BaseWebAPI): return await self.send_command("capabilities") async def system_update(self, file: Path | str, keep_settings: bool = True): - """Perform a system update by uploading a firmware file and sending a + """Perform a system update by uploading a firmware file and sending a command to initiate the update.""" # calculate the SHA256 checksum of the firmware file @@ -161,8 +160,8 @@ class ePICWebAPI(BaseWebAPI): # prepare the multipart/form-data request form_data = aiohttp.FormData() - form_data.add_field('checksum', checksum) - form_data.add_field('keepsettings', str(keep_settings).lower()) - form_data.add_field('update.zip', open(file, 'rb'), filename='update.zip') + form_data.add_field("checksum", checksum) + form_data.add_field("keepsettings", str(keep_settings).lower()) + form_data.add_field("update.zip", open(file, "rb"), filename="update.zip") - await self.send_command("systemupdate", form=form_data) \ No newline at end of file + await self.send_command("systemupdate", form=form_data)