improved format and readability
This commit is contained in:
@@ -18,18 +18,25 @@ class APIError(Exception):
|
||||
|
||||
|
||||
class BaseMinerAPI:
|
||||
def __init__(self, ip: str, port: int):
|
||||
def __init__(self, ip: str, port: int) -> None:
|
||||
self.port = port
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
|
||||
def get_commands(self):
|
||||
return [func for func in dir(self) if callable(getattr(self, func)) and not func.startswith("__") and func not in [func for func in dir(BaseMinerAPI) if callable(getattr(BaseMinerAPI, func))]]
|
||||
def get_commands(self) -> list:
|
||||
return [func for func in
|
||||
dir(self) if callable(getattr(self, func)) and
|
||||
not func.startswith("__") and
|
||||
func not in
|
||||
[func for func in
|
||||
dir(BaseMinerAPI) if callable(getattr(BaseMinerAPI, func))
|
||||
]
|
||||
]
|
||||
|
||||
async def multicommand(self, *commands: str) -> dict:
|
||||
command = "+".join(commands)
|
||||
return await self.send_command(command)
|
||||
|
||||
async def send_command(self, command: str, parameters: str = None) -> dict:
|
||||
async def send_command(self, command: str, parameters: str or int or bool = None) -> dict:
|
||||
try:
|
||||
# get reader and writer streams
|
||||
reader, writer = await asyncio.open_connection(str(self.ip), self.port)
|
||||
|
||||
@@ -2,7 +2,7 @@ from API import BaseMinerAPI
|
||||
|
||||
|
||||
class BMMinerAPI(BaseMinerAPI):
|
||||
def __init__(self, ip, port=4028):
|
||||
def __init__(self, ip: str, port: int = 4028) -> None:
|
||||
super().__init__(ip, port)
|
||||
|
||||
async def version(self) -> dict:
|
||||
@@ -42,7 +42,7 @@ class BMMinerAPI(BaseMinerAPI):
|
||||
return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
|
||||
|
||||
async def poolpriority(self, *n: int) -> dict:
|
||||
return await self.send_command("poolpriority", parameters=f"{','.join(n)}")
|
||||
return await self.send_command("poolpriority", parameters=f"{','.join([str(item) for item in n])}")
|
||||
|
||||
async def poolquota(self, n: int, q: int) -> dict:
|
||||
return await self.send_command("poolquota", parameters=f"{n}, {q}")
|
||||
@@ -96,7 +96,7 @@ class BMMinerAPI(BaseMinerAPI):
|
||||
return await self.send_command("check", parameters=command)
|
||||
|
||||
async def failover_only(self, failover: bool) -> dict:
|
||||
return self.send_command("failover-only", parameters=failover)
|
||||
return await self.send_command("failover-only", parameters=failover)
|
||||
|
||||
async def coin(self) -> dict:
|
||||
return await self.send_command("coin")
|
||||
|
||||
@@ -49,27 +49,27 @@ class BOSMinerAPI(BaseMinerAPI):
|
||||
|
||||
async def switchpool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("switchpool", parameters=n)
|
||||
|
||||
async def enablepool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("enablepool", parameters=n)
|
||||
|
||||
async def disablepool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("disablepool", parameters=n)
|
||||
|
||||
async def addpool(self, url: str, username: str, password: str) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
|
||||
|
||||
async def removepool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("removepool", parameters=n)
|
||||
|
||||
async def fans(self) -> dict:
|
||||
|
||||
@@ -39,7 +39,7 @@ class CGMinerAPI(BaseMinerAPI):
|
||||
return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
|
||||
|
||||
async def poolpriority(self, *n: int) -> dict:
|
||||
return await self.send_command("poolpriority", parameters=f"{','.join(n)}")
|
||||
return await self.send_command("poolpriority", parameters=f"{','.join([str(item) for item in n])}")
|
||||
|
||||
async def poolquota(self, n: int, q: int) -> dict:
|
||||
return await self.send_command("poolquota", parameters=f"{n}, {q}")
|
||||
@@ -93,7 +93,7 @@ class CGMinerAPI(BaseMinerAPI):
|
||||
return await self.send_command("check", parameters=command)
|
||||
|
||||
async def failover_only(self, failover: bool) -> dict:
|
||||
return self.send_command("failover-only", parameters=failover)
|
||||
return await self.send_command("failover-only", parameters=failover)
|
||||
|
||||
async def coin(self) -> dict:
|
||||
return await self.send_command("coin")
|
||||
|
||||
@@ -49,25 +49,25 @@ class UnknownAPI(BaseMinerAPI):
|
||||
|
||||
async def switchpool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("switchpool", parameters=n)
|
||||
|
||||
async def enablepool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("enablepool", parameters=n)
|
||||
|
||||
async def disablepool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("disablepool", parameters=n)
|
||||
|
||||
async def addpool(self, url: str, username: str, password: str) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
|
||||
|
||||
async def removepool(self, n: int) -> dict:
|
||||
# BOS has not implemented this yet, they will in the future
|
||||
return NotImplementedError
|
||||
raise NotImplementedError
|
||||
# return await self.send_command("removepool", parameters=n)
|
||||
|
||||
@@ -28,7 +28,8 @@ layout = [
|
||||
[sg.Listbox([], size=(20, 32), key="ip_list", select_mode='extended')]
|
||||
]),
|
||||
sg.Column([
|
||||
[sg.Text("Data: ", pad=(0, 0)), sg.Button('GET', key="get_data"), sg.Button('SORT IP', key="sort_data_ip"), sg.Button('SORT HR', key="sort_data_hr"), sg.Button('SORT USER', key="sort_data_user")],
|
||||
[sg.Text("Data: ", pad=(0, 0)), sg.Button('GET', key="get_data"), sg.Button('SORT IP', key="sort_data_ip"),
|
||||
sg.Button('SORT HR', key="sort_data_hr"), sg.Button('SORT USER', key="sort_data_user")],
|
||||
[sg.Listbox([], size=(50, 32), key="hr_list")]
|
||||
]),
|
||||
sg.Column([
|
||||
@@ -146,17 +147,18 @@ async def get_data(ip_list: list):
|
||||
ips.sort()
|
||||
data = await asyncio.gather(*[get_formatted_data(miner) for miner in ips])
|
||||
window["hr_list"].update(disabled=False)
|
||||
window["hr_list"].update([item['IP'] + " | " + str(item['TH/s']) + " TH/s | " + item['user'] for item in data])
|
||||
window["hr_list"].update([item['IP'] + " | " + str(item['TH/s']) + " TH/s | " + item['user'] + " | " + str(item['wattage']) + " W" for item in data])
|
||||
window["hr_list"].update(disabled=True)
|
||||
await update_ui_with_data("status", "")
|
||||
|
||||
|
||||
async def get_formatted_data(ip: ipaddress.ip_address):
|
||||
miner = await miner_factory.get_miner(ip)
|
||||
data = await miner.api.multicommand("summary", "pools")
|
||||
data = await miner.api.multicommand("summary", "pools", "tunerstatus")
|
||||
wattage = data['tunerstatus'][0]['TUNERSTATUS'][0]['PowerLimit']
|
||||
mh5s = round(data['summary'][0]['SUMMARY'][0]['MHS 5s'] / 1000000, 2)
|
||||
user = data['pools'][0]['POOLS'][0]['User']
|
||||
return {'TH/s': mh5s, 'IP': str(miner.ip), 'user': user}
|
||||
return {'TH/s': mh5s, 'IP': str(miner.ip), 'user': user, 'wattage': wattage}
|
||||
|
||||
|
||||
async def generate_config():
|
||||
@@ -195,12 +197,14 @@ async def generate_config():
|
||||
}
|
||||
window['config'].update(toml.dumps(config))
|
||||
|
||||
|
||||
async def sort_data(index: int):
|
||||
await update_ui_with_data("status", "Sorting Data")
|
||||
data_list = window['hr_list'].Values
|
||||
new_list = []
|
||||
for item in data_list:
|
||||
item_data = [part.strip() for part in item.split("|")]
|
||||
item_data[3] = item_data[3].replace(" W", "")
|
||||
item_data[1] = item_data[1].replace(" TH/s", "")
|
||||
item_data[0] = ipaddress.ip_address(item_data[0])
|
||||
new_list.append(item_data)
|
||||
@@ -208,7 +212,11 @@ async def sort_data(index: int):
|
||||
new_data_list = sorted(new_list, key=lambda x: float(x[index]))
|
||||
else:
|
||||
new_data_list = sorted(new_list, key=itemgetter(index))
|
||||
new_data_list = [str(item[0]) + " | " + item[1] + " TH/s | " + item[2] for item in new_data_list]
|
||||
new_data_list = [str(item[0]) + " | "
|
||||
+ item[1] + " TH/s | "
|
||||
+ item[2] + " | "
|
||||
+ str(item[3]) + " W"
|
||||
for item in new_data_list]
|
||||
window["hr_list"].update(disabled=False)
|
||||
window["hr_list"].update(new_data_list)
|
||||
window["hr_list"].update(disabled=True)
|
||||
|
||||
1
main.py
1
main.py
@@ -12,6 +12,7 @@ async def main():
|
||||
print(len(good_list))
|
||||
# print('\n'.join([f"{str(miner.ip)}" for miner in miners]))
|
||||
|
||||
|
||||
async def main_bad():
|
||||
miner_network = MinerNetwork('192.168.1.1')
|
||||
miners = await miner_network.scan_network_for_miners()
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from API import BaseMinerAPI
|
||||
from API.bmminer import BMMinerAPI
|
||||
from API.bosminer import BOSMinerAPI
|
||||
from API.cgminer import CGMinerAPI
|
||||
import ipaddress
|
||||
import typing
|
||||
|
||||
|
||||
class BaseMiner:
|
||||
def __init__(self, ip: str, api: typing.Type[BaseMinerAPI]):
|
||||
def __init__(self, ip: str, api: BMMinerAPI or BOSMinerAPI or CGMinerAPI) -> None:
|
||||
self.ip = ipaddress.ip_address(ip)
|
||||
self.api = api
|
||||
|
||||
@@ -3,11 +3,11 @@ from miners import BaseMiner
|
||||
|
||||
|
||||
class BMMiner(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
def __init__(self, ip: str) -> None:
|
||||
api = BMMinerAPI(ip)
|
||||
super().__init__(ip, api)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"BMMiner: {str(self.ip)}"
|
||||
|
||||
async def send_config(self):
|
||||
|
||||
@@ -6,12 +6,12 @@ import time
|
||||
|
||||
|
||||
class BOSminer(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
def __init__(self, ip: str) -> None:
|
||||
api = BOSMinerAPI(ip)
|
||||
super().__init__(ip, api)
|
||||
self.config = None
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"BOSminer: {str(self.ip)}"
|
||||
|
||||
async def get_ssh_connection(self, username: str, password: str) -> asyncssh.connect:
|
||||
@@ -21,7 +21,7 @@ class BOSminer(BaseMiner):
|
||||
# return created connection
|
||||
return conn
|
||||
|
||||
async def send_ssh_command(self, cmd):
|
||||
async def send_ssh_command(self, cmd: str) -> None:
|
||||
result = None
|
||||
conn = await self.get_ssh_connection('root', 'admin')
|
||||
for i in range(3):
|
||||
@@ -43,19 +43,19 @@ class BOSminer(BaseMiner):
|
||||
else:
|
||||
print(cmd)
|
||||
|
||||
async def fault_light_on(self):
|
||||
async def fault_light_on(self) -> None:
|
||||
await self.send_ssh_command('miner fault_light on')
|
||||
|
||||
async def fault_light_off(self):
|
||||
async def fault_light_off(self) -> None:
|
||||
await self.send_ssh_command('miner fault_light off')
|
||||
|
||||
async def bosminer_restart(self):
|
||||
async def bosminer_restart(self) -> None:
|
||||
await self.send_ssh_command('/etc/init.d/bosminer restart')
|
||||
|
||||
async def reboot(self):
|
||||
async def reboot(self) -> None:
|
||||
await self.send_ssh_command('/sbin/reboot')
|
||||
|
||||
async def get_config(self):
|
||||
async def get_config(self) -> None:
|
||||
async with asyncssh.connect(str(self.ip), known_hosts=None, username='root', password='admin',
|
||||
server_host_key_algs=['ssh-rsa']) as conn:
|
||||
async with conn.start_sftp_client() as sftp:
|
||||
@@ -63,10 +63,10 @@ class BOSminer(BaseMiner):
|
||||
toml_data = toml.loads(await file.read())
|
||||
self.config = toml_data
|
||||
|
||||
def update_config(self, config: dict):
|
||||
def update_config(self, config: dict) -> None:
|
||||
self.config = config
|
||||
|
||||
async def change_config_format(self, update_config: bool = False):
|
||||
async def change_config_format(self, update_config: bool = False) -> dict:
|
||||
if not self.config:
|
||||
await self.get_config()
|
||||
config = self.config
|
||||
@@ -76,7 +76,7 @@ class BOSminer(BaseMiner):
|
||||
self.update_config(config)
|
||||
return config
|
||||
|
||||
async def change_config_tuning(self, wattage: int, enabled: bool = True, update_config: bool = False):
|
||||
async def change_config_tuning(self, wattage: int, enabled: bool = True, update_config: bool = False) -> dict:
|
||||
if not self.config:
|
||||
await self.get_config()
|
||||
config = self.config
|
||||
@@ -87,7 +87,7 @@ class BOSminer(BaseMiner):
|
||||
return config
|
||||
|
||||
async def change_config_temp_ctrl(self, target: float = 80.0, hot: float = 100.0,
|
||||
dangerous: float = 110.0, update_config: bool = False):
|
||||
dangerous: float = 110.0, update_config: bool = False) -> dict:
|
||||
if not self.config:
|
||||
await self.get_config()
|
||||
config = self.config
|
||||
@@ -98,7 +98,7 @@ class BOSminer(BaseMiner):
|
||||
self.update_config(config)
|
||||
return config
|
||||
|
||||
async def send_config(self, config):
|
||||
async def send_config(self, config: dict) -> None:
|
||||
toml_conf = toml.dumps(config)
|
||||
async with asyncssh.connect(str(self.ip), known_hosts=None, username='root', password='admin',
|
||||
server_host_key_algs=['ssh-rsa']) as conn:
|
||||
@@ -107,7 +107,7 @@ class BOSminer(BaseMiner):
|
||||
await file.write(toml_conf)
|
||||
await conn.run("/etc/init.d/bosminer restart")
|
||||
|
||||
async def get_bad_boards(self):
|
||||
async def get_bad_boards(self) -> list:
|
||||
devs = await self.api.devdetails()
|
||||
bad = 0
|
||||
chains = devs['DEVDETAILS']
|
||||
@@ -115,9 +115,9 @@ class BOSminer(BaseMiner):
|
||||
if chain['Chips'] == 0:
|
||||
bad += 1
|
||||
if bad > 0:
|
||||
return (str(self.ip), bad)
|
||||
return [str(self.ip), bad]
|
||||
|
||||
async def check_good_boards(self):
|
||||
async def check_good_boards(self) -> str:
|
||||
devs = await self.api.devdetails()
|
||||
bad = 0
|
||||
chains = devs['DEVDETAILS']
|
||||
|
||||
@@ -3,11 +3,11 @@ from miners import BaseMiner
|
||||
|
||||
|
||||
class CGMiner(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
def __init__(self, ip: str) -> None:
|
||||
api = CGMinerAPI(ip)
|
||||
super().__init__(ip, api)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"CGMiner: {str(self.ip)}"
|
||||
|
||||
async def send_config(self):
|
||||
|
||||
@@ -6,14 +6,13 @@ from API import APIError
|
||||
import asyncio
|
||||
import ipaddress
|
||||
import json
|
||||
import typing
|
||||
|
||||
|
||||
class MinerFactory:
|
||||
def __init__(self):
|
||||
self.miners = {}
|
||||
|
||||
async def get_miner(self, ip: ipaddress.ip_address):
|
||||
async def get_miner(self, ip: ipaddress.ip_address) -> BOSminer or CGMiner or BMMiner or UnknownMiner:
|
||||
if ip in self.miners:
|
||||
return self.miners[ip]
|
||||
version_data = await self._get_version_data(ip)
|
||||
@@ -35,7 +34,7 @@ class MinerFactory:
|
||||
return miner
|
||||
|
||||
@staticmethod
|
||||
async def _get_version_data(ip: ipaddress.ip_address):
|
||||
async def _get_version_data(ip: ipaddress.ip_address) -> dict or None:
|
||||
for i in range(3):
|
||||
try:
|
||||
fut = asyncio.open_connection(str(ip), 4028)
|
||||
|
||||
@@ -3,11 +3,11 @@ from miners import BaseMiner
|
||||
|
||||
|
||||
class UnknownMiner(BaseMiner):
|
||||
def __init__(self, ip: str):
|
||||
def __init__(self, ip: str) -> None:
|
||||
api = UnknownAPI(ip)
|
||||
super().__init__(ip, api)
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"Unknown: {str(self.ip)}"
|
||||
|
||||
async def send_config(self):
|
||||
|
||||
@@ -2,20 +2,24 @@ import netifaces
|
||||
import ipaddress
|
||||
import asyncio
|
||||
from miners.miner_factory import MinerFactory
|
||||
from miners.bmminer import BMMiner
|
||||
from miners.bosminer import BOSminer
|
||||
from miners.cgminer import CGMiner
|
||||
from miners.unknown import UnknownMiner
|
||||
|
||||
PING_RETRIES: int = 3
|
||||
PING_TIMEOUT: int = 1
|
||||
|
||||
|
||||
class MinerNetwork:
|
||||
def __init__(self, ip_addr=None, mask=None):
|
||||
def __init__(self, ip_addr: str or None = None, mask: str or int or None = None) -> None:
|
||||
self.network = None
|
||||
self.miner_factory = MinerFactory()
|
||||
self.ip_addr = ip_addr
|
||||
self.connected_miners = {}
|
||||
self.mask = mask
|
||||
|
||||
def get_network(self):
|
||||
def get_network(self) -> ipaddress.ip_network:
|
||||
if self.network:
|
||||
return self.network
|
||||
gateways = netifaces.gateways()
|
||||
@@ -29,7 +33,7 @@ class MinerNetwork:
|
||||
subnet_mask = netifaces.ifaddresses(gateways['default'][netifaces.AF_INET][1])[netifaces.AF_INET][0]['netmask']
|
||||
return ipaddress.ip_network(f"{default_gateway}/{subnet_mask}", strict=False)
|
||||
|
||||
async def scan_network_for_miners(self):
|
||||
async def scan_network_for_miners(self) -> None or list[BOSminer or BMMiner or CGMiner or UnknownMiner]:
|
||||
local_network = self.get_network()
|
||||
print(f"Scanning {local_network} for miners...")
|
||||
scan_tasks = []
|
||||
@@ -44,7 +48,8 @@ class MinerNetwork:
|
||||
miners = await asyncio.gather(*create_miners_tasks)
|
||||
return miners
|
||||
|
||||
async def ping_miner(self, ip: ipaddress.ip_address):
|
||||
@staticmethod
|
||||
async def ping_miner(ip: ipaddress.ip_address) -> None or ipaddress.ip_address:
|
||||
for i in range(PING_RETRIES):
|
||||
connection_fut = asyncio.open_connection(str(ip), 4028)
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user