Compare commits

...

9 Commits

Author SHA1 Message Date
Upstream Data
2d057ca9f6 version: bump version number. 2023-02-11 19:45:03 -07:00
Upstream Data
b71b23d2a0 bug: add a check for power_on in reboot check. 2023-02-11 19:44:26 -07:00
UpstreamData
b32649435d version: bump version number. 2023-02-09 15:48:49 -07:00
UpstreamData
c0096126df bug: Reverse check for whatsminer fault light as it was backwards. 2023-02-09 15:47:11 -07:00
UpstreamData
d632360932 version: bump version number. 2023-02-09 10:37:19 -07:00
UpstreamData
400001fa38 version: bump version number. 2023-02-07 13:41:06 -07:00
UpstreamData
4ff32a8081 feature: allow get_data to pick which data to gather in the first place to allow for speed optimizations. 2023-02-07 13:40:09 -07:00
UpstreamData
33b4ae2f2f version: bump version number. 2023-01-31 09:56:16 -07:00
UpstreamData
62194bd627 bug: add chip counts for M31S+ V30, V40, and V100. 2023-01-31 09:54:35 -07:00
18 changed files with 455 additions and 1051 deletions

View File

@@ -95,7 +95,9 @@ class BaseMinerAPI:
Parameters: Parameters:
*commands: The commands to send as a multicommand to the miner. *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. allow_warning: A boolean to supress APIWarnings.
""" """
# make sure we can actually run each command, otherwise they will fail # make sure we can actually run each command, otherwise they will fail
commands = self._check_commands(*commands) commands = self._check_commands(*commands)

View File

