improved format and readability

This commit is contained in:
UpstreamData
2021-10-20 13:47:58 -06:00
parent 62ea4578a2
commit ea2ec6463e
14 changed files with 77 additions and 56 deletions

View File

@@ -18,18 +18,25 @@ class APIError(Exception):
class BaseMinerAPI: class BaseMinerAPI:
def __init__(self, ip: str, port: int): def __init__(self, ip: str, port: int) -> None:
self.port = port self.port = port
self.ip = ipaddress.ip_address(ip) self.ip = ipaddress.ip_address(ip)
def get_commands(self): 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))]] 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: async def multicommand(self, *commands: str) -> dict:
command = "+".join(commands) command = "+".join(commands)
return await self.send_command(command) 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: try:
# get reader and writer streams # get reader and writer streams
reader, writer = await asyncio.open_connection(str(self.ip), self.port) reader, writer = await asyncio.open_connection(str(self.ip), self.port)

View File

@@ -2,7 +2,7 @@ from API import BaseMinerAPI
class BMMinerAPI(BaseMinerAPI): class BMMinerAPI(BaseMinerAPI):
def __init__(self, ip, port=4028): def __init__(self, ip: str, port: int = 4028) -> None:
super().__init__(ip, port) super().__init__(ip, port)
async def version(self) -> dict: async def version(self) -> dict:
@@ -42,7 +42,7 @@ class BMMinerAPI(BaseMinerAPI):
return await self.send_command("addpool", parameters=f"{url}, {username}, {password}") return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
async def poolpriority(self, *n: int) -> dict: 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: async def poolquota(self, n: int, q: int) -> dict:
return await self.send_command("poolquota", parameters=f"{n}, {q}") 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) return await self.send_command("check", parameters=command)
async def failover_only(self, failover: bool) -> dict: 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: async def coin(self) -> dict:
return await self.send_command("coin") return await self.send_command("coin")

View File

@@ -49,27 +49,27 @@ class BOSMinerAPI(BaseMinerAPI):
async def switchpool(self, n: int) -> dict: async def switchpool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("switchpool", parameters=n) # return await self.send_command("switchpool", parameters=n)
async def enablepool(self, n: int) -> dict: async def enablepool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("enablepool", parameters=n) # return await self.send_command("enablepool", parameters=n)
async def disablepool(self, n: int) -> dict: async def disablepool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("disablepool", parameters=n) # return await self.send_command("disablepool", parameters=n)
async def addpool(self, url: str, username: str, password: str) -> dict: async def addpool(self, url: str, username: str, password: str) -> dict:
# BOS has not implemented this yet, they will in the future # 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}") # return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
async def removepool(self, n: int) -> dict: async def removepool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("removepool", parameters=n) # return await self.send_command("removepool", parameters=n)
async def fans(self) -> dict: async def fans(self) -> dict:

View File

@@ -39,7 +39,7 @@ class CGMinerAPI(BaseMinerAPI):
return await self.send_command("addpool", parameters=f"{url}, {username}, {password}") return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
async def poolpriority(self, *n: int) -> dict: 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: async def poolquota(self, n: int, q: int) -> dict:
return await self.send_command("poolquota", parameters=f"{n}, {q}") 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) return await self.send_command("check", parameters=command)
async def failover_only(self, failover: bool) -> dict: 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: async def coin(self) -> dict:
return await self.send_command("coin") return await self.send_command("coin")

View File

@@ -49,25 +49,25 @@ class UnknownAPI(BaseMinerAPI):
async def switchpool(self, n: int) -> dict: async def switchpool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("switchpool", parameters=n) # return await self.send_command("switchpool", parameters=n)
async def enablepool(self, n: int) -> dict: async def enablepool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("enablepool", parameters=n) # return await self.send_command("enablepool", parameters=n)
async def disablepool(self, n: int) -> dict: async def disablepool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("disablepool", parameters=n) # return await self.send_command("disablepool", parameters=n)
async def addpool(self, url: str, username: str, password: str) -> dict: async def addpool(self, url: str, username: str, password: str) -> dict:
# BOS has not implemented this yet, they will in the future # 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}") # return await self.send_command("addpool", parameters=f"{url}, {username}, {password}")
async def removepool(self, n: int) -> dict: async def removepool(self, n: int) -> dict:
# BOS has not implemented this yet, they will in the future # BOS has not implemented this yet, they will in the future
return NotImplementedError raise NotImplementedError
# return await self.send_command("removepool", parameters=n) # return await self.send_command("removepool", parameters=n)

View File

