From 99ff28d3e13edaba93dd4bcac3f15bd0f438181f Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Thu, 9 May 2024 15:01:40 -0600 Subject: [PATCH] feature: add custom hashrate types and conversion. --- pyasic/data/__init__.py | 23 +++++++-- pyasic/data/boards.py | 4 +- pyasic/data/device.py | 2 + pyasic/data/hashrate/__init__.py | 15 ++++++ pyasic/data/hashrate/sha256.py | 54 ++++++++++++++++++++ pyasic/device/__init__.py | 1 + pyasic/device/algorithm/__init__.py | 5 ++ pyasic/device/algorithm/sha256.py | 69 ++++++++++++++++++++++++++ pyasic/miners/antminer/hiveon/X9/T9.py | 6 ++- pyasic/miners/backends/antminer.py | 21 ++++---- pyasic/miners/backends/auradine.py | 14 +++--- pyasic/miners/backends/avalonminer.py | 14 ++++-- pyasic/miners/backends/bfgminer.py | 19 +++---- pyasic/miners/backends/bmminer.py | 19 +++---- pyasic/miners/backends/braiins_os.py | 36 ++++++++------ pyasic/miners/backends/btminer.py | 17 ++++--- pyasic/miners/backends/cgminer.py | 7 +-- pyasic/miners/backends/epic.py | 14 ++++-- pyasic/miners/backends/goldshell.py | 8 +-- pyasic/miners/backends/innosilicon.py | 25 +++++----- pyasic/miners/backends/luxminer.py | 19 +++---- pyasic/miners/backends/marathon.py | 14 ++++-- pyasic/miners/backends/vnish.py | 7 +-- pyasic/miners/base.py | 9 +++- 24 files changed, 314 insertions(+), 108 deletions(-) create mode 100644 pyasic/data/hashrate/__init__.py create mode 100644 pyasic/data/hashrate/sha256.py create mode 100644 pyasic/device/algorithm/__init__.py create mode 100644 pyasic/device/algorithm/sha256.py diff --git a/pyasic/data/__init__.py b/pyasic/data/__init__.py index 66752530..a513bdfe 100644 --- a/pyasic/data/__init__.py +++ b/pyasic/data/__init__.py @@ -28,6 +28,7 @@ from .boards import HashBoard from .device import DeviceInfo from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error from .fans import Fan +from .hashrate import AlgoHashRate, HashUnit @dataclass @@ -40,6 +41,10 @@ class MinerData: uptime: The uptime of the miner in seconds. mac: The MAC address of the miner as a str. device_info: Info about the device, such as model, make, and firmware. + model: The model of the miner as a str. + make: The make of the miner as a str. + firmware: The firmware on the miner as a str. + algo: The mining algorithm of the miner as a str. api_ver: The current api version on the miner as a str. fw_ver: The current firmware version on the miner as a str. hostname: The network hostname of the miner as a str. @@ -78,14 +83,15 @@ class MinerData: make: str = field(init=False) model: str = field(init=False) firmware: str = field(init=False) + algo: str = field(init=False) mac: str = None api_ver: str = None fw_ver: str = None hostname: str = None # hashrate - hashrate: float = field(init=False) - _hashrate: float = field(repr=False, default=None) + hashrate: AlgoHashRate = field(init=False) + _hashrate: AlgoHashRate = field(repr=False, default=None) # expected expected_hashrate: float = None @@ -212,7 +218,7 @@ class MinerData: if item.hashrate is not None: hr_data.append(item.hashrate) if len(hr_data) > 0: - return round(sum(hr_data), 2) + return sum(hr_data, start=type(hr_data[0])(0)) return self._hashrate @hashrate.setter @@ -313,7 +319,7 @@ class MinerData: return None if self.hashrate == 0 or self.wattage == 0: return 0 - return round(self.wattage / self.hashrate) + return round(self.wattage / float(self.hashrate)) @efficiency.setter def efficiency(self, val): @@ -362,6 +368,15 @@ class MinerData: def firmware(self, val): pass + @property + def algo(self): # noqa - Skip PyCharm inspection + if self.device_info.algo is not None: + return str(self.device_info.algo) + + @algo.setter + def algo(self, val): + pass + def asdict(self) -> dict: return asdict(self, dict_factory=self.dict_factory) diff --git a/pyasic/data/boards.py b/pyasic/data/boards.py index d4f37dd6..b501e16e 100644 --- a/pyasic/data/boards.py +++ b/pyasic/data/boards.py @@ -17,6 +17,8 @@ from dataclasses import dataclass from typing import Any +from .hashrate import AlgoHashRate + @dataclass class HashBoard: @@ -34,7 +36,7 @@ class HashBoard: """ slot: int = 0 - hashrate: float = None + hashrate: AlgoHashRate = None temp: int = None chip_temp: int = None chips: int = None diff --git a/pyasic/data/device.py b/pyasic/data/device.py index 153e9bb4..3a1edb90 100644 --- a/pyasic/data/device.py +++ b/pyasic/data/device.py @@ -1,5 +1,6 @@ from dataclasses import dataclass +from pyasic.device.algorithm import MinerAlgo from pyasic.device.firmware import MinerFirmware from pyasic.device.makes import MinerMake from pyasic.device.models import MinerModel @@ -10,3 +11,4 @@ class DeviceInfo: make: MinerMake = None model: MinerModel = None firmware: MinerFirmware = None + algo: MinerAlgo = None diff --git a/pyasic/data/hashrate/__init__.py b/pyasic/data/hashrate/__init__.py new file mode 100644 index 00000000..5a8f2e12 --- /dev/null +++ b/pyasic/data/hashrate/__init__.py @@ -0,0 +1,15 @@ +from enum import Enum + +from pyasic.data.hashrate.sha256 import SHA256HashRate +from pyasic.device.algorithm.sha256 import SHA256Unit + + +class AlgoHashRate(Enum): + SHA256 = SHA256HashRate + + def __call__(self, *args, **kwargs): + return self.value(*args, **kwargs) + + +class HashUnit: + SHA256 = SHA256Unit diff --git a/pyasic/data/hashrate/sha256.py b/pyasic/data/hashrate/sha256.py new file mode 100644 index 00000000..722b222e --- /dev/null +++ b/pyasic/data/hashrate/sha256.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from pyasic.device.algorithm import MinerAlgo +from pyasic.device.algorithm.sha256 import SHA256Unit + + +@dataclass +class SHA256HashRate: + rate: float + unit: SHA256Unit = MinerAlgo.SHA256.unit.default + + def __float__(self): + return float(self.rate) + + def __int__(self): + return int(self.rate) + + def __repr__(self): + return f"{self.rate} {str(self.unit)}" + + def __round__(self, n: int = None): + return round(self.rate, n) + + def __add__(self, other: SHA256HashRate | int | float) -> SHA256HashRate: + if isinstance(other, SHA256HashRate): + return SHA256HashRate(self.rate + other.into(self.unit).rate, self.unit) + return SHA256HashRate(self.rate + other, self.unit) + + def __sub__(self, other: SHA256HashRate | int | float) -> SHA256HashRate: + if isinstance(other, SHA256HashRate): + return SHA256HashRate(self.rate - other.into(self.unit).rate, self.unit) + return SHA256HashRate(self.rate - other, self.unit) + + def __truediv__(self, other: SHA256HashRate | int | float): + if isinstance(other, SHA256HashRate): + return SHA256HashRate(self.rate / other.into(self.unit).rate, self.unit) + return SHA256HashRate(self.rate / other, self.unit) + + def __floordiv__(self, other: SHA256HashRate | int | float): + if isinstance(other, SHA256HashRate): + return SHA256HashRate(self.rate // other.into(self.unit).rate, self.unit) + return SHA256HashRate(self.rate // other, self.unit) + + def __mul__(self, other: SHA256HashRate | int | float): + if isinstance(other, SHA256HashRate): + return SHA256HashRate(self.rate * other.into(self.unit).rate, self.unit) + return SHA256HashRate(self.rate * other, self.unit) + + def into(self, other: SHA256Unit) -> SHA256HashRate: + return SHA256HashRate( + rate=self.rate / (other.value / self.unit.value), unit=other + ) diff --git a/pyasic/device/__init__.py b/pyasic/device/__init__.py index ac373529..86ba4f32 100644 --- a/pyasic/device/__init__.py +++ b/pyasic/device/__init__.py @@ -1,3 +1,4 @@ +from .algorithm import MinerAlgo from .firmware import MinerFirmware from .makes import MinerMake from .models import MinerModel diff --git a/pyasic/device/algorithm/__init__.py b/pyasic/device/algorithm/__init__.py new file mode 100644 index 00000000..d9c85521 --- /dev/null +++ b/pyasic/device/algorithm/__init__.py @@ -0,0 +1,5 @@ +from pyasic.device.algorithm.sha256 import SHA256Algo + + +class MinerAlgo: + SHA256 = SHA256Algo diff --git a/pyasic/device/algorithm/sha256.py b/pyasic/device/algorithm/sha256.py new file mode 100644 index 00000000..0627bba5 --- /dev/null +++ b/pyasic/device/algorithm/sha256.py @@ -0,0 +1,69 @@ +from __future__ import annotations + +from dataclasses import dataclass +from enum import IntEnum + + +class SHA256Unit(IntEnum): + H = 1 + KH = int(H) * 1000 + MH = int(KH) * 1000 + GH = int(MH) * 1000 + TH = int(GH) * 1000 + PH = int(TH) * 1000 + EH = int(PH) * 1000 + ZH = int(EH) * 1000 + + default = TH + + def __str__(self): + if self.value == self.H: + return "H/s" + if self.value == self.KH: + return "KH/s" + if self.value == self.MH: + return "MH/s" + if self.value == self.GH: + return "GH/s" + if self.value == self.TH: + return "TH/s" + if self.value == self.PH: + return "PH/s" + if self.value == self.EH: + return "EH/s" + if self.value == self.ZH: + return "ZH/s" + + @classmethod + def from_str(cls, value: str): + if value == "H": + return cls.H + elif value == "KH": + return cls.KH + elif value == "MH": + return cls.MH + elif value == "GH": + return cls.GH + elif value == "TH": + return cls.TH + elif value == "PH": + return cls.PH + elif value == "EH": + return cls.EH + elif value == "ZH": + return cls.ZH + return cls.default + + def __repr__(self): + return str(self) + + +# make this json serializable +class _SHA256Algo(str): + unit = SHA256Unit + + def __repr__(self): + return "SHA256Algo" + + +SHA256Algo = _SHA256Algo("SHA256") diff --git a/pyasic/miners/antminer/hiveon/X9/T9.py b/pyasic/miners/antminer/hiveon/X9/T9.py index a9e7296d..ff71f3ce 100644 --- a/pyasic/miners/antminer/hiveon/X9/T9.py +++ b/pyasic/miners/antminer/hiveon/X9/T9.py @@ -18,7 +18,7 @@ from typing import List, Optional import asyncssh -from pyasic.data import HashBoard +from pyasic.data import AlgoHashRate, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.backends import Hiveon from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand @@ -121,7 +121,9 @@ class HiveonT9(Hiveon, T9): chips += rpc_stats["STATS"][1][f"chain_acn{chipset}"] except (KeyError, IndexError): pass - hashboards[board].hashrate = round(hashrate / 1000, 2) + hashboards[board].hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.GH + ).into(self.algo.unit.default) hashboards[board].chips = chips return hashboards diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index 1cf695fc..7e072e32 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -17,7 +17,7 @@ from typing import List, Optional, Union from pyasic.config import MinerConfig, MiningModeConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.errors import APIError from pyasic.miners.backends.bmminer import BMMiner @@ -218,9 +218,9 @@ class AntminerModern(BMMiner): if rpc_stats is not None: try: for board in rpc_stats["STATS"][0]["chain"]: - hashboards[board["index"]].hashrate = round( - board["rate_real"] / 1000, 2 - ) + hashboards[board["index"]].hashrate = AlgoHashRate.SHA256( + board["rate_real"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) hashboards[board["index"]].chips = board["asic_num"] board_temp_data = list( filter(lambda x: not x == 0, board["temp_pcb"]) @@ -273,12 +273,9 @@ class AntminerModern(BMMiner): rate_unit = rpc_stats["STATS"][1]["rate_unit"] except KeyError: rate_unit = "GH" - if rate_unit == "GH": - return round(expected_rate / 1000, 2) - if rate_unit == "MH": - return round(expected_rate / 1000000, 2) - else: - return round(expected_rate, 2) + return AlgoHashRate.SHA256( + expected_rate, HashUnit.SHA256.from_str(rate_unit) + ).int(self.algo.unit.default) except LookupError: pass @@ -552,7 +549,9 @@ class AntminerOld(CGMiner): hashrate = boards[1].get(f"chain_rate{i}") if hashrate: - hashboard.hashrate = round(float(hashrate) / 1000, 2) + hashboard.hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.GH + ).into(self.algo.unit.default) chips = boards[1].get(f"chain_acn{i}") if chips: diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 76281159..37290048 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -18,7 +18,7 @@ from enum import Enum from typing import List, Optional from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.data import ( DataFunction, @@ -245,9 +245,9 @@ class Auradine(StockFirmware): if rpc_summary is not None: try: - return round( - float(float(rpc_summary["SUMMARY"][0]["MHS 5s"]) / 1000000), 2 - ) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["MHS 5s"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (LookupError, ValueError, TypeError): pass @@ -274,9 +274,9 @@ class Auradine(StockFirmware): try: for board in rpc_devs["DEVS"]: b_id = board["ID"] - 1 - hashboards[b_id].hashrate = round( - float(float(board["MHS 5s"]) / 1000000), 2 - ) + hashboards[b_id].hashrate = AlgoHashRate.SHA256( + board["MHS 5s"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) hashboards[b_id].temp = round(float(float(board["Temperature"])), 2) hashboards[b_id].missing = False except LookupError: diff --git a/pyasic/miners/backends/avalonminer.py b/pyasic/miners/backends/avalonminer.py index 58e11acf..2a1b9b99 100644 --- a/pyasic/miners/backends/avalonminer.py +++ b/pyasic/miners/backends/avalonminer.py @@ -17,7 +17,7 @@ import re from typing import List, Optional -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.backends.cgminer import CGMiner from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand @@ -182,7 +182,9 @@ class AvalonMiner(CGMiner): if rpc_devs is not None: try: - return round(float(rpc_devs["DEVS"][0]["MHS 1m"] / 1000000), 2) + return AlgoHashRate.SHA256( + rpc_devs["DEVS"][0]["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (KeyError, IndexError, ValueError, TypeError): pass @@ -213,7 +215,9 @@ class AvalonMiner(CGMiner): try: board_hr = parsed_stats["MGHS"][board] - hashboards[board].hashrate = round(float(board_hr) / 1000, 2) + hashboards[board].hashrate = AlgoHashRate.SHA256( + board_hr, HashUnit.SHA256.GH + ).into(self.algo.unit.default) except LookupError: pass @@ -245,7 +249,9 @@ class AvalonMiner(CGMiner): try: unparsed_stats = rpc_stats["STATS"][0]["MM ID0"] parsed_stats = self.parse_stats(unparsed_stats) - return round(float(parsed_stats["GHSmm"]) / 1000, 2) + return AlgoHashRate.SHA256( + parsed_stats["GHSmm"], HashUnit.SHA256.GH + ).int(self.algo.unit.default) except (IndexError, KeyError, ValueError, TypeError): pass diff --git a/pyasic/miners/backends/bfgminer.py b/pyasic/miners/backends/bfgminer.py index 8d33b758..9b1975b0 100644 --- a/pyasic/miners/backends/bfgminer.py +++ b/pyasic/miners/backends/bfgminer.py @@ -17,7 +17,7 @@ from typing import List, Optional from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware @@ -115,7 +115,9 @@ class BFGMiner(StockFirmware): if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["MHS 20s"] / 1000000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["MHS 20s"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (LookupError, ValueError, TypeError): pass @@ -159,7 +161,9 @@ class BFGMiner(StockFirmware): hashrate = boards[1].get(f"chain_rate{i}") if hashrate: - hashboard.hashrate = round(float(hashrate) / 1000, 2) + hashboard.hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.GH + ).into(self.algo.unit.default) chips = boards[1].get(f"chain_acn{i}") if chips: @@ -218,11 +222,8 @@ class BFGMiner(StockFirmware): rate_unit = rpc_stats["STATS"][1]["rate_unit"] except KeyError: rate_unit = "GH" - if rate_unit == "GH": - return round(expected_rate / 1000, 2) - if rate_unit == "MH": - return round(expected_rate / 1000000, 2) - else: - return round(expected_rate, 2) + return AlgoHashRate.SHA256( + expected_rate, HashUnit.SHA256.from_str(rate_unit) + ).int(self.algo.unit.default) except LookupError: pass diff --git a/pyasic/miners/backends/bmminer.py b/pyasic/miners/backends/bmminer.py index e8fa7d19..6501051e 100644 --- a/pyasic/miners/backends/bmminer.py +++ b/pyasic/miners/backends/bmminer.py @@ -17,7 +17,7 @@ from typing import List, Optional from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware @@ -119,7 +119,9 @@ class BMMiner(StockFirmware): if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) except (LookupError, ValueError, TypeError): pass @@ -176,7 +178,9 @@ class BMMiner(StockFirmware): hashrate = boards[1].get(f"chain_rate{i}") if hashrate: - hashboard.hashrate = round(float(hashrate) / 1000, 2) + hashboard.hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.GH + ).into(self.algo.unit.default) chips = boards[1].get(f"chain_acn{i}") if chips: @@ -234,12 +238,9 @@ class BMMiner(StockFirmware): rate_unit = rpc_stats["STATS"][1]["rate_unit"] except KeyError: rate_unit = "GH" - if rate_unit == "GH": - return round(expected_rate / 1000, 2) - if rate_unit == "MH": - return round(expected_rate / 1000000, 2) - else: - return round(expected_rate, 2) + return AlgoHashRate.SHA256( + expected_rate, HashUnit.SHA256.from_str(rate_unit) + ).int(self.algo.unit.default) except LookupError: pass diff --git a/pyasic/miners/backends/braiins_os.py b/pyasic/miners/backends/braiins_os.py index baf486bc..9684eb1b 100644 --- a/pyasic/miners/backends/braiins_os.py +++ b/pyasic/miners/backends/braiins_os.py @@ -21,7 +21,7 @@ import toml from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePowerTune -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import BraiinsOSError, MinerErrorData from pyasic.errors import APIError from pyasic.miners.data import ( @@ -349,7 +349,9 @@ class BOSMiner(BraiinsOSFirmware): if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (KeyError, IndexError, ValueError, TypeError): pass @@ -419,8 +421,9 @@ class BOSMiner(BraiinsOSFirmware): for board in rpc_devs["DEVS"]: _id = board["ID"] - offset - hashrate = round(float(board["MHS 1m"] / 1000000), 2) - hashboards[_id].hashrate = hashrate + hashboards[_id].hashrate = AlgoHashRate.SHA256( + board["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (IndexError, KeyError): pass @@ -528,11 +531,12 @@ class BOSMiner(BraiinsOSFirmware): expected_hashrate = round(float(board["Nominal MHS"] / 1000000), 2) if expected_hashrate: hr_list.append(expected_hashrate) + if len(hr_list) == 0: - return 0 + return AlgoHashRate.SHA256(0) else: - return round( - (sum(hr_list) / len(hr_list)) * self.expected_hashboards, 2 + return AlgoHashRate.SHA256( + (sum(hr_list) / len(hr_list)) * self.expected_hashboards ) except (IndexError, KeyError): pass @@ -783,7 +787,9 @@ class BOSer(BraiinsOSFirmware): if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (KeyError, IndexError, ValueError, TypeError): pass @@ -798,7 +804,10 @@ class BOSer(BraiinsOSFirmware): if grpc_miner_details is not None: try: - return grpc_miner_details["stickerHashrate"]["gigahashPerSecond"] / 1000 + return AlgoHashRate.SHA256( + grpc_miner_details["stickerHashrate"]["gigahashPerSecond"], + HashUnit.SHA256.GH, + ).into(self.algo.unit.default) except LookupError: pass @@ -827,13 +836,12 @@ class BOSer(BraiinsOSFirmware): ] if board.get("stats") is not None: if not board["stats"]["realHashrate"]["last5S"] == {}: - hashboards[idx].hashrate = round( + hashboards[idx].hashrate = AlgoHashRate.SHA256( board["stats"]["realHashrate"]["last5S"][ "gigahashPerSecond" - ] - / 1000, - 2, - ) + ], + HashUnit.SHA256.GH, + ).into(self.algo.unit.default) hashboards[idx].missing = False return hashboards diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index b7565258..01e4ac8c 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -18,7 +18,7 @@ import logging from typing import List, Optional from pyasic.config import MinerConfig, MiningModeConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData, WhatsminerError from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand @@ -395,7 +395,9 @@ class BTMiner(StockFirmware): if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except LookupError: pass @@ -423,9 +425,9 @@ class BTMiner(StockFirmware): self.expected_hashboards += 1 hashboards[board["ASC"]].chip_temp = round(board["Chip Temp Avg"]) hashboards[board["ASC"]].temp = round(board["Temperature"]) - hashboards[board["ASC"]].hashrate = round( - float(board["MHS 1m"] / 1000000), 2 - ) + hashboards[board["ASC"]].hashrate = AlgoHashRate.SHA256( + board["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) hashboards[board["ASC"]].chips = board["Effective Chips"] hashboards[board["ASC"]].serial_number = board["PCB SN"] hashboards[board["ASC"]].missing = False @@ -571,7 +573,10 @@ class BTMiner(StockFirmware): try: expected_hashrate = rpc_summary["SUMMARY"][0]["Factory GHS"] if expected_hashrate: - return round(expected_hashrate / 1000, 2) + return AlgoHashRate.SHA256( + expected_hashrate, HashUnit.SHA256.GH + ).int(self.algo.unit.default) + except LookupError: pass diff --git a/pyasic/miners/backends/cgminer.py b/pyasic/miners/backends/cgminer.py index 687ecc3b..53322aed 100644 --- a/pyasic/miners/backends/cgminer.py +++ b/pyasic/miners/backends/cgminer.py @@ -17,6 +17,7 @@ from typing import Optional from pyasic.config import MinerConfig +from pyasic.data import AlgoHashRate, HashUnit from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware @@ -117,9 +118,9 @@ class CGMiner(StockFirmware): if rpc_summary is not None: try: - return round( - float(float(rpc_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2 - ) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) except (LookupError, ValueError, TypeError): pass diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index 02932146..3c1a7feb 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -17,7 +17,7 @@ from typing import List, Optional from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.errors import APIError from pyasic.logger import logger @@ -227,7 +227,9 @@ class ePIC(ePICFirmware): if web_summary["HBs"] is not None: for hb in web_summary["HBs"]: hashrate += hb["Hashrate"][0] - return round(float(float(hashrate / 1000000)), 2) + return AlgoHashRate.SHA256(hashrate, HashUnit.SHA256.MH).into( + HashUnit.SHA256.TH + ) except (LookupError, ValueError, TypeError): pass @@ -249,7 +251,9 @@ class ePIC(ePICFirmware): ideal = hb["Hashrate"][1] / 100 hashrate += hb["Hashrate"][0] / ideal - return round(float(float(hashrate / 1000000)), 2) + return AlgoHashRate.SHA256(hashrate, HashUnit.SHA256.GH).int( + self.algo.unit.default + ) except (LookupError, ValueError, TypeError): pass @@ -310,7 +314,9 @@ class ePIC(ePICFirmware): hashrate = hb["Hashrate"][0] # Update the Hashboard object hb_list[hb["Index"]].missing = False - hb_list[hb["Index"]].hashrate = round(hashrate / 1000000, 2) + hb_list[hb["Index"]].hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.MH + ).into(self.algo.unit.default) hb_list[hb["Index"]].chips = num_of_chips hb_list[hb["Index"]].temp = hb["Temperature"] return hb_list diff --git a/pyasic/miners/backends/goldshell.py b/pyasic/miners/backends/goldshell.py index c6159db3..8bf1553f 100644 --- a/pyasic/miners/backends/goldshell.py +++ b/pyasic/miners/backends/goldshell.py @@ -16,7 +16,7 @@ from typing import List from pyasic.config import MinerConfig, MiningModeConfig -from pyasic.data import HashBoard +from pyasic.data import AlgoHashRate, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.logger import logger from pyasic.miners.backends import BFGMiner @@ -158,9 +158,9 @@ class GoldshellMiner(BFGMiner): if board.get("ID") is not None: try: b_id = board["ID"] - hashboards[b_id].hashrate = round( - board["MHS 20s"] / 1000000, 2 - ) + hashboards[b_id].hashrate = AlgoHashRate.SHA256( + board["MHS 20s"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) hashboards[b_id].temp = board["tstemp-2"] hashboards[b_id].missing = False except KeyError: diff --git a/pyasic/miners/backends/innosilicon.py b/pyasic/miners/backends/innosilicon.py index d806aa15..8efe28f2 100644 --- a/pyasic/miners/backends/innosilicon.py +++ b/pyasic/miners/backends/innosilicon.py @@ -16,7 +16,7 @@ from typing import List, Optional from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes.innosilicon import InnosiliconError from pyasic.errors import APIError @@ -182,20 +182,21 @@ class Innosilicon(CGMiner): if web_get_all is not None: try: if "Hash Rate H" in web_get_all["total_hash"].keys(): - return round( - float(web_get_all["total_hash"]["Hash Rate H"] / 1000000000000), - 2, - ) + return AlgoHashRate.SHA256( + web_get_all["total_hash"]["Hash Rate H"], HashUnit.SHA256.H + ).into(self.algo.unit.default) elif "Hash Rate" in web_get_all["total_hash"].keys(): - return round( - float(web_get_all["total_hash"]["Hash Rate"] / 1000000), 5 - ) + return AlgoHashRate.SHA256( + web_get_all["total_hash"]["Hash Rate"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except KeyError: pass if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH + ).into(self.algo.unit.default) except (KeyError, IndexError): pass @@ -247,9 +248,9 @@ class Innosilicon(CGMiner): hashrate = board.get("Hash Rate H") if hashrate: - hashboards[idx].hashrate = round( - hashrate / 1000000000000, 2 - ) + hashboards[idx].hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.H + ).into(self.algo.unit.default) chip_temp = board.get("Temp max") if chip_temp: diff --git a/pyasic/miners/backends/luxminer.py b/pyasic/miners/backends/luxminer.py index e0faed20..ccf00bdb 100644 --- a/pyasic/miners/backends/luxminer.py +++ b/pyasic/miners/backends/luxminer.py @@ -16,7 +16,7 @@ from typing import List, Optional from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import LuxOSFirmware @@ -171,7 +171,9 @@ class LUXMiner(LuxOSFirmware): if rpc_summary is not None: try: - return round(float(rpc_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) except (LookupError, ValueError, TypeError): pass @@ -215,7 +217,9 @@ class LUXMiner(LuxOSFirmware): hashrate = boards[1].get(f"chain_rate{i}") if hashrate: - hashboard.hashrate = round(float(hashrate) / 1000, 2) + hashboard.hashrate = AlgoHashRate.SHA256( + hashrate, HashUnit.SHA256.GH + ).into(self.algo.unit.default) chips = boards[1].get(f"chain_acn{i}") if chips: @@ -273,12 +277,9 @@ class LUXMiner(LuxOSFirmware): rate_unit = rpc_stats["STATS"][1]["rate_unit"] except KeyError: rate_unit = "GH" - if rate_unit == "GH": - return round(expected_rate / 1000, 2) - if rate_unit == "MH": - return round(expected_rate / 1000000, 2) - else: - return round(expected_rate, 2) + return AlgoHashRate.SHA256( + expected_rate, HashUnit.SHA256.from_str(rate_unit) + ).int(self.algo.unit.default) except LookupError: pass diff --git a/pyasic/miners/backends/marathon.py b/pyasic/miners/backends/marathon.py index 4b60daa3..7fa1d913 100644 --- a/pyasic/miners/backends/marathon.py +++ b/pyasic/miners/backends/marathon.py @@ -2,7 +2,7 @@ from typing import List, Optional from pyasic import MinerConfig from pyasic.config import MiningModeConfig -from pyasic.data import Fan, HashBoard +from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.miners.device.firmware import MaraFirmware @@ -170,7 +170,9 @@ class MaraMiner(MaraFirmware): try: for hb in web_hashboards["hashboards"]: idx = hb["index"] - hashboards[idx].hashrate = round(hb["hashrate_average"] / 1000, 2) + hashboards[idx].hashrate = AlgoHashRate.SHA256( + hb["hashrate_average"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) hashboards[idx].temp = round( sum(hb["temperature_pcb"]) / len(hb["temperature_pcb"]), 2 ) @@ -232,7 +234,9 @@ class MaraMiner(MaraFirmware): if web_brief is not None: try: - return round(web_brief["hashrate_realtime"], 2) + return AlgoHashRate.SHA256( + web_brief["hashrate_realtime"], HashUnit.SHA256.TH + ).into(self.algo.unit.default) except LookupError: pass @@ -276,7 +280,9 @@ class MaraMiner(MaraFirmware): if web_brief is not None: try: - return round(web_brief["hashrate_ideal"] / 1000, 2) + return AlgoHashRate.SHA256( + web_brief["hashrate_ideal"], HashUnit.SHA256.GH + ).int(self.algo.unit.default) except LookupError: pass diff --git a/pyasic/miners/backends/vnish.py b/pyasic/miners/backends/vnish.py index da690362..91b3c6f0 100644 --- a/pyasic/miners/backends/vnish.py +++ b/pyasic/miners/backends/vnish.py @@ -17,6 +17,7 @@ from typing import Optional from pyasic import MinerConfig +from pyasic.data import AlgoHashRate, HashUnit from pyasic.errors import APIError from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.data import ( @@ -186,9 +187,9 @@ class VNish(BMMiner, VNishFirmware): if rpc_summary is not None: try: - return round( - float(float(rpc_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2 - ) + return AlgoHashRate.SHA256( + rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH + ).into(self.algo.unit.default) except (LookupError, ValueError, TypeError): pass diff --git a/pyasic/miners/base.py b/pyasic/miners/base.py index 67d4036d..b09084bc 100644 --- a/pyasic/miners/base.py +++ b/pyasic/miners/base.py @@ -22,6 +22,8 @@ from pyasic.config import MinerConfig from pyasic.data import Fan, HashBoard, MinerData from pyasic.data.device import DeviceInfo from pyasic.data.error_codes import MinerErrorData +from pyasic.device import MinerModel +from pyasic.device.algorithm import MinerAlgo from pyasic.device.firmware import MinerFirmware from pyasic.device.makes import MinerMake from pyasic.errors import APIError @@ -40,8 +42,9 @@ class MinerProtocol(Protocol): ssh: _ssh_cls = None make: MinerMake = None - raw_model: str = None + raw_model: MinerModel = None firmware: MinerFirmware = None + algo = MinerAlgo.SHA256 expected_hashboards: int = 3 expected_chips: int = None @@ -84,7 +87,9 @@ class MinerProtocol(Protocol): @property def device_info(self) -> DeviceInfo: - return DeviceInfo(make=self.make, model=self.raw_model, firmware=self.firmware) + return DeviceInfo( + make=self.make, model=self.raw_model, firmware=self.firmware, algo=self.algo + ) @property def api(self):