Merge pull request #125 from UpstreamData/dev_marafw
feature: Add maraFW support.
This commit is contained in:
@@ -138,6 +138,15 @@ class MinerConfig:
|
||||
**self.power_scaling.as_auradine(),
|
||||
}
|
||||
|
||||
def as_mara(self, user_suffix: str = None) -> dict:
|
||||
return {
|
||||
**self.fan_mode.as_mara(),
|
||||
**self.temperature.as_mara(),
|
||||
**self.mining_mode.as_mara(),
|
||||
**self.pools.as_mara(user_suffix=user_suffix),
|
||||
**self.power_scaling.as_mara(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
|
||||
"""Constructs a MinerConfig object from a dictionary."""
|
||||
@@ -228,3 +237,11 @@ class MinerConfig:
|
||||
fan_mode=FanModeConfig.from_auradine(web_conf["fan"]),
|
||||
mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_mara(cls, web_miner_config: dict) -> "MinerConfig":
|
||||
return cls(
|
||||
pools=PoolConfig.from_mara(web_miner_config),
|
||||
fan_mode=FanModeConfig.from_mara(web_miner_config),
|
||||
mining_mode=MiningModeConfig.from_mara(web_miner_config),
|
||||
)
|
||||
|
||||
@@ -57,6 +57,9 @@ class MinerConfigOption(Enum):
|
||||
def as_auradine(self) -> dict:
|
||||
return self.value.as_auradine()
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return self.value.as_mara()
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.value(*args, **kwargs)
|
||||
|
||||
@@ -106,3 +109,6 @@ class MinerConfigValue:
|
||||
|
||||
def as_auradine(self) -> dict:
|
||||
return {}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {}
|
||||
|
||||
@@ -71,6 +71,15 @@ class FanModeNormal(MinerConfigValue):
|
||||
}
|
||||
}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"general-config": {"environment-profile": "AirCooling"},
|
||||
"advance-config": {
|
||||
"override-fan-control": False,
|
||||
"fan-fixed-percent": 0,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class FanModeManual(MinerConfigValue):
|
||||
@@ -120,6 +129,15 @@ class FanModeManual(MinerConfigValue):
|
||||
def as_epic(self) -> dict:
|
||||
return {"fans": {"Manual": {"speed": self.speed}}}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"general-config": {"environment-profile": "AirCooling"},
|
||||
"advance-config": {
|
||||
"override-fan-control": True,
|
||||
"fan-fixed-percent": self.speed,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class FanModeImmersion(MinerConfigValue):
|
||||
@@ -140,6 +158,9 @@ class FanModeImmersion(MinerConfigValue):
|
||||
def as_auradine(self) -> dict:
|
||||
return {"fan": {"percentage": 0}}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {"general-config": {"environment-profile": "OilImmersionCooling"}}
|
||||
|
||||
|
||||
class FanModeConfig(MinerConfigOption):
|
||||
normal = FanModeNormal
|
||||
@@ -255,4 +276,18 @@ class FanModeConfig(MinerConfigOption):
|
||||
fan_1_target = fan_data["Target"]
|
||||
return cls.manual(speed=round((fan_1_target / fan_1_max) * 100))
|
||||
except LookupError:
|
||||
return cls.default()
|
||||
pass
|
||||
return cls.default()
|
||||
|
||||
@classmethod
|
||||
def from_mara(cls, web_config: dict):
|
||||
try:
|
||||
mode = web_config["general-config"]["environment-profile"]
|
||||
if mode == "AirCooling":
|
||||
if web_config["advance-config"]["override-fan-control"]:
|
||||
return cls.manual(web_config["advance-config"]["fan-fixed-percent"])
|
||||
return cls.normal()
|
||||
return cls.immersion()
|
||||
except LookupError:
|
||||
pass
|
||||
return cls.default()
|
||||
|
||||
@@ -56,6 +56,13 @@ class MiningModeNormal(MinerConfigValue):
|
||||
def as_goldshell(self) -> dict:
|
||||
return {"settings": {"level": 0}}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"mode": {
|
||||
"work-mode-selector": "Stock",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MiningModeSleep(MinerConfigValue):
|
||||
@@ -82,6 +89,13 @@ class MiningModeSleep(MinerConfigValue):
|
||||
def as_goldshell(self) -> dict:
|
||||
return {"settings": {"level": 3}}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"mode": {
|
||||
"work-mode-selector": "Sleep",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MiningModeLPM(MinerConfigValue):
|
||||
@@ -219,6 +233,17 @@ class MiningModePowerTune(MinerConfigValue):
|
||||
def as_auradine(self) -> dict:
|
||||
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"mode": {
|
||||
"work-mode-selector": "Auto",
|
||||
"concorde": {
|
||||
"mode-select": "PowerTarget",
|
||||
"power-target": self.power,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MiningModeHashrateTune(MinerConfigValue):
|
||||
@@ -269,6 +294,17 @@ class MiningModeHashrateTune(MinerConfigValue):
|
||||
def as_epic(self) -> dict:
|
||||
return {"ptune": {"algo": self.algo.as_epic(), "target": self.hashrate}}
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"mode": {
|
||||
"work-mode-selector": "Auto",
|
||||
"concorde": {
|
||||
"mode-select": "Hashrate",
|
||||
"hash-target": self.hashrate,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ManualBoardSettings(MinerConfigValue):
|
||||
@@ -320,6 +356,17 @@ class MiningModeManual(MinerConfigValue):
|
||||
}
|
||||
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
||||
|
||||
def as_mara(self) -> dict:
|
||||
return {
|
||||
"mode": {
|
||||
"work-mode-selector": "Fixed",
|
||||
"fixed": {
|
||||
"frequency": str(self.global_freq),
|
||||
"voltage": self.global_volt,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MiningModeConfig(MinerConfigOption):
|
||||
normal = MiningModeNormal
|
||||
@@ -468,3 +515,28 @@ class MiningModeConfig(MinerConfigOption):
|
||||
return cls.power_tuning(mode_data["Power"])
|
||||
except LookupError:
|
||||
return cls.default()
|
||||
|
||||
@classmethod
|
||||
def from_mara(cls, web_config: dict):
|
||||
try:
|
||||
mode = web_config["mode"]["work-mode-selector"]
|
||||
if mode == "Fixed":
|
||||
fixed_conf = web_config["mode"]["fixed"]
|
||||
return cls.manual(
|
||||
global_freq=int(fixed_conf["frequency"]),
|
||||
global_volt=fixed_conf["voltage"],
|
||||
)
|
||||
elif mode == "Stock":
|
||||
return cls.normal()
|
||||
elif mode == "Sleep":
|
||||
return cls.sleep()
|
||||
elif mode == "Auto":
|
||||
auto_conf = web_config["mode"]["concorde"]
|
||||
auto_mode = auto_conf["mode-select"]
|
||||
if auto_mode == "Hashrate":
|
||||
return cls.hashrate_tuning(hashrate=auto_conf["hash-target"])
|
||||
elif auto_mode == "PowerTarget":
|
||||
return cls.power_tuning(power=auto_conf["power-target"])
|
||||
except LookupError:
|
||||
pass
|
||||
return cls.default()
|
||||
|
||||
@@ -118,6 +118,15 @@ class Pool(MinerConfigValue):
|
||||
}
|
||||
return {"pool": self.url, "login": self.user, "password": self.password}
|
||||
|
||||
def as_mara(self, user_suffix: str = None) -> dict:
|
||||
if user_suffix is not None:
|
||||
return {
|
||||
"url": self.url,
|
||||
"user": f"{self.user}{user_suffix}",
|
||||
"pass": self.password,
|
||||
}
|
||||
return {"url": self.url, "user": self.user, "pass": self.password}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict_conf: dict | None) -> "Pool":
|
||||
return cls(
|
||||
@@ -177,6 +186,14 @@ class Pool(MinerConfigValue):
|
||||
password=grpc_pool["password"],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_mara(cls, web_pool: dict) -> "Pool":
|
||||
return cls(
|
||||
url=web_pool["url"],
|
||||
user=web_pool["user"],
|
||||
password=web_pool["pass"],
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class PoolGroup(MinerConfigValue):
|
||||
@@ -264,9 +281,12 @@ class PoolGroup(MinerConfigValue):
|
||||
def as_auradine(self, user_suffix: str = None) -> list:
|
||||
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
|
||||
|
||||
def as_epic(self, user_suffix: str = None) -> dict:
|
||||
def as_epic(self, user_suffix: str = None) -> list:
|
||||
return [p.as_epic(user_suffix=user_suffix) for p in self.pools]
|
||||
|
||||
def as_mara(self, user_suffix: str = None) -> list:
|
||||
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
|
||||
cls_conf = {}
|
||||
@@ -336,6 +356,10 @@ class PoolGroup(MinerConfigValue):
|
||||
except LookupError:
|
||||
return cls()
|
||||
|
||||
@classmethod
|
||||
def from_mara(cls, web_config_pools: dict) -> "PoolGroup":
|
||||
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
|
||||
|
||||
|
||||
@dataclass
|
||||
class PoolConfig(MinerConfigValue):
|
||||
@@ -427,6 +451,11 @@ class PoolConfig(MinerConfigValue):
|
||||
}
|
||||
}
|
||||
|
||||
def as_mara(self, user_suffix: str = None) -> dict:
|
||||
if len(self.groups) > 0:
|
||||
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
|
||||
return {"pools": []}
|
||||
|
||||
@classmethod
|
||||
def from_api(cls, api_pools: dict) -> "PoolConfig":
|
||||
try:
|
||||
@@ -481,3 +510,7 @@ class PoolConfig(MinerConfigValue):
|
||||
)
|
||||
except LookupError:
|
||||
return cls()
|
||||
|
||||
@classmethod
|
||||
def from_mara(cls, web_config: dict) -> "PoolConfig":
|
||||
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
|
||||
|
||||
@@ -1,71 +1,69 @@
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
from pyasic import MinerConfig
|
||||
from pyasic.config import MiningModeConfig
|
||||
from pyasic.data import Fan, HashBoard
|
||||
from pyasic.errors import APIError
|
||||
from pyasic.miners.backends import AntminerModern
|
||||
from pyasic.miners.data import (
|
||||
DataFunction,
|
||||
DataLocations,
|
||||
DataOptions,
|
||||
RPCAPICommand,
|
||||
WebAPICommand,
|
||||
)
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
|
||||
from pyasic.misc import merge_dicts
|
||||
from pyasic.web.marathon import MaraWebAPI
|
||||
|
||||
MARA_DATA_LOC = DataLocations(
|
||||
**{
|
||||
str(DataOptions.MAC): DataFunction(
|
||||
"_get_mac",
|
||||
[WebAPICommand("web_get_system_info", "get_system_info")],
|
||||
),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
"_get_api_ver",
|
||||
[RPCAPICommand("rpc_version", "version")],
|
||||
[WebAPICommand("web_overview", "overview")],
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"_get_fw_ver",
|
||||
[RPCAPICommand("rpc_version", "version")],
|
||||
[WebAPICommand("web_overview", "overview")],
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction(
|
||||
"_get_hostname",
|
||||
[WebAPICommand("web_get_system_info", "get_system_info")],
|
||||
[WebAPICommand("web_network_config", "network_config")],
|
||||
),
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"_get_hashrate",
|
||||
[RPCAPICommand("rpc_summary", "summary")],
|
||||
[WebAPICommand("web_brief", "brief")],
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"_get_expected_hashrate",
|
||||
[RPCAPICommand("rpc_stats", "stats")],
|
||||
[WebAPICommand("web_brief", "brief")],
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"_get_fans",
|
||||
[RPCAPICommand("rpc_stats", "stats")],
|
||||
),
|
||||
str(DataOptions.ERRORS): DataFunction(
|
||||
"_get_errors",
|
||||
[WebAPICommand("web_summary", "summary")],
|
||||
),
|
||||
str(DataOptions.FAULT_LIGHT): DataFunction(
|
||||
"_get_fault_light",
|
||||
[WebAPICommand("web_get_blink_status", "get_blink_status")],
|
||||
),
|
||||
str(DataOptions.IS_MINING): DataFunction(
|
||||
"_is_mining",
|
||||
[WebAPICommand("web_get_conf", "get_miner_conf")],
|
||||
),
|
||||
str(DataOptions.UPTIME): DataFunction(
|
||||
"_get_uptime",
|
||||
[RPCAPICommand("rpc_stats", "stats")],
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"_get_hashboards",
|
||||
[WebAPICommand("web_hashboards", "hashboards")],
|
||||
),
|
||||
str(DataOptions.WATTAGE): DataFunction(
|
||||
"_get_wattage",
|
||||
[WebAPICommand("web_brief", "brief")],
|
||||
),
|
||||
str(DataOptions.WATTAGE_LIMIT): DataFunction(
|
||||
"_get_wattage_limit",
|
||||
[WebAPICommand("web_miner_config", "miner_config")],
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"_get_fans",
|
||||
[WebAPICommand("web_fans", "fans")],
|
||||
),
|
||||
str(DataOptions.FAULT_LIGHT): DataFunction(
|
||||
"_get_fault_light",
|
||||
[WebAPICommand("web_locate_miner", "locate_miner")],
|
||||
),
|
||||
str(DataOptions.IS_MINING): DataFunction(
|
||||
"_is_mining",
|
||||
[WebAPICommand("web_brief", "brief")],
|
||||
),
|
||||
str(DataOptions.UPTIME): DataFunction(
|
||||
"_get_uptime",
|
||||
[WebAPICommand("web_brief", "brief")],
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class MaraMiner(AntminerModern):
|
||||
class MaraMiner(BaseMiner):
|
||||
_web_cls = MaraWebAPI
|
||||
web: MaraWebAPI
|
||||
|
||||
@@ -73,6 +71,52 @@ class MaraMiner(AntminerModern):
|
||||
|
||||
firmware = "MaraFW"
|
||||
|
||||
async def fault_light_off(self) -> bool:
|
||||
res = await self.web.set_locate_miner(blinking=False)
|
||||
return res.get("blinking") is False
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
res = await self.web.set_locate_miner(blinking=True)
|
||||
return res.get("blinking") is True
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
data = await self.web.get_miner_config()
|
||||
if data:
|
||||
self.config = MinerConfig.from_mara(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
data = await self.web.get_miner_config()
|
||||
cfg_data = config.as_mara(user_suffix=user_suffix)
|
||||
merged_cfg = merge_dicts(data, cfg_data)
|
||||
await self.web.set_miner_config(**merged_cfg)
|
||||
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
cfg = await self.get_config()
|
||||
cfg.mining_mode = MiningModeConfig.power_tuning(wattage)
|
||||
await self.send_config(cfg)
|
||||
return True
|
||||
|
||||
async def stop_mining(self) -> bool:
|
||||
data = await self.web.get_miner_config()
|
||||
data["mode"]["work-mode-selector"] = "Sleep"
|
||||
await self.web.set_miner_config(**data)
|
||||
return True
|
||||
|
||||
async def resume_mining(self) -> bool:
|
||||
data = await self.web.get_miner_config()
|
||||
data["mode"]["work-mode-selector"] = "Auto"
|
||||
await self.web.set_miner_config(**data)
|
||||
return True
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
await self.web.reboot()
|
||||
return True
|
||||
|
||||
async def restart_backend(self) -> bool:
|
||||
await self.web.reload()
|
||||
return True
|
||||
|
||||
async def _get_wattage(self, web_brief: dict = None) -> Optional[int]:
|
||||
if web_brief is None:
|
||||
try:
|
||||
@@ -85,3 +129,170 @@ class MaraMiner(AntminerModern):
|
||||
return web_brief["power_consumption_estimated"]
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _is_mining(self, web_brief: dict = None) -> Optional[bool]:
|
||||
if web_brief is None:
|
||||
try:
|
||||
web_brief = await self.web.brief()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_brief is not None:
|
||||
try:
|
||||
return web_brief["status"] == "Mining"
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_uptime(self, web_brief: dict = None) -> Optional[int]:
|
||||
if web_brief is None:
|
||||
try:
|
||||
web_brief = await self.web.brief()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_brief is not None:
|
||||
try:
|
||||
return web_brief["elapsed"]
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_hashboards(self, web_hashboards: dict = None) -> List[HashBoard]:
|
||||
hashboards = [
|
||||
HashBoard(slot=i, expected_chips=self.expected_chips)
|
||||
for i in range(self.expected_hashboards)
|
||||
]
|
||||
|
||||
if web_hashboards is None:
|
||||
try:
|
||||
web_hashboards = await self.web.hashboards()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_hashboards is not None:
|
||||
try:
|
||||
for hb in web_hashboards["hashboards"]:
|
||||
idx = hb["index"]
|
||||
hashboards[idx].hashrate = hb["hashrate_average"]
|
||||
hashboards[idx].temp = round(
|
||||
sum(hb["temperature_pcb"]) / len(hb["temperature_pcb"]), 2
|
||||
)
|
||||
hashboards[idx].chip_temp = round(
|
||||
sum(hb["temperature_chip"]) / len(hb["temperature_chip"]), 2
|
||||
)
|
||||
hashboards[idx].chips = hb["asic_num"]
|
||||
hashboards[idx].serial_number = hb["serial_number"]
|
||||
hashboards[idx].missing = False
|
||||
except LookupError:
|
||||
pass
|
||||
return hashboards
|
||||
|
||||
async def _get_mac(self, web_overview: dict = None) -> Optional[str]:
|
||||
if web_overview is None:
|
||||
try:
|
||||
web_overview = await self.web.overview()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_overview is not None:
|
||||
try:
|
||||
return web_overview["mac"].upper()
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_fw_ver(self, web_overview: dict = None) -> Optional[str]:
|
||||
if web_overview is None:
|
||||
try:
|
||||
web_overview = await self.web.overview()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_overview is not None:
|
||||
try:
|
||||
return web_overview["version_firmware"]
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_hostname(self, web_network_config: dict = None) -> Optional[str]:
|
||||
if web_network_config is None:
|
||||
try:
|
||||
web_network_config = await self.web.get_network_config()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_network_config is not None:
|
||||
try:
|
||||
return web_network_config["hostname"]
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_hashrate(self, web_brief: dict = None) -> Optional[float]:
|
||||
if web_brief is None:
|
||||
try:
|
||||
web_brief = await self.web.brief()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_brief is not None:
|
||||
try:
|
||||
return round(web_brief["hashrate_realtime"], 2)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_fans(self, web_fans: dict = None) -> List[Fan]:
|
||||
if web_fans is None:
|
||||
try:
|
||||
web_fans = await self.web.fans()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_fans is not None:
|
||||
fans = []
|
||||
for n in range(self.expected_fans):
|
||||
try:
|
||||
fans.append(Fan(web_fans["fans"][n]["current_speed"]))
|
||||
except (IndexError, KeyError):
|
||||
pass
|
||||
return fans
|
||||
return [Fan() for _ in range(self.expected_fans)]
|
||||
|
||||
async def _get_fault_light(self, web_locate_miner: dict = None) -> bool:
|
||||
if web_locate_miner is None:
|
||||
try:
|
||||
web_locate_miner = await self.web.get_locate_miner()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_locate_miner is not None:
|
||||
try:
|
||||
return web_locate_miner["blinking"]
|
||||
except LookupError:
|
||||
pass
|
||||
return False
|
||||
|
||||
async def _get_expected_hashrate(self, web_brief: dict = None) -> Optional[float]:
|
||||
if web_brief is None:
|
||||
try:
|
||||
web_brief = await self.web.brief()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_brief is not None:
|
||||
try:
|
||||
return round(web_brief["hashrate_ideal"], 2)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
async def _get_wattage_limit(
|
||||
self, web_miner_config: dict = None
|
||||
) -> Optional[float]:
|
||||
if web_miner_config is None:
|
||||
try:
|
||||
web_miner_config = await self.web.get_miner_config()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_miner_config is not None:
|
||||
try:
|
||||
return web_miner_config["mode"]["concorde"]["power-target"]
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
@@ -553,7 +553,17 @@ class MinerFactory:
|
||||
and self._parse_web_type(x[0], x[1]) is not None,
|
||||
)
|
||||
if text is not None:
|
||||
return self._parse_web_type(text, resp)
|
||||
mtype = self._parse_web_type(text, resp)
|
||||
if mtype == MinerTypes.ANTMINER:
|
||||
# could still be mara
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
res = await self.send_web_command(
|
||||
ip, "/kaonsu/v1/brief", auth=auth
|
||||
)
|
||||
if res is not None:
|
||||
mtype = MinerTypes.MARATHON
|
||||
return mtype
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def _web_ping(
|
||||
|
||||
@@ -1,30 +1,56 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from pyasic import settings
|
||||
from pyasic.web.antminer import AntminerModernWebAPI
|
||||
from pyasic.web.base import BaseWebAPI
|
||||
|
||||
|
||||
class MaraWebAPI(AntminerModernWebAPI):
|
||||
class MaraWebAPI(BaseWebAPI):
|
||||
def __init__(self, ip: str) -> None:
|
||||
self.am_commands = [
|
||||
"get_miner_conf",
|
||||
"set_miner_conf",
|
||||
"blink",
|
||||
"reboot",
|
||||
"get_system_info",
|
||||
"get_network_info",
|
||||
"summary",
|
||||
"get_blink_status",
|
||||
"set_network_conf",
|
||||
]
|
||||
super().__init__(ip)
|
||||
|
||||
async def _send_mara_command(
|
||||
async def multicommand(
|
||||
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||
) -> dict:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
tasks = [
|
||||
asyncio.create_task(self._handle_multicommand(client, command))
|
||||
for command in commands
|
||||
]
|
||||
all_data = await asyncio.gather(*tasks)
|
||||
|
||||
data = {}
|
||||
for item in all_data:
|
||||
data.update(item)
|
||||
|
||||
data["multicommand"] = True
|
||||
return data
|
||||
|
||||
async def _handle_multicommand(
|
||||
self, client: httpx.AsyncClient, command: str
|
||||
) -> dict:
|
||||
auth = httpx.DigestAuth(self.username, self.pwd)
|
||||
|
||||
try:
|
||||
url = f"http://{self.ip}:{self.port}/kaonsu/v1/{command}"
|
||||
ret = await client.get(url, auth=auth)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if ret.status_code == 200:
|
||||
try:
|
||||
json_data = ret.json()
|
||||
return {command: json_data}
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
return {command: {}}
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
@@ -56,76 +82,66 @@ class MaraWebAPI(AntminerModernWebAPI):
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
|
||||
async def _send_am_command(
|
||||
self,
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
):
|
||||
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:
|
||||
if parameters:
|
||||
data = await client.post(
|
||||
url,
|
||||
auth=auth,
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
data = await client.get(url, auth=auth)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if data.status_code == 200:
|
||||
try:
|
||||
return data.json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
|
||||
async def send_command(
|
||||
self,
|
||||
command: str | bytes,
|
||||
ignore_errors: bool = False,
|
||||
allow_warning: bool = True,
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
if command in self.am_commands:
|
||||
return await self._send_am_command(
|
||||
command,
|
||||
ignore_errors=ignore_errors,
|
||||
allow_warning=allow_warning,
|
||||
privileged=privileged,
|
||||
**parameters,
|
||||
)
|
||||
return await self._send_mara_command(
|
||||
command,
|
||||
ignore_errors=ignore_errors,
|
||||
allow_warning=allow_warning,
|
||||
privileged=privileged,
|
||||
**parameters,
|
||||
)
|
||||
|
||||
async def brief(self):
|
||||
return await self.send_command("brief")
|
||||
|
||||
async def ping(self):
|
||||
return await self.send_command("ping")
|
||||
|
||||
async def get_locate_miner(self):
|
||||
return await self.send_command("locate_miner")
|
||||
|
||||
async def set_locate_miner(self, blinking: bool):
|
||||
return await self.send_command("locate_miner", blinking=blinking)
|
||||
|
||||
async def reboot(self):
|
||||
return await self.send_command("maintenance", type="reboot")
|
||||
|
||||
async def reset(self):
|
||||
return await self.send_command("maintenance", type="reset")
|
||||
|
||||
async def reload(self):
|
||||
return await self.send_command("maintenance", type="reload")
|
||||
|
||||
async def set_password(self, new_pwd: str):
|
||||
return await self.send_command(
|
||||
"maintenance",
|
||||
type="passwd",
|
||||
params={"curPwd": self.pwd, "confirmPwd": self.pwd, "newPwd": new_pwd},
|
||||
)
|
||||
|
||||
async def get_network_config(self):
|
||||
return await self.send_command("network_config")
|
||||
|
||||
async def set_network_config(self, **params):
|
||||
return await self.send_command("network_config", **params)
|
||||
|
||||
async def get_miner_config(self):
|
||||
return await self.send_command("miner_config")
|
||||
|
||||
async def set_miner_config(self, **params):
|
||||
return await self.send_command("miner_config", **params)
|
||||
|
||||
async def fans(self):
|
||||
return await self.send_command("fans")
|
||||
|
||||
async def log(self):
|
||||
return await self.send_command("log")
|
||||
|
||||
async def overview(self):
|
||||
return await self.send_command("overview")
|
||||
|
||||
async def connections(self):
|
||||
return await self.send_command("connections")
|
||||
|
||||
async def controlboard_info(self):
|
||||
return await self.send_command("controlboard_info")
|
||||
|
||||
async def event_chart(self):
|
||||
return await self.send_command("event_chart")
|
||||
|
||||
async def hashboards(self):
|
||||
return await self.send_command("hashboards")
|
||||
|
||||
async def mara_pools(self):
|
||||
return await self._send_mara_command("pools")
|
||||
async def pools(self):
|
||||
return await self.send_command("pools")
|
||||
|
||||
Reference in New Issue
Block a user