Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d632360932 | ||
|
|
400001fa38 | ||
|
|
4ff32a8081 | ||
|
|
33b4ae2f2f | ||
|
|
62194bd627 |
@@ -95,7 +95,9 @@ class BaseMinerAPI:
|
||||
|
||||
Parameters:
|
||||
*commands: The commands to send as a multicommand to the miner.
|
||||
ignore_errors: Whether to raise APIError when the command returns an error.
|
||||
allow_warning: A boolean to supress APIWarnings.
|
||||
|
||||
"""
|
||||
# make sure we can actually run each command, otherwise they will fail
|
||||
commands = self._check_commands(*commands)
|
||||
|
||||
@@ -63,8 +63,8 @@ class BMMinerAPI(BaseMinerAPI):
|
||||
data[cmd].append(
|
||||
await self.send_command(cmd, allow_warning=allow_warning)
|
||||
)
|
||||
except APIError as e:
|
||||
raise APIError(e)
|
||||
except APIError:
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
|
||||
|
||||
@@ -61,7 +61,7 @@ class CGMinerAPI(BaseMinerAPI):
|
||||
data[cmd] = []
|
||||
data[cmd].append(await self.send_command(cmd, allow_warning=True))
|
||||
except APIError as e:
|
||||
raise APIError(e)
|
||||
pass
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
|
||||
|
||||
@@ -46,6 +46,17 @@ class HashBoard:
|
||||
missing: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class Fan:
|
||||
"""A Dataclass to standardize fan data.
|
||||
|
||||
Attributes:
|
||||
speed: The speed of the fan.
|
||||
"""
|
||||
|
||||
speed: int = -1
|
||||
|
||||
|
||||
@dataclass
|
||||
class MinerData:
|
||||
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
|
||||
@@ -120,10 +131,11 @@ class MinerData:
|
||||
right_board_chip_temp: int = field(init=False)
|
||||
wattage: int = -1
|
||||
wattage_limit: int = -1
|
||||
fan_1: int = -1
|
||||
fan_2: int = -1
|
||||
fan_3: int = -1
|
||||
fan_4: int = -1
|
||||
fans: List[Fan] = field(default_factory=list)
|
||||
fan_1: int = field(init=False)
|
||||
fan_2: int = field(init=False)
|
||||
fan_3: int = field(init=False)
|
||||
fan_4: int = field(init=False)
|
||||
fan_psu: int = -1
|
||||
left_chips: int = field(init=False)
|
||||
center_chips: int = field(init=False)
|
||||
@@ -199,6 +211,42 @@ class MinerData:
|
||||
setattr(cp, key, item & other_item)
|
||||
return cp
|
||||
|
||||
@property
|
||||
def fan_1(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 0:
|
||||
return self.fans[0].speed
|
||||
|
||||
@fan_1.setter
|
||||
def fan_1(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def fan_2(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 1:
|
||||
return self.fans[1].speed
|
||||
|
||||
@fan_2.setter
|
||||
def fan_2(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def fan_3(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 2:
|
||||
return self.fans[2].speed
|
||||
|
||||
@fan_3.setter
|
||||
def fan_3(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def fan_4(self): # noqa - Skip PyCharm inspection
|
||||
if len(self.fans) > 3:
|
||||
return self.fans[3].speed
|
||||
|
||||
@fan_4.setter
|
||||
def fan_4(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def total_chips(self): # noqa - Skip PyCharm inspection
|
||||
return sum([hb.chips for hb in self.hashboards])
|
||||
|
||||
@@ -21,7 +21,7 @@ import asyncssh
|
||||
|
||||
from pyasic.API.bmminer import BMMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -138,13 +138,10 @@ class BMMiner(BaseMiner):
|
||||
logging.warning(f"Failed to get model for miner: {self}")
|
||||
return None
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.api_ver and self.fw_ver:
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
if self.api_ver:
|
||||
return self.api_ver
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
@@ -157,12 +154,40 @@ class BMMiner(BaseMiner):
|
||||
self.api_ver = api_version["VERSION"][0]["API"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.fw_ver:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_version:
|
||||
try:
|
||||
self.fw_ver = api_version["VERSION"][0]["CompileTime"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
return self.fw_ver
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
# check if version is cached
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
return miner_version(
|
||||
api_ver=await self.get_api_ver(api_version),
|
||||
fw_ver=await self.get_fw_ver(api_version=api_version),
|
||||
)
|
||||
|
||||
async def get_fan_psu(self):
|
||||
return None
|
||||
|
||||
async def get_hostname(self) -> Optional[str]:
|
||||
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
|
||||
@@ -245,18 +270,7 @@ class BMMiner(BaseMiner):
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_fans(
|
||||
self, api_stats: dict = None
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
@@ -280,9 +294,9 @@ class BMMiner(BaseMiner):
|
||||
fans_data[fan] = api_stats["STATS"][1].get(f"fan{fan_offset+fan}")
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
fans = fan_speeds(*fans_data)
|
||||
fans = [Fan(speed=d) if d else Fan() for d in fans_data]
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
return fans
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -339,108 +353,3 @@ class BMMiner(BaseMiner):
|
||||
return round(ideal_rate, 2)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"pools",
|
||||
"version",
|
||||
"devdetails",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"version",
|
||||
"pools",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
version = miner_data.get("version")
|
||||
if version:
|
||||
version = version[0]
|
||||
devdetails = miner_data.get("devdetails")
|
||||
if devdetails:
|
||||
devdetails = devdetails[0]
|
||||
stats = miner_data.get("stats")
|
||||
if stats:
|
||||
stats = stats[0]
|
||||
else:
|
||||
summary, pools, devdetails, version, stats = (None for _ in range(5))
|
||||
|
||||
data = { # noqa - Ignore dictionary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(),
|
||||
"model": await self.get_model(api_devdetails=devdetails),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # Done at end.
|
||||
"hostname": await self.get_hostname(),
|
||||
"hashrate": await self.get_hashrate(api_summary=summary),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_stats=stats),
|
||||
"hashboards": await self.get_hashboards(api_stats=stats),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(),
|
||||
"wattage": await self.get_wattage(),
|
||||
"wattage_limit": await self.get_wattage_limit(),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(),
|
||||
"fault_light": await self.get_fault_light(),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(api_version=version)
|
||||
fan_data = await self.get_fans()
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools)
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -24,7 +24,7 @@ import toml
|
||||
|
||||
from pyasic.API.bosminer import BOSMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -287,10 +287,29 @@ class BOSMiner(BaseMiner):
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
# check if version is cached
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
# if self.fw_ver and self.api_ver:
|
||||
# logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||
# return miner_version(self.api_ver, self.fw_ver)
|
||||
api_ver = await self.get_api_ver(api_version)
|
||||
fw_ver = await self.get_fw_ver(graphql_version)
|
||||
return miner_version(api_ver, fw_ver)
|
||||
|
||||
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
# Now get the API version
|
||||
if api_version:
|
||||
try:
|
||||
api_ver = api_version["VERSION"][0]["API"]
|
||||
except (KeyError, IndexError):
|
||||
api_ver = None
|
||||
self.api_ver = api_ver
|
||||
self.api.api_ver = self.api_ver
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(self, graphql_version: dict = None) -> Optional[str]:
|
||||
if not graphql_version:
|
||||
try:
|
||||
graphql_version = await self.send_graphql_query(
|
||||
@@ -299,11 +318,6 @@ class BOSMiner(BaseMiner):
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
fw_ver = None
|
||||
|
||||
if graphql_version:
|
||||
@@ -323,16 +337,7 @@ class BOSMiner(BaseMiner):
|
||||
self.fw_ver = ver
|
||||
logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||
|
||||
# Now get the API version
|
||||
if api_version:
|
||||
try:
|
||||
api_ver = api_version["VERSION"][0]["API"]
|
||||
except (KeyError, IndexError):
|
||||
api_ver = None
|
||||
self.api_ver = api_ver
|
||||
self.api.api_ver = self.api_ver
|
||||
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
return self.fw_ver
|
||||
|
||||
async def get_hostname(self, graphql_hostname: dict = None) -> Union[str, None]:
|
||||
if self.hostname:
|
||||
@@ -584,16 +589,7 @@ class BOSMiner(BaseMiner):
|
||||
|
||||
async def get_fans(
|
||||
self, api_fans: dict = None, graphql_fans: dict = None
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
psu_fan = None
|
||||
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
|
||||
) -> List[Fan]:
|
||||
if not graphql_fans and not api_fans:
|
||||
try:
|
||||
graphql_fans = await self.send_graphql_query(
|
||||
@@ -603,18 +599,15 @@ class BOSMiner(BaseMiner):
|
||||
pass
|
||||
|
||||
if graphql_fans:
|
||||
fans = {"fan_1": None, "fan_2": None, "fan_3": None, "fan_4": None}
|
||||
fans = {"fan_1": Fan(), "fan_2": Fan(), "fan_3": Fan(), "fan_4": Fan()}
|
||||
for n in range(self.fan_count):
|
||||
try:
|
||||
fans[f"fan_{n + 1}"] = graphql_fans["bosminer"]["info"]["fans"][n][
|
||||
"rpm"
|
||||
]
|
||||
fans[f"fan_{n + 1}"].speed = graphql_fans["bosminer"]["info"][
|
||||
"fans"
|
||||
][n]["rpm"]
|
||||
except KeyError:
|
||||
pass
|
||||
return miner_fan_speeds(
|
||||
fan_speeds(fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]),
|
||||
psu_fan_speeds(psu_fan),
|
||||
)
|
||||
return [fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]]
|
||||
|
||||
if not api_fans:
|
||||
try:
|
||||
@@ -623,19 +616,17 @@ class BOSMiner(BaseMiner):
|
||||
pass
|
||||
|
||||
if api_fans:
|
||||
fans = {"fan_1": None, "fan_2": None, "fan_3": None, "fan_4": None}
|
||||
fans = {"fan_1": Fan(), "fan_2": Fan(), "fan_3": Fan(), "fan_4": Fan()}
|
||||
for n in range(self.fan_count):
|
||||
try:
|
||||
fans[f"fan_{n + 1}"] = api_fans["FANS"][n]["RPM"]
|
||||
fans[f"fan_{n + 1}"].speed = api_fans["FANS"][n]["RPM"]
|
||||
except KeyError:
|
||||
pass
|
||||
return miner_fan_speeds(
|
||||
fan_speeds(fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]),
|
||||
psu_fan_speeds(psu_fan),
|
||||
)
|
||||
return miner_fan_speeds(
|
||||
fan_speeds(None, None, None, None), psu_fan_speeds(None)
|
||||
)
|
||||
return [fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]]
|
||||
return [Fan(), Fan(), Fan(), Fan()]
|
||||
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_pools(
|
||||
self, api_pools: dict = None, graphql_pools: dict = None
|
||||
@@ -851,144 +842,3 @@ class BOSMiner(BaseMiner):
|
||||
)
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"temps",
|
||||
"tunerstatus",
|
||||
"pools",
|
||||
"devdetails",
|
||||
"fans",
|
||||
"devs",
|
||||
"version",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError as e:
|
||||
if str(e.message) == "Not ready":
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary", "tunerstatus", "pools", "devs", "version"
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
version = miner_data.get("version")
|
||||
if version:
|
||||
version = version[0]
|
||||
temps = miner_data.get("temps")
|
||||
if temps:
|
||||
temps = temps[0]
|
||||
tunerstatus = miner_data.get("tunerstatus")
|
||||
if tunerstatus:
|
||||
tunerstatus = tunerstatus[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
devdetails = miner_data.get("devdetails")
|
||||
if devdetails:
|
||||
devdetails = devdetails[0]
|
||||
devs = miner_data.get("devs")
|
||||
if devs:
|
||||
devs = devs[0]
|
||||
fans = miner_data.get("fans")
|
||||
if fans:
|
||||
fans = fans[0]
|
||||
else:
|
||||
summary, version, temps, tunerstatus, pools, devdetails, devs, fans = (
|
||||
None for _ in range(8)
|
||||
)
|
||||
try:
|
||||
gql_data = await self.send_graphql_query(
|
||||
"{bos {hostname}, bosminer{config{... on BosminerConfig{groups{pools{url, user}, strategy{... on QuotaStrategy {quota}}}}}, info{fans{name, rpm}, workSolver{realHashrate{mhs1M}, temperatures{degreesC}, power{limitW, approxConsumptionW}, childSolvers{name, realHashrate{mhs1M}, hwDetails{chips}, tuner{statusMessages}, temperatures{degreesC}}}}}}"
|
||||
)
|
||||
except APIError:
|
||||
gql_data = None
|
||||
|
||||
if gql_data:
|
||||
if "data" in gql_data:
|
||||
gql_data = gql_data["data"]
|
||||
|
||||
data = { # noqa - Ignore dictioonary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(),
|
||||
"model": await self.get_model(),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # - Done at end
|
||||
"hostname": await self.get_hostname(graphql_hostname=gql_data),
|
||||
"hashrate": await self.get_hashrate(
|
||||
api_summary=summary, graphql_hashrate=gql_data
|
||||
),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_devs=devs),
|
||||
"hashboards": await self.get_hashboards(
|
||||
api_temps=temps,
|
||||
api_devdetails=devdetails,
|
||||
api_devs=devs,
|
||||
graphql_boards=gql_data,
|
||||
),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(),
|
||||
"wattage": await self.get_wattage(
|
||||
api_tunerstatus=tunerstatus, graphql_wattage=gql_data
|
||||
),
|
||||
"wattage_limit": await self.get_wattage_limit(
|
||||
api_tunerstatus=tunerstatus, graphql_wattage_limit=gql_data
|
||||
),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(
|
||||
api_tunerstatus=tunerstatus, graphql_errors=gql_data
|
||||
),
|
||||
"fault_light": await self.get_fault_light(),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(
|
||||
api_version=version, graphql_version=gql_data
|
||||
)
|
||||
fan_data = await self.get_fans(api_fans=fans, graphql_fans=gql_data)
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools, graphql_pools=gql_data)
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -21,7 +21,7 @@ import asyncssh
|
||||
|
||||
from pyasic.API.bosminer import BOSMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -131,18 +131,17 @@ class BOSMinerOld(BaseMiner):
|
||||
|
||||
async def get_fans(
|
||||
self,
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
) -> List[Fan]:
|
||||
return [Fan(), Fan(), Fan(), Fan()]
|
||||
|
||||
fans = fan_speeds(None, None, None, None)
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
async def get_api_ver(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_fw_ver(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -179,8 +178,5 @@ class BOSMinerOld(BaseMiner):
|
||||
async def get_nominal_hashrate(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
return {}
|
||||
|
||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||
return MinerData(ip=str(self.ip))
|
||||
|
||||
@@ -19,7 +19,7 @@ from typing import List, Optional, Tuple, Union
|
||||
|
||||
from pyasic.API.btminer import BTMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -172,17 +172,17 @@ class BTMiner(BaseMiner):
|
||||
##################################################
|
||||
|
||||
async def get_mac(
|
||||
self, api_summary: dict = None, api_miner_info: dict = None
|
||||
self, api_summary: dict = None, api_get_miner_info: dict = None
|
||||
) -> Optional[str]:
|
||||
if not api_miner_info:
|
||||
if not api_get_miner_info:
|
||||
try:
|
||||
api_miner_info = await self.api.get_miner_info()
|
||||
api_get_miner_info = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_miner_info:
|
||||
if api_get_miner_info:
|
||||
try:
|
||||
mac = api_miner_info["Msg"]["mac"]
|
||||
mac = api_get_miner_info["Msg"]["mac"]
|
||||
return str(mac).upper()
|
||||
except KeyError:
|
||||
pass
|
||||
@@ -223,34 +223,64 @@ class BTMiner(BaseMiner):
|
||||
return None
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None, api_summary: dict = None
|
||||
self, api_get_version: dict = None, api_summary: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
# check if version is cached
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
# Check to see if the version info is already cached
|
||||
if self.api_ver and self.fw_ver:
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
api_ver = await self.get_api_ver(api_get_version=api_get_version)
|
||||
fw_ver = await self.get_fw_ver(
|
||||
api_get_version=api_get_version, api_summary=api_summary
|
||||
)
|
||||
return miner_version(api_ver, fw_ver)
|
||||
|
||||
if not api_version:
|
||||
async def get_api_ver(self, api_get_version: dict = None) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.api_ver:
|
||||
return self.api_ver
|
||||
|
||||
if not api_get_version:
|
||||
try:
|
||||
api_version = await self.api.get_version()
|
||||
api_get_version = await self.api.get_version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_version:
|
||||
if "Code" in api_version.keys():
|
||||
if api_version["Code"] == 131:
|
||||
if api_get_version:
|
||||
if "Code" in api_get_version.keys():
|
||||
if api_get_version["Code"] == 131:
|
||||
try:
|
||||
api_ver = api_version["Msg"]
|
||||
api_ver = api_get_version["Msg"]
|
||||
if not isinstance(api_ver, str):
|
||||
api_ver = api_ver["api_ver"]
|
||||
self.api_ver = api_ver.replace("whatsminer v", "")
|
||||
self.fw_ver = api_version["Msg"]["fw_ver"]
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
self.api.api_ver = self.api_ver
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
return self.api_ver
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(
|
||||
self, api_get_version: dict = None, api_summary: dict = None
|
||||
) -> Optional[str]:
|
||||
# Check to see if the version info is already cached
|
||||
if self.fw_ver:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_get_version:
|
||||
try:
|
||||
api_get_version = await self.api.get_version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_get_version:
|
||||
if "Code" in api_get_version.keys():
|
||||
if api_get_version["Code"] == 131:
|
||||
try:
|
||||
self.fw_ver = api_get_version["Msg"]["fw_ver"]
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
else:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_summary:
|
||||
try:
|
||||
@@ -266,21 +296,21 @@ class BTMiner(BaseMiner):
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
return self.fw_ver
|
||||
|
||||
async def get_hostname(self, api_miner_info: dict = None) -> Optional[str]:
|
||||
async def get_hostname(self, api_get_miner_info: dict = None) -> Optional[str]:
|
||||
if self.hostname:
|
||||
return self.hostname
|
||||
|
||||
if not api_miner_info:
|
||||
if not api_get_miner_info:
|
||||
try:
|
||||
api_miner_info = await self.api.get_miner_info()
|
||||
api_get_miner_info = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
return None # only one way to get this
|
||||
|
||||
if api_miner_info:
|
||||
if api_get_miner_info:
|
||||
try:
|
||||
self.hostname = api_miner_info["Msg"]["hostname"]
|
||||
self.hostname = api_get_miner_info["Msg"]["hostname"]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
@@ -375,18 +405,32 @@ class BTMiner(BaseMiner):
|
||||
pass
|
||||
|
||||
async def get_fans(
|
||||
self, api_summary: dict = None, api_psu: dict = None
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
self, api_summary: dict = None, api_get_psu: dict = None
|
||||
) -> List[Fan]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
fans = fan_speeds(None, None, None, None)
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
fans = [Fan(), Fan(), Fan(), Fan()]
|
||||
if api_summary:
|
||||
try:
|
||||
if self.fan_count > 0:
|
||||
fans = [
|
||||
Fan(api_summary["SUMMARY"][0]["Fan Speed In"]),
|
||||
Fan(api_summary["SUMMARY"][0]["Fan Speed Out"]),
|
||||
Fan(),
|
||||
Fan(),
|
||||
]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return fans
|
||||
|
||||
async def get_fan_psu(
|
||||
self, api_summary: dict = None, api_get_psu: dict = None
|
||||
) -> Optional[int]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
@@ -395,33 +439,21 @@ class BTMiner(BaseMiner):
|
||||
|
||||
if api_summary:
|
||||
try:
|
||||
if self.fan_count > 0:
|
||||
fans = fan_speeds(
|
||||
api_summary["SUMMARY"][0]["Fan Speed In"],
|
||||
api_summary["SUMMARY"][0]["Fan Speed Out"],
|
||||
None,
|
||||
None,
|
||||
)
|
||||
psu_fans = psu_fan_speeds(
|
||||
int(api_summary["SUMMARY"][0]["Power Fanspeed"])
|
||||
)
|
||||
return int(api_summary["SUMMARY"][0]["Power Fanspeed"])
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
if not psu_fans[0]:
|
||||
if not api_psu:
|
||||
try:
|
||||
api_psu = await self.api.get_psu()
|
||||
except APIError:
|
||||
pass
|
||||
if not api_get_psu:
|
||||
try:
|
||||
api_get_psu = await self.api.get_psu()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_psu:
|
||||
try:
|
||||
psu_fans = psu_fan_speeds(int(api_psu["Msg"]["fan_speed"]))
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
if api_get_psu:
|
||||
try:
|
||||
return int(api_get_psu["Msg"]["fan_speed"])
|
||||
except (KeyError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -450,10 +482,10 @@ class BTMiner(BaseMiner):
|
||||
return groups
|
||||
|
||||
async def get_errors(
|
||||
self, api_summary: dict = None, api_error_codes: dict = None
|
||||
self, api_summary: dict = None, api_get_error_code: dict = None
|
||||
) -> List[MinerErrorData]:
|
||||
errors = []
|
||||
if not api_summary and not api_error_codes:
|
||||
if not api_summary and not api_get_error_code:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
except APIError:
|
||||
@@ -468,14 +500,14 @@ class BTMiner(BaseMiner):
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
if not api_error_codes:
|
||||
if not api_get_error_code:
|
||||
try:
|
||||
api_error_codes = await self.api.get_error_code()
|
||||
api_get_error_code = await self.api.get_error_code()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_error_codes:
|
||||
for err in api_error_codes["Msg"]["error_code"]:
|
||||
if api_get_error_code:
|
||||
for err in api_get_error_code["Msg"]["error_code"]:
|
||||
if isinstance(err, dict):
|
||||
for code in err:
|
||||
errors.append(WhatsminerError(error_code=int(code)))
|
||||
@@ -499,134 +531,20 @@ class BTMiner(BaseMiner):
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_fault_light(self, api_miner_info: dict = None) -> bool:
|
||||
async def get_fault_light(self, api_get_miner_info: dict = None) -> bool:
|
||||
data = None
|
||||
|
||||
if not api_miner_info:
|
||||
if not api_get_miner_info:
|
||||
try:
|
||||
api_miner_info = await self.api.get_miner_info()
|
||||
api_get_miner_info = await self.api.get_miner_info()
|
||||
except APIError:
|
||||
if not self.light:
|
||||
self.light = False
|
||||
|
||||
if api_miner_info:
|
||||
if api_get_miner_info:
|
||||
try:
|
||||
self.light = api_miner_info["Msg"]["ledstat"] == "auto"
|
||||
self.light = api_get_miner_info["Msg"]["ledstat"] == "auto"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return self.light if self.light else False
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"get_version",
|
||||
"pools",
|
||||
"devdetails",
|
||||
"devs",
|
||||
"get_psu",
|
||||
"get_miner_info",
|
||||
"get_error_code",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
version = miner_data.get("get_version")
|
||||
if version:
|
||||
version = version[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
devdetails = miner_data.get("devdetails")
|
||||
if devdetails:
|
||||
devdetails = devdetails[0]
|
||||
devs = miner_data.get("devs")
|
||||
if devs:
|
||||
devs = devs[0]
|
||||
psu = miner_data.get("get_psu")
|
||||
if psu:
|
||||
psu = psu[0]
|
||||
miner_info = miner_data.get("get_miner_info")
|
||||
if miner_info:
|
||||
miner_info = miner_info[0]
|
||||
error_codes = miner_data.get("get_error_codes")
|
||||
if error_codes:
|
||||
error_codes = error_codes[0]
|
||||
else:
|
||||
summary, version, pools, devdetails, devs, psu, miner_info, error_codes = (
|
||||
None for _ in range(8)
|
||||
)
|
||||
|
||||
data = { # noqa - Ignore dictionary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(api_summary=summary, api_miner_info=miner_info),
|
||||
"model": await self.get_model(api_devdetails=devdetails),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # - Done at end
|
||||
"hostname": await self.get_hostname(api_miner_info=miner_info),
|
||||
"hashrate": await self.get_hashrate(api_summary=summary),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_summary=summary),
|
||||
"hashboards": await self.get_hashboards(api_devs=devs),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(api_summary=summary),
|
||||
"wattage": await self.get_wattage(api_summary=summary),
|
||||
"wattage_limit": await self.get_wattage_limit(api_summary=summary),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(
|
||||
api_summary=summary, api_error_codes=error_codes
|
||||
),
|
||||
"fault_light": await self.get_fault_light(api_miner_info=miner_info),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(api_version=version)
|
||||
fan_data = await self.get_fans()
|
||||
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools)
|
||||
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -21,7 +21,7 @@ import asyncssh
|
||||
|
||||
from pyasic.API.cgminer import CGMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -185,9 +185,14 @@ class CGMiner(BaseMiner):
|
||||
self, api_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
# Check to see if the version info is already cached
|
||||
if self.api_ver and self.fw_ver:
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
return miner_version(
|
||||
api_ver=await self.get_api_ver(api_version=api_version),
|
||||
fw_ver=await self.get_fw_ver(api_version=api_version),
|
||||
)
|
||||
|
||||
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if self.api_ver:
|
||||
return self.api_ver
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
@@ -200,12 +205,26 @@ class CGMiner(BaseMiner):
|
||||
self.api_ver = api_version["VERSION"][0]["API"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if self.fw_ver:
|
||||
return self.fw_ver
|
||||
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if api_version:
|
||||
try:
|
||||
self.fw_ver = api_version["VERSION"][0]["CGMiner"]
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
return miner_version(self.api_ver, self.fw_ver)
|
||||
return self.fw_ver
|
||||
|
||||
async def get_hostname(self) -> Optional[str]:
|
||||
try:
|
||||
@@ -293,25 +312,14 @@ class CGMiner(BaseMiner):
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_fans(
|
||||
self, api_stats: dict = None
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
fans_data = [None, None, None, None]
|
||||
fans_data = [Fan(), Fan(), Fan(), Fan()]
|
||||
if api_stats:
|
||||
try:
|
||||
fan_offset = -1
|
||||
@@ -325,12 +333,15 @@ class CGMiner(BaseMiner):
|
||||
fan_offset = 1
|
||||
|
||||
for fan in range(self.fan_count):
|
||||
fans_data[fan] = api_stats["STATS"][1].get(f"fan{fan_offset+fan}")
|
||||
fans_data[fan] = Fan(
|
||||
api_stats["STATS"][1].get(f"fan{fan_offset+fan}")
|
||||
)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
fans = fan_speeds(*fans_data)
|
||||
return fans_data
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -387,109 +398,3 @@ class CGMiner(BaseMiner):
|
||||
return round(ideal_rate, 2)
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"pools",
|
||||
"devdetails",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"pools",
|
||||
"version",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
version = miner_data.get("version")
|
||||
if version:
|
||||
version = version[0]
|
||||
devdetails = miner_data.get("devdetails")
|
||||
if devdetails:
|
||||
devdetails = devdetails[0]
|
||||
stats = miner_data.get("stats")
|
||||
if stats:
|
||||
stats = stats[0]
|
||||
else:
|
||||
summary, pools, devdetails, version, stats = (None for _ in range(5))
|
||||
|
||||
data = { # noqa - Ignore dictionary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(),
|
||||
"model": await self.get_model(api_devdetails=devdetails),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # - Done at end
|
||||
"hostname": await self.get_hostname(),
|
||||
"hashrate": await self.get_hashrate(api_summary=summary),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_stats=stats),
|
||||
"hashboards": await self.get_hashboards(api_stats=stats),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(),
|
||||
"wattage": await self.get_wattage(),
|
||||
"wattage_limit": await self.get_wattage_limit(),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(),
|
||||
"fault_light": await self.get_fault_light(),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(api_version=version)
|
||||
fan_data = await self.get_fans()
|
||||
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools)
|
||||
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -20,7 +20,7 @@ from typing import List, Optional, Tuple, Union
|
||||
|
||||
from pyasic.API.cgminer import CGMinerAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners._backends import CGMiner
|
||||
@@ -206,25 +206,14 @@ class CGMinerAvalon(CGMiner):
|
||||
async def get_wattage_limit(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_fans(
|
||||
self, api_stats: dict = None
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
fans_data = [None, None, None, None]
|
||||
fans_data = [Fan(), Fan(), Fan(), Fan()]
|
||||
if api_stats:
|
||||
try:
|
||||
stats_data = api_stats[0].get("STATS")
|
||||
@@ -233,12 +222,11 @@ class CGMinerAvalon(CGMiner):
|
||||
if key.startswith("MM ID"):
|
||||
raw_data = self.parse_stats(stats_data[0][key])
|
||||
for fan in range(self.fan_count):
|
||||
fans_data[fan] = int(raw_data[f"Fan{fan + 1}"])
|
||||
fans_data[fan] = Fan(int(raw_data[f"Fan{fan + 1}"]))
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
fans = fan_speeds(*fans_data)
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
return fans_data
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -279,101 +267,3 @@ class CGMinerAvalon(CGMiner):
|
||||
if data["STATUS"][0]["Msg"] == "ASC 0 set info: LED[1]":
|
||||
return True
|
||||
return False
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"pools",
|
||||
"version",
|
||||
"devdetails",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
version = miner_data.get("version")
|
||||
if version:
|
||||
version = version[0]
|
||||
devdetails = miner_data.get("devdetails")
|
||||
if devdetails:
|
||||
devdetails = devdetails[0]
|
||||
stats = miner_data.get("stats")
|
||||
if stats:
|
||||
stats = stats[0]
|
||||
else:
|
||||
summary, pools, devdetails, version, stats = (None for _ in range(5))
|
||||
|
||||
data = { # noqa - Ignore dictionary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(),
|
||||
"model": await self.get_model(api_devdetails=devdetails),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # - Done at end
|
||||
"hostname": await self.get_hostname(),
|
||||
"hashrate": await self.get_hashrate(api_summary=summary),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_stats=stats),
|
||||
"hashboards": await self.get_hashboards(api_stats=stats),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(),
|
||||
"wattage": await self.get_wattage(),
|
||||
"wattage_limit": await self.get_wattage_limit(),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(),
|
||||
"fault_light": await self.get_fault_light(),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(api_version=version)
|
||||
fan_data = await self.get_fans()
|
||||
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools)
|
||||
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -46,10 +46,7 @@ class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V30"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -58,10 +55,7 @@ class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V40"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 123
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
@@ -109,10 +103,7 @@ class M31SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ V100"
|
||||
self.nominal_chips = 0
|
||||
warnings.warn(
|
||||
"Unknown chip count for miner type M30S+ V100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
||||
)
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
from typing import Optional, Union
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -114,101 +114,3 @@ class HiveonT9(Hiveon, T9):
|
||||
|
||||
if not env_temp_list == []:
|
||||
return round(float(sum(env_temp_list) / len(env_temp_list)), 2)
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"pools",
|
||||
"version",
|
||||
"devdetails",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
version = miner_data.get("version")
|
||||
if version:
|
||||
version = version[0]
|
||||
devdetails = miner_data.get("devdetails")
|
||||
if devdetails:
|
||||
devdetails = devdetails[0]
|
||||
stats = miner_data.get("stats")
|
||||
if stats:
|
||||
stats = stats[0]
|
||||
else:
|
||||
summary, pools, devdetails, version, stats = (None for _ in range(5))
|
||||
|
||||
data = { # noqa - Ignore dictionary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(),
|
||||
"model": await self.get_model(api_devdetails=devdetails),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # - Done at end
|
||||
"hostname": await self.get_hostname(),
|
||||
"hashrate": await self.get_hashrate(api_summary=summary),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_stats=stats),
|
||||
"hashboards": await self.get_hashboards(api_stats=stats),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(api_stats=stats),
|
||||
"wattage": await self.get_wattage(api_stats=stats),
|
||||
"wattage_limit": await self.get_wattage_limit(),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(),
|
||||
"fault_light": await self.get_fault_light(),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(api_version=version)
|
||||
fan_data = await self.get_fans()
|
||||
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools)
|
||||
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import inspect
|
||||
import ipaddress
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -20,7 +21,7 @@ from typing import List, Optional, Tuple, TypeVar
|
||||
import asyncssh
|
||||
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
|
||||
|
||||
@@ -199,6 +200,24 @@ class BaseMiner(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_api_ver(self, *args, **kwargs) -> Optional[str]:
|
||||
"""Get the API version of the miner and is as a string.
|
||||
|
||||
Returns:
|
||||
API version as a string.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_fw_ver(self, *args, **kwargs) -> Optional[str]:
|
||||
"""Get the firmware version of the miner and is as a string.
|
||||
|
||||
Returns:
|
||||
Firmware version as a string.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_version(self, *args, **kwargs) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""Get the API version and firmware version of the miner and return them as strings.
|
||||
@@ -263,15 +282,20 @@ class BaseMiner(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_fans(
|
||||
self, *args, **kwargs
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]], Optional[int]
|
||||
]:
|
||||
"""Get fan data from the miner in the form ((fan_1, fan_2, fan_3, fan_4), psu_fan).
|
||||
async def get_fans(self, *args, **kwargs) -> List[Fan]:
|
||||
"""Get fan data from the miner in the form [fan_1, fan_2, fan_3, fan_4].
|
||||
|
||||
Returns:
|
||||
A list of error classes representing different errors.
|
||||
A list of fan data.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_fan_psu(self, *args, **kwargs) -> Optional[int]:
|
||||
"""Get PSU fan speed from the miner.
|
||||
|
||||
Returns:
|
||||
PSU fan speed.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -282,6 +306,7 @@ class BaseMiner(ABC):
|
||||
Returns:
|
||||
Pool groups and quotas in a list of dicts.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
|
||||
@@ -310,11 +335,104 @@ class BaseMiner(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||
async def _get_data(self, allow_warning: bool, data_to_get: list = None) -> dict:
|
||||
if not data_to_get:
|
||||
# everything
|
||||
data_to_get = [
|
||||
"mac",
|
||||
"model",
|
||||
"api_ver",
|
||||
"fw_ver",
|
||||
"hostname",
|
||||
"hashrate",
|
||||
"nominal_hashrate",
|
||||
"hashboards",
|
||||
"env_temp",
|
||||
"wattage",
|
||||
"wattage_limit",
|
||||
"fans",
|
||||
"fan_psu",
|
||||
"errors",
|
||||
"fault_light",
|
||||
"pools",
|
||||
]
|
||||
api_params = []
|
||||
web_params = []
|
||||
gql = False
|
||||
for data_name in data_to_get:
|
||||
function = getattr(self, "get_" + data_name)
|
||||
sig = inspect.signature(function)
|
||||
for item in sig.parameters:
|
||||
if item.startswith("api_"):
|
||||
command = item.replace("api_", "")
|
||||
api_params.append(command)
|
||||
elif item.startswith("graphql_"):
|
||||
gql = True
|
||||
elif item.startswith("web_"):
|
||||
web_params.append(item.replace("web_", ""))
|
||||
api_params = list(set(api_params))
|
||||
web_params = list(set(web_params))
|
||||
miner_data = {}
|
||||
command_data = await self.api.multicommand(
|
||||
*api_params, allow_warning=allow_warning
|
||||
)
|
||||
if gql:
|
||||
gql_data = await self.send_graphql_query( # noqa: bosminer only anyway
|
||||
"{bos {hostname}, bosminer{config{... on BosminerConfig{groups{pools{url, user}, strategy{... on QuotaStrategy {quota}}}}}, info{fans{name, rpm}, workSolver{realHashrate{mhs1M}, temperatures{degreesC}, power{limitW, approxConsumptionW}, childSolvers{name, realHashrate{mhs1M}, hwDetails{chips}, tuner{statusMessages}, temperatures{degreesC}}}}}}"
|
||||
)
|
||||
web_data = {}
|
||||
for command in web_params:
|
||||
data = await self.send_web_command(command) # noqa: web only anyway
|
||||
web_data[command] = data
|
||||
for data_name in data_to_get:
|
||||
function = getattr(self, "get_" + data_name)
|
||||
sig = inspect.signature(function)
|
||||
kwargs = {}
|
||||
for arg_name in sig.parameters:
|
||||
if arg_name.startswith("api_"):
|
||||
try:
|
||||
kwargs[arg_name] = command_data[arg_name.replace("api_", "")][0]
|
||||
except (KeyError, IndexError):
|
||||
kwargs[arg_name] = None
|
||||
elif arg_name.startswith("graphql_"):
|
||||
kwargs[
|
||||
arg_name
|
||||
] = gql_data # noqa: variable is not referenced before assignment
|
||||
elif arg_name.startswith("web_"):
|
||||
try:
|
||||
kwargs[arg_name] = web_data[arg_name.replace("web_", "")]
|
||||
except (KeyError, IndexError):
|
||||
kwargs[arg_name] = None
|
||||
if not data_name == "pools":
|
||||
miner_data[data_name] = await function(**kwargs)
|
||||
else:
|
||||
pools_data = await function(**kwargs)
|
||||
if pools_data:
|
||||
miner_data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
miner_data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
miner_data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
miner_data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
miner_data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
miner_data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
miner_data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
miner_data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
return miner_data
|
||||
|
||||
async def get_data(
|
||||
self, allow_warning: bool = False, data_to_get: list = None
|
||||
) -> MinerData:
|
||||
"""Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].
|
||||
|
||||
Parameters:
|
||||
allow_warning: Allow warning when an API command fails.
|
||||
data_to_get: Names of data items you want to gather. Defaults to all data.
|
||||
|
||||
Returns:
|
||||
A [`MinerData`][pyasic.data.MinerData] instance containing data from the miner.
|
||||
@@ -330,16 +448,12 @@ class BaseMiner(ABC):
|
||||
],
|
||||
)
|
||||
|
||||
gathered_data = await self._get_data(allow_warning)
|
||||
gathered_data = await self._get_data(allow_warning, data_to_get=data_to_get)
|
||||
for item in gathered_data:
|
||||
if gathered_data[item] is not None:
|
||||
setattr(data, item, gathered_data[item])
|
||||
|
||||
return data
|
||||
|
||||
@abstractmethod
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
pass
|
||||
|
||||
|
||||
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)
|
||||
|
||||
@@ -20,7 +20,7 @@ from typing import List, Optional, Tuple, Union
|
||||
import httpx
|
||||
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard
|
||||
from pyasic.data import Fan, HashBoard
|
||||
from pyasic.data.error_codes import InnosiliconError, MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
|
||||
@@ -133,8 +133,11 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
##################################################
|
||||
|
||||
async def get_mac(
|
||||
self, web_all_data: dict = None, web_overview: dict = None
|
||||
self,
|
||||
web_getAll: dict = None,
|
||||
web_overview: dict = None, # noqa: named this way for automatic functionality
|
||||
) -> Optional[str]:
|
||||
web_all_data = web_getAll
|
||||
if not web_all_data and not web_overview:
|
||||
try:
|
||||
web_overview = await self.send_web_command("overview")
|
||||
@@ -174,8 +177,11 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
pass
|
||||
|
||||
async def get_hashrate(
|
||||
self, api_summary: dict = None, web_all_data: dict = None
|
||||
self,
|
||||
api_summary: dict = None,
|
||||
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
||||
) -> Optional[float]:
|
||||
web_all_data = web_getAll
|
||||
if not api_summary and not web_all_data:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
@@ -197,8 +203,11 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
pass
|
||||
|
||||
async def get_hashboards(
|
||||
self, api_stats: dict = None, web_all_data: dict = None
|
||||
self,
|
||||
api_stats: dict = None,
|
||||
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
||||
) -> List[HashBoard]:
|
||||
web_all_data = web_getAll
|
||||
hashboards = [
|
||||
HashBoard(slot=i, expected_chips=self.nominal_chips)
|
||||
for i in range(self.ideal_hashboards)
|
||||
@@ -246,7 +255,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_wattage(self, web_all_data: dict = None) -> Optional[int]:
|
||||
async def get_wattage(
|
||||
self, web_getAll: dict = None
|
||||
) -> Optional[int]: # noqa: named this way for automatic functionality
|
||||
web_all_data = web_getAll
|
||||
if not web_all_data:
|
||||
try:
|
||||
web_all_data = await self.send_web_command("getAll")
|
||||
@@ -262,18 +274,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
pass
|
||||
|
||||
async def get_fans(
|
||||
self, web_all_data: dict = None
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
|
||||
fans = fan_speeds(None, None, None, None)
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
|
||||
self,
|
||||
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
||||
) -> List[Fan]:
|
||||
web_all_data = web_getAll
|
||||
if not web_all_data:
|
||||
try:
|
||||
web_all_data = await self.send_web_command("getAll")
|
||||
@@ -282,19 +286,18 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
else:
|
||||
web_all_data = web_all_data["all"]
|
||||
|
||||
fan_data = [Fan(), Fan(), Fan(), Fan()]
|
||||
if web_all_data:
|
||||
try:
|
||||
spd = web_all_data["fansSpeed"]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
f = [None, None, None, None]
|
||||
round((int(spd) * 6000) / 100)
|
||||
for i in range(self.fan_count):
|
||||
f[i] = spd
|
||||
fans = fan_speeds(*f)
|
||||
fan_data[i] = Fan(spd)
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
return fan_data
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -322,7 +325,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
pass
|
||||
return groups
|
||||
|
||||
async def get_errors(self, web_error_details: dict = None) -> List[MinerErrorData]:
|
||||
async def get_errors(
|
||||
self, web_getErrorDetail: dict = None
|
||||
) -> List[MinerErrorData]: # noqa: named this way for automatic functionality
|
||||
web_error_details = web_getErrorDetail
|
||||
errors = []
|
||||
if not web_error_details:
|
||||
try:
|
||||
@@ -342,129 +348,3 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
||||
if not err == 0:
|
||||
errors.append(InnosiliconError(error_code=err))
|
||||
return errors
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"version",
|
||||
"pools",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
miner_data = None
|
||||
for i in range(PyasicSettings().miner_get_data_retries):
|
||||
try:
|
||||
miner_data = await self.api.multicommand(
|
||||
"summary",
|
||||
"pools",
|
||||
"version",
|
||||
"devdetails",
|
||||
"stats",
|
||||
allow_warning=allow_warning,
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
if miner_data:
|
||||
break
|
||||
if miner_data:
|
||||
summary = miner_data.get("summary")
|
||||
if summary:
|
||||
summary = summary[0]
|
||||
pools = miner_data.get("pools")
|
||||
if pools:
|
||||
pools = pools[0]
|
||||
version = miner_data.get("version")
|
||||
if version:
|
||||
version = version[0]
|
||||
stats = miner_data.get("stats")
|
||||
if stats:
|
||||
stats = stats[0]
|
||||
else:
|
||||
summary, pools, version, stats = (None for _ in range(4))
|
||||
|
||||
try:
|
||||
web_all_data = await self.send_web_command("getAll")
|
||||
except APIError:
|
||||
web_all_data = None
|
||||
try:
|
||||
web_type = await self.send_web_command("type")
|
||||
except APIError:
|
||||
web_type = None
|
||||
try:
|
||||
web_error_details = await self.send_web_command("getErrorDetail")
|
||||
except APIError:
|
||||
web_error_details = None
|
||||
|
||||
data = { # noqa - Ignore dictionary could be re-written
|
||||
# ip - Done at start
|
||||
# datetime - Done auto
|
||||
"mac": await self.get_mac(web_all_data=web_all_data),
|
||||
"model": await self.get_model(web_type=web_type),
|
||||
# make - Done at start
|
||||
"api_ver": None, # - Done at end
|
||||
"fw_ver": None, # - Done at end
|
||||
"hostname": await self.get_hostname(),
|
||||
"hashrate": await self.get_hashrate(
|
||||
api_summary=summary, web_all_data=web_all_data
|
||||
),
|
||||
"nominal_hashrate": await self.get_nominal_hashrate(api_stats=stats),
|
||||
"hashboards": await self.get_hashboards(
|
||||
api_stats=stats, web_all_data=web_all_data
|
||||
),
|
||||
# ideal_hashboards - Done at start
|
||||
"env_temp": await self.get_env_temp(),
|
||||
"wattage": await self.get_wattage(web_all_data=web_all_data),
|
||||
"wattage_limit": await self.get_wattage_limit(),
|
||||
"fan_1": None, # - Done at end
|
||||
"fan_2": None, # - Done at end
|
||||
"fan_3": None, # - Done at end
|
||||
"fan_4": None, # - Done at end
|
||||
"fan_psu": None, # - Done at end
|
||||
# ideal_chips - Done at start
|
||||
"pool_split": None, # - Done at end
|
||||
"pool_1_url": None, # - Done at end
|
||||
"pool_1_user": None, # - Done at end
|
||||
"pool_2_url": None, # - Done at end
|
||||
"pool_2_user": None, # - Done at end
|
||||
"errors": await self.get_errors(web_error_details=web_error_details),
|
||||
"fault_light": await self.get_fault_light(),
|
||||
}
|
||||
|
||||
data["api_ver"], data["fw_ver"] = await self.get_version(api_version=version)
|
||||
fan_data = await self.get_fans(web_all_data=web_all_data)
|
||||
|
||||
if fan_data:
|
||||
data["fan_1"] = fan_data.fan_speeds.fan_1 # noqa
|
||||
data["fan_2"] = fan_data.fan_speeds.fan_2 # noqa
|
||||
data["fan_3"] = fan_data.fan_speeds.fan_3 # noqa
|
||||
data["fan_4"] = fan_data.fan_speeds.fan_4 # noqa
|
||||
|
||||
data["fan_psu"] = fan_data.psu_fan_speeds.psu_fan # noqa
|
||||
|
||||
pools_data = await self.get_pools(api_pools=pools)
|
||||
|
||||
if pools_data:
|
||||
data["pool_1_url"] = pools_data[0]["pool_1_url"]
|
||||
data["pool_1_user"] = pools_data[0]["pool_1_user"]
|
||||
if len(pools_data) > 1:
|
||||
data["pool_2_url"] = pools_data[1]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[1]["pool_2_user"]
|
||||
data[
|
||||
"pool_split"
|
||||
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
|
||||
else:
|
||||
try:
|
||||
data["pool_2_url"] = pools_data[0]["pool_2_url"]
|
||||
data["pool_2_user"] = pools_data[0]["pool_2_user"]
|
||||
data["quota"] = "0"
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return data
|
||||
|
||||
@@ -17,7 +17,7 @@ from typing import List, Optional, Tuple
|
||||
|
||||
from pyasic.API.unknown import UnknownAPI
|
||||
from pyasic.config import MinerConfig
|
||||
from pyasic.data import HashBoard, MinerData
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.base import BaseMiner
|
||||
@@ -98,18 +98,17 @@ class UnknownMiner(BaseMiner):
|
||||
|
||||
async def get_fans(
|
||||
self,
|
||||
) -> Tuple[
|
||||
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]],
|
||||
Tuple[Optional[int]],
|
||||
]:
|
||||
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4")
|
||||
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan")
|
||||
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
|
||||
) -> List[Fan]:
|
||||
return [Fan(), Fan(), Fan(), Fan()]
|
||||
|
||||
fans = fan_speeds(None, None, None, None)
|
||||
psu_fans = psu_fan_speeds(None)
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
return miner_fan_speeds(fans, psu_fans)
|
||||
async def get_api_ver(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_fw_ver(self) -> Optional[str]:
|
||||
return None
|
||||
|
||||
async def get_pools(self, api_pools: dict = None) -> List[dict]:
|
||||
groups = []
|
||||
@@ -146,8 +145,7 @@ class UnknownMiner(BaseMiner):
|
||||
async def get_nominal_hashrate(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def _get_data(self, allow_warning: bool) -> dict:
|
||||
return {}
|
||||
|
||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||
async def get_data(
|
||||
self, allow_warning: bool = False, data_to_get: list = None
|
||||
) -> MinerData:
|
||||
return MinerData(ip=str(self.ip))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "pyasic"
|
||||
version = "0.26.1"
|
||||
version = "0.27.1"
|
||||
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
|
||||
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||
repository = "https://github.com/UpstreamData/pyasic"
|
||||
|
||||
Reference in New Issue
Block a user