Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8a8315ad0 | ||
|
|
dac9bcc3de | ||
|
|
46621d6b93 | ||
|
|
35700f7e57 | ||
|
|
08e6744595 | ||
|
|
2de3e5e328 | ||
|
|
51f2eb1b1d | ||
|
|
521853863b | ||
|
|
b7a5a647b3 | ||
|
|
4434f9ccad | ||
|
|
82a1cc3cfe |
@@ -29,6 +29,7 @@ from .device import DeviceInfo
|
||||
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
||||
from .fans import Fan
|
||||
from .hashrate import AlgoHashRate, HashUnit
|
||||
from pyasic.data.pools import PoolMetrics
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -71,6 +72,7 @@ class MinerData:
|
||||
fault_light: Whether the fault light is on as a boolean.
|
||||
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
|
||||
is_mining: Whether the miner is mining.
|
||||
pools: A list of PoolMetrics instances, each representing metrics for a pool.
|
||||
"""
|
||||
|
||||
# general
|
||||
@@ -143,6 +145,9 @@ class MinerData:
|
||||
uptime: int = None
|
||||
efficiency: int = field(init=False)
|
||||
|
||||
# pools
|
||||
pools: list[PoolMetrics] = field(default_factory=list)
|
||||
|
||||
@classmethod
|
||||
def fields(cls):
|
||||
return [f.name for f in fields(cls) if not f.name.startswith("_")]
|
||||
|
||||
@@ -19,7 +19,6 @@ from typing import List, Optional, Union
|
||||
from pyasic.config import MinerConfig, MiningModeConfig
|
||||
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
|
||||
from pyasic.data.error_codes import MinerErrorData, X19Error
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.backends.bmminer import BMMiner
|
||||
from pyasic.miners.backends.cgminer import CGMiner
|
||||
from pyasic.miners.data import (
|
||||
@@ -32,6 +31,8 @@ from pyasic.miners.data import (
|
||||
from pyasic.rpc.antminer import AntminerRPCAPI
|
||||
from pyasic.ssh.antminer import AntminerModernSSH
|
||||
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
|
||||
from pyasic.data.pools import PoolMetrics
|
||||
from pyasic.errors import APIError
|
||||
|
||||
ANTMINER_MODERN_DATA_LOC = DataLocations(
|
||||
**{
|
||||
@@ -79,6 +80,10 @@ ANTMINER_MODERN_DATA_LOC = DataLocations(
|
||||
"_get_uptime",
|
||||
[RPCAPICommand("rpc_stats", "stats")],
|
||||
),
|
||||
str(DataOptions.POOLS): DataFunction(
|
||||
"_get_pools",
|
||||
[RPCAPICommand("rpc_pools", "pools")],
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -351,6 +356,35 @@ class AntminerModern(BMMiner):
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
|
||||
if rpc_pools is None:
|
||||
try:
|
||||
rpc_pools = await self.rpc.pools()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
pools_data = []
|
||||
if rpc_pools is not None:
|
||||
try:
|
||||
pools = rpc_pools.get("POOLS", [])
|
||||
for pool_info in pools:
|
||||
pool_data = PoolMetrics(
|
||||
accepted=pool_info.get("Accepted"),
|
||||
rejected=pool_info.get("Rejected"),
|
||||
get_failures=pool_info.get("Get Failures"),
|
||||
remote_failures=pool_info.get("Remote Failures"),
|
||||
active=pool_info.get("Stratum Active"),
|
||||
alive=pool_info.get("Status") == "Alive",
|
||||
url=pool_info.get("URL"),
|
||||
user=pool_info.get("User"),
|
||||
index=pool_info.get("POOL")
|
||||
|
||||
)
|
||||
pools_data.append(pool_data)
|
||||
except LookupError:
|
||||
pass
|
||||
return pools_data
|
||||
|
||||
|
||||
ANTMINER_OLD_DATA_LOC = DataLocations(
|
||||
**{
|
||||
|
||||
@@ -22,6 +22,7 @@ from pyasic.config import MinerConfig
|
||||
from pyasic.data import Fan, HashBoard, MinerData
|
||||
from pyasic.data.device import DeviceInfo
|
||||
from pyasic.data.error_codes import MinerErrorData
|
||||
from pyasic.data.pools import PoolMetrics
|
||||
from pyasic.device import MinerModel
|
||||
from pyasic.device.algorithm import MinerAlgo
|
||||
from pyasic.device.firmware import MinerFirmware
|
||||
@@ -341,6 +342,14 @@ class MinerProtocol(Protocol):
|
||||
"""
|
||||
return await self._get_uptime()
|
||||
|
||||
async def get_pools(self) -> List[PoolMetrics]:
|
||||
"""Get the pools information from Miner.
|
||||
|
||||
Return:
|
||||
The pool information of the miner.
|
||||
"""
|
||||
return await self._get_pools()
|
||||
|
||||
async def _get_mac(self) -> Optional[str]:
|
||||
pass
|
||||
|
||||
@@ -392,56 +401,69 @@ class MinerProtocol(Protocol):
|
||||
async def _get_uptime(self) -> Optional[int]:
|
||||
pass
|
||||
|
||||
async def _get_pools(self) -> List[PoolMetrics]:
|
||||
pass
|
||||
|
||||
async def _get_data(
|
||||
self,
|
||||
allow_warning: bool,
|
||||
include: List[Union[str, DataOptions]] = None,
|
||||
exclude: List[Union[str, DataOptions]] = None,
|
||||
) -> dict:
|
||||
# handle include
|
||||
if include is not None:
|
||||
include = [str(i) for i in include]
|
||||
else:
|
||||
# everything
|
||||
include = [str(enum_value.value) for enum_value in DataOptions]
|
||||
|
||||
# handle exclude
|
||||
# prioritized over include, including x and excluding x will exclude x
|
||||
if exclude is not None:
|
||||
for item in exclude:
|
||||
if str(item) in include:
|
||||
include.remove(str(item))
|
||||
|
||||
api_multicommand = set()
|
||||
web_multicommand = []
|
||||
rpc_multicommand = set()
|
||||
web_multicommand = set()
|
||||
# create multicommand
|
||||
for data_name in include:
|
||||
try:
|
||||
# get kwargs needed for the _get_xyz function
|
||||
fn_args = getattr(self.data_locations, data_name).kwargs
|
||||
|
||||
# keep track of which RPC/Web commands need to be sent
|
||||
for arg in fn_args:
|
||||
if isinstance(arg, RPCAPICommand):
|
||||
api_multicommand.add(arg.cmd)
|
||||
rpc_multicommand.add(arg.cmd)
|
||||
if isinstance(arg, WebAPICommand):
|
||||
if arg.cmd not in web_multicommand:
|
||||
web_multicommand.append(arg.cmd)
|
||||
web_multicommand.add(arg.cmd)
|
||||
except KeyError as e:
|
||||
logger.error(e, data_name)
|
||||
logger.error(type(e), e, data_name)
|
||||
continue
|
||||
|
||||
if len(api_multicommand) > 0:
|
||||
api_command_task = asyncio.create_task(
|
||||
self.api.multicommand(*api_multicommand, allow_warning=allow_warning)
|
||||
# create tasks for all commands that need to be sent, or no-op with sleep(0) -> None
|
||||
if len(rpc_multicommand) > 0:
|
||||
rpc_command_task = asyncio.create_task(
|
||||
self.rpc.multicommand(*rpc_multicommand, allow_warning=allow_warning)
|
||||
)
|
||||
else:
|
||||
api_command_task = asyncio.sleep(0)
|
||||
rpc_command_task = asyncio.create_task(asyncio.sleep(0))
|
||||
if len(web_multicommand) > 0:
|
||||
web_command_task = asyncio.create_task(
|
||||
self.web.multicommand(*web_multicommand, allow_warning=allow_warning)
|
||||
)
|
||||
else:
|
||||
web_command_task = asyncio.sleep(0)
|
||||
web_command_task = asyncio.create_task(asyncio.sleep(0))
|
||||
|
||||
web_command_data = await web_command_task
|
||||
# make sure the tasks complete
|
||||
await asyncio.gather(rpc_command_task, web_command_task)
|
||||
|
||||
# grab data out of the tasks
|
||||
web_command_data = web_command_task.result()
|
||||
if web_command_data is None:
|
||||
web_command_data = {}
|
||||
|
||||
api_command_data = await api_command_task
|
||||
api_command_data = rpc_command_task.result()
|
||||
if api_command_data is None:
|
||||
api_command_data = {}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ class DataOptions(Enum):
|
||||
UPTIME = "uptime"
|
||||
CONFIG = "config"
|
||||
VOLTAGE = "voltage"
|
||||
POOLS = "pools"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "pyasic"
|
||||
version = "0.57.5"
|
||||
version = "0.57.6"
|
||||
description = "A simplified and standardized interface for Bitcoin ASICs."
|
||||
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||
repository = "https://github.com/UpstreamData/pyasic"
|
||||
|
||||
@@ -57,6 +57,7 @@ class MinersTest(unittest.TestCase):
|
||||
"wattage",
|
||||
"voltage",
|
||||
"wattage_limit",
|
||||
"pools",
|
||||
]
|
||||
)
|
||||
warnings.filterwarnings("ignore")
|
||||
|
||||
Reference in New Issue
Block a user