Compare commits

..

17 Commits

Author SHA1 Message Date
UpstreamData
e7f05f7a28 version: bump version number. 2024-01-05 16:22:03 -07:00
UpstreamData
2d229be9fd feature: add board serial numbers to whatsminers. 2024-01-05 16:18:03 -07:00
UpstreamData
de5038e57a feature: add AntminerModern serial numbers to Hashboard data. 2024-01-05 15:57:26 -07:00
UpstreamData
8ad1b3f72a refactor: fix formatting issue. 2024-01-05 08:49:44 -07:00
b-rowan
070fb26dbc version: bump version number. 2024-01-04 20:58:44 -07:00
b-rowan
80d9d7df1d bug: fix possible empty command when getting small data points. 2024-01-04 20:58:15 -07:00
UpstreamData
928c24f56f version: bump version number. 2024-01-04 13:07:13 -07:00
UpstreamData
6e7442f90d Update data locations to be typed with dataclasses and enums. (#82)
* feature: swap AntminerModern to new data location style.

* bug: fix a bunch of missed instances of `nominal_` naming.

* feature: add support for S19 Pro Hydro.

* version: bump version number.

* dependencies: bump httpx version

* version: bump version number.

* feature: implement data locations for all remaining miners.

* refactor: remove some unused docstrings.

* feature: swap AntminerModern to new data location style.

* feature: implement data locations for all remaining miners.

* refactor: remove some unused docstrings.

* bug: fix misnamed data locations, and update base miner get_data to use new data locations.

* bug: fix include/exclude implementation on get_data.

* bug: swap ePIC to BaseMiner subclass.

* feature: add DataOptions to __all__

* tests: update data tests with new data locations method.

* bug: remove bad command from bosminer commands.

* dependencies: update dependencies.

* bug: fix some typing issues with python 3.8, and remove useless semaphore and scan threads.

* bug: fix KeyError when pools rpc command returns broken data.
2024-01-04 13:03:45 -07:00
b-rowan
936474ed3b Merge pull request #84 from jpcomps/master 2023-12-23 13:07:47 -07:00
John-Paul Compagnone
2e28060e05 fixes, changes, and formatting 2023-12-23 15:01:42 -05:00
John-Paul Compagnone
07f92557c6 cover chiptune case 2023-12-22 23:35:13 -05:00
John-Paul Compagnone
6f6f5743cf add get_config to ePIC backend 2023-12-22 23:35:13 -05:00
Upstream Data
b89ea1fa92 version: bump version number. 2023-12-22 16:29:03 -07:00
Upstream Data
3588197741 dependencies: bump httpx version 2023-12-22 16:28:46 -07:00
Upstream Data
8adc3d2adf version: bump version number. 2023-12-22 15:47:25 -07:00
Upstream Data
040c0b6842 feature: add support for S19 Pro Hydro. 2023-12-22 15:40:23 -07:00
Upstream Data
550b4a97a1 bug: fix a bunch of missed instances of nominal_ naming. 2023-12-22 15:32:01 -07:00
46 changed files with 1228 additions and 1161 deletions

View File

@@ -263,6 +263,12 @@ If you are sure you want to use this command please use API.send_command("{comma
else:
return False, data["STATUS"][0]["Msg"]
elif isinstance(data["STATUS"], dict):
# new style X19 command
if data["STATUS"]["STATUS"] not in ["S", "I"]:
return False, data["STATUS"]["Msg"]
return True, None
if data["STATUS"] not in ["S", "I"]:
return False, data["Msg"]
else:

View File

@@ -48,10 +48,10 @@ PrePowerOnMessage = Union[
def _crypt(word: str, salt: str) -> str:
"""Encrypts a word with a salt, using a standard salt format.
r"""Encrypts a word with a salt, using a standard salt format.
Encrypts a word using a salt with the format
'\s*\$(\d+)\$([\w\./]*)\$'. If this format is not used, a
\s*\$(\d+)\$([\w\./]*)\$. If this format is not used, a
ValueError is raised.
Parameters:
@@ -62,7 +62,7 @@ def _crypt(word: str, salt: str) -> str:
An MD5 hash of the word with the salt.
"""
# compile a standard format for the salt
standard_salt = re.compile("\s*\$(\d+)\$([\w\./]*)\$")
standard_salt = re.compile(r"\s*\$(\d+)\$([\w\./]*)\$")
# check if the salt matches
match = standard_salt.match(salt)
# if the matching fails, the salt is incorrect

View File

@@ -29,7 +29,7 @@ from pyasic.data import (
)
from pyasic.errors import APIError, APIWarning
from pyasic.miners import get_miner
from pyasic.miners.base import AnyMiner
from pyasic.miners.base import AnyMiner, DataOptions
from pyasic.miners.miner_factory import MinerFactory, miner_factory
from pyasic.miners.miner_listener import MinerListener
from pyasic.network import MinerNetwork
@@ -50,6 +50,7 @@ __all__ = [
"APIWarning",
"get_miner",
"AnyMiner",
"DataOptions",
"MinerFactory",
"miner_factory",
"MinerListener",

View File

@@ -161,6 +161,15 @@ class MinerConfig:
power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
)
@classmethod
def from_epic(cls, web_conf: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_epic(web_conf),
fan_mode=FanModeConfig.from_epic(web_conf),
temperature=TemperatureConfig.from_epic(web_conf),
mining_mode=MiningModeConfig.from_epic(web_conf),
)
def merge(a: dict, b: dict) -> dict:
result = deepcopy(a)

View File

@@ -116,6 +116,17 @@ class FanModeConfig(MinerConfigOption):
else:
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
try:
fan_mode = web_conf["Fans"]["Fan Mode"]
if fan_mode.get("Manual") is not None:
return cls.manual(speed=fan_mode.get("Manual"))
else:
return cls.normal()
except KeyError:
return cls.default()
@classmethod
def from_bosminer(cls, toml_conf: dict):
if toml_conf.get("temp_control") is None:

View File

@@ -14,7 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import dataclass, field
from typing import Union
from typing import Dict, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@@ -132,7 +132,7 @@ class MiningModeManual(MinerConfigValue):
global_freq: float
global_volt: float
boards: dict[int, ManualBoardSettings] = field(default_factory=dict)
boards: Dict[int, ManualBoardSettings] = field(default_factory=dict)
@classmethod
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual":
@@ -186,6 +186,29 @@ class MiningModeConfig(MinerConfigOption):
return cls.low()
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
try:
work_mode = web_conf["PerpetualTune"]["Running"]
if work_mode:
if (
web_conf["PerpetualTune"]["Algorithm"].get("VoltageOptimizer")
is not None
):
return cls.hashrate_tuning(
web_conf["PerpetualTune"]["Algorithm"]["VoltageOptimizer"][
"Target"
]
)
else:
return cls.hashrate_tuning(
web_conf["PerpetualTune"]["Algorithm"]["ChipTune"]["Target"]
)
else:
return cls.normal()
except KeyError:
return cls.default()
@classmethod
def from_bosminer(cls, toml_conf: dict):
if toml_conf.get("autotuning") is None:

View File

@@ -16,7 +16,7 @@
import random
import string
from dataclasses import dataclass, field
from typing import Union
from typing import Dict, List, Union
from pyasic.config.base import MinerConfigValue
@@ -108,6 +108,12 @@ class Pool(MinerConfigValue):
def from_api(cls, api_pool: dict) -> "Pool":
return cls(url=api_pool["URL"], user=api_pool["User"], password="x")
@classmethod
def from_epic(cls, api_pool: dict) -> "Pool":
return cls(
url=api_pool["pool"], user=api_pool["login"], password=api_pool["password"]
)
@classmethod
def from_am_modern(cls, web_pool: dict) -> "Pool":
return cls(
@@ -138,7 +144,7 @@ class Pool(MinerConfigValue):
@dataclass
class PoolGroup(MinerConfigValue):
pools: list[Pool] = field(default_factory=list)
pools: List[Pool] = field(default_factory=list)
quota: int = 1
name: str = None
@@ -237,6 +243,13 @@ class PoolGroup(MinerConfigValue):
pools.append(Pool.from_api(pool))
return cls(pools=pools)
@classmethod
def from_epic(cls, api_pool_list: list) -> "PoolGroup":
pools = []
for pool in api_pool_list:
pools.append(Pool.from_epic(pool))
return cls(pools=pools)
@classmethod
def from_am_modern(cls, web_pool_list: list) -> "PoolGroup":
pools = []
@@ -265,7 +278,7 @@ class PoolGroup(MinerConfigValue):
@dataclass
class PoolConfig(MinerConfigValue):
groups: list[PoolGroup] = field(default_factory=list)
groups: List[PoolGroup] = field(default_factory=list)
@classmethod
def default(cls) -> "PoolConfig":
@@ -279,7 +292,7 @@ class PoolConfig(MinerConfigValue):
return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]])
@classmethod
def simple(cls, pools: list[Union[Pool, dict[str, str]]]) -> "PoolConfig":
def simple(cls, pools: List[Union[Pool, Dict[str, str]]]) -> "PoolConfig":
group_pools = []
for pool in pools:
if isinstance(pool, dict):
@@ -329,11 +342,19 @@ class PoolConfig(MinerConfigValue):
@classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig":
pool_data = api_pools["POOLS"]
try:
pool_data = api_pools["POOLS"]
except KeyError:
return PoolConfig.default()
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
return cls([PoolGroup.from_api(pool_data)])
@classmethod
def from_epic(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["StratumConfigs"]
return cls([PoolGroup.from_epic(pool_data)])
@classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"]

View File

@@ -56,3 +56,18 @@ class TemperatureConfig(MinerConfigValue):
hot=temp_control.get("hot_temp"),
danger=temp_control.get("dangerous_temp"),
)
@classmethod
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
dangerous_temp = None
try:
hot_temp = web_conf["Misc"]["Shutdown Temp"]
except KeyError:
hot_temp = None
# Need to do this in two blocks to avoid KeyError if one is missing
try:
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
except KeyError:
target_temp = None
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp)

View File

@@ -48,6 +48,7 @@ class HashBoard:
chip_temp: int = None
chips: int = None
expected_chips: int = None
serial_number: str = None
missing: bool = True
def get(self, __key: str, default: Any = None):

View File

@@ -27,6 +27,7 @@ from pyasic.miners.types import (
S19jPro,
S19Plus,
S19Pro,
S19ProHydro,
S19ProPlus,
)
@@ -77,3 +78,7 @@ class BMMinerS19jPro(AntminerModern, S19jPro):
class BMMinerS19L(AntminerModern, S19L):
pass
class BMMinerS19ProHydro(AntminerModern, S19ProHydro):
pass

View File

@@ -25,6 +25,7 @@ from .S19 import (
BMMinerS19L,
BMMinerS19Plus,
BMMinerS19Pro,
BMMinerS19ProHydro,
BMMinerS19ProPlus,
BMMinerS19XP,
)

View File

@@ -14,7 +14,6 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
import asyncio
from typing import List, Optional, Union
from pyasic.API import APIError
@@ -23,50 +22,60 @@ from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.backends.cgminer import CGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
ANTMINER_MODERN_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {
"cmd": "is_mining",
"kwargs": {"web_get_conf": {"web": "get_miner_conf"}},
},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_stats": {"api": "stats"}},
},
"config": {
"cmd": "get_config",
"kwargs": {},
},
}
ANTMINER_MODERN_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_get_system_info", "get_system_info")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_get_system_info", "get_system_info")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction("get_hashboards", []),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"get_errors", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [WebAPICommand("web_get_conf", "get_miner_conf")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class AntminerModern(BMMiner):
@@ -185,6 +194,42 @@ class AntminerModern(BMMiner):
pass
return errors
async def get_hashboards(self) -> List[HashBoard]:
hashboards = [
HashBoard(idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
try:
api_stats = await self.api.send_command("stats", new_api=True)
except APIError:
return hashboards
if api_stats:
try:
for board in api_stats["STATS"][0]["chain"]:
hashboards[board["index"]].hashrate = round(
board["rate_real"] / 1000, 2
)
hashboards[board["index"]].chips = board["asic_num"]
board_temp_data = list(
filter(lambda x: not x == 0, board["temp_pcb"])
)
hashboards[board["index"]].temp = sum(board_temp_data) / len(
board_temp_data
)
chip_temp_data = list(
filter(lambda x: not x == 0, board["temp_chip"])
)
hashboards[board["index"]].chip_temp = sum(chip_temp_data) / len(
chip_temp_data
)
hashboards[board["index"]].serial_number = board["sn"]
hashboards[board["index"]].missing = False
except LookupError:
pass
return hashboards
async def get_fault_light(self, web_get_blink_status: dict = None) -> bool:
if self.light:
return self.light
@@ -298,39 +343,49 @@ class AntminerModern(BMMiner):
pass
ANTMINER_OLD_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {
"cmd": "is_mining",
"kwargs": {"web_get_conf": {"web": "get_miner_conf"}},
},
"uptime": {"cmd": "get_uptime", "kwargs": {"api_stats": {"api": "stats"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
ANTMINER_OLD_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_get_system_info", "get_system_info")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [WebAPICommand("web_get_conf", "get_miner_conf")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class AntminerOld(CGMiner):

View File

@@ -22,32 +22,48 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BFGMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
"config": {"cmd": "get_config", "kwargs": {}},
}
BFGMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BFGMiner(BaseMiner):
@@ -268,32 +284,6 @@ class BFGMiner(BaseMiner):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []

View File

@@ -20,41 +20,55 @@ from pyasic.data import HashBoard
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends import BFGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.goldshell import GoldshellWebAPI
GOLDSHELL_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"web_setting": {"web": "setting"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_status": {"web": "status"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {
"api_devs": {"api": "devs"},
"api_devdetails": {"api": "devdetails"},
},
},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
"config": {
"cmd": "get_config",
"kwargs": {},
},
}
GOLDSHELL_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_setting", "setting")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [WebAPICommand("web_status", "status")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards",
[
RPCAPICommand("api_devs", "devs"),
RPCAPICommand("api_devdetails", "devdetails"),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BFGMinerGoldshell(BFGMiner):

View File

@@ -23,32 +23,50 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BMMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {"api_stats": {"api": "stats"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
BMMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BMMiner(BaseMiner):
@@ -315,32 +333,6 @@ class BMMiner(BaseMiner):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []

View File

@@ -27,157 +27,157 @@ from pyasic.config.mining import MiningModePowerTune
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
GraphQLCommand,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.bosminer import BOSMinerWebAPI
BOSMINER_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {
"web_net_conf": {"web": "/cgi-bin/luci/admin/network/iface_status/lan"}
},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {
"cmd": "get_fw_ver",
"kwargs": {
"graphql_version": {"web": {"bos": {"info": {"version": {"full": None}}}}}
},
},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"graphql_hostname": {"web": {"bos": {"hostname": None}}}},
},
"hashrate": {
"cmd": "get_hashrate",
"kwargs": {
"api_summary": {"api": "summary"},
"graphql_hashrate": {
"web": {
"bosminer": {
"info": {"workSolver": {"realHashrate": {"mhs1M": None}}}
}
},
},
},
},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_devs": {"api": "devs"}},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {
"api_temps": {"api": "temps"},
"api_devdetails": {"api": "devdetails"},
"api_devs": {"api": "devs"},
"graphql_boards": {
"web": {
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"realHashrate": {"mhs1M": None},
"hwDetails": {"chips": None},
"temperatures": {"degreesC": None},
BOSMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac",
[
WebAPICommand(
"web_net_conf", "/cgi-bin/luci/admin/network/iface_status/lan"
)
],
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver",
[
GraphQLCommand(
"graphql_version", {"bos": {"info": {"version": {"full": None}}}}
)
],
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname",
[GraphQLCommand("graphql_hostname", {"bos": {"hostname": None}})],
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate",
[
RPCAPICommand("api_summary", "summary"),
GraphQLCommand(
"graphql_hashrate",
{
"bosminer": {
"info": {"workSolver": {"realHashrate": {"mhs1M": None}}}
}
},
),
],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_devs", "devs")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards",
[
RPCAPICommand("api_temps", "temps"),
RPCAPICommand("api_devdetails", "devdetails"),
RPCAPICommand("api_devs", "devs"),
GraphQLCommand(
"graphql_boards",
{
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"realHashrate": {"mhs1M": None},
"hwDetails": {"chips": None},
"temperatures": {"degreesC": None},
}
}
}
}
}
},
},
},
},
"wattage": {
"cmd": "get_wattage",
"kwargs": {
"api_tunerstatus": {"api": "tunerstatus"},
"graphql_wattage": {
"web": {
"bosminer": {
"info": {"workSolver": {"power": {"approxConsumptionW": None}}}
}
}
},
},
},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {
"api_tunerstatus": {"api": "tunerstatus"},
"graphql_wattage_limit": {
"web": {
"bosminer": {"info": {"workSolver": {"power": {"limitW": None}}}}
}
},
},
},
"fans": {
"cmd": "get_fans",
"kwargs": {
"api_fans": {"api": "fans"},
"graphql_fans": {
"web": {"bosminer": {"info": {"fans": {"name": None, "rpm": None}}}}
},
},
},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"errors": {
"cmd": "get_errors",
"kwargs": {
"api_tunerstatus": {"api": "tunerstatus"},
"graphql_errors": {
"web": {
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"tuner": {"statusMessages": None},
},
),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage",
[
RPCAPICommand("api_tunerstatus", "tunerstatus"),
GraphQLCommand(
"graphql_wattage",
{
"bosminer": {
"info": {
"workSolver": {"power": {"approxConsumptionW": None}}
}
}
},
),
],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit",
[
RPCAPICommand("api_tunerstatus", "tunerstatus"),
GraphQLCommand(
"graphql_wattage_limit",
{"bosminer": {"info": {"workSolver": {"power": {"limitW": None}}}}},
),
],
),
str(DataOptions.FANS): DataFunction(
"get_fans",
[
RPCAPICommand("api_fans", "fans"),
GraphQLCommand(
"graphql_fans",
{"bosminer": {"info": {"fans": {"name": None, "rpm": None}}}},
),
],
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"get_errors",
[
RPCAPICommand("api_tunerstatus", "tunerstatus"),
GraphQLCommand(
"graphql_errors",
{
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"tuner": {"statusMessages": None},
}
}
}
}
}
}
},
},
},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"graphql_fault_light": {"web": {"bos": {"faultLight": None}}}},
},
"pools": {
"cmd": "get_pools",
"kwargs": {
"api_pools": {"api": "pools"},
"graphql_pools": {
"web": {
"bosminer": {
"config": {
"... on BosminerConfig": {
"groups": {
"pools": {"url": None, "user": None},
"strategy": {
"... on QuotaStrategy": {"quota": None}
},
}
}
}
}
}
},
},
},
"is_mining": {
"cmd": "is_mining",
"kwargs": {"api_devdetails": {"api": "devdetails"}},
},
"uptime": {"cmd": "get_uptime", "kwargs": {"api_summary": {"api": "summary"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
},
),
],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[GraphQLCommand("graphql_fault_light", {"bos": {"faultLight": None}})],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [RPCAPICommand("api_devdetails", "devdetails")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BOSMiner(BaseMiner):
@@ -231,7 +231,6 @@ class BOSMiner(BaseMiner):
return result
async def fault_light_on(self) -> bool:
"""Sends command to turn on fault light on the miner."""
logging.debug(f"{self}: Sending fault_light on command.")
ret = await self.send_ssh_command("miner fault_light on")
logging.debug(f"{self}: fault_light on command completed.")
@@ -241,7 +240,6 @@ class BOSMiner(BaseMiner):
return False
async def fault_light_off(self) -> bool:
"""Sends command to turn off fault light on the miner."""
logging.debug(f"{self}: Sending fault_light off command.")
self.light = False
ret = await self.send_ssh_command("miner fault_light off")
@@ -252,11 +250,9 @@ class BOSMiner(BaseMiner):
return False
async def restart_backend(self) -> bool:
"""Restart bosminer hashing process. Wraps [`restart_bosminer`][pyasic.miners.backends.bosminer.BOSMiner.restart_bosminer] to standardize."""
return await self.restart_bosminer()
async def restart_bosminer(self) -> bool:
"""Restart bosminer hashing process."""
logging.debug(f"{self}: Sending bosminer restart command.")
ret = await self.send_ssh_command("/etc/init.d/bosminer restart")
logging.debug(f"{self}: bosminer restart command completed.")
@@ -285,7 +281,6 @@ class BOSMiner(BaseMiner):
return False
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.")
ret = await self.send_ssh_command("/sbin/reboot")
logging.debug(f"{self}: Reboot command completed.")
@@ -537,7 +532,7 @@ class BOSMiner(BaseMiner):
pass
try:
async with (await self._get_ssh_connection()) as conn:
async with await self._get_ssh_connection() as conn:
if conn is not None:
data = await conn.run("cat /proc/sys/kernel/hostname")
host = data.stdout.strip()
@@ -833,95 +828,6 @@ class BOSMiner(BaseMiner):
async def get_fan_psu(self) -> Optional[int]:
return None
async def get_pools(
self, api_pools: dict = None, graphql_pools: dict = None
) -> List[dict]:
if not graphql_pools and not api_pools:
try:
graphql_pools = await self.web.send_command(
{
"bosminer": {
"config": {
"... on BosminerConfig": {
"groups": {
"pools": {"urluser"},
"strategy": {"... on QuotaStrategy": {"quota"}},
}
}
}
}
}
)
except APIError:
pass
if graphql_pools:
groups = []
try:
g = graphql_pools["data"]["bosminer"]["config"]["groups"]
for group in g:
pools = {"quota": group["strategy"]["quota"]}
for i, pool in enumerate(group["pools"]):
pools[f"pool_{i + 1}_url"] = (
pool["url"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["user"]
groups.append(pools)
return groups
except (KeyError, TypeError):
pass
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
seen = []
groups = [{"quota": "0"}]
if api_pools.get("POOLS"):
for i, pool in enumerate(api_pools["POOLS"]):
if len(seen) == 0:
seen.append(pool["User"])
if not pool["User"] in seen:
# need to use get_config, as this will never read perfectly as there are some bad edge cases
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
else:
groups[0][f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
groups[0][f"pool_{i + 1}_user"] = pool["User"]
else:
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
return groups
async def get_errors(
self, api_tunerstatus: dict = None, graphql_errors: dict = None
) -> List[MinerErrorData]:

View File

@@ -136,9 +136,6 @@ class BOSMinerOld(BOSMiner):
async def get_fw_ver(self, *args, **kwargs) -> Optional[str]:
return None
async def get_pools(self, *args, **kwargs) -> List[dict]:
return []
async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
return []

View File

@@ -23,74 +23,86 @@ from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
BTMINER_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_miner_info": {"api": "get_miner_info"},
},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {
"cmd": "get_api_ver",
"kwargs": {"api_get_version": {"api": "get_version"}},
},
"fw_ver": {
"cmd": "get_fw_ver",
"kwargs": {
"api_get_version": {"api": "get_version"},
"api_summary": {"api": "summary"},
},
},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"api_get_miner_info": {"api": "get_miner_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_summary": {"api": "summary"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_devs": {"api": "devs"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {"api_summary": {"api": "summary"}}},
"wattage": {"cmd": "get_wattage", "kwargs": {"api_summary": {"api": "summary"}}},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {"api_summary": {"api": "summary"}},
},
"fans": {
"cmd": "get_fans",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_psu": {"api": "get_psu"},
},
},
"fan_psu": {
"cmd": "get_fan_psu",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_psu": {"api": "get_psu"},
},
},
"errors": {
"cmd": "get_errors",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_error_code": {"api": "get_error_code"},
},
},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"api_get_miner_info": {"api": "get_miner_info"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {"api_status": {"api": "status"}}},
"uptime": {"cmd": "get_uptime", "kwargs": {"api_summary": {"api": "summary"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
BTMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac",
[
RPCAPICommand("api_summary", "summary"),
RPCAPICommand("api_get_miner_info", "get_miner_info"),
],
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_get_version", "get_version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver",
[
RPCAPICommand("api_get_version", "get_version"),
RPCAPICommand("api_summary", "summary"),
],
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [RPCAPICommand("api_get_miner_info", "get_miner_info")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_devs", "devs")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"get_env_temp", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.FANS): DataFunction(
"get_fans",
[
RPCAPICommand("api_summary", "summary"),
RPCAPICommand("api_get_psu", "get_psu"),
],
),
str(DataOptions.FAN_PSU): DataFunction(
"get_fan_psu",
[
RPCAPICommand("api_summary", "summary"),
RPCAPICommand("api_get_psu", "get_psu"),
],
),
str(DataOptions.ERRORS): DataFunction(
"get_errors", [RPCAPICommand("api_get_error_code", "get_error_code")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[RPCAPICommand("api_get_miner_info", "get_miner_info")],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [RPCAPICommand("api_status", "status")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BTMiner(BaseMiner):
@@ -434,6 +446,7 @@ class BTMiner(BaseMiner):
float(board["MHS 1m"] / 1000000), 2
)
hashboards[board["ASC"]].chips = board["Effective Chips"]
hashboards[board["ASC"]].serial_number = board["PCB SN"]
hashboards[board["ASC"]].missing = False
except (KeyError, IndexError):
pass
@@ -529,32 +542,6 @@ class BTMiner(BaseMiner):
except (KeyError, TypeError):
pass
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(
self, api_summary: dict = None, api_get_error_code: dict = None
) -> List[MinerErrorData]:
@@ -600,8 +587,8 @@ class BTMiner(BaseMiner):
if api_summary:
try:
expected_hashrate = api_summary["SUMMARY"][0]["Factory GHS"]
if nominal_hashrate:
return round(nominal_hashrate / 1000, 2)
if expected_hashrate:
return round(expected_hashrate / 1000, 2)
except (KeyError, IndexError):
pass

View File

@@ -23,32 +23,50 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
CGMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {"api_stats": {"api": "stats"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
CGMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class CGMiner(BaseMiner):
@@ -94,11 +112,9 @@ class CGMiner(BaseMiner):
return result
async def restart_backend(self) -> bool:
"""Restart cgminer hashing process. Wraps [`restart_cgminer`][pyasic.miners.backends.cgminer.CGMiner.restart_cgminer] to standardize."""
return await self.restart_cgminer()
async def restart_cgminer(self) -> bool:
"""Restart cgminer hashing process."""
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
commands = ";".join(commands)
ret = await self.send_ssh_command(commands)
@@ -107,7 +123,6 @@ class CGMiner(BaseMiner):
return True
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.")
ret = await self.send_ssh_command("reboot")
if ret is None:
@@ -328,32 +343,6 @@ class CGMiner(BaseMiner):
async def get_fan_psu(self) -> Optional[int]:
return None
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []

View File

@@ -23,37 +23,52 @@ from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.base import DataFunction, DataLocations, DataOptions, RPCAPICommand
AVALON_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"api_version": {"api": "version"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {"mac": {"api": "version"}}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_devs": {"api": "devs"}}},
"expected_hashrate": {
"cmd": "get_expected_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {"api_stats": {"api": "stats"}}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {"api_stats": {"api": "stats"}},
},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"api_stats": {"api": "stats"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
"config": {"cmd": "get_config", "kwargs": {}},
}
AVALON_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_devs", "devs")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"get_env_temp", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class CGMinerAvalon(CGMiner):
@@ -116,7 +131,7 @@ class CGMinerAvalon(CGMiner):
@staticmethod
def parse_stats(stats):
_stats_items = re.findall(".+?\[*?]", stats)
_stats_items = re.findall(".+?\\[*?]", stats)
stats_items = []
stats_dict = {}
for item in _stats_items:
@@ -318,32 +333,6 @@ class CGMinerAvalon(CGMiner):
pass
return fans_data
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []

View File

@@ -14,51 +14,74 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import List, Optional, Tuple, Union
from typing import List, Optional, Tuple
from pyasic import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
WebAPICommand,
)
from pyasic.web.epic import ePICWebAPI
EPIC_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "network"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"web_summary": {"web": "summary"}}},
"expected_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"web_summary": {"web": "summary"}},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {
"web_summary": {"web": "summary"},
"web_hashrate": {"web": "hashrate"},
},
},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"web_summary": {"web": "summary"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_summary": {"web": "summary"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"web_summary": {"web": "summary"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {"web_summary": {"web": "summary"}}},
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
EPIC_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_network", "network")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction("get_api_ver"),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards",
[
WebAPICommand("web_summary", "summary"),
WebAPICommand("web_hashrate", "hashrate"),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"get_errors", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class ePIC(BMMiner):
class ePIC(BaseMiner):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver)
# interfaces
@@ -74,6 +97,23 @@ class ePIC(BMMiner):
return self.model + " (ePIC)"
return "? (ePIC)"
async def get_config(self) -> MinerConfig:
summary = None
try:
summary = await self.web.summary()
except APIError as e:
logger.warning(e)
except LookupError:
pass
if summary is not None:
cfg = MinerConfig.from_epic(summary)
else:
cfg = MinerConfig()
self.config = cfg
return self.config
async def restart_backend(self) -> bool:
data = await self.web.restart_epic()
if data:
@@ -110,13 +150,13 @@ class ePIC(BMMiner):
pass
return False
async def get_mac(self, web_summary: dict = None) -> str:
if not web_summary:
web_summary = await self.web.network()
if web_summary:
async def get_mac(self, web_network: dict = None) -> str:
if not web_network:
web_network = await self.web.network()
if web_network:
try:
for network in web_summary:
mac = web_summary[network]["mac_address"]
for network in web_network:
mac = web_network[network]["mac_address"]
return mac
except KeyError:
pass
@@ -249,32 +289,6 @@ class ePIC(BMMiner):
async def is_mining(self, *args, **kwargs) -> Optional[bool]:
return None
async def get_pools(self, web_summary: dict = None) -> List[dict]:
groups = []
if not web_summary:
try:
web_summary = await self.api.summary()
except APIError:
pass
if web_summary:
try:
pools = {}
for i, pool in enumerate(web_summary["StratumConfigs"]):
pools[f"pool_{i + 1}_url"] = (
pool["pool"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["login"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_uptime(self, web_summary: dict = None) -> Optional[int]:
if not web_summary:
web_summary = await self.web.summary()
@@ -310,3 +324,33 @@ class ePIC(BMMiner):
except KeyError:
pass
return errors
def fault_light_off(self) -> bool:
return False
def fault_light_on(self) -> bool:
return False
def get_api_ver(self, *args, **kwargs) -> Optional[str]:
pass
def get_config(self) -> MinerConfig:
return self.config
def get_env_temp(self, *args, **kwargs) -> Optional[float]:
pass
def get_fan_psu(self, *args, **kwargs) -> Optional[int]:
pass
def get_version(self, *args, **kwargs) -> Tuple[Optional[str], Optional[str]]:
pass
def get_wattage_limit(self, *args, **kwargs) -> Optional[int]:
pass
def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
pass
def set_power_limit(self, wattage: int) -> bool:
return False

View File

@@ -26,30 +26,52 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.bosminer import BOSMinerWebAPI
LUXMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"api_config": {"api": "config"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {}},
"expected_hashrate": {"cmd": "get_nominal_hashrate", "kwargs": {}},
"hashboards": {"cmd": "get_hashboards", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {"api_stats": {"api": "stats"}}},
"config": {"cmd": "get_config", "kwargs": {}},
}
LUXMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [RPCAPICommand("api_config", "config")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction("get_api_ver"),
str(DataOptions.FW_VERSION): DataFunction("get_fw_ver"),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [RPCAPICommand("api_power", "power")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_fans", "fans")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class LUXMiner(BaseMiner):
@@ -85,7 +107,6 @@ class LUXMiner(BaseMiner):
return
async def fault_light_on(self) -> bool:
"""Sends command to turn on fault light on the miner."""
try:
session_id = await self._get_session()
if session_id:
@@ -96,7 +117,6 @@ class LUXMiner(BaseMiner):
return False
async def fault_light_off(self) -> bool:
"""Sends command to turn off fault light on the miner."""
try:
session_id = await self._get_session()
if session_id:
@@ -107,11 +127,9 @@ class LUXMiner(BaseMiner):
return False
async def restart_backend(self) -> bool:
"""Restart luxminer hashing process. Wraps [`restart_luxminer`][pyasic.miners.backends.luxminer.LUXMiner.restart_luxminer] to standardize."""
return await self.restart_luxminer()
async def restart_luxminer(self) -> bool:
"""Restart luxminer hashing process."""
try:
session_id = await self._get_session()
if session_id:
@@ -141,7 +159,6 @@ class LUXMiner(BaseMiner):
pass
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
try:
session_id = await self._get_session()
if session_id:
@@ -303,56 +320,6 @@ class LUXMiner(BaseMiner):
async def get_fan_psu(self) -> Optional[int]:
return None
async def get_pools(self, api_pools: dict = None) -> List[dict]:
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
seen = []
groups = [{"quota": "0"}]
if api_pools.get("POOLS"):
for i, pool in enumerate(api_pools["POOLS"]):
if len(seen) == 0:
seen.append(pool["User"])
if not pool["User"] in seen:
# need to use get_config, as this will never read perfectly as there are some bad edge cases
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
else:
groups[0][f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
groups[0][f"pool_{i + 1}_user"] = pool["User"]
else:
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
return groups
async def get_errors(self) -> List[MinerErrorData]:
pass

View File

@@ -19,35 +19,57 @@ from typing import Optional
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.vnish import VNishWebAPI
VNISH_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "summary"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"expected_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {"web_settings": {"web": "settings"}},
},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
"config": {"cmd": "get_config", "kwargs": {}},
}
VNISH_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit", [WebAPICommand("web_settings", "settings")]
),
str(DataOptions.FANS): DataFunction(
"get_fans", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class VNish(BMMiner):

View File

@@ -17,7 +17,9 @@ import asyncio
import ipaddress
import logging
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, TypeVar
from dataclasses import dataclass, field, make_dataclass
from enum import Enum
from typing import List, Optional, Tuple, TypeVar, Union
import asyncssh
@@ -27,6 +29,70 @@ from pyasic.data.error_codes import MinerErrorData
from pyasic.logger import logger
class DataOptions(Enum):
MAC = "mac"
MODEL = "model"
API_VERSION = "api_ver"
FW_VERSION = "fw_ver"
HOSTNAME = "hostname"
HASHRATE = "hashrate"
EXPECTED_HASHRATE = "expected_hashrate"
HASHBOARDS = "hashboards"
ENVIRONMENT_TEMP = "env_temp"
WATTAGE = "wattage"
WATTAGE_LIMIT = "wattage_limit"
FANS = "fans"
FAN_PSU = "fan_psu"
ERRORS = "errors"
FAULT_LIGHT = "fault_light"
IS_MINING = "is_mining"
UPTIME = "uptime"
CONFIG = "config"
def __str__(self):
return self.value
@dataclass
class RPCAPICommand:
name: str
cmd: str
@dataclass
class WebAPICommand:
name: str
cmd: str
@dataclass
class GRPCCommand(WebAPICommand):
name: str
cmd: str
@dataclass
class GraphQLCommand(WebAPICommand):
name: str
cmd: dict
@dataclass
class DataFunction:
cmd: str
kwargs: list[
Union[RPCAPICommand, WebAPICommand, GRPCCommand, GraphQLCommand]
] = field(default_factory=list)
DataLocations = make_dataclass(
"DataLocations",
[(enum_value.value, str) for enum_value in DataOptions],
)
# add default value with
# [(enum_value.value, str, , DataFunction(enum_value.value)) for enum_value in DataOptions],
class BaseMiner(ABC):
def __init__(self, ip: str, *args, **kwargs) -> None:
# interfaces
@@ -46,7 +112,7 @@ class BaseMiner(ABC):
self.expected_chips = 0
self.fan_count = 2
# data gathering locations
self.data_locations = None
self.data_locations: DataLocations = None
# autotuning/shutdown support
self.supports_autotuning = False
self.supports_shutdown = False
@@ -359,15 +425,6 @@ class BaseMiner(ABC):
"""
pass
@abstractmethod
async def get_pools(self, *args, **kwargs) -> List[dict]:
"""Get pool information from the miner.
Returns:
Pool groups and quotas in a list of dicts.
"""
pass
@abstractmethod
async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
"""Get a list of the errors the miner is experiencing.
@@ -414,28 +471,33 @@ class BaseMiner(ABC):
pass
async def _get_data(
self, allow_warning: bool, include: list = None, exclude: list = None
self,
allow_warning: bool,
include: List[Union[str, DataOptions]] = None,
exclude: List[Union[str, DataOptions]] = None,
) -> dict:
if include is None:
if include is not None:
include = [str(i) for i in include]
else:
# everything
include = list(self.data_locations.keys())
include = [str(enum_value.value) for enum_value in DataOptions]
if exclude is not None:
for item in exclude:
if item in include:
include.remove(item)
if str(item) in include:
include.remove(str(item))
api_multicommand = set()
web_multicommand = []
for data_name in include:
try:
fn_args = self.data_locations[data_name]["kwargs"]
for arg_name in fn_args:
if fn_args[arg_name].get("api"):
api_multicommand.add(fn_args[arg_name]["api"])
if fn_args[arg_name].get("web"):
if not fn_args[arg_name]["web"] in web_multicommand:
web_multicommand.append(fn_args[arg_name]["web"])
fn_args = getattr(self.data_locations, data_name).kwargs
for arg in fn_args:
if isinstance(arg, RPCAPICommand):
api_multicommand.add(arg.cmd)
if isinstance(arg, WebAPICommand):
if arg.cmd not in web_multicommand:
web_multicommand.append(arg.cmd)
except KeyError as e:
logger.error(e, data_name)
continue
@@ -465,37 +527,36 @@ class BaseMiner(ABC):
for data_name in include:
try:
fn_args = self.data_locations[data_name]["kwargs"]
args_to_send = {k: None for k in fn_args}
for arg_name in fn_args:
fn_args = getattr(self.data_locations, data_name).kwargs
args_to_send = {k.name: None for k in fn_args}
for arg in fn_args:
try:
if fn_args[arg_name].get("api"):
if isinstance(arg, RPCAPICommand):
if api_command_data.get("multicommand"):
args_to_send[arg_name] = api_command_data[
fn_args[arg_name]["api"]
][0]
args_to_send[arg.name] = api_command_data[arg.cmd][0]
else:
args_to_send[arg_name] = api_command_data
if fn_args[arg_name].get("web"):
args_to_send[arg.name] = api_command_data
if isinstance(arg, WebAPICommand):
if web_command_data is not None:
if web_command_data.get("multicommand"):
args_to_send[arg_name] = web_command_data[
fn_args[arg_name]["web"]
]
args_to_send[arg.name] = web_command_data[arg.cmd]
else:
if not web_command_data == {"multicommand": False}:
args_to_send[arg_name] = web_command_data
args_to_send[arg.name] = web_command_data
except LookupError:
args_to_send[arg_name] = None
args_to_send[arg.name] = None
except LookupError:
continue
function = getattr(self, self.data_locations[data_name]["cmd"])
function = getattr(self, getattr(self.data_locations, data_name).cmd)
miner_data[data_name] = await function(**args_to_send)
return miner_data
async def get_data(
self, allow_warning: bool = False, include: list = None, exclude: list = None
self,
allow_warning: bool = False,
include: List[Union[str, DataOptions]] = None,
exclude: List[Union[str, DataOptions]] = None,
) -> MinerData:
"""Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].

View File

@@ -267,32 +267,6 @@ class CGMinerA10X(CGMiner, A10X):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(
self, web_get_error_detail: dict = None
) -> List[MinerErrorData]: # noqa: named this way for automatic functionality

View File

@@ -246,32 +246,6 @@ class CGMinerT3HPlus(CGMiner, T3HPlus):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(
self, web_get_error_detail: dict = None
) -> List[MinerErrorData]: # noqa: named this way for automatic functionality

View File

@@ -92,6 +92,7 @@ MINER_CLASSES = {
"ANTMINER S19 XP": BMMinerS19XP,
"ANTMINER S19A": BMMinerS19a,
"ANTMINER S19A PRO": BMMinerS19aPro,
"ANTMINER S19 PRO HYD.": BMMinerS19ProHydro,
"ANTMINER T19": BMMinerT19,
},
MinerTypes.WHATSMINER: {

View File

@@ -31,7 +31,7 @@ class S19NoPIC(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 No PIC"
self.nominal_chips = 88
self.expected_chips = 88
self.fan_count = 4
@@ -49,7 +49,7 @@ class S19i(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19i"
self.nominal_chips = 80
self.expected_chips = 80
self.fan_count = 4
@@ -58,7 +58,7 @@ class S19Plus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19+"
self.nominal_chips = 80
self.expected_chips = 80
self.fan_count = 4
@@ -76,7 +76,7 @@ class S19XP(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 XP"
self.nominal_chips = 110
self.expected_chips = 110
self.fan_count = 4
@@ -94,7 +94,7 @@ class S19aPro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19a Pro"
self.nominal_chips = 100
self.expected_chips = 100
self.fan_count = 4
@@ -103,7 +103,7 @@ class S19j(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 4
@@ -112,7 +112,7 @@ class S19jNoPIC(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j No PIC"
self.nominal_chips = 88
self.expected_chips = 88
self.fan_count = 4
@@ -121,7 +121,7 @@ class S19jPro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j Pro"
self.nominal_chips = 126
self.expected_chips = 126
self.fan_count = 4
@@ -130,7 +130,7 @@ class S19jProPlus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j Pro+"
self.nominal_chips = 120
self.expected_chips = 120
self.fan_count = 4
@@ -139,7 +139,7 @@ class S19kPro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19k Pro"
self.nominal_chips = 77
self.expected_chips = 77
self.fan_count = 4
@@ -157,5 +157,15 @@ class S19kProNoPIC(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19k Pro No PIC"
self.nominal_chips = 77
self.expected_chips = 77
self.fan_count = 4
class S19ProHydro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 Pro Hydro"
self.expected_chips = 180
self.expected_hashboards = 4
self.fan_count = 0

View File

@@ -30,6 +30,7 @@ from .S19 import (
S19NoPIC,
S19Plus,
S19Pro,
S19ProHydro,
S19ProPlus,
)
from .T19 import T19

View File

@@ -40,5 +40,5 @@ class S9j(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S9j"
self.nominal_chips = 63
self.expected_chips = 63
self.fan_count = 2

View File

@@ -33,7 +33,7 @@ class M20SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20S V20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2

View File

@@ -33,5 +33,5 @@ class M30V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30 V20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2

View File

@@ -33,7 +33,7 @@ class M30SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V20"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -42,7 +42,7 @@ class M30SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V30"
self.nominal_chips = 164
self.expected_chips = 164
self.fan_count = 2
@@ -60,7 +60,7 @@ class M30SV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V50"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -69,7 +69,7 @@ class M30SV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V60"
self.nominal_chips = 164
self.expected_chips = 164
self.fan_count = 2
@@ -78,7 +78,7 @@ class M30SV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -90,7 +90,7 @@ class M30SV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V80"
self.nominal_chips = 129
self.expected_chips = 129
self.fan_count = 2
@@ -108,7 +108,7 @@ class M30SVE20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -117,7 +117,7 @@ class M30SVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -126,7 +126,7 @@ class M30SVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE40"
self.nominal_chips = 123
self.expected_chips = 123
self.fan_count = 2
@@ -135,7 +135,7 @@ class M30SVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE50"
self.nominal_chips = 129
self.expected_chips = 129
self.fan_count = 2
@@ -144,7 +144,7 @@ class M30SVE60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE60"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -156,7 +156,7 @@ class M30SVE70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -168,7 +168,7 @@ class M30SVF10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF10"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -186,7 +186,7 @@ class M30SVF30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF30"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -195,7 +195,7 @@ class M30SVG10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG10"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -213,7 +213,7 @@ class M30SVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG30"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -222,7 +222,7 @@ class M30SVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG40"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -240,7 +240,7 @@ class M30SVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -249,7 +249,7 @@ class M30SVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -261,7 +261,7 @@ class M30SVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH40"
self.nominal_chips = 64
self.expected_chips = 64
self.fan_count = 2
@@ -270,7 +270,7 @@ class M30SVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH50"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -291,5 +291,5 @@ class M30SVI20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VI20"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2

View File

@@ -42,7 +42,7 @@ class M30SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -54,7 +54,7 @@ class M30SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V40"
self.nominal_chips = 235
self.expected_chips = 235
self.fan_count = 2
@@ -63,7 +63,7 @@ class M30SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V50"
self.nominal_chips = 225
self.expected_chips = 225
self.fan_count = 2
@@ -72,7 +72,7 @@ class M30SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V60"
self.nominal_chips = 245
self.expected_chips = 245
self.fan_count = 2
@@ -81,7 +81,7 @@ class M30SPlusV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V70"
self.nominal_chips = 235
self.expected_chips = 235
self.fan_count = 2
@@ -99,7 +99,7 @@ class M30SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V90"
self.nominal_chips = 225
self.expected_chips = 225
self.fan_count = 2
@@ -108,7 +108,7 @@ class M30SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V100"
self.nominal_chips = 215
self.expected_chips = 215
self.fan_count = 2
@@ -126,7 +126,7 @@ class M30SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE40"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -135,7 +135,7 @@ class M30SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE50"
self.nominal_chips = 164
self.expected_chips = 164
self.fan_count = 2
@@ -153,7 +153,7 @@ class M30SPlusVE70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -165,7 +165,7 @@ class M30SPlusVE80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE80"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -177,7 +177,7 @@ class M30SPlusVE90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE90"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -189,7 +189,7 @@ class M30SPlusVE100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE100"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -201,7 +201,7 @@ class M30SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VF20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -219,7 +219,7 @@ class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG20"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -228,7 +228,7 @@ class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG30"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -246,7 +246,7 @@ class M30SPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG50"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -255,7 +255,7 @@ class M30SPlusVG60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG60"
self.nominal_chips = 86
self.expected_chips = 86
self.fan_count = 2
@@ -273,7 +273,7 @@ class M30SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -282,7 +282,7 @@ class M30SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH30"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -291,7 +291,7 @@ class M30SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH40"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -300,7 +300,7 @@ class M30SPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH50"
self.nominal_chips = 64
self.expected_chips = 64
self.fan_count = 2

View File

@@ -44,7 +44,7 @@ class M30SPlusPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE30"
self.nominal_chips = 215
self.expected_chips = 215
self.fan_count = 2
@@ -53,7 +53,7 @@ class M30SPlusPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE40"
self.nominal_chips = 225
self.expected_chips = 225
self.fan_count = 2
@@ -71,7 +71,7 @@ class M30SPlusPlusVF40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VF40"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -80,7 +80,7 @@ class M30SPlusPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VG30"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -89,7 +89,7 @@ class M30SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VG40"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -98,7 +98,7 @@ class M30SPlusPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VG50"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VG50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -110,7 +110,7 @@ class M30SPlusPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH10"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -128,7 +128,7 @@ class M30SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH30"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -137,7 +137,7 @@ class M30SPlusPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH40"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -146,7 +146,7 @@ class M30SPlusPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH50"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -155,7 +155,7 @@ class M30SPlusPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH60"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -173,7 +173,7 @@ class M30SPlusPlusVH80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH80"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -182,7 +182,7 @@ class M30SPlusPlusVH90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH90"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -200,7 +200,7 @@ class M30SPlusPlusVJ20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VJ20"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -212,7 +212,7 @@ class M30SPlusPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VJ30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -33,5 +33,5 @@ class M31V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31 V20"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2

View File

@@ -51,7 +51,7 @@ class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V40"
self.nominal_chips = 123
self.expected_chips = 123
self.fan_count = 2
@@ -60,7 +60,7 @@ class M31SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V50"
self.nominal_chips = 148
self.expected_chips = 148
self.fan_count = 2
@@ -69,7 +69,7 @@ class M31SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V60"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -78,7 +78,7 @@ class M31SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V80"
self.nominal_chips = 129
self.expected_chips = 129
self.fan_count = 2
@@ -87,7 +87,7 @@ class M31SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V90"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -96,7 +96,7 @@ class M31SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V100"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -105,7 +105,7 @@ class M31SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE10"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -114,7 +114,7 @@ class M31SPlusVE20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE20"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2

View File

@@ -61,7 +61,7 @@ class M50VH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -70,7 +70,7 @@ class M50VH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH40"
self.nominal_chips = 84
self.expected_chips = 84
self.fan_count = 2
@@ -79,7 +79,7 @@ class M50VH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH50"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -88,7 +88,7 @@ class M50VH60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH60"
self.nominal_chips = 84
self.expected_chips = 84
self.fan_count = 2
@@ -97,7 +97,7 @@ class M50VH70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VH70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -109,7 +109,7 @@ class M50VH80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH80"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -118,7 +118,7 @@ class M50VJ10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VJ10"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VJ10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -130,7 +130,7 @@ class M50VJ20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VJ20"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -60,7 +60,7 @@ class M60VK40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M60 VK40"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M60 VK40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -113,32 +113,6 @@ class UnknownMiner(BaseMiner):
async def get_fw_ver(self) -> Optional[str]:
return None
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []
@@ -155,6 +129,6 @@ class UnknownMiner(BaseMiner):
return None
async def get_data(
self, allow_warning: bool = False, data_to_get: list = None
self, allow_warning: bool = False, data_to_get: list = None, **kwargs
) -> MinerData:
return MinerData(ip=str(self.ip))

View File

@@ -123,9 +123,8 @@ class MinerNetwork:
# clear cached miners
miner_factory.clear_cached_miners()
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
miners = await asyncio.gather(
*[self.ping_and_get_miner(host, limit) for host in self.hosts]
*[self.ping_and_get_miner(host) for host in self.hosts]
)
# remove all None from the miner list
@@ -148,12 +147,8 @@ class MinerNetwork:
loop = asyncio.get_event_loop()
# create a list of scan tasks
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
miners = asyncio.as_completed(
[
loop.create_task(self.ping_and_get_miner(host, limit))
for host in self.hosts
]
[loop.create_task(self.ping_and_get_miner(host)) for host in self.hosts]
)
for miner in miners:
try:
@@ -162,21 +157,16 @@ class MinerNetwork:
yield None
@staticmethod
async def ping_and_get_miner(
ip: ipaddress.ip_address, semaphore: asyncio.Semaphore
) -> Union[None, AnyMiner]:
async with semaphore:
try:
return await ping_and_get_miner(ip)
except ConnectionRefusedError:
tasks = [
ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]
]
for miner in asyncio.as_completed(tasks):
try:
return await miner
except ConnectionRefusedError:
pass
async def ping_and_get_miner(ip: ipaddress.ip_address) -> Union[None, AnyMiner]:
try:
return await ping_and_get_miner(ip)
except ConnectionRefusedError:
tasks = [ping_and_get_miner(ip, port=port) for port in [4028, 4029, 8889]]
for miner in asyncio.as_completed(tasks):
try:
return await miner
except ConnectionRefusedError:
pass
async def ping_and_get_miner(

View File

@@ -24,7 +24,6 @@ from httpx import AsyncHTTPTransport
_settings = { # defaults
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"factory_get_retries": 1,
"factory_get_timeout": 3,
"get_data_retries": 1,

View File

@@ -156,6 +156,8 @@ class BOSMinerGQLAPI:
) -> dict:
url = f"http://{self.ip}/graphql"
query = command
if command is None:
return {}
if command.get("query") is None:
query = {"query": self.parse_command(command)}
try:
@@ -189,11 +191,13 @@ class BOSMinerGQLAPI:
await client.post(
url,
json={
"query": 'mutation{auth{login(username:"'
+ "root"
+ '", password:"'
+ self.pwd
+ '"){__typename}}}'
"query": (
'mutation{auth{login(username:"'
+ "root"
+ '", password:"'
+ self.pwd
+ '"){__typename}}}'
)
},
)
@@ -233,7 +237,9 @@ class BOSMinerLuCIAPI:
login = {"luci_username": self.username, "luci_password": self.pwd}
url = f"http://{self.ip}/cgi-bin/luci"
headers = {
"User-Agent": "BTC Tools v0.1", # only seems to respond if this user-agent is set
"User-Agent": (
"BTC Tools v0.1"
), # only seems to respond if this user-agent is set
"Content-Type": "application/x-www-form-urlencoded",
}
await session.post(url, headers=headers, data=login)

View File

@@ -2357,17 +2357,21 @@ class ActionsServiceBase(ServiceBase):
RebootRequest,
RebootResponse,
),
"/braiins.bos.v1.ActionsService/SetLocateDeviceStatus": grpclib.const.Handler(
self.__rpc_set_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
SetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
"/braiins.bos.v1.ActionsService/SetLocateDeviceStatus": (
grpclib.const.Handler(
self.__rpc_set_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
SetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
)
),
"/braiins.bos.v1.ActionsService/GetLocateDeviceStatus": grpclib.const.Handler(
self.__rpc_get_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
GetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
"/braiins.bos.v1.ActionsService/GetLocateDeviceStatus": (
grpclib.const.Handler(
self.__rpc_get_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
GetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
)
),
}
@@ -2644,17 +2648,21 @@ class PerformanceServiceBase(ServiceBase):
GetTunerStateRequest,
GetTunerStateResponse,
),
"/braiins.bos.v1.PerformanceService/ListTargetProfiles": grpclib.const.Handler(
self.__rpc_list_target_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
ListTargetProfilesRequest,
ListTargetProfilesResponse,
"/braiins.bos.v1.PerformanceService/ListTargetProfiles": (
grpclib.const.Handler(
self.__rpc_list_target_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
ListTargetProfilesRequest,
ListTargetProfilesResponse,
)
),
"/braiins.bos.v1.PerformanceService/SetDefaultPowerTarget": grpclib.const.Handler(
self.__rpc_set_default_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultPowerTargetRequest,
SetPowerTargetResponse,
"/braiins.bos.v1.PerformanceService/SetDefaultPowerTarget": (
grpclib.const.Handler(
self.__rpc_set_default_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultPowerTargetRequest,
SetPowerTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/SetPowerTarget": grpclib.const.Handler(
self.__rpc_set_power_target,
@@ -2662,41 +2670,53 @@ class PerformanceServiceBase(ServiceBase):
SetPowerTargetRequest,
SetPowerTargetResponse,
),
"/braiins.bos.v1.PerformanceService/IncrementPowerTarget": grpclib.const.Handler(
self.__rpc_increment_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementPowerTargetRequest,
SetPowerTargetResponse,
"/braiins.bos.v1.PerformanceService/IncrementPowerTarget": (
grpclib.const.Handler(
self.__rpc_increment_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementPowerTargetRequest,
SetPowerTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/DecrementPowerTarget": grpclib.const.Handler(
self.__rpc_decrement_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementPowerTargetRequest,
SetPowerTargetResponse,
"/braiins.bos.v1.PerformanceService/DecrementPowerTarget": (
grpclib.const.Handler(
self.__rpc_decrement_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementPowerTargetRequest,
SetPowerTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget": grpclib.const.Handler(
self.__rpc_set_default_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultHashrateTargetRequest,
SetHashrateTargetResponse,
"/braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget": (
grpclib.const.Handler(
self.__rpc_set_default_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultHashrateTargetRequest,
SetHashrateTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/SetHashrateTarget": grpclib.const.Handler(
self.__rpc_set_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetHashrateTargetRequest,
SetHashrateTargetResponse,
"/braiins.bos.v1.PerformanceService/SetHashrateTarget": (
grpclib.const.Handler(
self.__rpc_set_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetHashrateTargetRequest,
SetHashrateTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/IncrementHashrateTarget": grpclib.const.Handler(
self.__rpc_increment_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementHashrateTargetRequest,
SetHashrateTargetResponse,
"/braiins.bos.v1.PerformanceService/IncrementHashrateTarget": (
grpclib.const.Handler(
self.__rpc_increment_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementHashrateTargetRequest,
SetHashrateTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/DecrementHashrateTarget": grpclib.const.Handler(
self.__rpc_decrement_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementHashrateTargetRequest,
SetHashrateTargetResponse,
"/braiins.bos.v1.PerformanceService/DecrementHashrateTarget": (
grpclib.const.Handler(
self.__rpc_decrement_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementHashrateTargetRequest,
SetHashrateTargetResponse,
)
),
"/braiins.bos.v1.PerformanceService/SetDPS": grpclib.const.Handler(
self.__rpc_set_dps,
@@ -2704,23 +2724,29 @@ class PerformanceServiceBase(ServiceBase):
SetDpsRequest,
SetDpsResponse,
),
"/braiins.bos.v1.PerformanceService/SetPerformanceMode": grpclib.const.Handler(
self.__rpc_set_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
SetPerformanceModeRequest,
PerformanceMode,
"/braiins.bos.v1.PerformanceService/SetPerformanceMode": (
grpclib.const.Handler(
self.__rpc_set_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
SetPerformanceModeRequest,
PerformanceMode,
)
),
"/braiins.bos.v1.PerformanceService/GetActivePerformanceMode": grpclib.const.Handler(
self.__rpc_get_active_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
GetPerformanceModeRequest,
PerformanceMode,
"/braiins.bos.v1.PerformanceService/GetActivePerformanceMode": (
grpclib.const.Handler(
self.__rpc_get_active_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
GetPerformanceModeRequest,
PerformanceMode,
)
),
"/braiins.bos.v1.PerformanceService/RemoveTunedProfiles": grpclib.const.Handler(
self.__rpc_remove_tuned_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
RemoveTunedProfilesRequest,
RemoveTunedProfilesResponse,
"/braiins.bos.v1.PerformanceService/RemoveTunedProfiles": (
grpclib.const.Handler(
self.__rpc_remove_tuned_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
RemoveTunedProfilesRequest,
RemoveTunedProfilesResponse,
)
),
}
@@ -2836,17 +2862,21 @@ class ConfigurationServiceBase(ServiceBase):
def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
return {
"/braiins.bos.v1.ConfigurationService/GetMinerConfiguration": grpclib.const.Handler(
self.__rpc_get_miner_configuration,
grpclib.const.Cardinality.UNARY_UNARY,
GetMinerConfigurationRequest,
GetMinerConfigurationResponse,
"/braiins.bos.v1.ConfigurationService/GetMinerConfiguration": (
grpclib.const.Handler(
self.__rpc_get_miner_configuration,
grpclib.const.Cardinality.UNARY_UNARY,
GetMinerConfigurationRequest,
GetMinerConfigurationResponse,
)
),
"/braiins.bos.v1.ConfigurationService/GetConstraints": grpclib.const.Handler(
self.__rpc_get_constraints,
grpclib.const.Cardinality.UNARY_UNARY,
GetConstraintsRequest,
GetConstraintsResponse,
"/braiins.bos.v1.ConfigurationService/GetConstraints": (
grpclib.const.Handler(
self.__rpc_get_constraints,
grpclib.const.Cardinality.UNARY_UNARY,
GetConstraintsRequest,
GetConstraintsResponse,
)
),
}

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.44.0"
version = "0.46.0"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"
@@ -9,11 +9,11 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
httpx = "^0.25.2"
asyncssh = "^2.14.1"
grpc-requests = "^0.1.12"
httpx = "^0.26.0"
asyncssh = "^2.14.2"
grpc-requests = "^0.1.13"
passlib = "^1.7.4"
pyaml = "^23.9.7"
pyaml = "^23.12.0"
toml = "^0.10.2"
betterproto = "2.0.0b6"

View File

@@ -18,6 +18,7 @@ import inspect
import sys
import unittest
import warnings
from dataclasses import asdict
from pyasic.miners.backends import CGMiner # noqa
from pyasic.miners.base import BaseMiner
@@ -57,7 +58,6 @@ class MinersTest(unittest.TestCase):
"mac",
"model",
"expected_hashrate",
"pools",
"uptime",
"wattage",
"wattage_limit",
@@ -72,7 +72,9 @@ class MinersTest(unittest.TestCase):
miner_api=miner_api,
):
miner = MINER_CLASSES[miner_model][miner_api]("127.0.0.1")
miner_keys = sorted(list(miner.data_locations.keys()))
miner_keys = sorted(
[str(k) for k in asdict(miner.data_locations).keys()]
)
self.assertEqual(miner_keys, keys)