Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15fc27e6fa | ||
|
|
943ebc77a1 | ||
|
|
733437ef03 | ||
|
|
b444245e98 | ||
|
|
481d31a0f1 | ||
|
|
264db3bdd6 | ||
|
|
d292b9c195 | ||
|
|
dce25a679f | ||
|
|
c903631742 | ||
|
|
e70bfdc886 | ||
|
|
8e1803add1 | ||
|
|
7d61056ea3 | ||
|
|
0d497baa45 | ||
|
|
d3a71c5a93 | ||
|
|
895a5b7ac8 | ||
|
|
7a5a0b287c | ||
|
|
c7d73276c8 | ||
|
|
4bbb9d0b08 |
@@ -76,3 +76,13 @@ SAMPLE CONFIG
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def general_config_convert_pools(config: dict):
|
||||
out_config = {}
|
||||
pools = config.get("pool_groups")
|
||||
if pools:
|
||||
if len(pools) > 0:
|
||||
pools = pools[0]
|
||||
out_config = pools["pools"][:3]
|
||||
return out_config
|
||||
|
||||
317
config/miner_config.py
Normal file
317
config/miner_config.py
Normal file
@@ -0,0 +1,317 @@
|
||||
from dataclasses import dataclass, asdict
|
||||
from typing import List, Literal
|
||||
import random
|
||||
import string
|
||||
|
||||
import toml
|
||||
import yaml
|
||||
import json
|
||||
import time
|
||||
|
||||
|
||||
@dataclass
|
||||
class _Pool:
|
||||
url: str = ""
|
||||
username: str = ""
|
||||
password: str = ""
|
||||
|
||||
def from_dict(self, data: dict):
|
||||
for key in data.keys():
|
||||
if key == "url":
|
||||
self.url = data[key]
|
||||
if key in ["user", "username"]:
|
||||
self.username = data[key]
|
||||
if key in ["pass", "password"]:
|
||||
self.password = data[key]
|
||||
return self
|
||||
|
||||
def as_x19(self, user_suffix: str = None):
|
||||
username = self.username
|
||||
if user_suffix:
|
||||
username = f"{username}{user_suffix}"
|
||||
|
||||
pool = {"url": self.url, "user": username, "pass": self.password}
|
||||
return pool
|
||||
|
||||
def as_bos(self, user_suffix: str = None):
|
||||
username = self.username
|
||||
if user_suffix:
|
||||
username = f"{username}{user_suffix}"
|
||||
|
||||
pool = {"url": self.url, "user": username, "password": self.password}
|
||||
return pool
|
||||
|
||||
|
||||
@dataclass
|
||||
class _PoolGroup:
|
||||
quota: int = 1
|
||||
group_name: str = None
|
||||
pools: List[_Pool] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.group_name:
|
||||
self.group_name = "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(6)
|
||||
) # generate random pool group name in case it isn't set
|
||||
|
||||
def from_dict(self, data: dict):
|
||||
pools = []
|
||||
for key in data.keys():
|
||||
if key in ["name", "group_name"]:
|
||||
self.group_name = data[key]
|
||||
if key == "quota":
|
||||
self.quota = data[key]
|
||||
if key in ["pools", "pool"]:
|
||||
for pool in data[key]:
|
||||
pools.append(_Pool().from_dict(pool))
|
||||
self.pools = pools
|
||||
return self
|
||||
|
||||
def as_x19(self, user_suffix: str = None):
|
||||
pools = []
|
||||
for pool in self.pools[:3]:
|
||||
pools.append(pool.as_x19(user_suffix=user_suffix))
|
||||
return pools
|
||||
|
||||
def as_bos(self, user_suffix: str = None):
|
||||
group = {
|
||||
"name": self.group_name,
|
||||
"quota": self.quota,
|
||||
"pool": [pool.as_bos(user_suffix=user_suffix) for pool in self.pools],
|
||||
}
|
||||
return group
|
||||
|
||||
|
||||
@dataclass
|
||||
class MinerConfig:
|
||||
pool_groups: List[_PoolGroup] = None
|
||||
|
||||
temp_mode: Literal["auto", "manual", "disabled"] = "auto"
|
||||
temp_target: float = 70.0
|
||||
temp_hot: float = 80.0
|
||||
temp_dangerous: float = 10.0
|
||||
|
||||
minimum_fans: int = None
|
||||
fan_speed: Literal[tuple(range(101))] = None # noqa - Ignore weird Literal usage
|
||||
|
||||
asicboost: bool = None
|
||||
|
||||
autotuning_enabled: bool = True
|
||||
autotuning_wattage: int = 900
|
||||
|
||||
dps_enabled: bool = None
|
||||
dps_power_step: int = None
|
||||
dps_min_power: int = None
|
||||
dps_shutdown_enabled: bool = None
|
||||
dps_shutdown_duration: float = None
|
||||
|
||||
def as_dict(self):
|
||||
data_dict = asdict(self)
|
||||
for key in asdict(self).keys():
|
||||
if data_dict[key] is None:
|
||||
del data_dict[key]
|
||||
return data_dict
|
||||
|
||||
def as_toml(self):
|
||||
return toml.dumps(self.as_dict())
|
||||
|
||||
def as_yaml(self):
|
||||
return yaml.dump(self.as_dict(), sort_keys=False)
|
||||
|
||||
def from_raw(self, data: dict):
|
||||
pool_groups = []
|
||||
for key in data.keys():
|
||||
if key == "pools":
|
||||
pool_groups.append(_PoolGroup().from_dict({"pools": data[key]}))
|
||||
elif key == "group":
|
||||
for group in data[key]:
|
||||
pool_groups.append(_PoolGroup().from_dict(group))
|
||||
|
||||
if key == "bitmain-fan-ctrl":
|
||||
if data[key]:
|
||||
self.temp_mode = "manual"
|
||||
if data.get("bitmain-fan-pwm"):
|
||||
self.fan_speed = int(data["bitmain-fan-pwm"])
|
||||
elif key == "fan_control":
|
||||
for _key in data[key].keys():
|
||||
if _key == "min_fans":
|
||||
self.minimum_fans = data[key][_key]
|
||||
elif _key == "speed":
|
||||
self.fan_speed = data[key][_key]
|
||||
elif key == "temp_control":
|
||||
for _key in data[key].keys():
|
||||
if _key == "mode":
|
||||
self.temp_mode = data[key][_key]
|
||||
elif _key == "target_temp":
|
||||
self.temp_target = data[key][_key]
|
||||
elif _key == "hot_temp":
|
||||
self.temp_hot = data[key][_key]
|
||||
elif _key == "dangerous_temp":
|
||||
self.temp_dangerous = data[key][_key]
|
||||
|
||||
if key == "hash_chain_global":
|
||||
if data[key].get("asic_boost"):
|
||||
self.asicboost = data[key]["asic_boost"]
|
||||
|
||||
if key == "autotuning":
|
||||
for _key in data[key].keys():
|
||||
if _key == "enabled":
|
||||
self.autotuning_enabled = data[key][_key]
|
||||
elif _key == "psu_power_limit":
|
||||
self.autotuning_wattage = data[key][_key]
|
||||
|
||||
if key == "power_scaling":
|
||||
for _key in data[key].keys():
|
||||
if _key == "enabled":
|
||||
self.dps_enabled = data[key][_key]
|
||||
elif _key == "power_step":
|
||||
self.dps_power_step = data[key][_key]
|
||||
elif _key == "min_psu_power_limit":
|
||||
self.dps_min_power = data[key][_key]
|
||||
elif _key == "shutdown_enabled":
|
||||
self.dps_shutdown_enabled = data[key][_key]
|
||||
elif _key == "shutdown_duration":
|
||||
self.dps_shutdown_duration = data[key][_key]
|
||||
|
||||
self.pool_groups = pool_groups
|
||||
return self
|
||||
|
||||
def from_dict(self, data: dict):
|
||||
pool_groups = []
|
||||
for group in data["pool_groups"]:
|
||||
pool_groups.append(_PoolGroup().from_dict(group))
|
||||
for key in data.keys():
|
||||
if getattr(self, key) and not key == "pool_groups":
|
||||
setattr(self, key, data[key])
|
||||
self.pool_groups = pool_groups
|
||||
return self
|
||||
|
||||
def from_toml(self, data: str):
|
||||
return self.from_dict(toml.loads(data))
|
||||
|
||||
def from_yaml(self, data: str):
|
||||
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
|
||||
|
||||
def as_x19(self, user_suffix: str = None):
|
||||
cfg = {
|
||||
"pools": self.pool_groups[0].as_x19(user_suffix=user_suffix),
|
||||
"bitmain-fan-ctrl": False,
|
||||
"bitmain-fan-pwn": 100,
|
||||
}
|
||||
|
||||
if not self.temp_mode == "auto":
|
||||
cfg["bitmain-fan-ctrl"] = True
|
||||
|
||||
if self.fan_speed:
|
||||
cfg["bitmain-fan-ctrl"] = str(self.fan_speed)
|
||||
|
||||
return json.dumps(cfg)
|
||||
|
||||
def as_bos(self, model: str = "S9", user_suffix: str = None):
|
||||
cfg = {
|
||||
"format": {
|
||||
"version": "1.2+",
|
||||
"model": f"Antminer {model}",
|
||||
"generator": "Upstream Config Utility",
|
||||
"timestamp": int(time.time()),
|
||||
},
|
||||
"group": [
|
||||
group.as_bos(user_suffix=user_suffix) for group in self.pool_groups
|
||||
],
|
||||
"temp_control": {
|
||||
"mode": self.temp_mode,
|
||||
"target_temp": self.temp_target,
|
||||
"hot_temp": self.temp_hot,
|
||||
"dangerous_temp": self.temp_dangerous,
|
||||
},
|
||||
}
|
||||
|
||||
if self.autotuning_enabled or self.autotuning_wattage:
|
||||
cfg["autotuning"] = {}
|
||||
if self.autotuning_enabled:
|
||||
cfg["autotuning"]["enabled"] = self.autotuning_enabled
|
||||
if self.autotuning_wattage:
|
||||
cfg["autotuning"]["psu_power_limit"] = self.autotuning_wattage
|
||||
|
||||
if self.asicboost:
|
||||
cfg["hash_chain_global"] = {}
|
||||
cfg["hash_chain_global"]["asic_boost"] = self.asicboost
|
||||
|
||||
if any(
|
||||
[
|
||||
getattr(self, item)
|
||||
for item in [
|
||||
"dps_enabled",
|
||||
"dps_power_step",
|
||||
"dps_min_power",
|
||||
"dps_shutdown_enabled",
|
||||
"dps_shutdown_duration",
|
||||
]
|
||||
]
|
||||
):
|
||||
cfg["power_scaling"] = {}
|
||||
if self.dps_enabled:
|
||||
cfg["power_scaling"]["enabled"] = self.dps_enabled
|
||||
if self.dps_power_step:
|
||||
cfg["power_scaling"]["power_step"] = self.dps_power_step
|
||||
if self.dps_min_power:
|
||||
cfg["power_scaling"]["min_psu_power_limit"] = self.dps_min_power
|
||||
if self.dps_shutdown_enabled:
|
||||
cfg["power_scaling"]["shutdown_enabled"] = self.dps_shutdown_enabled
|
||||
if self.dps_shutdown_duration:
|
||||
cfg["power_scaling"]["shutdown_duration"] = self.dps_shutdown_duration
|
||||
|
||||
return toml.dumps(cfg)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pprint
|
||||
|
||||
bos_conf = {
|
||||
"group": [
|
||||
{
|
||||
"name": "group",
|
||||
"pool": [
|
||||
{
|
||||
"password": "123",
|
||||
"url": "stratum2+tcp://v2.us-east.stratum.slushpool.com/u95GEReVMjK6k5YqiSFNqqTnKU4ypU2Wm8awa6tmbmDmk1bWt",
|
||||
"user": "UpstreamDataInc.test",
|
||||
},
|
||||
{
|
||||
"password": "123",
|
||||
"url": "stratum2+tcp://v2.stratum.slushpool.com/u95GEReVMjK6k5YqiSFNqqTnKU4ypU2Wm8awa6tmbmDmk1bWt",
|
||||
"user": "UpstreamDataInc.test",
|
||||
},
|
||||
{
|
||||
"password": "123",
|
||||
"url": "stratum+tcp://stratum.slushpool.com:3333",
|
||||
"user": "UpstreamDataInc.test",
|
||||
},
|
||||
],
|
||||
"quota": 1,
|
||||
},
|
||||
{
|
||||
"name": "group_2",
|
||||
"pool": [
|
||||
{
|
||||
"password": "123",
|
||||
"url": "stratum2+tcp://v2.us-east.stratum.slushpool.com/u95GEReVMjK6k5YqiSFNqqTnKU4ypU2Wm8awa6tmbmDmk1bWt",
|
||||
"user": "UpstreamDataInc.test",
|
||||
},
|
||||
{
|
||||
"password": "123",
|
||||
"url": "stratum2+tcp://v2.stratum.slushpool.com/u95GEReVMjK6k5YqiSFNqqTnKU4ypU2Wm8awa6tmbmDmk1bWt",
|
||||
"user": "UpstreamDataInc.test",
|
||||
},
|
||||
{
|
||||
"password": "123",
|
||||
"url": "stratum+tcp://stratum.slushpool.com:3333",
|
||||
"user": "UpstreamDataInc.test",
|
||||
},
|
||||
],
|
||||
"quota": 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
pprint.pprint(MinerConfig().from_dict(bos_conf).as_dict())
|
||||
@@ -7,6 +7,7 @@ class MinerData:
|
||||
"""A Dataclass to standardize data returned from miners (specifically AnyMiner().get_data())
|
||||
|
||||
:param ip: The IP of the miner as a str.
|
||||
:param datetime: The time and date this data was generated.
|
||||
:param model: The model of the miner as a str.
|
||||
:param hostname: The network hostname of the miner as a str.
|
||||
:param hashrate: The hashrate of the miner in TH/s as a int.
|
||||
@@ -34,6 +35,7 @@ class MinerData:
|
||||
|
||||
ip: str
|
||||
datetime: datetime = None
|
||||
mac: str = "00:00:00:00:00:00"
|
||||
model: str = "Unknown"
|
||||
hostname: str = "Unknown"
|
||||
hashrate: float = 0
|
||||
|
||||
@@ -2,17 +2,23 @@ import logging
|
||||
from settings import DEBUG
|
||||
|
||||
|
||||
logging.basicConfig(
|
||||
# filename="logfile.txt",
|
||||
# filemode="a",
|
||||
format="[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
|
||||
datefmt="%x %X",
|
||||
)
|
||||
logger = logging.getLogger()
|
||||
def init_logger():
|
||||
logging.basicConfig(
|
||||
# filename="logfile.txt",
|
||||
# filemode="a",
|
||||
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
|
||||
datefmt="%x %X",
|
||||
)
|
||||
_logger = logging.getLogger()
|
||||
|
||||
if DEBUG:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logging.getLogger("asyncssh").setLevel(logging.DEBUG)
|
||||
else:
|
||||
logger.setLevel(logging.INFO)
|
||||
logging.getLogger("asyncssh").setLevel(logging.WARNING)
|
||||
if DEBUG:
|
||||
_logger.setLevel(logging.DEBUG)
|
||||
logging.getLogger("asyncssh").setLevel(logging.DEBUG)
|
||||
else:
|
||||
_logger.setLevel(logging.WARNING)
|
||||
logging.getLogger("asyncssh").setLevel(logging.WARNING)
|
||||
|
||||
return _logger
|
||||
|
||||
|
||||
logger = init_logger()
|
||||
|
||||
@@ -9,6 +9,7 @@ import datetime
|
||||
import sys
|
||||
import os
|
||||
from cx_Freeze import setup, Executable
|
||||
from setuptools import find_packages
|
||||
|
||||
base = None
|
||||
if sys.platform == "win32":
|
||||
@@ -16,7 +17,6 @@ if sys.platform == "win32":
|
||||
|
||||
version = datetime.datetime.now()
|
||||
version = version.strftime("%y.%m.%d")
|
||||
print(version)
|
||||
|
||||
|
||||
setup(
|
||||
@@ -30,9 +30,7 @@ setup(
|
||||
os.path.join(os.getcwd(), "settings/settings.toml"),
|
||||
os.path.join(os.getcwd(), "static/CFG-Util-README.md"),
|
||||
],
|
||||
"excludes": [
|
||||
os.path.join(os.getcwd(), "tools/web_testbench/files"),
|
||||
],
|
||||
"excludes": ["tests", "tools.web_testbench", "tools.web_monitor"],
|
||||
},
|
||||
},
|
||||
executables=[
|
||||
|
||||
@@ -18,6 +18,7 @@ class BaseMiner:
|
||||
self.nominal_chips = 1
|
||||
self.version = None
|
||||
self.fan_count = 2
|
||||
self.config = None
|
||||
|
||||
def __repr__(self):
|
||||
return f"{'' if not self.api_type else self.api_type} {'' if not self.model else self.model}: {str(self.ip)}"
|
||||
@@ -99,5 +100,5 @@ class BaseMiner:
|
||||
async def get_mac(self):
|
||||
return None
|
||||
|
||||
async def get_data(self):
|
||||
async def get_data(self) -> MinerData:
|
||||
return MinerData(ip=str(self.ip))
|
||||
|
||||
@@ -135,6 +135,7 @@ class BMMiner(BaseMiner):
|
||||
|
||||
model = await self.get_model()
|
||||
hostname = await self.get_hostname()
|
||||
mac = await self.get_mac()
|
||||
|
||||
if model:
|
||||
data.model = model
|
||||
@@ -142,6 +143,9 @@ class BMMiner(BaseMiner):
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
miner_data = await self.api.multicommand(
|
||||
|
||||
@@ -11,7 +11,7 @@ from API import APIError
|
||||
|
||||
from data import MinerData
|
||||
|
||||
from config.bos import bos_config_convert, general_config_convert_bos
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
from settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||
|
||||
@@ -102,8 +102,9 @@ class BOSMiner(BaseMiner):
|
||||
async with sftp.open("/etc/bosminer.toml") as file:
|
||||
toml_data = toml.loads(await file.read())
|
||||
logging.debug(f"{self}: Converting config file.")
|
||||
cfg = bos_config_convert(toml_data)
|
||||
cfg = MinerConfig().from_raw(toml_data)
|
||||
self.config = cfg
|
||||
return self.config
|
||||
|
||||
async def get_hostname(self) -> str:
|
||||
"""Get miner hostname.
|
||||
@@ -192,11 +193,17 @@ class BOSMiner(BaseMiner):
|
||||
logging.debug(f"{self}: Sending config.")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
toml_conf = toml.dumps(
|
||||
general_config_convert_bos(yaml_config, user_suffix=suffix)
|
||||
toml_conf = (
|
||||
MinerConfig()
|
||||
.from_yaml(yaml_config)
|
||||
.as_bos(model=self.model.replace(" (BOS)", ""), user_suffix=suffix)
|
||||
)
|
||||
else:
|
||||
toml_conf = toml.dumps(general_config_convert_bos(yaml_config))
|
||||
toml_conf = (
|
||||
MinerConfig()
|
||||
.from_yaml(yaml_config)
|
||||
.as_bos(model=self.model.replace(" (BOS)", ""))
|
||||
)
|
||||
async with (await self._get_ssh_connection()) as conn:
|
||||
logging.debug(f"{self}: Opening SFTP connection.")
|
||||
async with conn.start_sftp_client() as sftp:
|
||||
@@ -264,6 +271,7 @@ class BOSMiner(BaseMiner):
|
||||
|
||||
model = await self.get_model()
|
||||
hostname = await self.get_hostname()
|
||||
mac = await self.get_mac()
|
||||
|
||||
if model:
|
||||
data.model = model
|
||||
@@ -271,6 +279,9 @@ class BOSMiner(BaseMiner):
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
try:
|
||||
|
||||
@@ -41,7 +41,7 @@ class BTMiner(BaseMiner):
|
||||
self.hostname = host
|
||||
return self.hostname
|
||||
except APIError:
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
logging.info(f"Failed to get hostname for miner: {self}")
|
||||
return "?"
|
||||
except Exception:
|
||||
logging.warning(f"Failed to get hostname for miner: {self}")
|
||||
@@ -80,23 +80,41 @@ class BTMiner(BaseMiner):
|
||||
|
||||
async def get_mac(self):
|
||||
mac = ""
|
||||
data = await self.api.get_miner_info()
|
||||
data = await self.api.summary()
|
||||
if data:
|
||||
if "Msg" in data.keys():
|
||||
if "mac" in data["Msg"].keys():
|
||||
mac = data["Msg"]["mac"]
|
||||
if data.get("SUMMARY"):
|
||||
if len(data["SUMMARY"]) > 0:
|
||||
_mac = data["SUMMARY"][0].get("MAC")
|
||||
if _mac:
|
||||
mac = _mac
|
||||
if mac == "":
|
||||
try:
|
||||
data = await self.api.get_miner_info()
|
||||
if data:
|
||||
if "Msg" in data.keys():
|
||||
if "mac" in data["Msg"].keys():
|
||||
mac = data["Msg"]["mac"]
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
return str(mac).upper()
|
||||
|
||||
async def get_data(self):
|
||||
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
|
||||
|
||||
mac = None
|
||||
|
||||
try:
|
||||
model = await self.get_model()
|
||||
hostname = await self.get_hostname()
|
||||
except APIError:
|
||||
logging.warning(f"Failed to get hostname and model: {self}")
|
||||
logging.info(f"Failed to get model: {self}")
|
||||
model = None
|
||||
data.model = "Whatsminer"
|
||||
|
||||
try:
|
||||
hostname = await self.get_hostname()
|
||||
except APIError:
|
||||
logging.info(f"Failed to get hostname: {self}")
|
||||
hostname = None
|
||||
data.hostname = "Whatsminer"
|
||||
|
||||
@@ -105,6 +123,7 @@ class BTMiner(BaseMiner):
|
||||
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
try:
|
||||
@@ -125,6 +144,9 @@ class BTMiner(BaseMiner):
|
||||
summary_data = summary.get("SUMMARY")
|
||||
if summary_data:
|
||||
if len(summary_data) > 0:
|
||||
if summary_data[0].get("MAC"):
|
||||
mac = summary_data[0]["MAC"]
|
||||
|
||||
data.fan_1 = summary_data[0]["Fan Speed In"]
|
||||
data.fan_2 = summary_data[0]["Fan Speed Out"]
|
||||
|
||||
@@ -208,4 +230,14 @@ class BTMiner(BaseMiner):
|
||||
if quota:
|
||||
data.pool_split = str(quota)
|
||||
|
||||
if not mac:
|
||||
try:
|
||||
mac = await self.get_mac()
|
||||
except APIError:
|
||||
logging.info(f"Failed to get mac: {self}")
|
||||
mac = None
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
|
||||
return data
|
||||
|
||||
@@ -113,12 +113,17 @@ class CGMiner(BaseMiner):
|
||||
|
||||
model = await self.get_model()
|
||||
hostname = await self.get_hostname()
|
||||
mac = await self.get_mac()
|
||||
|
||||
if model:
|
||||
data.model = model
|
||||
|
||||
if hostname:
|
||||
data.hostname = hostname
|
||||
|
||||
if mac:
|
||||
data.mac = mac
|
||||
|
||||
miner_data = None
|
||||
for i in range(DATA_RETRIES):
|
||||
miner_data = await self.api.multicommand("summary", "pools", "stats")
|
||||
|
||||
@@ -6,5 +6,23 @@ class M21S(BaseMiner):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21S"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M21SV60(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21S V60"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M21SV20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M21S V20"
|
||||
self.nominal_chips = 66
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -2,5 +2,5 @@ from .M20S import M20S
|
||||
from .M20S_Plus import M20SPlus
|
||||
|
||||
from .M21 import M21
|
||||
from .M21S import M21S
|
||||
from .M21S import M21S, M21SV20, M21SV60
|
||||
from .M21S_Plus import M21SPlus
|
||||
|
||||
@@ -8,3 +8,39 @@ class M30S(BaseMiner):
|
||||
self.model = "M30S"
|
||||
self.nominal_chips = 148
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SV50(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S V50"
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVG20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VG20"
|
||||
self.nominal_chips = 70
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VE20"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SVE10(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S VE10"
|
||||
self.nominal_chips = 105
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -8,3 +8,30 @@ class M30SPlus(BaseMiner):
|
||||
self.model = "M30S+"
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVG60(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VG60"
|
||||
self.nominal_chips = 86
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVE40(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VE40"
|
||||
self.nominal_chips = 156
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusVF20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S+ VF20"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
from miners import BaseMiner
|
||||
|
||||
|
||||
class M30SPlusPlus(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M30SPlusPlusVG30(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++V30"
|
||||
self.model = "M30S++ V30"
|
||||
self.nominal_chips = 111
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -14,6 +23,6 @@ class M30SPlusPlusVG40(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M30S++V40"
|
||||
self.model = "M30S++ V40"
|
||||
self.nominal_chips = 117
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -8,3 +8,12 @@ class M31SPlus(BaseMiner):
|
||||
self.model = "M31S+"
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
|
||||
class M31SPlusVE20(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
super().__init__()
|
||||
self.ip = ip
|
||||
self.model = "M31S+ VE20"
|
||||
self.nominal_chips = 78
|
||||
self.fan_count = 2
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from .M30S import M30S
|
||||
from .M30S_Plus import M30SPlus
|
||||
from .M30S_Plus_Plus import M30SPlusPlusVG30, M30SPlusPlusVG40
|
||||
from .M30S import M30S, M30SVE10, M30SVE20, M30SVG20, M30SV50
|
||||
from .M30S_Plus import M30SPlus, M30SPlusVG60, M30SPlusVE40, M30SPlusVF20
|
||||
from .M30S_Plus_Plus import M30SPlusPlus, M30SPlusPlusVG30, M30SPlusPlusVG40
|
||||
|
||||
from .M31S import M31S
|
||||
from .M31S_Plus import M31SPlus
|
||||
from .M31S_Plus import M31SPlus, M31SPlusVE20
|
||||
|
||||
from .M32S import M32S
|
||||
|
||||
@@ -4,6 +4,9 @@ from miners._types import S17 # noqa - Ignore access to _module
|
||||
import httpx
|
||||
|
||||
|
||||
# TODO add config
|
||||
|
||||
|
||||
class BMMinerS17(BMMiner, S17):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
@@ -22,6 +25,19 @@ class BMMinerS17(BMMiner, S17):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -22,6 +22,19 @@ class BMMinerS17Plus(BMMiner, S17Plus):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -22,6 +22,19 @@ class BMMinerS17Pro(BMMiner, S17Pro):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -22,6 +22,19 @@ class BMMinerS17e(BMMiner, S17e):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -22,6 +22,19 @@ class BMMinerT17(BMMiner, T17):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -22,6 +22,19 @@ class BMMinerT17Plus(BMMiner, T17Plus):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -22,6 +22,19 @@ class BMMinerT17e(BMMiner, T17e):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from miners._backends import BMMiner # noqa - Ignore access to _module
|
||||
from miners._types import S19 # noqa - Ignore access to _module
|
||||
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
|
||||
class BMMinerS19(BMMiner, S19):
|
||||
@@ -10,6 +13,36 @@ class BMMinerS19(BMMiner, S19):
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
self.config = MinerConfig().from_raw(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||
url = f"http://{self.ip}/cgi-bin/set_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19(user_suffix=suffix)
|
||||
else:
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19()
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(url, data=conf, auth=auth)
|
||||
except httpx.ReadTimeout:
|
||||
pass
|
||||
for i in range(7):
|
||||
data = await self.get_config()
|
||||
if data.as_x19() == conf:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
hostname = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
@@ -23,6 +56,19 @@ class BMMinerS19(BMMiner, S19):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from miners._backends import BMMiner # noqa - Ignore access to _module
|
||||
from miners._types import S19Pro # noqa - Ignore access to _module
|
||||
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
|
||||
class BMMinerS19Pro(BMMiner, S19Pro):
|
||||
@@ -10,6 +13,36 @@ class BMMinerS19Pro(BMMiner, S19Pro):
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
self.config = MinerConfig().from_raw(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||
url = f"http://{self.ip}/cgi-bin/set_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19(user_suffix=suffix)
|
||||
else:
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19()
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(url, data=conf, auth=auth)
|
||||
except httpx.ReadTimeout:
|
||||
pass
|
||||
for i in range(7):
|
||||
data = await self.get_config()
|
||||
if data.as_x19() == conf:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
hostname = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
@@ -23,6 +56,19 @@ class BMMinerS19Pro(BMMiner, S19Pro):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from miners._backends import BMMiner # noqa - Ignore access to _module
|
||||
from miners._types import S19a # noqa - Ignore access to _module
|
||||
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
|
||||
class BMMinerS19a(BMMiner, S19a):
|
||||
@@ -10,6 +13,36 @@ class BMMinerS19a(BMMiner, S19a):
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
self.config = MinerConfig().from_raw(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||
url = f"http://{self.ip}/cgi-bin/set_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19(user_suffix=suffix)
|
||||
else:
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19()
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(url, data=conf, auth=auth)
|
||||
except httpx.ReadTimeout:
|
||||
pass
|
||||
for i in range(7):
|
||||
data = await self.get_config()
|
||||
if data.as_x19() == conf:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
hostname = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
@@ -23,6 +56,19 @@ class BMMinerS19a(BMMiner, S19a):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from miners._backends import BMMiner # noqa - Ignore access to _module
|
||||
from miners._types import S19j # noqa - Ignore access to _module
|
||||
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
|
||||
class BMMinerS19j(BMMiner, S19j):
|
||||
@@ -10,6 +13,36 @@ class BMMinerS19j(BMMiner, S19j):
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
self.config = MinerConfig().from_raw(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||
url = f"http://{self.ip}/cgi-bin/set_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19(user_suffix=suffix)
|
||||
else:
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19()
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(url, data=conf, auth=auth)
|
||||
except httpx.ReadTimeout:
|
||||
pass
|
||||
for i in range(7):
|
||||
data = await self.get_config()
|
||||
if data.as_x19() == conf:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
hostname = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
@@ -23,6 +56,19 @@ class BMMinerS19j(BMMiner, S19j):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from miners._backends import BMMiner # noqa - Ignore access to _module
|
||||
from miners._types import S19jPro # noqa - Ignore access to _module
|
||||
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
|
||||
class BMMinerS19jPro(BMMiner, S19jPro):
|
||||
@@ -10,6 +13,36 @@ class BMMinerS19jPro(BMMiner, S19jPro):
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
self.config = MinerConfig().from_raw(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||
url = f"http://{self.ip}/cgi-bin/set_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19(user_suffix=suffix)
|
||||
else:
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19()
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(url, data=conf, auth=auth)
|
||||
except httpx.ReadTimeout:
|
||||
pass
|
||||
for i in range(7):
|
||||
data = await self.get_config()
|
||||
if data.as_x19() == conf:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
hostname = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
@@ -23,6 +56,19 @@ class BMMinerS19jPro(BMMiner, S19jPro):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from miners._backends import BMMiner # noqa - Ignore access to _module
|
||||
from miners._types import T19 # noqa - Ignore access to _module
|
||||
|
||||
from config.miner_config import MinerConfig
|
||||
|
||||
import httpx
|
||||
import json
|
||||
import asyncio
|
||||
|
||||
|
||||
class BMMinerT19(BMMiner, T19):
|
||||
@@ -10,6 +13,36 @@ class BMMinerT19(BMMiner, T19):
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
url = f"http://{self.ip}/cgi-bin/get_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
self.config = MinerConfig().from_raw(data)
|
||||
return self.config
|
||||
|
||||
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||
url = f"http://{self.ip}/cgi-bin/set_miner_conf.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
if ip_user:
|
||||
suffix = str(self.ip).split(".")[-1]
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19(user_suffix=suffix)
|
||||
else:
|
||||
conf = MinerConfig().from_yaml(yaml_config).as_x19()
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient() as client:
|
||||
await client.post(url, data=conf, auth=auth)
|
||||
except httpx.ReadTimeout:
|
||||
pass
|
||||
for i in range(7):
|
||||
data = await self.get_config()
|
||||
if data.as_x19() == conf:
|
||||
break
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def get_hostname(self) -> str or None:
|
||||
hostname = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
@@ -23,6 +56,19 @@ class BMMinerT19(BMMiner, T19):
|
||||
hostname = data["hostname"]
|
||||
return hostname
|
||||
|
||||
async def get_mac(self):
|
||||
mac = None
|
||||
url = f"http://{self.ip}/cgi-bin/get_system_info.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
async with httpx.AsyncClient() as client:
|
||||
data = await client.get(url, auth=auth)
|
||||
if data.status_code == 200:
|
||||
data = data.json()
|
||||
if len(data.keys()) > 0:
|
||||
if "macaddr" in data.keys():
|
||||
mac = data["macaddr"]
|
||||
return mac
|
||||
|
||||
async def fault_light_on(self) -> bool:
|
||||
url = f"http://{self.ip}/cgi-bin/blink.cgi"
|
||||
auth = httpx.DigestAuth("root", "root")
|
||||
|
||||
@@ -135,6 +135,8 @@ MINER_CLASSES = {
|
||||
"M21S": {
|
||||
"Default": BTMinerM21S,
|
||||
"BTMiner": BTMinerM21S,
|
||||
"60": BTMinerM21SV60,
|
||||
"20": BTMinerM21SV20,
|
||||
},
|
||||
"M21S+": {
|
||||
"Default": BTMinerM21SPlus,
|
||||
@@ -143,16 +145,23 @@ MINER_CLASSES = {
|
||||
"M30S": {
|
||||
"Default": BTMinerM30S,
|
||||
"BTMiner": BTMinerM30S,
|
||||
"50": BTMinerM30SV50,
|
||||
"G20": BTMinerM30SVG20,
|
||||
"E20": BTMinerM30SVE20,
|
||||
"E10": BTMinerM30SVE10,
|
||||
},
|
||||
"M30S+": {
|
||||
"Default": BTMinerM30SPlus,
|
||||
"BTMiner": BTMinerM30SPlus,
|
||||
"F20": BTMinerM30SPlusVF20,
|
||||
"E40": BTMinerM30SPlusVE40,
|
||||
"G60": BTMinerM30SPlusVG60,
|
||||
},
|
||||
"M30S++": {
|
||||
"Default": BTMinerM30SPlusPlusVG40,
|
||||
"BTMiner": BTMinerM30SPlusPlusVG40,
|
||||
"40": BTMinerM30SPlusPlusVG40,
|
||||
"30": BTMinerM30SPlusPlusVG30,
|
||||
"Default": BTMinerM30SPlusPlus,
|
||||
"BTMiner": BTMinerM30SPlusPlus,
|
||||
"G40": BTMinerM30SPlusPlusVG40,
|
||||
"G30": BTMinerM30SPlusPlusVG30,
|
||||
},
|
||||
"M31S": {
|
||||
"Default": BTMinerM31S,
|
||||
@@ -161,6 +170,7 @@ MINER_CLASSES = {
|
||||
"M31S+": {
|
||||
"Default": BTMinerM31SPlus,
|
||||
"BTMiner": BTMinerM31SPlus,
|
||||
"E20": BTMinerM31SPlusVE20,
|
||||
},
|
||||
"M32S": {
|
||||
"Default": BTMinerM32S,
|
||||
@@ -430,12 +440,13 @@ class MinerFactory(metaclass=Singleton):
|
||||
if model:
|
||||
# whatsminer have a V in their version string (M20SV41), remove everything after it
|
||||
if "V" in model:
|
||||
ver = model.split("VG")[1]
|
||||
model = model.split("V")[0]
|
||||
_ver = model.split("V")
|
||||
if len(_ver) > 1:
|
||||
ver = model.split("V")[1]
|
||||
model = model.split("V")[0]
|
||||
# don't need "Bitmain", just "Antminer XX" as model
|
||||
if "Bitmain " in model:
|
||||
model = model.replace("Bitmain ", "")
|
||||
|
||||
return model, api, ver
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
from miners._backends import BTMiner # noqa - Ignore access to _module
|
||||
from miners._types import M21S # noqa - Ignore access to _module
|
||||
from miners._types import M21S, M21SV20, M21SV60 # noqa - Ignore access to _module
|
||||
|
||||
|
||||
class BTMinerM21S(BTMiner, M21S):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM21SV20(BTMiner, M21SV20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM21SV60(BTMiner, M21SV60):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -2,5 +2,5 @@ from .M20S import BTMinerM20S
|
||||
from .M20S_Plus import BTMinerM20SPlus
|
||||
|
||||
from .M21 import BTMinerM21
|
||||
from .M21S import BTMinerM21S
|
||||
from .M21S import BTMinerM21S, BTMinerM21SV20, BTMinerM21SV60
|
||||
from .M21S_Plus import BTMinerM21SPlus
|
||||
|
||||
@@ -1,8 +1,38 @@
|
||||
from miners._backends import BTMiner # noqa - Ignore access to _module
|
||||
from miners._types import M30S # noqa - Ignore access to _module
|
||||
from miners._types import (
|
||||
M30S,
|
||||
M30SV50,
|
||||
M30SVG20,
|
||||
M30SVE20,
|
||||
M30SVE10,
|
||||
) # noqa - Ignore access to _module
|
||||
|
||||
|
||||
class BTMinerM30S(BTMiner, M30S):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SV50(BTMiner, M30SV50):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SVG20(BTMiner, M30SVG20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SVE20(BTMiner, M30SVE20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SVE10(BTMiner, M30SVE10):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
from miners._backends import BTMiner # noqa - Ignore access to _module
|
||||
from miners._types import M30SPlus # noqa - Ignore access to _module
|
||||
from miners._types import (
|
||||
M30SPlus,
|
||||
M30SPlusVE40,
|
||||
M30SPlusVF20,
|
||||
M30SPlusVG60,
|
||||
) # noqa - Ignore access to _module
|
||||
|
||||
|
||||
class BTMinerM30SPlus(BTMiner, M30SPlus):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusVE40(BTMiner, M30SPlusVE40):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusVF20(BTMiner, M30SPlusVF20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusVG60(BTMiner, M30SPlusVG60):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
from miners._backends import BTMiner # noqa - Ignore access to _module
|
||||
from miners._types import ( # noqa - Ignore access to _module
|
||||
M30SPlusPlus,
|
||||
M30SPlusPlusVG40,
|
||||
M30SPlusPlusVG30,
|
||||
)
|
||||
|
||||
|
||||
class BTMinerM30SPlusPlus(BTMiner, M30SPlusPlus):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM30SPlusPlusVG40(BTMiner, M30SPlusPlusVG40):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
from miners._backends import BTMiner # noqa - Ignore access to _module
|
||||
from miners._types import M31SPlus # noqa - Ignore access to _module
|
||||
from miners._types import M31SPlus, M31SPlusVE20 # noqa - Ignore access to _module
|
||||
|
||||
|
||||
class BTMinerM31SPlus(BTMiner, M31SPlus):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
|
||||
class BTMinerM31SPlusVE20(BTMiner, M31SPlusVE20):
|
||||
def __init__(self, ip: str) -> None:
|
||||
super().__init__(ip)
|
||||
self.ip = ip
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
from .M30S import BTMinerM30S
|
||||
from .M30S_Plus import BTMinerM30SPlus
|
||||
from .M30S_Plus_Plus import BTMinerM30SPlusPlusVG40, BTMinerM30SPlusPlusVG30
|
||||
from .M30S import (
|
||||
BTMinerM30S,
|
||||
BTMinerM30SVE10,
|
||||
BTMinerM30SVE20,
|
||||
BTMinerM30SVG20,
|
||||
BTMinerM30SV50,
|
||||
)
|
||||
from .M30S_Plus import (
|
||||
BTMinerM30SPlus,
|
||||
BTMinerM30SPlusVF20,
|
||||
BTMinerM30SPlusVE40,
|
||||
BTMinerM30SPlusVG60,
|
||||
)
|
||||
from .M30S_Plus_Plus import (
|
||||
BTMinerM30SPlusPlus,
|
||||
BTMinerM30SPlusPlusVG40,
|
||||
BTMinerM30SPlusPlusVG30,
|
||||
)
|
||||
|
||||
from .M31S import BTMinerM31S
|
||||
from .M31S_Plus import BTMinerM31SPlus
|
||||
from .M31S_Plus import BTMinerM31SPlus, BTMinerM31SPlusVE20
|
||||
|
||||
from .M32S import BTMinerM32S
|
||||
|
||||
@@ -13,6 +13,10 @@ if (
|
||||
|
||||
|
||||
def main():
|
||||
from logger import init_logger
|
||||
|
||||
init_logger()
|
||||
|
||||
asyncio.run(ui())
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ async def btn_import(table, selected):
|
||||
miner = await MinerFactory().get_miner(ip)
|
||||
await miner.get_config()
|
||||
config = miner.config
|
||||
window["cfg_config_txt"].update(config)
|
||||
if config:
|
||||
window["cfg_config_txt"].update(config.as_yaml())
|
||||
|
||||
|
||||
@disable_buttons("Configuring")
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
from datetime import datetime, timedelta
|
||||
from io import BytesIO
|
||||
from typing import List, Dict
|
||||
from data import MinerData
|
||||
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.ticker import MultipleLocator
|
||||
from matplotlib.dates import DateFormatter
|
||||
import numpy as np
|
||||
from reportlab.lib import colors
|
||||
from matplotlib.ticker import MultipleLocator
|
||||
from reportlab.lib.pagesizes import letter, inch
|
||||
from reportlab.lib.styles import (
|
||||
ParagraphStyle,
|
||||
TA_CENTER, # noqa - not declared in __all__
|
||||
)
|
||||
from reportlab.lib.utils import ImageReader
|
||||
from reportlab.platypus import (
|
||||
SimpleDocTemplate,
|
||||
KeepInFrame,
|
||||
Table,
|
||||
Image,
|
||||
Paragraph,
|
||||
TableStyle,
|
||||
PageBreak,
|
||||
Spacer,
|
||||
)
|
||||
from io import BytesIO
|
||||
from svglib.svglib import svg2rlg
|
||||
|
||||
from data import MinerData
|
||||
|
||||
|
||||
async def generate_pdf(data: Dict[str, List[MinerData]], file_loc):
|
||||
doc = SimpleDocTemplate(
|
||||
|
||||
@@ -229,9 +229,9 @@ class TableManager(metaclass=Singleton):
|
||||
"Temp",
|
||||
"Total",
|
||||
"Ideal",
|
||||
"Left Chips",
|
||||
"Center Chips",
|
||||
"Right Chips",
|
||||
"Left Board",
|
||||
"Center Board",
|
||||
"Right Board",
|
||||
]:
|
||||
if isinstance(self.data[data_key][self.sort_key], str):
|
||||
return -300
|
||||
|
||||
Reference in New Issue
Block a user