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