@@ -28,7 +28,8 @@ layout = [
[sg.Listbox([], size=(20, 32), key="ip_list", select_mode='extended')] [sg.Listbox([], size=(20, 32), key="ip_list", select_mode='extended')]
]), ]),
sg.Column([ 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.Listbox([], size=(50, 32), key="hr_list")]
]), ]),
sg.Column([ sg.Column([
@@ -146,17 +147,18 @@ async def get_data(ip_list: list):
ips.sort() ips.sort()
data = await asyncio.gather(*[get_formatted_data(miner) for miner in ips]) data = await asyncio.gather(*[get_formatted_data(miner) for miner in ips])
window["hr_list"].update(disabled=False) 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) window["hr_list"].update(disabled=True)
await update_ui_with_data("status", "") await update_ui_with_data("status", "")
async def get_formatted_data(ip: ipaddress.ip_address): async def get_formatted_data(ip: ipaddress.ip_address):
miner = await miner_factory.get_miner(ip) 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) mh5s = round(data['summary'][0]['SUMMARY'][0]['MHS 5s'] / 1000000, 2)
user = data['pools'][0]['POOLS'][0]['User'] 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(): async def generate_config():
@@ -195,12 +197,14 @@ async def generate_config():
} }
window['config'].update(toml.dumps(config)) window['config'].update(toml.dumps(config))
async def sort_data(index: int): async def sort_data(index: int):
await update_ui_with_data("status", "Sorting Data") await update_ui_with_data("status", "Sorting Data")
data_list = window['hr_list'].Values data_list = window['hr_list'].Values
new_list = [] new_list = []
for item in data_list: for item in data_list:
item_data = [part.strip() for part in item.split("|")] 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[1] = item_data[1].replace(" TH/s", "")
item_data[0] = ipaddress.ip_address(item_data[0]) item_data[0] = ipaddress.ip_address(item_data[0])
new_list.append(item_data) 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])) new_data_list = sorted(new_list, key=lambda x: float(x[index]))
else: else:
new_data_list = sorted(new_list, key=itemgetter(index)) 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(disabled=False)
window["hr_list"].update(new_data_list) window["hr_list"].update(new_data_list)
window["hr_list"].update(disabled=True) window["hr_list"].update(disabled=True)

View File

@@ -12,6 +12,7 @@ async def main():
print(len(good_list)) print(len(good_list))
# print('\n'.join([f"{str(miner.ip)}" for miner in miners])) # print('\n'.join([f"{str(miner.ip)}" for miner in miners]))
async def main_bad(): async def main_bad():
miner_network = MinerNetwork('192.168.1.1') miner_network = MinerNetwork('192.168.1.1')
miners = await miner_network.scan_network_for_miners() miners = await miner_network.scan_network_for_miners()

View File

@@ -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 ipaddress
import typing
class BaseMiner: 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.ip = ipaddress.ip_address(ip)
self.api = api self.api = api

View File

@@ -3,11 +3,11 @@ from miners import BaseMiner
class BMMiner(BaseMiner): class BMMiner(BaseMiner):
def __init__(self, ip: str): def __init__(self, ip: str) -> None:
api = BMMinerAPI(ip) api = BMMinerAPI(ip)
super().__init__(ip, api) super().__init__(ip, api)
def __repr__(self): def __repr__(self) -> str:
return f"BMMiner: {str(self.ip)}" return f"BMMiner: {str(self.ip)}"
async def send_config(self): async def send_config(self):

View File

