Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d057ca9f6 | ||
|
|
b71b23d2a0 | ||
|
|
b32649435d | ||
|
|
c0096126df | ||
|
|
d632360932 | ||
|
|
400001fa38 | ||
|
|
4ff32a8081 | ||
|
|
33b4ae2f2f | ||
|
|
62194bd627 |
@@ -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)
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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.")
|
||||||
|
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user