Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8a8315ad0 | ||
|
|
dac9bcc3de | ||
|
|
46621d6b93 | ||
|
|
35700f7e57 | ||
|
|
08e6744595 | ||
|
|
2de3e5e328 | ||
|
|
51f2eb1b1d | ||
|
|
d40d92c1ca | ||
|
|
7ea63643a9 | ||
|
|
313c324771 | ||
|
|
a9fd9343d8 | ||
|
|
8f41d4d0bc | ||
|
|
521853863b | ||
|
|
b7a5a647b3 | ||
|
|
4434f9ccad | ||
|
|
82a1cc3cfe | ||
|
|
6f10c91482 | ||
|
|
f2d6bce165 | ||
|
|
61623cc44d | ||
|
|
a30a726324 | ||
|
|
0e90ad64cd | ||
|
|
53572c6236 | ||
|
|
67da56a03b | ||
|
|
be8633185d | ||
|
|
1d656da2a2 | ||
|
|
189deae3d1 | ||
|
|
46188ad52b | ||
|
|
19e232ddb9 | ||
|
|
5d90b7e938 | ||
|
|
3f90799544 | ||
|
|
1f70ec0d28 | ||
|
|
c26b78aa01 | ||
|
|
0debf16f7c | ||
|
|
d7f48d8f9f | ||
|
|
58c95559dd | ||
|
|
03caa9fe94 | ||
|
|
afd8697f07 | ||
|
|
91d504fc1c |
1180
poetry.lock
generated
Normal file
1180
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -444,7 +444,7 @@ class MiningModeConfig(MinerConfigOption):
|
||||
)
|
||||
else:
|
||||
return cls.hashrate_tuning(
|
||||
hashrate=algo_info["ChipTune"]["Target"],
|
||||
hashrate=algo_info["ChipTune"].get("Target"),
|
||||
algo=TunerAlgo.chip_tune(),
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -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("_")]
|
||||
|
||||
59
pyasic/data/pools.py
Normal file
59
pyasic/data/pools.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class PoolMetrics:
|
||||
"""A dataclass to standardize pool metrics returned from miners.
|
||||
Attributes:
|
||||
|
||||
accepted: Number of accepted shares.
|
||||
rejected: Number of rejected shares.
|
||||
get_failures: Number of failures in obtaining work from the pool.
|
||||
remote_failures: Number of failures communicating with the pool server.
|
||||
active: Indicates if the miner is connected to the stratum server.
|
||||
Alive : Indicates if a pool is alive.
|
||||
url: URL of the pool.
|
||||
index: Index of the pool.
|
||||
user: Username for the pool.
|
||||
pool_rejected_percent: Percentage of rejected shares by the pool.
|
||||
pool_stale_percent: Percentage of stale shares by the pool.
|
||||
"""
|
||||
|
||||
accepted: int = None
|
||||
rejected: int = None
|
||||
get_failures: int = None
|
||||
remote_failures: int = None
|
||||
active: bool = None
|
||||
alive: bool = None
|
||||
url: str = None
|
||||
index: int = None
|
||||
user: str = None
|
||||
pool_rejected_percent: float = field(init=False)
|
||||
pool_stale_percent: float = field(init=False)
|
||||
|
||||
@property
|
||||
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
|
||||
"""Calculate and return the percentage of rejected shares"""
|
||||
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
|
||||
|
||||
@pool_rejected_percent.setter
|
||||
def pool_rejected_percent(self, val):
|
||||
pass
|
||||
|
||||
@property
|
||||
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
|
||||
"""Calculate and return the percentage of stale shares."""
|
||||
return self._calculate_percentage(
|
||||
self.get_failures, self.accepted + self.rejected
|
||||
)
|
||||
|
||||
@pool_stale_percent.setter
|
||||
def pool_stale_percent(self, val):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _calculate_percentage(value: int, total: int) -> float:
|
||||
"""Calculate the percentage."""
|
||||
if total == 0:
|
||||
return 0
|
||||
return (value / total) * 100
|
||||
@@ -14,10 +14,10 @@
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from enum import StrEnum
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class MinerFirmware(StrEnum):
|
||||
class MinerFirmware(str, Enum):
|
||||
STOCK = "Stock"
|
||||
BRAIINS_OS = "BOS+"
|
||||
VNISH = "VNish"
|
||||
@@ -25,3 +25,6 @@ class MinerFirmware(StrEnum):
|
||||
HIVEON = "Hive"
|
||||
LUXOS = "LuxOS"
|
||||
MARATHON = "MaraFW"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from enum import StrEnum
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class MinerMake(StrEnum):
|
||||
class MinerMake(str, Enum):
|
||||
WHATSMINER = "WhatsMiner"
|
||||
ANTMINER = "AntMiner"
|
||||
AVALONMINER = "AvalonMiner"
|
||||
@@ -25,3 +25,6 @@ class MinerMake(StrEnum):
|
||||
GOLDSHELL = "Goldshell"
|
||||
AURADINE = "Auradine"
|
||||
EPIC = "ePIC"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from enum import StrEnum
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class AntminerModels(StrEnum):
|
||||
class AntminerModels(str, Enum):
|
||||
D3 = "D3"
|
||||
HS3 = "HS3"
|
||||
L3Plus = "L3+"
|
||||
@@ -46,8 +46,11 @@ class AntminerModels(StrEnum):
|
||||
S21 = "S21"
|
||||
T21 = "T21"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class WhatsminerModels(StrEnum):
|
||||
|
||||
class WhatsminerModels(str, Enum):
|
||||
M20V10 = "M20 V10"
|
||||
M20SV10 = "M20S V10"
|
||||
M20SV20 = "M20S V20"
|
||||
@@ -263,8 +266,11 @@ class WhatsminerModels(StrEnum):
|
||||
M66SVK30 = "M66S VK30"
|
||||
M66SVK40 = "M66S VK40"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class AvalonminerModels(StrEnum):
|
||||
|
||||
class AvalonminerModels(str, Enum):
|
||||
Avalon721 = "Avalon 721"
|
||||
Avalon741 = "Avalon 741"
|
||||
Avalon761 = "Avalon 761"
|
||||
@@ -278,13 +284,19 @@ class AvalonminerModels(StrEnum):
|
||||
Avalon1166Pro = "Avalon 1166 Pro"
|
||||
Avalon1246 = "Avalon 1246"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class InnosiliconModels(StrEnum):
|
||||
|
||||
class InnosiliconModels(str, Enum):
|
||||
T3HPlus = "T3H+"
|
||||
A10X = "A10X"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class GoldshellModels(StrEnum):
|
||||
|
||||
class GoldshellModels(str, Enum):
|
||||
CK5 = "CK5"
|
||||
HS5 = "HS5"
|
||||
KD5 = "KD5"
|
||||
@@ -292,13 +304,19 @@ class GoldshellModels(StrEnum):
|
||||
KDBoxII = "KD Box II"
|
||||
KDBoxPro = "KD Box Pro"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class ePICModels(StrEnum):
|
||||
|
||||
class ePICModels(str, Enum):
|
||||
BM520i = "BlockMiner 520i"
|
||||
BM720i = "BlockMiner 720i"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class AuradineModels(StrEnum):
|
||||
|
||||
class AuradineModels(str, Enum):
|
||||
AT1500 = "AT1500"
|
||||
AT2860 = "AT2860"
|
||||
AT2880 = "AT2880"
|
||||
@@ -307,6 +325,9 @@ class AuradineModels(StrEnum):
|
||||
AD2500 = "AD2500"
|
||||
AD3500 = "AD3500"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class MinerModel:
|
||||
ANTMINER = AntminerModels
|
||||
|
||||
22
pyasic/miners/antminer/vnish/X21/S21.py
Normal file
22
pyasic/miners/antminer/vnish/X21/S21.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from pyasic.miners.backends import VNish
|
||||
from pyasic.miners.device.models import S21
|
||||
|
||||
|
||||
class VNishS21(VNish, S21):
|
||||
pass
|
||||
17
pyasic/miners/antminer/vnish/X21/__init__.py
Normal file
17
pyasic/miners/antminer/vnish/X21/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .S21 import VNishS21
|
||||
@@ -18,3 +18,4 @@ from .X3 import *
|
||||
from .X7 import *
|
||||
from .X17 import *
|
||||
from .X19 import *
|
||||
from .X21 import *
|
||||
|
||||
@@ -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")],
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -275,7 +280,7 @@ class AntminerModern(BMMiner):
|
||||
rate_unit = "GH"
|
||||
return AlgoHashRate.SHA256(
|
||||
expected_rate, HashUnit.SHA256.from_str(rate_unit)
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
@@ -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(
|
||||
**{
|
||||
|
||||
@@ -251,7 +251,7 @@ class AvalonMiner(CGMiner):
|
||||
parsed_stats = self.parse_stats(unparsed_stats)
|
||||
return AlgoHashRate.SHA256(
|
||||
parsed_stats["GHSmm"], HashUnit.SHA256.GH
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
|
||||
@@ -224,6 +224,6 @@ class BFGMiner(StockFirmware):
|
||||
rate_unit = "GH"
|
||||
return AlgoHashRate.SHA256(
|
||||
expected_rate, HashUnit.SHA256.from_str(rate_unit)
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
@@ -240,7 +240,7 @@ class BMMiner(StockFirmware):
|
||||
rate_unit = "GH"
|
||||
return AlgoHashRate.SHA256(
|
||||
expected_rate, HashUnit.SHA256.from_str(rate_unit)
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -865,7 +865,9 @@ class BOSer(BraiinsOSFirmware):
|
||||
) -> Optional[int]:
|
||||
if grpc_active_performance_mode is None:
|
||||
try:
|
||||
grpc_active_performance_mode = self.web.get_active_performance_mode()
|
||||
grpc_active_performance_mode = (
|
||||
await self.web.get_active_performance_mode()
|
||||
)
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -575,7 +575,7 @@ class BTMiner(StockFirmware):
|
||||
if expected_hashrate:
|
||||
return AlgoHashRate.SHA256(
|
||||
expected_hashrate, HashUnit.SHA256.GH
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
@@ -270,7 +270,7 @@ class ePIC(ePICFirmware):
|
||||
ideal = hb["Hashrate"][1] / 100
|
||||
|
||||
hashrate += hb["Hashrate"][0] / ideal
|
||||
return AlgoHashRate.SHA256(hashrate, HashUnit.SHA256.GH).int(
|
||||
return AlgoHashRate.SHA256(hashrate, HashUnit.SHA256.MH).into(
|
||||
self.algo.unit.default
|
||||
)
|
||||
except (LookupError, ValueError, TypeError):
|
||||
@@ -317,7 +317,7 @@ class ePIC(ePICFirmware):
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_capabilities is not None:
|
||||
if web_capabilities is None:
|
||||
try:
|
||||
web_capabilities = await self.web.capabilities()
|
||||
except APIError:
|
||||
|
||||
@@ -279,7 +279,7 @@ class LUXMiner(LuxOSFirmware):
|
||||
rate_unit = "GH"
|
||||
return AlgoHashRate.SHA256(
|
||||
expected_rate, HashUnit.SHA256.from_str(rate_unit)
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ class MaraMiner(MaraFirmware):
|
||||
try:
|
||||
return AlgoHashRate.SHA256(
|
||||
web_brief["hashrate_ideal"], HashUnit.SHA256.GH
|
||||
).int(self.algo.unit.default)
|
||||
).into(self.algo.unit.default)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ VNISH_DATA_LOC = DataLocations(
|
||||
)
|
||||
|
||||
|
||||
class VNish(BMMiner, VNishFirmware):
|
||||
class VNish(VNishFirmware, BMMiner):
|
||||
"""Handler for VNish miners"""
|
||||
|
||||
_web_cls = VNishWebAPI
|
||||
@@ -147,6 +147,22 @@ class VNish(BMMiner, VNishFirmware):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def fault_light_off(self) -> bool:
|
||||
result = await self.web.find_miner()
|
||||
if result is not None:
|
||||
if result.get("on") is False:
|
||||
return True
|
||||
else:
|
||||
await self.web.find_miner()
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
result = await self.web.find_miner()
|
||||
if result is not None:
|
||||
if result.get("on") is True:
|
||||
return True
|
||||
else:
|
||||
await self.web.find_miner()
|
||||
|
||||
async def _get_hostname(self, web_summary: dict = None) -> str:
|
||||
if web_summary is None:
|
||||
web_info = await self.web.info()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -384,6 +384,7 @@ MINER_CLASSES = {
|
||||
"ANTMINER S19A": VNishS19a,
|
||||
"ANTMINER S19A PRO": VNishS19aPro,
|
||||
"ANTMINER T19": VNishT19,
|
||||
"ANTMINER S21": VNishS21,
|
||||
},
|
||||
MinerTypes.EPIC: {
|
||||
None: ePIC,
|
||||
@@ -530,7 +531,6 @@ class MinerFactory:
|
||||
miner_type=miner_type,
|
||||
miner_model=miner_model,
|
||||
)
|
||||
|
||||
return miner
|
||||
|
||||
async def _get_miner_type(self, ip: str) -> MinerTypes | None:
|
||||
@@ -673,7 +673,7 @@ class MinerFactory:
|
||||
return MinerTypes.BRAIINS_OS
|
||||
if "BTMINER" in upper_data or "BITMICRO" in upper_data:
|
||||
return MinerTypes.WHATSMINER
|
||||
if "VNISH" in upper_data:
|
||||
if "VNISH" in upper_data or "DEVICE PATH" in upper_data:
|
||||
return MinerTypes.VNISH
|
||||
if "HIVEON" in upper_data:
|
||||
return MinerTypes.HIVEON
|
||||
|
||||
@@ -78,13 +78,16 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
for command in commands:
|
||||
try:
|
||||
tasks[command] = asyncio.create_task(getattr(self, command)())
|
||||
except (APIError, AttributeError):
|
||||
result["command"] = {}
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
await asyncio.gather(*list(tasks.values()))
|
||||
await asyncio.gather(*[t for t in tasks.values()], return_exceptions=True)
|
||||
|
||||
for cmd in tasks:
|
||||
result[cmd] = tasks[cmd].result()
|
||||
try:
|
||||
result[cmd] = await tasks[cmd]
|
||||
except (GRPCError, APIError):
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
@@ -149,42 +152,55 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
)
|
||||
|
||||
async def start(self) -> dict:
|
||||
return await self.send_command("start", message=StartRequest())
|
||||
return await self.send_command("start", message=StartRequest(), privileged=True)
|
||||
|
||||
async def stop(self) -> dict:
|
||||
return await self.send_command("stop", message=StopRequest())
|
||||
return await self.send_command("stop", message=StopRequest(), privileged=True)
|
||||
|
||||
async def pause_mining(self) -> dict:
|
||||
return await self.send_command("pause_mining", message=PauseMiningRequest())
|
||||
return await self.send_command(
|
||||
"pause_mining", message=PauseMiningRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def resume_mining(self) -> dict:
|
||||
return await self.send_command("resume_mining", message=ResumeMiningRequest())
|
||||
return await self.send_command(
|
||||
"resume_mining", message=ResumeMiningRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def restart(self) -> dict:
|
||||
return await self.send_command("restart", message=RestartRequest())
|
||||
return await self.send_command(
|
||||
"restart", message=RestartRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def reboot(self) -> dict:
|
||||
return await self.send_command("reboot", message=RebootRequest())
|
||||
return await self.send_command(
|
||||
"reboot", message=RebootRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def set_locate_device_status(self, enable: bool) -> dict:
|
||||
return await self.send_command(
|
||||
"set_locate_device_status",
|
||||
message=SetLocateDeviceStatusRequest(enable=enable),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def get_locate_device_status(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_locate_device_status", message=GetLocateDeviceStatusRequest()
|
||||
"get_locate_device_status",
|
||||
message=GetLocateDeviceStatusRequest(),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def set_password(self, password: str = None) -> dict:
|
||||
return await self.send_command(
|
||||
"set_password", message=SetPasswordRequest(password=password)
|
||||
"set_password",
|
||||
message=SetPasswordRequest(password=password),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def get_cooling_state(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_cooling_state", message=GetCoolingStateRequest()
|
||||
"get_cooling_state", message=GetCoolingStateRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def set_immersion_mode(
|
||||
@@ -197,16 +213,17 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
message=SetImmersionModeRequest(
|
||||
enable_immersion_mode=enable, save_action=save_action
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def get_tuner_state(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_tuner_state", message=GetTunerStateRequest()
|
||||
"get_tuner_state", message=GetTunerStateRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def list_target_profiles(self) -> dict:
|
||||
return await self.send_command(
|
||||
"list_target_profiles", message=ListTargetProfilesRequest()
|
||||
"list_target_profiles", message=ListTargetProfilesRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def set_default_power_target(
|
||||
@@ -215,6 +232,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
return await self.send_command(
|
||||
"set_default_power_target",
|
||||
message=SetDefaultPowerTargetRequest(save_action=save_action),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def set_power_target(
|
||||
@@ -227,6 +245,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
message=SetPowerTargetRequest(
|
||||
power_target=Power(watt=power_target), save_action=save_action
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def increment_power_target(
|
||||
@@ -240,6 +259,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
power_target_increment=Power(watt=power_target_increment),
|
||||
save_action=save_action,
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def decrement_power_target(
|
||||
@@ -253,6 +273,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
power_target_decrement=Power(watt=power_target_decrement),
|
||||
save_action=save_action,
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def set_default_hashrate_target(
|
||||
@@ -261,6 +282,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
return await self.send_command(
|
||||
"set_default_hashrate_target",
|
||||
message=SetDefaultHashrateTargetRequest(save_action=save_action),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def set_hashrate_target(
|
||||
@@ -274,6 +296,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
hashrate_target=TeraHashrate(terahash_per_second=hashrate_target),
|
||||
save_action=save_action,
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def increment_hashrate_target(
|
||||
@@ -289,6 +312,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
),
|
||||
save_action=save_action,
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def decrement_hashrate_target(
|
||||
@@ -304,6 +328,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
),
|
||||
save_action=save_action,
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def set_dps(
|
||||
@@ -327,6 +352,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
)
|
||||
),
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def set_performance_mode(
|
||||
@@ -356,6 +382,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
)
|
||||
),
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
if hashrate_target is not None:
|
||||
return await self.send_command(
|
||||
@@ -372,16 +399,19 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
)
|
||||
),
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def get_active_performance_mode(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_active_performance_mode", message=GetPerformanceModeRequest()
|
||||
"get_active_performance_mode",
|
||||
message=GetPerformanceModeRequest(),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def get_pool_groups(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_pool_groups", message=GetPoolGroupsRequest()
|
||||
"get_pool_groups", message=GetPoolGroupsRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def create_pool_group(self) -> dict:
|
||||
@@ -395,40 +425,44 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
|
||||
async def get_miner_configuration(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_configuration", message=GetMinerConfigurationRequest()
|
||||
"get_miner_configuration",
|
||||
message=GetMinerConfigurationRequest(),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def get_constraints(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_constraints", message=GetConstraintsRequest()
|
||||
"get_constraints", message=GetConstraintsRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def get_license_state(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_license_state", message=GetLicenseStateRequest()
|
||||
"get_license_state", message=GetLicenseStateRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def get_miner_status(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_status", message=GetMinerStatusRequest()
|
||||
"get_miner_status", message=GetMinerStatusRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def get_miner_details(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_details", message=GetMinerDetailsRequest()
|
||||
"get_miner_details", message=GetMinerDetailsRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def get_miner_stats(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_miner_stats", message=GetMinerStatsRequest()
|
||||
"get_miner_stats", message=GetMinerStatsRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def get_hashboards(self) -> dict:
|
||||
return await self.send_command("get_hashboards", message=GetHashboardsRequest())
|
||||
return await self.send_command(
|
||||
"get_hashboards", message=GetHashboardsRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def get_support_archive(self) -> dict:
|
||||
return await self.send_command(
|
||||
"get_support_archive", message=GetSupportArchiveRequest()
|
||||
"get_support_archive", message=GetSupportArchiveRequest(), privileged=True
|
||||
)
|
||||
|
||||
async def enable_hashboards(
|
||||
@@ -441,6 +475,7 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
message=EnableHashboardsRequest(
|
||||
hashboard_ids=hashboard_ids, save_action=save_action
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
async def disable_hashboards(
|
||||
@@ -453,4 +488,5 @@ class BOSerWebAPI(BaseWebAPI):
|
||||
message=DisableHashboardsRequest(
|
||||
hashboard_ids=hashboard_ids, save_action=save_action
|
||||
),
|
||||
privileged=True,
|
||||
)
|
||||
|
||||
@@ -140,3 +140,6 @@ class VNishWebAPI(BaseWebAPI):
|
||||
|
||||
async def autotune_presets(self) -> dict:
|
||||
return await self.send_command("autotune/presets")
|
||||
|
||||
async def find_miner(self) -> dict:
|
||||
return await self.send_command("find-miner", privileged=True)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "pyasic"
|
||||
version = "0.57.0"
|
||||
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