refactor: fix merge.
This commit is contained in:
@@ -17,7 +17,13 @@ from dataclasses import dataclass, field
|
||||
from typing import Union
|
||||
|
||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||
from pyasic.web.braiins_os.proto.braiins.bos.v1 import DpsPowerTarget, DpsTarget, Hours
|
||||
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
||||
DpsPowerTarget,
|
||||
DpsTarget,
|
||||
Hours,
|
||||
Power,
|
||||
SetDpsRequest,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -37,13 +43,8 @@ class PowerScalingShutdownEnabled(MinerConfigValue):
|
||||
|
||||
return cfg
|
||||
|
||||
def as_bos_grpc(self) -> dict:
|
||||
cfg = {"enable_shutdown ": True}
|
||||
|
||||
if self.duration is not None:
|
||||
cfg["shutdown_duration"] = Hours(self.duration)
|
||||
|
||||
return cfg
|
||||
def as_boser(self) -> dict:
|
||||
return {"enable_shutdown": True, "shutdown_duration": self.duration}
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -57,7 +58,7 @@ class PowerScalingShutdownDisabled(MinerConfigValue):
|
||||
def as_bosminer(self) -> dict:
|
||||
return {"shutdown_enabled": False}
|
||||
|
||||
def as_bos_grpc(self) -> dict:
|
||||
def as_boser(self) -> dict:
|
||||
return {"enable_shutdown ": False}
|
||||
|
||||
|
||||
@@ -88,6 +89,19 @@ class PowerScalingShutdown(MinerConfigOption):
|
||||
return cls.disabled()
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def from_boser(cls, power_scaling_conf: dict):
|
||||
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
||||
if sd_enabled is not None:
|
||||
if sd_enabled:
|
||||
try:
|
||||
return cls.enabled(power_scaling_conf["shutdownDuration"]["hours"])
|
||||
except KeyError:
|
||||
return cls.enabled()
|
||||
else:
|
||||
return cls.disabled()
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class PowerScalingEnabled(MinerConfigValue):
|
||||
@@ -133,20 +147,19 @@ class PowerScalingEnabled(MinerConfigValue):
|
||||
|
||||
return {"power_scaling": cfg}
|
||||
|
||||
def as_bos_grpc(self) -> dict:
|
||||
cfg = {"enable": True}
|
||||
target_conf = {}
|
||||
if self.power_step is not None:
|
||||
target_conf["power_step"] = self.power_step
|
||||
if self.minimum_power is not None:
|
||||
target_conf["min_power_target"] = self.minimum_power
|
||||
|
||||
cfg["target"] = DpsTarget(power_target=DpsPowerTarget(**target_conf))
|
||||
|
||||
if self.shutdown_enabled is not None:
|
||||
cfg = {**cfg, **self.shutdown_enabled.as_bos_grpc()}
|
||||
|
||||
return {"dps": cfg}
|
||||
def as_boser(self) -> dict:
|
||||
return {
|
||||
"set_dps": SetDpsRequest(
|
||||
enable=True,
|
||||
**self.shutdown_enabled.as_boser(),
|
||||
target=DpsTarget(
|
||||
power_target=DpsPowerTarget(
|
||||
power_step=Power(self.power_step),
|
||||
min_power_target=Power(self.minimum_power),
|
||||
)
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -187,3 +200,21 @@ class PowerScalingConfig(MinerConfigOption):
|
||||
return cls.disabled()
|
||||
|
||||
return cls.default()
|
||||
|
||||
@classmethod
|
||||
def from_boser(cls, grpc_miner_conf: dict):
|
||||
try:
|
||||
dps_conf = grpc_miner_conf["dps"]
|
||||
if not dps_conf.get("enabled", False):
|
||||
return cls.disabled()
|
||||
except LookupError:
|
||||
return cls.default()
|
||||
|
||||
conf = {}
|
||||
|
||||
conf["shutdown_enabled"] = PowerScalingShutdown.from_boser(dps_conf)
|
||||
if dps_conf.get("minPowerTarget") is not None:
|
||||
conf["minimum_power"] = dps_conf["minPowerTarget"]["watt"]
|
||||
if dps_conf.get("powerStep") is not None:
|
||||
conf["power_step"] = dps_conf["powerStep"]["watt"]
|
||||
return cls.enabled(**conf)
|
||||
|
||||
@@ -41,7 +41,7 @@ class HiveonT9(Hiveon, T9):
|
||||
except (TypeError, ValueError, asyncssh.Error, OSError, AttributeError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
hashboards = [
|
||||
HashBoard(slot=board, expected_chips=self.expected_chips)
|
||||
for board in range(self.expected_hashboards)
|
||||
@@ -83,7 +83,7 @@ class HiveonT9(Hiveon, T9):
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_wattage(self, api_stats: dict = None) -> Optional[int]:
|
||||
async def _get_wattage(self, api_stats: dict = None) -> Optional[int]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
@@ -100,7 +100,7 @@ class HiveonT9(Hiveon, T9):
|
||||
# parse wattage position out of raw data
|
||||
return round(float(wattage_raw.split(" ")[0]))
|
||||
|
||||
async def get_env_temp(self, api_stats: dict = None) -> Optional[float]:
|
||||
async def _get_env_temp(self, api_stats: dict = None) -> Optional[float]:
|
||||
env_temp_list = []
|
||||
board_map = {
|
||||
0: [2, 9, 10],
|
||||
|
||||
@@ -45,51 +45,52 @@ BOSMINER_DATA_LOC = DataLocations(
|
||||
"_get_mac",
|
||||
[WebAPICommand("web_net_conf", "admin/network/iface_status/lan")],
|
||||
),
|
||||
str(DataOptions.MODEL): DataFunction("get_model"),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
"get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
"_get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction("get_fw_ver"),
|
||||
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"_get_fw_ver", [WebAPICommand("web_bos_info", "bos/info")]
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"get_hashrate",
|
||||
"_get_hashrate",
|
||||
[RPCAPICommand("api_summary", "summary")],
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"get_expected_hashrate", [RPCAPICommand("api_devs", "devs")]
|
||||
"_get_expected_hashrate", [RPCAPICommand("api_devs", "devs")]
|
||||
),
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"get_hashboards",
|
||||
"_get_hashboards",
|
||||
[
|
||||
RPCAPICommand("api_temps", "temps"),
|
||||
RPCAPICommand("api_devdetails", "devdetails"),
|
||||
RPCAPICommand("api_devs", "devs"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
|
||||
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
|
||||
str(DataOptions.WATTAGE): DataFunction(
|
||||
"get_wattage",
|
||||
"_get_wattage",
|
||||
[RPCAPICommand("api_tunerstatus", "tunerstatus")],
|
||||
),
|
||||
str(DataOptions.WATTAGE_LIMIT): DataFunction(
|
||||
"get_wattage_limit",
|
||||
"_get_wattage_limit",
|
||||
[RPCAPICommand("api_tunerstatus", "tunerstatus")],
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"get_fans",
|
||||
"_get_fans",
|
||||
[RPCAPICommand("api_fans", "fans")],
|
||||
),
|
||||
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
|
||||
str(DataOptions.FAN_PSU): DataFunction("_get_fan_psu"),
|
||||
str(DataOptions.ERRORS): DataFunction(
|
||||
"get_errors",
|
||||
"_get_errors",
|
||||
[RPCAPICommand("api_tunerstatus", "tunerstatus")],
|
||||
),
|
||||
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
|
||||
str(DataOptions.FAULT_LIGHT): DataFunction("_get_fault_light"),
|
||||
str(DataOptions.IS_MINING): DataFunction(
|
||||
"is_mining", [RPCAPICommand("api_devdetails", "devdetails")]
|
||||
"_is_mining", [RPCAPICommand("api_devdetails", "devdetails")]
|
||||
),
|
||||
str(DataOptions.UPTIME): DataFunction(
|
||||
"get_uptime", [RPCAPICommand("api_summary", "summary")]
|
||||
"_get_uptime", [RPCAPICommand("api_summary", "summary")]
|
||||
),
|
||||
str(DataOptions.CONFIG): DataFunction("get_config"),
|
||||
}
|
||||
@@ -288,16 +289,17 @@ class BOSMiner(BaseMiner):
|
||||
gateway: str,
|
||||
subnet_mask: str = "255.255.255.0",
|
||||
):
|
||||
cfg_data_lan = (
|
||||
"config interface 'lan'\n\toption type 'bridge'\n\toption ifname 'eth0'\n\toption proto 'static'\n\toption ipaddr '"
|
||||
+ ip
|
||||
+ "'\n\toption netmask '"
|
||||
+ subnet_mask
|
||||
+ "'\n\toption gateway '"
|
||||
+ gateway
|
||||
+ "'\n\toption dns '"
|
||||
+ dns
|
||||
+ "'"
|
||||
cfg_data_lan = "\n\t".join(
|
||||
[
|
||||
"config interface 'lan'",
|
||||
"option type 'bridge'",
|
||||
"option ifname 'eth0'",
|
||||
"option proto 'static'",
|
||||
f"option ipaddr '{ip}'",
|
||||
f"option netmask '{subnet_mask}'",
|
||||
f"option gateway '{gateway}'",
|
||||
f"option dns '{dns}'",
|
||||
]
|
||||
)
|
||||
data = await self.send_ssh_command("cat /etc/config/network")
|
||||
|
||||
@@ -313,7 +315,14 @@ class BOSMiner(BaseMiner):
|
||||
await conn.run("echo '" + config + "' > /etc/config/network")
|
||||
|
||||
async def set_dhcp(self):
|
||||
cfg_data_lan = "config interface 'lan'\n\toption type 'bridge'\n\toption ifname 'eth0'\n\toption proto 'dhcp'"
|
||||
cfg_data_lan = "\n\t".join(
|
||||
[
|
||||
"config interface 'lan'",
|
||||
"option type 'bridge'",
|
||||
"option ifname 'eth0'",
|
||||
"option proto 'dhcp'",
|
||||
]
|
||||
)
|
||||
data = await self.send_ssh_command("cat /etc/config/network")
|
||||
|
||||
split_data = data.split("\n\n")
|
||||
@@ -331,12 +340,10 @@ class BOSMiner(BaseMiner):
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
async def get_mac(self, web_net_conf: Union[dict, list] = None) -> Optional[str]:
|
||||
async def _get_mac(self, web_net_conf: Union[dict, list] = None) -> Optional[str]:
|
||||
if not web_net_conf:
|
||||
try:
|
||||
web_net_conf = await self.web.luci.send_command(
|
||||
"admin/network/iface_status/lan"
|
||||
)
|
||||
web_net_conf = await self.web.luci.get_net_conf()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
@@ -354,21 +361,7 @@ class BOSMiner(BaseMiner):
|
||||
# if result:
|
||||
# return result.upper().strip()
|
||||
|
||||
async def get_model(self) -> Optional[str]:
|
||||
if self.model is not None:
|
||||
return self.model + " (BOS)"
|
||||
return "? (BOS)"
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None, graphql_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
api_ver_t = asyncio.create_task(self.get_api_ver(api_version))
|
||||
fw_ver_t = asyncio.create_task(self.get_fw_ver())
|
||||
await asyncio.gather(api_ver_t, fw_ver_t)
|
||||
return miner_version(api_ver=api_ver_t.result(), fw_ver=fw_ver_t.result())
|
||||
|
||||
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
async def _get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
@@ -386,19 +379,28 @@ class BOSMiner(BaseMiner):
|
||||
|
||||
return self.api_ver
|
||||
|
||||
async def get_fw_ver(self) -> Optional[str]:
|
||||
fw_ver = await self.send_ssh_command("cat /etc/bos_version")
|
||||
async def _get_fw_ver(self, web_bos_info: dict) -> Optional[str]:
|
||||
if web_bos_info is None:
|
||||
try:
|
||||
web_bos_info = await self.web.luci.get_bos_info()
|
||||
except APIError:
|
||||
return None
|
||||
|
||||
# if we get the version data, parse it
|
||||
if fw_ver is not None:
|
||||
ver = fw_ver.split("-")[5]
|
||||
if isinstance(web_bos_info, dict):
|
||||
if "bos/info" in web_bos_info.keys():
|
||||
web_bos_info = web_bos_info["bos/info"]
|
||||
|
||||
try:
|
||||
ver = web_bos_info["version"].split("-")[5]
|
||||
if "." in ver:
|
||||
self.fw_ver = ver
|
||||
logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||
except (LookupError, AttributeError):
|
||||
return None
|
||||
|
||||
return self.fw_ver
|
||||
|
||||
async def get_hostname(self) -> Union[str, None]:
|
||||
async def _get_hostname(self) -> Union[str, None]:
|
||||
try:
|
||||
hostname = (
|
||||
await self.send_ssh_command("cat /proc/sys/kernel/hostname")
|
||||
@@ -408,7 +410,7 @@ class BOSMiner(BaseMiner):
|
||||
return None
|
||||
return hostname
|
||||
|
||||
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||
async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||
# get hr from API
|
||||
if not api_summary:
|
||||
try:
|
||||
@@ -422,7 +424,7 @@ class BOSMiner(BaseMiner):
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(
|
||||
async def _get_hashboards(
|
||||
self,
|
||||
api_temps: dict = None,
|
||||
api_devdetails: dict = None,
|
||||
@@ -495,10 +497,10 @@ class BOSMiner(BaseMiner):
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_env_temp(self) -> Optional[float]:
|
||||
async def _get_env_temp(self) -> Optional[float]:
|
||||
return None
|
||||
|
||||
async def get_wattage(self, api_tunerstatus: dict = None) -> Optional[int]:
|
||||
async def _get_wattage(self, api_tunerstatus: dict = None) -> Optional[int]:
|
||||
if not api_tunerstatus:
|
||||
try:
|
||||
api_tunerstatus = await self.api.tunerstatus()
|
||||
@@ -513,7 +515,7 @@ class BOSMiner(BaseMiner):
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def get_wattage_limit(self, api_tunerstatus: dict = None) -> Optional[int]:
|
||||
async def _get_wattage_limit(self, api_tunerstatus: dict = None) -> Optional[int]:
|
||||
if not api_tunerstatus:
|
||||
try:
|
||||
api_tunerstatus = await self.api.tunerstatus()
|
||||
@@ -526,7 +528,7 @@ class BOSMiner(BaseMiner):
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def get_fans(self, api_fans: dict = None) -> List[Fan]:
|
||||
async def _get_fans(self, api_fans: dict = None) -> List[Fan]:
|
||||
if not api_fans:
|
||||
try:
|
||||
api_fans = await self.api.fans()
|
||||
@@ -543,10 +545,10 @@ class BOSMiner(BaseMiner):
|
||||
return fans
|
||||
return [Fan() for _ in range(self.fan_count)]
|
||||
|
||||
async def get_fan_psu(self) -> Optional[int]:
|
||||
async def _get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_errors(self, api_tunerstatus: dict = None) -> List[MinerErrorData]:
|
||||
async def _get_errors(self, api_tunerstatus: dict = None) -> List[MinerErrorData]:
|
||||
if not api_tunerstatus:
|
||||
try:
|
||||
api_tunerstatus = await self.api.tunerstatus()
|
||||
@@ -576,7 +578,7 @@ class BOSMiner(BaseMiner):
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_fault_light(self, graphql_fault_light: dict = None) -> bool:
|
||||
async def _get_fault_light(self) -> bool:
|
||||
if self.light:
|
||||
return self.light
|
||||
try:
|
||||
@@ -590,7 +592,7 @@ class BOSMiner(BaseMiner):
|
||||
except (TypeError, AttributeError):
|
||||
return self.light
|
||||
|
||||
async def get_expected_hashrate(self, api_devs: dict = None) -> Optional[float]:
|
||||
async def _get_expected_hashrate(self, api_devs: dict = None) -> Optional[float]:
|
||||
if not api_devs:
|
||||
try:
|
||||
api_devs = await self.api.devs()
|
||||
@@ -616,7 +618,7 @@ class BOSMiner(BaseMiner):
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
|
||||
async def is_mining(self, api_devdetails: dict = None) -> Optional[bool]:
|
||||
async def _is_mining(self, api_devdetails: dict = None) -> Optional[bool]:
|
||||
if not api_devdetails:
|
||||
try:
|
||||
api_devdetails = await self.api.send_command(
|
||||
@@ -631,7 +633,7 @@ class BOSMiner(BaseMiner):
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def get_uptime(self, api_summary: dict = None) -> Optional[int]:
|
||||
async def _get_uptime(self, api_summary: dict = None) -> Optional[int]:
|
||||
if not api_summary:
|
||||
try:
|
||||
api_summary = await self.api.summary()
|
||||
@@ -648,7 +650,7 @@ class BOSMiner(BaseMiner):
|
||||
BOSER_DATA_LOC = DataLocations(
|
||||
**{
|
||||
str(DataOptions.MAC): DataFunction(
|
||||
"get_mac",
|
||||
"_get_mac",
|
||||
[GRPCCommand("grpc_miner_details", "get_miner_details")],
|
||||
),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
@@ -720,7 +722,6 @@ class BOSer(BaseMiner):
|
||||
|
||||
# static data
|
||||
self.api_type = "BOSMiner"
|
||||
self.fw_str = "BOS"
|
||||
# data gathering locations
|
||||
self.data_locations = BOSER_DATA_LOC
|
||||
# autotuning/shutdown support
|
||||
@@ -770,41 +771,27 @@ class BOSer(BaseMiner):
|
||||
return False
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
logging.debug(f"{self}: Getting config.")
|
||||
grpc_conf = await self.web.grpc.get_miner_configuration()
|
||||
|
||||
try:
|
||||
conn = await self._get_ssh_connection()
|
||||
except ConnectionError:
|
||||
conn = None
|
||||
|
||||
if conn:
|
||||
async with conn:
|
||||
# good ol' BBB compatibility :/
|
||||
toml_data = toml.loads(
|
||||
(await conn.run("cat /etc/bosminer.toml")).stdout
|
||||
)
|
||||
logging.debug(f"{self}: Converting config file.")
|
||||
cfg = MinerConfig.from_bosminer(toml_data)
|
||||
self.config = cfg
|
||||
return self.config
|
||||
return MinerConfig.from_boser(grpc_conf)
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
raise NotImplementedError
|
||||
logging.debug(f"{self}: Sending config.")
|
||||
self.config = config
|
||||
raise NotImplementedError
|
||||
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
try:
|
||||
cfg = await self.get_config()
|
||||
if cfg is None:
|
||||
return False
|
||||
cfg.mining_mode = MiningModePowerTune(wattage)
|
||||
await self.send_config(cfg)
|
||||
except Exception as e:
|
||||
logging.warning(f"{self} set_power_limit: {e}")
|
||||
result = await self.web.grpc.set_power_target(wattage)
|
||||
except APIError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
try:
|
||||
if result["powerTarget"]["watt"] == wattage:
|
||||
return True
|
||||
except KeyError:
|
||||
pass
|
||||
return False
|
||||
|
||||
##################################################
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
@@ -823,20 +810,11 @@ class BOSer(BaseMiner):
|
||||
except (LookupError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_model(self) -> Optional[str]:
|
||||
if self.raw_model is not None:
|
||||
return self.raw_model + " (BOS)"
|
||||
async def _get_model(self) -> Optional[str]:
|
||||
if self.model is not None:
|
||||
return self.model + " (BOS)"
|
||||
return "? (BOS)"
|
||||
|
||||
async def get_version(
|
||||
self, api_version: dict = None, graphql_version: dict = None
|
||||
) -> Tuple[Optional[str], Optional[str]]:
|
||||
# check if version is cached
|
||||
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
|
||||
api_ver = await self.get_api_ver(api_version)
|
||||
fw_ver = await self.get_fw_ver(graphql_version)
|
||||
return miner_version(api_ver, fw_ver)
|
||||
|
||||
async def _get_api_ver(self, api_version: dict = None) -> Optional[str]:
|
||||
if not api_version:
|
||||
try:
|
||||
@@ -902,10 +880,10 @@ class BOSer(BaseMiner):
|
||||
if api_summary:
|
||||
try:
|
||||
return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2)
|
||||
except (LookupError, ValueError, TypeError):
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_expected_hashrate(
|
||||
async def _get_expected_hashrate(
|
||||
self, grpc_miner_details: dict = None
|
||||
) -> Optional[float]:
|
||||
if not grpc_miner_details:
|
||||
@@ -920,7 +898,7 @@ class BOSer(BaseMiner):
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def get_hashboards(self, grpc_hashboards: dict = None):
|
||||
async def _get_hashboards(self, grpc_hashboards: dict = None):
|
||||
hashboards = [
|
||||
HashBoard(slot=i, expected_chips=self.expected_chips)
|
||||
for i in range(self.expected_hashboards)
|
||||
@@ -991,7 +969,7 @@ class BOSer(BaseMiner):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def get_fans(self, grpc_cooling_state: dict = None) -> List[Fan]:
|
||||
async def _get_fans(self, grpc_cooling_state: dict = None) -> List[Fan]:
|
||||
if grpc_cooling_state is None:
|
||||
try:
|
||||
grpc_cooling_state = self.web.grpc.get_cooling_state()
|
||||
@@ -1000,13 +978,13 @@ class BOSer(BaseMiner):
|
||||
|
||||
if grpc_cooling_state:
|
||||
fans = []
|
||||
for n in range(self.expected_fans):
|
||||
for n in range(self.fan_count):
|
||||
try:
|
||||
fans.append(Fan(grpc_cooling_state["fans"][n]["rpm"]))
|
||||
except LookupError:
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
return fans
|
||||
return [Fan() for _ in range(self.expected_fans)]
|
||||
return [Fan() for _ in range(self.fan_count)]
|
||||
|
||||
async def _get_fan_psu(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
@@ -28,42 +28,41 @@ from pyasic.miners.base import DataFunction, DataLocations, DataOptions, RPCAPIC
|
||||
AVALON_DATA_LOC = DataLocations(
|
||||
**{
|
||||
str(DataOptions.MAC): DataFunction(
|
||||
"get_mac", [RPCAPICommand("api_version", "version")]
|
||||
"_get_mac", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.MODEL): DataFunction("get_model"),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
"get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
"_get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"get_fw_ver", [RPCAPICommand("api_version", "version")]
|
||||
"_get_fw_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
|
||||
str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"get_hashrate", [RPCAPICommand("api_devs", "devs")]
|
||||
"_get_hashrate", [RPCAPICommand("api_devs", "devs")]
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_hashboards", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
|
||||
"get_env_temp", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_env_temp", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
|
||||
str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
|
||||
str(DataOptions.WATTAGE_LIMIT): DataFunction(
|
||||
"get_wattage_limit", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_wattage_limit", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"get_fans", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_fans", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
|
||||
str(DataOptions.ERRORS): DataFunction("get_errors"),
|
||||
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")]
|
||||
"_get_fault_light", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.IS_MINING): DataFunction("is_mining"),
|
||||
str(DataOptions.UPTIME): DataFunction("get_uptime"),
|
||||
str(DataOptions.IS_MINING): DataFunction("_is_mining"),
|
||||
str(DataOptions.UPTIME): DataFunction("_get_uptime"),
|
||||
str(DataOptions.CONFIG): DataFunction("get_config"),
|
||||
}
|
||||
)
|
||||
@@ -174,7 +173,7 @@ class CGMinerAvalon(CGMiner):
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
async def get_mac(self, api_version: dict = None) -> Optional[str]:
|
||||
async def _get_mac(self, api_version: dict = None) -> Optional[str]:
|
||||
if not api_version:
|
||||
try:
|
||||
api_version = await self.api.version()
|
||||
@@ -192,7 +191,7 @@ class CGMinerAvalon(CGMiner):
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
|
||||
async def get_hostname(self) -> Optional[str]:
|
||||
async def _get_hostname(self) -> Optional[str]:
|
||||
return None
|
||||
# if not mac:
|
||||
# mac = await self.get_mac()
|
||||
@@ -200,7 +199,7 @@ class CGMinerAvalon(CGMiner):
|
||||
# if mac:
|
||||
# return f"Avalon{mac.replace(':', '')[-6:]}"
|
||||
|
||||
async def get_hashrate(self, api_devs: dict = None) -> Optional[float]:
|
||||
async def _get_hashrate(self, api_devs: dict = None) -> Optional[float]:
|
||||
if not api_devs:
|
||||
try:
|
||||
api_devs = await self.api.devs()
|
||||
@@ -213,7 +212,7 @@ class CGMinerAvalon(CGMiner):
|
||||
except (KeyError, IndexError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
hashboards = [
|
||||
HashBoard(slot=i, expected_chips=self.expected_chips)
|
||||
for i in range(self.expected_hashboards)
|
||||
@@ -261,7 +260,7 @@ class CGMinerAvalon(CGMiner):
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
|
||||
async def _get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
@@ -276,7 +275,7 @@ class CGMinerAvalon(CGMiner):
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_env_temp(self, api_stats: dict = None) -> Optional[float]:
|
||||
async def _get_env_temp(self, api_stats: dict = None) -> Optional[float]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
@@ -291,10 +290,10 @@ class CGMinerAvalon(CGMiner):
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_wattage(self) -> Optional[int]:
|
||||
async def _get_wattage(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
async def get_wattage_limit(self, api_stats: dict = None) -> Optional[int]:
|
||||
async def _get_wattage_limit(self, api_stats: dict = None) -> Optional[int]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
@@ -309,7 +308,7 @@ class CGMinerAvalon(CGMiner):
|
||||
except (IndexError, KeyError, ValueError, TypeError):
|
||||
pass
|
||||
|
||||
async def get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
async def _get_fans(self, api_stats: dict = None) -> List[Fan]:
|
||||
if not api_stats:
|
||||
try:
|
||||
api_stats = await self.api.stats()
|
||||
@@ -331,10 +330,10 @@ class CGMinerAvalon(CGMiner):
|
||||
pass
|
||||
return fans_data
|
||||
|
||||
async def get_errors(self) -> List[MinerErrorData]:
|
||||
async def _get_errors(self) -> List[MinerErrorData]:
|
||||
return []
|
||||
|
||||
async def get_fault_light(self, api_stats: dict = None) -> bool: # noqa
|
||||
async def _get_fault_light(self, api_stats: dict = None) -> bool: # noqa
|
||||
if self.light:
|
||||
return self.light
|
||||
if not api_stats:
|
||||
@@ -363,8 +362,8 @@ class CGMinerAvalon(CGMiner):
|
||||
pass
|
||||
return False
|
||||
|
||||
async def is_mining(self, *args, **kwargs) -> Optional[bool]:
|
||||
async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
|
||||
return None
|
||||
|
||||
async def get_uptime(self) -> Optional[int]:
|
||||
async def _get_uptime(self) -> Optional[int]:
|
||||
return None
|
||||
|
||||
@@ -23,39 +23,38 @@ from pyasic.miners.base import DataFunction, DataLocations, DataOptions, RPCAPIC
|
||||
HIVEON_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")]
|
||||
"_get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"get_fw_ver", [RPCAPICommand("api_version", "version")]
|
||||
"_get_fw_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
|
||||
str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
|
||||
"_get_hashrate", [RPCAPICommand("api_summary", "summary")]
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_hashboards", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
|
||||
"get_env_temp", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_env_temp", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.WATTAGE): DataFunction(
|
||||
"get_wattage", [RPCAPICommand("api_stats", "stats")]
|
||||
"_get_wattage", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"get_fans", [RPCAPICommand("api_stats", "stats")]
|
||||
"_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.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")]
|
||||
"_get_uptime", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.CONFIG): DataFunction("get_config"),
|
||||
}
|
||||
@@ -71,16 +70,11 @@ class Hiveon(BMMiner):
|
||||
# data gathering locations
|
||||
self.data_locations = HIVEON_DATA_LOC
|
||||
|
||||
async def get_model(self) -> Optional[str]:
|
||||
if self.model is not None:
|
||||
return self.model + " (Hiveon)"
|
||||
return "? (Hiveon)"
|
||||
|
||||
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
|
||||
pass
|
||||
|
||||
async def get_wattage(self, api_stats: dict = None) -> Optional[int]:
|
||||
async def _get_wattage(self, api_stats: dict = None) -> Optional[int]:
|
||||
pass
|
||||
|
||||
async def get_env_temp(self, api_stats: dict = None) -> Optional[float]:
|
||||
async def _get_env_temp(self, api_stats: dict = None) -> Optional[float]:
|
||||
pass
|
||||
|
||||
@@ -33,68 +33,67 @@ from pyasic.web.innosilicon import InnosiliconWebAPI
|
||||
INNOSILICON_DATA_LOC = DataLocations(
|
||||
**{
|
||||
str(DataOptions.MAC): DataFunction(
|
||||
"get_mac",
|
||||
"_get_mac",
|
||||
[
|
||||
WebAPICommand("web_get_all", "getAll"),
|
||||
WebAPICommand("web_overview", "overview"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.MODEL): DataFunction("get_model"),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
"get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
"_get_api_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"get_fw_ver", [RPCAPICommand("api_version", "version")]
|
||||
"_get_fw_ver", [RPCAPICommand("api_version", "version")]
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
|
||||
str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"get_hashrate",
|
||||
"_get_hashrate",
|
||||
[
|
||||
RPCAPICommand("api_summary", "summary"),
|
||||
WebAPICommand("web_get_all", "getAll"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"get_expected_hashrate",
|
||||
"_get_expected_hashrate",
|
||||
),
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"get_hashboards",
|
||||
"_get_hashboards",
|
||||
[
|
||||
RPCAPICommand("api_stats", "stats"),
|
||||
WebAPICommand("web_get_all", "getAll"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
|
||||
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
|
||||
str(DataOptions.WATTAGE): DataFunction(
|
||||
"get_wattage",
|
||||
"_get_wattage",
|
||||
[
|
||||
WebAPICommand("web_get_all", "getAll"),
|
||||
RPCAPICommand("api_stats", "stats"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.WATTAGE_LIMIT): DataFunction(
|
||||
"get_wattage_limit",
|
||||
"_get_wattage_limit",
|
||||
[
|
||||
WebAPICommand("web_get_all", "getAll"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"get_fans",
|
||||
"_get_fans",
|
||||
[
|
||||
WebAPICommand("web_get_all", "getAll"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
|
||||
str(DataOptions.FAN_PSU): DataFunction("_get_fan_psu"),
|
||||
str(DataOptions.ERRORS): DataFunction(
|
||||
"get_errors",
|
||||
"_get_errors",
|
||||
[
|
||||
WebAPICommand("web_get_error_detail", "getErrorDetail"),
|
||||
],
|
||||
),
|
||||
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
|
||||
str(DataOptions.IS_MINING): DataFunction("is_mining"),
|
||||
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")]
|
||||
"_get_uptime", [RPCAPICommand("api_stats", "stats")]
|
||||
),
|
||||
str(DataOptions.CONFIG): DataFunction("get_config"),
|
||||
}
|
||||
@@ -176,7 +175,7 @@ class Innosilicon(CGMiner):
|
||||
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
|
||||
##################################################
|
||||
|
||||
async def get_mac(
|
||||
async def _get_mac(
|
||||
self, web_get_all: dict = None, web_overview: dict = None
|
||||
) -> Optional[str]:
|
||||
if web_get_all:
|
||||
@@ -202,7 +201,7 @@ class Innosilicon(CGMiner):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def get_hashrate(
|
||||
async def _get_hashrate(
|
||||
self, api_summary: dict = None, web_get_all: dict = None
|
||||
) -> Optional[float]:
|
||||
if web_get_all:
|
||||
@@ -234,7 +233,7 @@ class Innosilicon(CGMiner):
|
||||
except (KeyError, IndexError):
|
||||
pass
|
||||
|
||||
async def get_hashboards(
|
||||
async def _get_hashboards(
|
||||
self, api_stats: dict = None, web_get_all: dict = None
|
||||
) -> List[HashBoard]:
|
||||
if web_get_all:
|
||||
@@ -292,7 +291,7 @@ class Innosilicon(CGMiner):
|
||||
|
||||
return hashboards
|
||||
|
||||
async def get_wattage(
|
||||
async def _get_wattage(
|
||||
self, web_get_all: dict = None, api_stats: dict = None
|
||||
) -> Optional[int]:
|
||||
if web_get_all:
|
||||
@@ -329,7 +328,7 @@ class Innosilicon(CGMiner):
|
||||
wattage = int(wattage)
|
||||
return wattage
|
||||
|
||||
async def get_fans(self, web_get_all: dict = None) -> List[Fan]:
|
||||
async def _get_fans(self, web_get_all: dict = None) -> List[Fan]:
|
||||
if web_get_all:
|
||||
web_get_all = web_get_all["all"]
|
||||
|
||||
@@ -354,7 +353,7 @@ class Innosilicon(CGMiner):
|
||||
|
||||
return fans
|
||||
|
||||
async def get_errors(
|
||||
async def _get_errors(
|
||||
self, web_get_error_detail: dict = None
|
||||
) -> List[MinerErrorData]:
|
||||
errors = []
|
||||
@@ -377,7 +376,7 @@ class Innosilicon(CGMiner):
|
||||
errors.append(InnosiliconError(error_code=err))
|
||||
return errors
|
||||
|
||||
async def get_wattage_limit(self, web_get_all: dict = None) -> Optional[int]:
|
||||
async def _get_wattage_limit(self, web_get_all: dict = None) -> Optional[int]:
|
||||
if web_get_all:
|
||||
web_get_all = web_get_all["all"]
|
||||
|
||||
@@ -400,5 +399,5 @@ class Innosilicon(CGMiner):
|
||||
limit = 1250 + (250 * level)
|
||||
return limit
|
||||
|
||||
async def get_expected_hashrate(self) -> Optional[float]:
|
||||
async def _get_expected_hashrate(self) -> Optional[float]:
|
||||
pass
|
||||
|
||||
@@ -142,7 +142,7 @@ class BaseMiner(ABC):
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
model_data = [self.raw_model]
|
||||
model_data = [self.raw_model if self.raw_model is not None else "Unknown"]
|
||||
if self.fw_str is not None:
|
||||
model_data.append(f"({self.fw_str})")
|
||||
return " ".join(model_data)
|
||||
|
||||
@@ -26,6 +26,7 @@ class BaseWebAPI(ABC):
|
||||
self.ip = ip # ipaddress.ip_address(ip)
|
||||
self.username = "root"
|
||||
self.pwd = "root"
|
||||
self.port = 80
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls is BaseWebAPI:
|
||||
|
||||
@@ -35,10 +35,12 @@ class AntminerModernWebAPI(BaseWebAPI):
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||
url = f"http://{self.ip}:{self.port}/cgi-bin/{command}.cgi"
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
try:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
async with httpx.AsyncClient(
|
||||
transport=settings.transport(),
|
||||
) as client:
|
||||
if parameters:
|
||||
data = await client.post(
|
||||
url,
|
||||
@@ -149,7 +151,7 @@ class AntminerOldWebAPI(BaseWebAPI):
|
||||
allow_warning: bool = True,
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
|
||||
url = f"http://{self.ip}:{self.port}/cgi-bin/{command}.cgi"
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
try:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
|
||||
@@ -31,6 +31,7 @@ class BOSMinerWebAPI(BaseWebAPI):
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
self._pwd = settings.get("default_bosminer_password", "root")
|
||||
self._port = 80
|
||||
super().__init__(ip)
|
||||
|
||||
@property
|
||||
@@ -42,6 +43,15 @@ class BOSMinerWebAPI(BaseWebAPI):
|
||||
self._pwd = other
|
||||
self.luci.pwd = other
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._port
|
||||
|
||||
@port.setter
|
||||
def port(self, other: str):
|
||||
self._port = other
|
||||
self.luci.port = other
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, dict],
|
||||
@@ -63,6 +73,7 @@ class BOSerWebAPI(BOSMinerWebAPI):
|
||||
ip, settings.get("default_bosminer_password", "root")
|
||||
)
|
||||
self.grpc = BOSerGRPCAPI(ip, settings.get("default_bosminer_password", "root"))
|
||||
self._port = 80
|
||||
super().__init__(ip)
|
||||
|
||||
@property
|
||||
@@ -76,6 +87,16 @@ class BOSerWebAPI(BOSMinerWebAPI):
|
||||
self.gql.pwd = other
|
||||
self.grpc.pwd = other
|
||||
|
||||
@property
|
||||
def port(self):
|
||||
return self._port
|
||||
|
||||
@port.setter
|
||||
def port(self, other: str):
|
||||
self._port = other
|
||||
self.luci.port = other
|
||||
self.gql.port = other
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: Union[str, dict],
|
||||
@@ -84,14 +105,14 @@ class BOSerWebAPI(BOSMinerWebAPI):
|
||||
**parameters: Union[str, int, bool],
|
||||
) -> dict:
|
||||
command_type = self.select_command_type(command)
|
||||
if command_type is "gql":
|
||||
if command_type == "gql":
|
||||
return await self.gql.send_command(command)
|
||||
elif command_type is "grpc":
|
||||
elif command_type == "grpc":
|
||||
try:
|
||||
return await (getattr(self.grpc, command.replace("grpc_", "")))()
|
||||
except AttributeError:
|
||||
raise APIError(f"No gRPC command found for command: {command}")
|
||||
elif command_type is "luci":
|
||||
elif command_type == "luci":
|
||||
return await self.luci.send_command(command)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -26,6 +26,7 @@ class BOSerGraphQLAPI:
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self.port = 80
|
||||
|
||||
async def multicommand(self, *commands: dict) -> dict:
|
||||
def merge(*d: dict):
|
||||
@@ -60,7 +61,7 @@ class BOSerGraphQLAPI:
|
||||
self,
|
||||
command: dict,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}/graphql"
|
||||
url = f"http://{self.ip}:{self.port}/graphql"
|
||||
query = command
|
||||
if command is None:
|
||||
return {}
|
||||
@@ -93,7 +94,7 @@ class BOSerGraphQLAPI:
|
||||
return "{" + ",".join(data) + "}"
|
||||
|
||||
async def auth(self, client: httpx.AsyncClient) -> None:
|
||||
url = f"http://{self.ip}/graphql"
|
||||
url = f"http://{self.ip}:{self.port}/graphql"
|
||||
await client.post(
|
||||
url,
|
||||
json={
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from betterproto import Message
|
||||
from grpclib import GRPCError, Status
|
||||
from grpclib.client import Channel
|
||||
|
||||
from pyasic.errors import APIError
|
||||
@@ -44,6 +46,7 @@ class BOSerGRPCAPI:
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self.port = 50051
|
||||
self._auth = None
|
||||
self._auth_time = datetime.now()
|
||||
|
||||
@@ -90,13 +93,23 @@ class BOSerGRPCAPI:
|
||||
metadata = []
|
||||
if auth:
|
||||
metadata.append(("authorization", await self.auth()))
|
||||
async with Channel(self.ip, 50051) as c:
|
||||
endpoint = getattr(BOSMinerGRPCStub(c), command)
|
||||
if endpoint is None:
|
||||
if not ignore_errors:
|
||||
raise APIError(f"Command not found - {endpoint}")
|
||||
return {}
|
||||
return (await endpoint(message, metadata=metadata)).to_pydict()
|
||||
try:
|
||||
async with Channel(self.ip, self.port) as c:
|
||||
endpoint = getattr(BOSMinerGRPCStub(c), command)
|
||||
if endpoint is None:
|
||||
if not ignore_errors:
|
||||
raise APIError(f"Command not found - {endpoint}")
|
||||
return {}
|
||||
try:
|
||||
return (await endpoint(message, metadata=metadata)).to_pydict()
|
||||
except GRPCError as e:
|
||||
if e.status == Status.UNAUTHENTICATED:
|
||||
await self._get_auth()
|
||||
metadata = [("authorization", await self.auth())]
|
||||
return (await endpoint(message, metadata=metadata)).to_pydict()
|
||||
raise e
|
||||
except GRPCError as e:
|
||||
raise APIError(f"gRPC command failed - {endpoint}") from e
|
||||
|
||||
async def auth(self):
|
||||
if self._auth is not None and self._auth_time - datetime.now() < timedelta(
|
||||
@@ -107,7 +120,7 @@ class BOSerGRPCAPI:
|
||||
return self._auth
|
||||
|
||||
async def _get_auth(self):
|
||||
async with Channel(self.ip, 50051) as c:
|
||||
async with Channel(self.ip, self.port) as c:
|
||||
req = LoginRequest(username=self.username, password=self.pwd)
|
||||
async with c.request(
|
||||
"/braiins.bos.v1.AuthenticationService/Login",
|
||||
@@ -152,7 +165,9 @@ class BOSerGRPCAPI:
|
||||
)
|
||||
|
||||
async def get_locate_device_status(self):
|
||||
return await self.send_command("get_locate_device_status")
|
||||
return await self.send_command(
|
||||
"get_locate_device_status", GetLocateDeviceStatusRequest()
|
||||
)
|
||||
|
||||
async def set_password(self, password: str = None):
|
||||
return await self.send_command(
|
||||
@@ -281,16 +296,72 @@ class BOSerGRPCAPI:
|
||||
|
||||
async def set_dps(
|
||||
self,
|
||||
enable: bool,
|
||||
power_step: int,
|
||||
min_power_target: int,
|
||||
enable_shutdown: bool = None,
|
||||
shutdown_duration: int = None,
|
||||
):
|
||||
raise NotImplementedError
|
||||
return await self.send_command("braiins.bos.v1.PerformanceService/SetDPS")
|
||||
|
||||
async def set_performance_mode(self):
|
||||
raise NotImplementedError
|
||||
return await self.send_command(
|
||||
"braiins.bos.v1.PerformanceService/SetPerformanceMode"
|
||||
"set_dps",
|
||||
message=SetDpsRequest(
|
||||
enable=enable,
|
||||
enable_shutdown=enable_shutdown,
|
||||
shutdown_duration=shutdown_duration,
|
||||
target=DpsTarget(
|
||||
power_target=DpsPowerTarget(
|
||||
power_step=Power(power_step),
|
||||
min_power_target=Power(min_power_target),
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
async def set_performance_mode(
|
||||
self,
|
||||
wattage_target: int = None,
|
||||
hashrate_target: int = None,
|
||||
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
||||
):
|
||||
if wattage_target is not None and hashrate_target is not None:
|
||||
logging.error(
|
||||
"Cannot use both wattage_target and hashrate_target, using wattage_target."
|
||||
)
|
||||
elif wattage_target is None and hashrate_target is None:
|
||||
raise APIError(
|
||||
"No target supplied, please supply either wattage_target or hashrate_target."
|
||||
)
|
||||
if wattage_target is not None:
|
||||
return await self.send_command(
|
||||
"set_performance_mode",
|
||||
message=SetPerformanceModeRequest(
|
||||
save_action=save_action,
|
||||
mode=PerformanceMode(
|
||||
tuner_mode=TunerPerformanceMode(
|
||||
power_target=PowerTargetMode(
|
||||
power_target=Power(watt=wattage_target)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
if hashrate_target is not None:
|
||||
return await self.send_command(
|
||||
"set_performance_mode",
|
||||
message=SetPerformanceModeRequest(
|
||||
save_action=save_action,
|
||||
mode=PerformanceMode(
|
||||
tuner_mode=TunerPerformanceMode(
|
||||
hashrate_target=HashrateTargetMode(
|
||||
hashrate_target=TeraHashrate(
|
||||
terahash_per_second=hashrate_target
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
async def get_active_performance_mode(self):
|
||||
return await self.send_command(
|
||||
"get_active_performance_mode", GetPerformanceModeRequest()
|
||||
|
||||
@@ -26,6 +26,7 @@ class BOSMinerLuCIAPI:
|
||||
self.ip = ip
|
||||
self.username = "root"
|
||||
self.pwd = pwd
|
||||
self.port = 80
|
||||
|
||||
async def multicommand(self, *commands: str) -> dict:
|
||||
data = {}
|
||||
@@ -38,7 +39,7 @@ class BOSMinerLuCIAPI:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
await self.auth(client)
|
||||
data = await client.get(
|
||||
f"http://{self.ip}/cgi-bin/luci/{path}",
|
||||
f"http://{self.ip}:{self.port}/cgi-bin/luci/{path}",
|
||||
headers={"User-Agent": "BTC Tools v0.1"},
|
||||
)
|
||||
if data.status_code == 200:
|
||||
@@ -55,7 +56,7 @@ class BOSMinerLuCIAPI:
|
||||
|
||||
async def auth(self, session: httpx.AsyncClient):
|
||||
login = {"luci_username": self.username, "luci_password": self.pwd}
|
||||
url = f"http://{self.ip}/cgi-bin/luci"
|
||||
url = f"http://{self.ip}:{self.port}/cgi-bin/luci"
|
||||
headers = {
|
||||
"User-Agent": "BTC Tools v0.1", # only seems to respond if this user-agent is set
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
|
||||
@@ -29,6 +29,7 @@ class ePICWebAPI(BaseWebAPI):
|
||||
self.username = "root"
|
||||
self.pwd = settings.get("default_epic_password", "letmein")
|
||||
self.token = None
|
||||
self.port = 4028
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
@@ -50,13 +51,13 @@ class ePICWebAPI(BaseWebAPI):
|
||||
"password": self.pwd,
|
||||
}
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:4028/{command}",
|
||||
f"http://{self.ip}:{self.port}/{command}",
|
||||
timeout=5,
|
||||
json=epic_param,
|
||||
)
|
||||
else:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}:4028/{command}",
|
||||
f"http://{self.ip}:{self.port}/{command}",
|
||||
timeout=5,
|
||||
)
|
||||
if not response.status_code == 200:
|
||||
|
||||
@@ -33,10 +33,10 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
async def auth(self):
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
await client.get(f"http://{self.ip}/user/logout")
|
||||
await client.get(f"http://{self.ip}:{self.port}/user/logout")
|
||||
auth = (
|
||||
await client.get(
|
||||
f"http://{self.ip}/user/login?username={self.username}&password={self.pwd}&cipher=false"
|
||||
f"http://{self.ip}:{self.port}/user/login?username={self.username}&password={self.pwd}&cipher=false"
|
||||
)
|
||||
).json()
|
||||
except httpx.HTTPError:
|
||||
@@ -46,7 +46,7 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
try:
|
||||
auth = (
|
||||
await client.get(
|
||||
f"http://{self.ip}/user/login?username=admin&password=bbad7537f4c8b6ea31eea0b3d760e257&cipher=true"
|
||||
f"http://{self.ip}:{self.port}/user/login?username=admin&password=bbad7537f4c8b6ea31eea0b3d760e257&cipher=true"
|
||||
)
|
||||
).json()
|
||||
except (httpx.HTTPError, json.JSONDecodeError):
|
||||
@@ -76,14 +76,14 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
try:
|
||||
if parameters:
|
||||
response = await client.put(
|
||||
f"http://{self.ip}/mcb/{command}",
|
||||
f"http://{self.ip}:{self.port}/mcb/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}/mcb/{command}",
|
||||
f"http://{self.ip}:{self.port}/mcb/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
@@ -106,7 +106,7 @@ class GoldshellWebAPI(BaseWebAPI):
|
||||
for command in commands:
|
||||
try:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}/mcb/{command}",
|
||||
f"http://{self.ip}:{self.port}/mcb/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
auth = await client.post(
|
||||
f"http://{self.ip}/api/auth",
|
||||
f"http://{self.ip}:{self.port}/api/auth",
|
||||
data={"username": self.username, "password": self.pwd},
|
||||
)
|
||||
except httpx.HTTPError:
|
||||
@@ -58,7 +58,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
for i in range(settings.get("get_data_retries", 1)):
|
||||
try:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}/api/{command}",
|
||||
f"http://{self.ip}:{self.port}/api/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
@@ -94,7 +94,7 @@ class InnosiliconWebAPI(BaseWebAPI):
|
||||
for command in commands:
|
||||
try:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}/api/{command}",
|
||||
f"http://{self.ip}:{self.port}/api/{command}",
|
||||
headers={"Authorization": "Bearer " + self.jwt},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
|
||||
@@ -34,7 +34,7 @@ class VNishWebAPI(BaseWebAPI):
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
auth = await client.post(
|
||||
f"http://{self.ip}/api/v1/unlock",
|
||||
f"http://{self.ip}:{self.port}/api/v1/unlock",
|
||||
json={"pw": self.pwd},
|
||||
)
|
||||
except httpx.HTTPError:
|
||||
@@ -68,21 +68,21 @@ class VNishWebAPI(BaseWebAPI):
|
||||
if parameters.get("post"):
|
||||
parameters.pop("post")
|
||||
response = await client.post(
|
||||
f"http://{self.ip}/api/v1/{command}",
|
||||
f"http://{self.ip}:{self.port}/api/v1/{command}",
|
||||
headers={"Authorization": auth},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
)
|
||||
elif not parameters == {}:
|
||||
response = await client.post(
|
||||
f"http://{self.ip}/api/v1/{command}",
|
||||
f"http://{self.ip}:{self.port}/api/v1/{command}",
|
||||
headers={"Authorization": auth},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
response = await client.get(
|
||||
f"http://{self.ip}/api/v1/{command}",
|
||||
f"http://{self.ip}:{self.port}/api/v1/{command}",
|
||||
headers={"Authorization": auth},
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
|
||||
@@ -13,31 +13,27 @@
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
import asyncio
|
||||
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
|
||||
from pyasic.miners.miner_factory import MINER_CLASSES, MinerFactory
|
||||
from pyasic.miners.miner_factory import MINER_CLASSES
|
||||
|
||||
|
||||
class MinersTest(unittest.TestCase):
|
||||
def test_miner_model_creation(self):
|
||||
def test_miner_type_creation(self):
|
||||
warnings.filterwarnings("ignore")
|
||||
for miner_model in MINER_CLASSES.keys():
|
||||
for miner_api in MINER_CLASSES[miner_model].keys():
|
||||
for miner_type in MINER_CLASSES.keys():
|
||||
for miner_model in MINER_CLASSES[miner_type].keys():
|
||||
with self.subTest(
|
||||
msg=f"Creation of miner using model={miner_model}, api={miner_api}",
|
||||
msg=f"Test creation of miner",
|
||||
miner_type=miner_type,
|
||||
miner_model=miner_model,
|
||||
miner_api=miner_api,
|
||||
):
|
||||
miner = MINER_CLASSES[miner_model][miner_api]("127.0.0.1")
|
||||
miner = MINER_CLASSES[miner_type][miner_model]("127.0.0.1")
|
||||
self.assertTrue(
|
||||
isinstance(miner, MINER_CLASSES[miner_model][miner_api])
|
||||
isinstance(miner, MINER_CLASSES[miner_type][miner_model])
|
||||
)
|
||||
|
||||
def test_miner_data_map_keys(self):
|
||||
@@ -56,7 +52,6 @@ class MinersTest(unittest.TestCase):
|
||||
"hostname",
|
||||
"is_mining",
|
||||
"mac",
|
||||
"model",
|
||||
"expected_hashrate",
|
||||
"uptime",
|
||||
"wattage",
|
||||
@@ -64,14 +59,14 @@ class MinersTest(unittest.TestCase):
|
||||
]
|
||||
)
|
||||
warnings.filterwarnings("ignore")
|
||||
for miner_model in MINER_CLASSES.keys():
|
||||
for miner_api in MINER_CLASSES[miner_model].keys():
|
||||
for miner_type in MINER_CLASSES.keys():
|
||||
for miner_model in MINER_CLASSES[miner_type].keys():
|
||||
with self.subTest(
|
||||
msg=f"Data map key check of miner using model={miner_model}, api={miner_api}",
|
||||
msg=f"Data map key check",
|
||||
miner_type=miner_type,
|
||||
miner_model=miner_model,
|
||||
miner_api=miner_api,
|
||||
):
|
||||
miner = MINER_CLASSES[miner_model][miner_api]("127.0.0.1")
|
||||
miner = MINER_CLASSES[miner_type][miner_model]("127.0.0.1")
|
||||
miner_keys = sorted(
|
||||
[str(k) for k in asdict(miner.data_locations).keys()]
|
||||
)
|
||||
@@ -79,14 +74,14 @@ class MinersTest(unittest.TestCase):
|
||||
|
||||
def test_data_locations_match_signatures_command(self):
|
||||
warnings.filterwarnings("ignore")
|
||||
for miner_model in MINER_CLASSES.keys():
|
||||
for miner_api in MINER_CLASSES[miner_model].keys():
|
||||
miner = MINER_CLASSES[miner_model][miner_api]("127.0.0.1")
|
||||
for miner_type in MINER_CLASSES.keys():
|
||||
for miner_model in MINER_CLASSES[miner_type].keys():
|
||||
miner = MINER_CLASSES[miner_type][miner_model]("127.0.0.1")
|
||||
for data_point in asdict(miner.data_locations).values():
|
||||
with self.subTest(
|
||||
msg=f"Test {data_point['cmd']} signature matches with model={miner_model}, api={miner_api}",
|
||||
msg=f"Test {data_point['cmd']} signature matches",
|
||||
miner_type=miner_type,
|
||||
miner_model=miner_model,
|
||||
miner_api=miner_api,
|
||||
):
|
||||
func = getattr(miner, data_point["cmd"])
|
||||
signature = inspect.signature(func)
|
||||
|
||||
Reference in New Issue
Block a user