@@ -63,8 +63,8 @@ class BMMinerAPI(BaseMinerAPI):
data[cmd].append( data[cmd].append(
await self.send_command(cmd, allow_warning=allow_warning) await self.send_command(cmd, allow_warning=allow_warning)
) )
except APIError as e: except APIError:
raise APIError(e) pass
except Exception as e: except Exception as e:
logging.warning( logging.warning(
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}" f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"

View File

@@ -245,13 +245,13 @@ class BTMinerAPI(BaseMinerAPI):
try: try:
data = await self._send_bytes(enc_command, timeout) data = await self._send_bytes(enc_command, timeout)
except (asyncio.CancelledError, asyncio.TimeoutError) as e: except (asyncio.CancelledError, asyncio.TimeoutError) as e:
if command["cmd"] in ["reboot", "restart"]: if command["cmd"] in ["reboot", "restart", "power_on"]:
logging.info( logging.info(
f"{self} - (reboot/restart) - Whatsminers currently break this. " f"{self} - (reboot/restart/power_on) - Whatsminers currently break this. "
f"Ignoring exception. Command probably worked." f"Ignoring exception. Command probably worked."
) )
# FAKING IT HERE # FAKING IT HERE
data = b'{"STATUS": "S", "When": 1670966423, "Code": 131, "Msg": "API command OK", "Description": "Reboot"}' data = b'{"STATUS": "S", "When": 1670966423, "Code": 131, "Msg": "API command OK", "Description": "Reboot/restart/power_on"}'
else: else:
raise APIError("No data was returned from the API.") raise APIError("No data was returned from the API.")

View File

@@ -61,7 +61,7 @@ class CGMinerAPI(BaseMinerAPI):
data[cmd] = [] data[cmd] = []
data[cmd].append(await self.send_command(cmd, allow_warning=True)) data[cmd].append(await self.send_command(cmd, allow_warning=True))
except APIError as e: except APIError as e:
raise APIError(e) pass
except Exception as e: except Exception as e:
logging.warning( logging.warning(
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}" f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"

View File

@@ -46,6 +46,17 @@ class HashBoard:
missing: bool = True missing: bool = True
@dataclass
class Fan:
"""A Dataclass to standardize fan data.
Attributes:
speed: The speed of the fan.
"""
speed: int = -1
@dataclass @dataclass
class MinerData: class MinerData:
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`) """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) right_board_chip_temp: int = field(init=False)
wattage: int = -1 wattage: int = -1
wattage_limit: int = -1 wattage_limit: int = -1
fan_1: int = -1 fans: List[Fan] = field(default_factory=list)
fan_2: int = -1 fan_1: int = field(init=False)
fan_3: int = -1 fan_2: int = field(init=False)
fan_4: int = -1 fan_3: int = field(init=False)
fan_4: int = field(init=False)
fan_psu: int = -1 fan_psu: int = -1
left_chips: int = field(init=False) left_chips: int = field(init=False)
center_chips: int = field(init=False) center_chips: int = field(init=False)
@@ -199,6 +211,42 @@ class MinerData:
setattr(cp, key, item & other_item) setattr(cp, key, item & other_item)
return cp 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 @property
def total_chips(self): # noqa - Skip PyCharm inspection def total_chips(self): # noqa - Skip PyCharm inspection
return sum([hb.chips for hb in self.hashboards]) return sum([hb.chips for hb in self.hashboards])

View File

@@ -21,7 +21,7 @@ import asyncssh
from pyasic.API.bmminer import BMMinerAPI from pyasic.API.bmminer import BMMinerAPI
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import BaseMiner
@@ -138,13 +138,10 @@ class BMMiner(BaseMiner):
logging.warning(f"Failed to get model for miner: {self}") logging.warning(f"Failed to get model for miner: {self}")
return None return None
async def get_version( async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
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 # Check to see if the version info is already cached
if self.api_ver and self.fw_ver: if self.api_ver:
return miner_version(self.api_ver, self.fw_ver) return self.api_ver
if not api_version: if not api_version:
try: try:
@@ -157,12 +154,40 @@ class BMMiner(BaseMiner):
self.api_ver = api_version["VERSION"][0]["API"] self.api_ver = api_version["VERSION"][0]["API"]
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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: try:
self.fw_ver = api_version["VERSION"][0]["CompileTime"] self.fw_ver = api_version["VERSION"][0]["CompileTime"]
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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]: async def get_hostname(self) -> Optional[str]:
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname") 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]: async def get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans( async def get_fans(self, api_stats: dict = None) -> List[Fan]:
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)
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() 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}") fans_data[fan] = api_stats["STATS"][1].get(f"fan{fan_offset+fan}")
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -339,108 +353,3 @@ class BMMiner(BaseMiner):
return round(ideal_rate, 2) return round(ideal_rate, 2)
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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

View File

@@ -24,7 +24,7 @@ import toml
from pyasic.API.bosminer import BOSMinerAPI from pyasic.API.bosminer import BOSMinerAPI
from pyasic.config import MinerConfig 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.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import BaseMiner
@@ -287,10 +287,29 @@ class BOSMiner(BaseMiner):
) -> Tuple[Optional[str], Optional[str]]: ) -> Tuple[Optional[str], Optional[str]]:
# check if version is cached # check if version is cached
miner_version = namedtuple("MinerVersion", "api_ver fw_ver") miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
# if self.fw_ver and self.api_ver: api_ver = await self.get_api_ver(api_version)
# logging.debug(f"Found version for {self.ip}: {self.fw_ver}") fw_ver = await self.get_fw_ver(graphql_version)
# return miner_version(self.api_ver, self.fw_ver) 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: if not graphql_version:
try: try:
graphql_version = await self.send_graphql_query( graphql_version = await self.send_graphql_query(
@@ -299,11 +318,6 @@ class BOSMiner(BaseMiner):
except APIError: except APIError:
pass pass
if not api_version:
try:
api_version = await self.api.version()
except APIError:
pass
fw_ver = None fw_ver = None
if graphql_version: if graphql_version:
@@ -323,16 +337,7 @@ class BOSMiner(BaseMiner):
self.fw_ver = ver self.fw_ver = ver
logging.debug(f"Found version for {self.ip}: {self.fw_ver}") logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
# Now get the API version return self.fw_ver
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)
async def get_hostname(self, graphql_hostname: dict = None) -> Union[str, None]: async def get_hostname(self, graphql_hostname: dict = None) -> Union[str, None]:
if self.hostname: if self.hostname:
@@ -584,16 +589,7 @@ class BOSMiner(BaseMiner):
async def get_fans( async def get_fans(
self, api_fans: dict = None, graphql_fans: dict = None self, api_fans: dict = None, graphql_fans: dict = None
) -> Tuple[ ) -> List[Fan]:
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")
if not graphql_fans and not api_fans: if not graphql_fans and not api_fans:
try: try:
graphql_fans = await self.send_graphql_query( graphql_fans = await self.send_graphql_query(
@@ -603,18 +599,15 @@ class BOSMiner(BaseMiner):
pass pass
if graphql_fans: 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): for n in range(self.fan_count):
try: try:
fans[f"fan_{n + 1}"] = graphql_fans["bosminer"]["info"]["fans"][n][ fans[f"fan_{n + 1}"].speed = graphql_fans["bosminer"]["info"][
"rpm" "fans"
] ][n]["rpm"]
except KeyError: except KeyError:
pass pass
return miner_fan_speeds( return [fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]]
fan_speeds(fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]),
psu_fan_speeds(psu_fan),
)
if not api_fans: if not api_fans:
try: try:
@@ -623,19 +616,17 @@ class BOSMiner(BaseMiner):
pass pass
if api_fans: 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): for n in range(self.fan_count):
try: try:
fans[f"fan_{n + 1}"] = api_fans["FANS"][n]["RPM"] fans[f"fan_{n + 1}"].speed = api_fans["FANS"][n]["RPM"]
except KeyError: except KeyError:
pass pass
return miner_fan_speeds( return [fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]]
fan_speeds(fans["fan_1"], fans["fan_2"], fans["fan_3"], fans["fan_4"]), return [Fan(), Fan(), Fan(), Fan()]
psu_fan_speeds(psu_fan),
) async def get_fan_psu(self) -> Optional[int]:
return miner_fan_speeds( return None
fan_speeds(None, None, None, None), psu_fan_speeds(None)
)
async def get_pools( async def get_pools(
self, api_pools: dict = None, graphql_pools: dict = None self, api_pools: dict = None, graphql_pools: dict = None
@@ -851,144 +842,3 @@ class BOSMiner(BaseMiner):
) )
except (IndexError, KeyError): except (IndexError, KeyError):
pass 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

View File

@@ -21,7 +21,7 @@ import asyncssh
from pyasic.API.bosminer import BOSMinerAPI from pyasic.API.bosminer import BOSMinerAPI
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import BaseMiner
@@ -131,18 +131,17 @@ class BOSMinerOld(BaseMiner):
async def get_fans( async def get_fans(
self, self,
) -> Tuple[ ) -> List[Fan]:
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]], return [Fan(), Fan(), Fan(), Fan()]
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) async def get_fan_psu(self) -> Optional[int]:
psu_fans = psu_fan_speeds(None) 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]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -179,8 +178,5 @@ class BOSMinerOld(BaseMiner):
async def get_nominal_hashrate(self) -> Optional[float]: async def get_nominal_hashrate(self) -> Optional[float]:
return None 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) -> MinerData:
return MinerData(ip=str(self.ip)) return MinerData(ip=str(self.ip))

View File

@@ -19,7 +19,7 @@ from typing import List, Optional, Tuple, Union
from pyasic.API.btminer import BTMinerAPI from pyasic.API.btminer import BTMinerAPI
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData, WhatsminerError
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import BaseMiner
@@ -172,17 +172,17 @@ class BTMiner(BaseMiner):
################################################## ##################################################
async def get_mac( 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]: ) -> Optional[str]:
if not api_miner_info: if not api_get_miner_info:
try: try:
api_miner_info = await self.api.get_miner_info() api_get_miner_info = await self.api.get_miner_info()
except APIError: except APIError:
pass pass
if api_miner_info: if api_get_miner_info:
try: try:
mac = api_miner_info["Msg"]["mac"] mac = api_get_miner_info["Msg"]["mac"]
return str(mac).upper() return str(mac).upper()
except KeyError: except KeyError:
pass pass
@@ -223,34 +223,64 @@ class BTMiner(BaseMiner):
return None return None
async def get_version( 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]]: ) -> Tuple[Optional[str], Optional[str]]:
# check if version is cached
miner_version = namedtuple("MinerVersion", "api_ver fw_ver") miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
# Check to see if the version info is already cached api_ver = await self.get_api_ver(api_get_version=api_get_version)
if self.api_ver and self.fw_ver: fw_ver = await self.get_fw_ver(
return miner_version(self.api_ver, self.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: try:
api_version = await self.api.get_version() api_get_version = await self.api.get_version()
except APIError: except APIError:
pass pass
if api_version: if api_get_version:
if "Code" in api_version.keys(): if "Code" in api_get_version.keys():
if api_version["Code"] == 131: if api_get_version["Code"] == 131:
try: try:
api_ver = api_version["Msg"] api_ver = api_get_version["Msg"]
if not isinstance(api_ver, str): if not isinstance(api_ver, str):
api_ver = api_ver["api_ver"] api_ver = api_ver["api_ver"]
self.api_ver = api_ver.replace("whatsminer v", "") self.api_ver = api_ver.replace("whatsminer v", "")
self.fw_ver = api_version["Msg"]["fw_ver"]
except (KeyError, TypeError): except (KeyError, TypeError):
pass pass
else: else:
self.api.api_ver = self.api_ver 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: if not api_summary:
try: try:
@@ -266,21 +296,21 @@ class BTMiner(BaseMiner):
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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: if self.hostname:
return self.hostname return self.hostname
if not api_miner_info: if not api_get_miner_info:
try: try:
api_miner_info = await self.api.get_miner_info() api_get_miner_info = await self.api.get_miner_info()
except APIError: except APIError:
return None # only one way to get this return None # only one way to get this
if api_miner_info: if api_get_miner_info:
try: try:
self.hostname = api_miner_info["Msg"]["hostname"] self.hostname = api_get_miner_info["Msg"]["hostname"]
except KeyError: except KeyError:
return None return None
@@ -375,18 +405,32 @@ class BTMiner(BaseMiner):
pass pass
async def get_fans( async def get_fans(
self, api_summary: dict = None, api_psu: dict = None self, api_summary: dict = None, api_get_psu: dict = None
) -> Tuple[ ) -> List[Fan]:
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]], if not api_summary:
Tuple[Optional[int]], try:
]: api_summary = await self.api.summary()
fan_speeds = namedtuple("FanSpeeds", "fan_1 fan_2 fan_3 fan_4") except APIError:
psu_fan_speeds = namedtuple("PSUFanSpeeds", "psu_fan") pass
miner_fan_speeds = namedtuple("MinerFans", "fan_speeds psu_fan_speeds")
fans = fan_speeds(None, None, None, None) fans = [Fan(), Fan(), Fan(), Fan()]
psu_fans = psu_fan_speeds(None) 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: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -395,33 +439,21 @@ class BTMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
if self.fan_count > 0: return int(api_summary["SUMMARY"][0]["Power Fanspeed"])
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"])
)
except (KeyError, IndexError): except (KeyError, IndexError):
pass pass
if not psu_fans[0]: if not api_get_psu:
if not api_psu: try:
try: api_get_psu = await self.api.get_psu()
api_psu = await self.api.get_psu() except APIError:
except APIError: pass
pass
if api_psu: if api_get_psu:
try: try:
psu_fans = psu_fan_speeds(int(api_psu["Msg"]["fan_speed"])) return int(api_get_psu["Msg"]["fan_speed"])
except (KeyError, TypeError): except (KeyError, TypeError):
pass pass
return miner_fan_speeds(fans, psu_fans)
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -450,10 +482,10 @@ class BTMiner(BaseMiner):
return groups return groups
async def get_errors( 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]: ) -> List[MinerErrorData]:
errors = [] errors = []
if not api_summary and not api_error_codes: if not api_summary and not api_get_error_code:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
except APIError: except APIError:
@@ -468,14 +500,14 @@ class BTMiner(BaseMiner):
except (KeyError, IndexError, ValueError, TypeError): except (KeyError, IndexError, ValueError, TypeError):
pass pass
if not api_error_codes: if not api_get_error_code:
try: try:
api_error_codes = await self.api.get_error_code() api_get_error_code = await self.api.get_error_code()
except APIError: except APIError:
pass pass
if api_error_codes: if api_get_error_code:
for err in api_error_codes["Msg"]["error_code"]: for err in api_get_error_code["Msg"]["error_code"]:
if isinstance(err, dict): if isinstance(err, dict):
for code in err: for code in err:
errors.append(WhatsminerError(error_code=int(code))) errors.append(WhatsminerError(error_code=int(code)))
@@ -499,134 +531,20 @@ class BTMiner(BaseMiner):
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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 data = None
if not api_miner_info: if not api_get_miner_info:
try: try:
api_miner_info = await self.api.get_miner_info() api_get_miner_info = await self.api.get_miner_info()
except APIError: except APIError:
if not self.light: if not self.light:
self.light = False self.light = False
if api_miner_info: if api_get_miner_info:
try: try:
self.light = api_miner_info["Msg"]["ledstat"] == "auto" self.light = not (api_get_miner_info["Msg"]["ledstat"] == "auto")
except KeyError: except KeyError:
pass pass
return self.light if self.light else False 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

View File

@@ -21,7 +21,7 @@ import asyncssh
from pyasic.API.cgminer import CGMinerAPI from pyasic.API.cgminer import CGMinerAPI
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import BaseMiner
@@ -185,9 +185,14 @@ class CGMiner(BaseMiner):
self, api_version: dict = None self, api_version: dict = None
) -> Tuple[Optional[str], Optional[str]]: ) -> Tuple[Optional[str], Optional[str]]:
miner_version = namedtuple("MinerVersion", "api_ver fw_ver") miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
# Check to see if the version info is already cached return miner_version(
if self.api_ver and self.fw_ver: api_ver=await self.get_api_ver(api_version=api_version),
return miner_version(self.api_ver, self.fw_ver) 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: if not api_version:
try: try:
@@ -200,12 +205,26 @@ class CGMiner(BaseMiner):
self.api_ver = api_version["VERSION"][0]["API"] self.api_ver = api_version["VERSION"][0]["API"]
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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: try:
self.fw_ver = api_version["VERSION"][0]["CGMiner"] self.fw_ver = api_version["VERSION"][0]["CGMiner"]
except (KeyError, IndexError): except (KeyError, IndexError):
pass pass
return miner_version(self.api_ver, self.fw_ver) return self.fw_ver
async def get_hostname(self) -> Optional[str]: async def get_hostname(self) -> Optional[str]:
try: try:
@@ -293,25 +312,14 @@ class CGMiner(BaseMiner):
async def get_wattage_limit(self) -> Optional[int]: async def get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans( async def get_fans(self, api_stats: dict = None) -> List[Fan]:
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)
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass
fans_data = [None, None, None, None] fans_data = [Fan(), Fan(), Fan(), Fan()]
if api_stats: if api_stats:
try: try:
fan_offset = -1 fan_offset = -1
@@ -325,12 +333,15 @@ class CGMiner(BaseMiner):
fan_offset = 1 fan_offset = 1
for fan in range(self.fan_count): 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): except (KeyError, IndexError):
pass 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]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -387,109 +398,3 @@ class CGMiner(BaseMiner):
return round(ideal_rate, 2) return round(ideal_rate, 2)
except (KeyError, IndexError): except (KeyError, IndexError):
pass 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

View File

@@ -20,7 +20,7 @@ from typing import List, Optional, Tuple, Union
from pyasic.API.cgminer import CGMinerAPI from pyasic.API.cgminer import CGMinerAPI
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners._backends import CGMiner from pyasic.miners._backends import CGMiner
@@ -206,25 +206,14 @@ class CGMinerAvalon(CGMiner):
async def get_wattage_limit(self) -> Optional[int]: async def get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans( async def get_fans(self, api_stats: dict = None) -> List[Fan]:
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)
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass
fans_data = [None, None, None, None] fans_data = [Fan(), Fan(), Fan(), Fan()]
if api_stats: if api_stats:
try: try:
stats_data = api_stats[0].get("STATS") stats_data = api_stats[0].get("STATS")
@@ -233,12 +222,11 @@ class CGMinerAvalon(CGMiner):
if key.startswith("MM ID"): if key.startswith("MM ID"):
raw_data = self.parse_stats(stats_data[0][key]) raw_data = self.parse_stats(stats_data[0][key])
for fan in range(self.fan_count): 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): except (KeyError, IndexError, ValueError, TypeError):
pass 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]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -279,101 +267,3 @@ class CGMinerAvalon(CGMiner):
if data["STATUS"][0]["Msg"] == "ASC 0 set info: LED[1]": if data["STATUS"][0]["Msg"] == "ASC 0 set info: LED[1]":
return True return True
return False 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

View File

@@ -46,10 +46,7 @@ class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__() super().__init__()
self.ip = ip self.ip = ip
self.model = "M31S+ V30" self.model = "M31S+ V30"
self.nominal_chips = 0 self.nominal_chips = 117
warnings.warn(
"Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.fan_count = 2 self.fan_count = 2
@@ -58,10 +55,7 @@ class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__() super().__init__()
self.ip = ip self.ip = ip
self.model = "M31S+ V40" self.model = "M31S+ V40"
self.nominal_chips = 0 self.nominal_chips = 123
warnings.warn(
"Unknown chip count for miner type M30S+ V40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.fan_count = 2 self.fan_count = 2
@@ -109,10 +103,7 @@ class M31SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__() super().__init__()
self.ip = ip self.ip = ip
self.model = "M31S+ V100" self.model = "M31S+ V100"
self.nominal_chips = 0 self.nominal_chips = 111
warnings.warn(
"Unknown chip count for miner type M30S+ V100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.fan_count = 2 self.fan_count = 2

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import json
from typing import Optional, Union from typing import Optional, Union
import httpx import httpx

View File

@@ -114,101 +114,3 @@ class HiveonT9(Hiveon, T9):
if not env_temp_list == []: if not env_temp_list == []:
return round(float(sum(env_temp_list) / len(env_temp_list)), 2) 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

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import inspect
import ipaddress import ipaddress
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
@@ -20,7 +21,7 @@ from typing import List, Optional, Tuple, TypeVar
import asyncssh import asyncssh
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData
@@ -199,6 +200,24 @@ class BaseMiner(ABC):
""" """
pass 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 @abstractmethod
async def get_version(self, *args, **kwargs) -> Tuple[Optional[str], Optional[str]]: 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. """Get the API version and firmware version of the miner and return them as strings.
@@ -263,15 +282,20 @@ class BaseMiner(ABC):
pass pass
@abstractmethod @abstractmethod
async def get_fans( async def get_fans(self, *args, **kwargs) -> List[Fan]:
self, *args, **kwargs """Get fan data from the miner in the form [fan_1, fan_2, fan_3, fan_4].
) -> 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).
Returns: 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 pass
@@ -282,6 +306,7 @@ class BaseMiner(ABC):
Returns: Returns:
Pool groups and quotas in a list of dicts. Pool groups and quotas in a list of dicts.
""" """
pass
@abstractmethod @abstractmethod
async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]: async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
@@ -310,11 +335,104 @@ class BaseMiner(ABC):
""" """
pass 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]. """Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].
Parameters: Parameters:
allow_warning: Allow warning when an API command fails. 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: Returns:
A [`MinerData`][pyasic.data.MinerData] instance containing data from the miner. 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: for item in gathered_data:
if gathered_data[item] is not None: if gathered_data[item] is not None:
setattr(data, item, gathered_data[item]) setattr(data, item, gathered_data[item])
return data return data
@abstractmethod
async def _get_data(self, allow_warning: bool) -> dict:
pass
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner) AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)

View File

@@ -20,7 +20,7 @@ from typing import List, Optional, Tuple, Union
import httpx import httpx
from pyasic.config import MinerConfig 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.data.error_codes import InnosiliconError, MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
@@ -133,8 +133,11 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
################################################## ##################################################
async def get_mac( 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]: ) -> Optional[str]:
web_all_data = web_getAll
if not web_all_data and not web_overview: if not web_all_data and not web_overview:
try: try:
web_overview = await self.send_web_command("overview") web_overview = await self.send_web_command("overview")
@@ -174,8 +177,11 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
pass pass
async def get_hashrate( 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]: ) -> Optional[float]:
web_all_data = web_getAll
if not api_summary and not web_all_data: if not api_summary and not web_all_data:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -197,8 +203,11 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
pass pass
async def get_hashboards( 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]: ) -> List[HashBoard]:
web_all_data = web_getAll
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips) HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards) for i in range(self.ideal_hashboards)
@@ -246,7 +255,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
return hashboards 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: if not web_all_data:
try: try:
web_all_data = await self.send_web_command("getAll") web_all_data = await self.send_web_command("getAll")
@@ -262,18 +274,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
pass pass
async def get_fans( async def get_fans(
self, web_all_data: dict = None self,
) -> Tuple[ web_getAll: dict = None, # noqa: named this way for automatic functionality
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]], ) -> List[Fan]:
Tuple[Optional[int]], web_all_data = web_getAll
]:
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)
if not web_all_data: if not web_all_data:
try: try:
web_all_data = await self.send_web_command("getAll") web_all_data = await self.send_web_command("getAll")
@@ -282,19 +286,18 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
else: else:
web_all_data = web_all_data["all"] web_all_data = web_all_data["all"]
fan_data = [Fan(), Fan(), Fan(), Fan()]
if web_all_data: if web_all_data:
try: try:
spd = web_all_data["fansSpeed"] spd = web_all_data["fansSpeed"]
except KeyError: except KeyError:
pass pass
else: else:
f = [None, None, None, None]
round((int(spd) * 6000) / 100) round((int(spd) * 6000) / 100)
for i in range(self.fan_count): for i in range(self.fan_count):
f[i] = spd fan_data[i] = Fan(spd)
fans = fan_speeds(*f)
return miner_fan_speeds(fans, psu_fans) return fan_data
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -322,7 +325,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
pass pass
return groups 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 = [] errors = []
if not web_error_details: if not web_error_details:
try: try:
@@ -342,129 +348,3 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
if not err == 0: if not err == 0:
errors.append(InnosiliconError(error_code=err)) errors.append(InnosiliconError(error_code=err))
return errors 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

View File

@@ -17,7 +17,7 @@ from typing import List, Optional, Tuple
from pyasic.API.unknown import UnknownAPI from pyasic.API.unknown import UnknownAPI
from pyasic.config import MinerConfig 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.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import BaseMiner
@@ -98,18 +98,17 @@ class UnknownMiner(BaseMiner):
async def get_fans( async def get_fans(
self, self,
) -> Tuple[ ) -> List[Fan]:
Tuple[Optional[int], Optional[int], Optional[int], Optional[int]], return [Fan(), Fan(), Fan(), Fan()]
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) async def get_fan_psu(self) -> Optional[int]:
psu_fans = psu_fan_speeds(None) 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]: async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = [] groups = []
@@ -146,8 +145,7 @@ class UnknownMiner(BaseMiner):
async def get_nominal_hashrate(self) -> Optional[float]: async def get_nominal_hashrate(self) -> Optional[float]:
return None return None
async def _get_data(self, allow_warning: bool) -> dict: async def get_data(
return {} self, allow_warning: bool = False, data_to_get: list = None
) -> MinerData:
async def get_data(self, allow_warning: bool = False) -> MinerData:
return MinerData(ip=str(self.ip)) return MinerData(ip=str(self.ip))

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.26.1" version = "0.27.3"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH." description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
authors = ["UpstreamData <brett@upstreamdata.ca>"] authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic" repository = "https://github.com/UpstreamData/pyasic"