@@ -6,12 +6,12 @@ import time
class BOSminer(BaseMiner): class BOSminer(BaseMiner):
def __init__(self, ip: str): def __init__(self, ip: str) -> None:
api = BOSMinerAPI(ip) api = BOSMinerAPI(ip)
super().__init__(ip, api) super().__init__(ip, api)
self.config = None self.config = None
def __repr__(self): def __repr__(self) -> str:
return f"BOSminer: {str(self.ip)}" return f"BOSminer: {str(self.ip)}"
async def get_ssh_connection(self, username: str, password: str) -> asyncssh.connect: async def get_ssh_connection(self, username: str, password: str) -> asyncssh.connect:
@@ -21,7 +21,7 @@ class BOSminer(BaseMiner):
# return created connection # return created connection
return conn return conn
async def send_ssh_command(self, cmd): async def send_ssh_command(self, cmd: str) -> None:
result = None result = None
conn = await self.get_ssh_connection('root', 'admin') conn = await self.get_ssh_connection('root', 'admin')
for i in range(3): for i in range(3):
@@ -43,19 +43,19 @@ class BOSminer(BaseMiner):
else: else:
print(cmd) print(cmd)
async def fault_light_on(self): async def fault_light_on(self) -> None:
await self.send_ssh_command('miner fault_light on') 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') 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') 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') 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', async with asyncssh.connect(str(self.ip), known_hosts=None, username='root', password='admin',
server_host_key_algs=['ssh-rsa']) as conn: server_host_key_algs=['ssh-rsa']) as conn:
async with conn.start_sftp_client() as sftp: async with conn.start_sftp_client() as sftp:
@@ -63,10 +63,10 @@ class BOSminer(BaseMiner):
toml_data = toml.loads(await file.read()) toml_data = toml.loads(await file.read())
self.config = toml_data self.config = toml_data
def update_config(self, config: dict): def update_config(self, config: dict) -> None:
self.config = config 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: if not self.config:
await self.get_config() await self.get_config()
config = self.config config = self.config
@@ -76,7 +76,7 @@ class BOSminer(BaseMiner):
self.update_config(config) self.update_config(config)
return 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: if not self.config:
await self.get_config() await self.get_config()
config = self.config config = self.config
@@ -87,7 +87,7 @@ class BOSminer(BaseMiner):
return config return config
async def change_config_temp_ctrl(self, target: float = 80.0, hot: float = 100.0, 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: if not self.config:
await self.get_config() await self.get_config()
config = self.config config = self.config
@@ -98,7 +98,7 @@ class BOSminer(BaseMiner):
self.update_config(config) self.update_config(config)
return config return config
async def send_config(self, config): async def send_config(self, config: dict) -> None:
toml_conf = toml.dumps(config) toml_conf = toml.dumps(config)
async with asyncssh.connect(str(self.ip), known_hosts=None, username='root', password='admin', async with asyncssh.connect(str(self.ip), known_hosts=None, username='root', password='admin',
server_host_key_algs=['ssh-rsa']) as conn: server_host_key_algs=['ssh-rsa']) as conn:
@@ -107,7 +107,7 @@ class BOSminer(BaseMiner):
await file.write(toml_conf) await file.write(toml_conf)
await conn.run("/etc/init.d/bosminer restart") 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() devs = await self.api.devdetails()
bad = 0 bad = 0
chains = devs['DEVDETAILS'] chains = devs['DEVDETAILS']
@@ -115,9 +115,9 @@ class BOSminer(BaseMiner):
if chain['Chips'] == 0: if chain['Chips'] == 0:
bad += 1 bad += 1
if bad > 0: 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() devs = await self.api.devdetails()
bad = 0 bad = 0
chains = devs['DEVDETAILS'] chains = devs['DEVDETAILS']

View File

@@ -3,11 +3,11 @@ from miners import BaseMiner
class CGMiner(BaseMiner): class CGMiner(BaseMiner):
def __init__(self, ip: str): def __init__(self, ip: str) -> None:
api = CGMinerAPI(ip) api = CGMinerAPI(ip)
super().__init__(ip, api) super().__init__(ip, api)
def __repr__(self): def __repr__(self) -> str:
return f"CGMiner: {str(self.ip)}" return f"CGMiner: {str(self.ip)}"
async def send_config(self): async def send_config(self):

View File

@@ -6,14 +6,13 @@ from API import APIError
import asyncio import asyncio
import ipaddress import ipaddress
import json import json
import typing
class MinerFactory: class MinerFactory:
def __init__(self): def __init__(self):
self.miners = {} 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: if ip in self.miners:
return self.miners[ip] return self.miners[ip]
version_data = await self._get_version_data(ip) version_data = await self._get_version_data(ip)
@@ -35,7 +34,7 @@ class MinerFactory:
return miner return miner
@staticmethod @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): for i in range(3):
try: try:
fut = asyncio.open_connection(str(ip), 4028) fut = asyncio.open_connection(str(ip), 4028)

View File

@@ -3,11 +3,11 @@ from miners import BaseMiner
class UnknownMiner(BaseMiner): class UnknownMiner(BaseMiner):
def __init__(self, ip: str): def __init__(self, ip: str) -> None:
api = UnknownAPI(ip) api = UnknownAPI(ip)
super().__init__(ip, api) super().__init__(ip, api)
def __repr__(self): def __repr__(self) -> str:
return f"Unknown: {str(self.ip)}" return f"Unknown: {str(self.ip)}"
async def send_config(self): async def send_config(self):

View File

@@ -2,20 +2,24 @@ import netifaces
import ipaddress import ipaddress
import asyncio import asyncio
from miners.miner_factory import MinerFactory 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_RETRIES: int = 3
PING_TIMEOUT: int = 1 PING_TIMEOUT: int = 1
class MinerNetwork: 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.network = None
self.miner_factory = MinerFactory() self.miner_factory = MinerFactory()
self.ip_addr = ip_addr self.ip_addr = ip_addr
self.connected_miners = {} self.connected_miners = {}
self.mask = mask self.mask = mask
def get_network(self): def get_network(self) -> ipaddress.ip_network:
if self.network: if self.network:
return self.network return self.network
gateways = netifaces.gateways() gateways = netifaces.gateways()
@@ -29,7 +33,7 @@ class MinerNetwork:
subnet_mask = netifaces.ifaddresses(gateways['default'][netifaces.AF_INET][1])[netifaces.AF_INET][0]['netmask'] 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) 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() local_network = self.get_network()
print(f"Scanning {local_network} for miners...") print(f"Scanning {local_network} for miners...")
scan_tasks = [] scan_tasks = []
@@ -44,7 +48,8 @@ class MinerNetwork:
miners = await asyncio.gather(*create_miners_tasks) miners = await asyncio.gather(*create_miners_tasks)
return miners 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): for i in range(PING_RETRIES):
connection_fut = asyncio.open_connection(str(ip), 4028) connection_fut = asyncio.open_connection(str(ip), 4028)
try: try: