added fault lights to 1066 miners, and framework for configuring (although it may not work, the documentation implementation is broken)
This commit is contained in:
@@ -152,6 +152,43 @@ If you are sure you want to use this command please use API.send_command("{item}
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
async def send_raw(
|
||||||
|
self,
|
||||||
|
command: str or bytes,
|
||||||
|
) -> str:
|
||||||
|
"""Send an API command to the miner and return the result."""
|
||||||
|
try:
|
||||||
|
# get reader and writer streams
|
||||||
|
reader, writer = await asyncio.open_connection(str(self.ip), self.port)
|
||||||
|
# handle OSError 121
|
||||||
|
except OSError as e:
|
||||||
|
if e.winerror == "121":
|
||||||
|
logging.warning("Semaphore Timeout has Expired.")
|
||||||
|
return b""
|
||||||
|
|
||||||
|
# send the command
|
||||||
|
writer.write(command.encode("utf-8"))
|
||||||
|
await writer.drain()
|
||||||
|
|
||||||
|
# instantiate data
|
||||||
|
data = b""
|
||||||
|
|
||||||
|
# loop to receive all the data
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
d = await reader.read(4096)
|
||||||
|
if not d:
|
||||||
|
break
|
||||||
|
data += d
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"{self.ip}: API Command Error: {e}")
|
||||||
|
|
||||||
|
# close the connection
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
|
||||||
|
return data.decode("utf-8")[:-1]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_command_output(data: dict) -> tuple:
|
def validate_command_output(data: dict) -> tuple:
|
||||||
"""Check if the returned command output is correctly formatted."""
|
"""Check if the returned command output is correctly formatted."""
|
||||||
@@ -171,7 +208,10 @@ If you are sure you want to use this command please use API.send_command("{item}
|
|||||||
return False, data["Msg"]
|
return False, data["Msg"]
|
||||||
else:
|
else:
|
||||||
# make sure the command succeeded
|
# make sure the command succeeded
|
||||||
if data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
if type(data["STATUS"]) == str:
|
||||||
|
if data["STATUS"] in ["RESTART"]:
|
||||||
|
return True, None
|
||||||
|
elif data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
||||||
# this is an error
|
# this is an error
|
||||||
if data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
if data["STATUS"][0]["STATUS"] not in ("S", "I"):
|
||||||
return False, data["STATUS"][0]["Msg"]
|
return False, data["STATUS"][0]["Msg"]
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ class _Pool:
|
|||||||
pool = {"url": self.url, "user": username, "pass": self.password}
|
pool = {"url": self.url, "user": username, "pass": self.password}
|
||||||
return pool
|
return pool
|
||||||
|
|
||||||
|
def as_avalon(self, user_suffix: str = None):
|
||||||
|
username = self.username
|
||||||
|
if user_suffix:
|
||||||
|
username = f"{username}{user_suffix}"
|
||||||
|
|
||||||
|
pool = ",".join([self.url, username, self.password])
|
||||||
|
return pool
|
||||||
|
|
||||||
def as_bos(self, user_suffix: str = None):
|
def as_bos(self, user_suffix: str = None):
|
||||||
"""Convert the data in this class to a dict usable by an BOSMiner device.
|
"""Convert the data in this class to a dict usable by an BOSMiner device.
|
||||||
|
|
||||||
@@ -107,6 +115,10 @@ class _PoolGroup:
|
|||||||
pools.append(pool.as_x19(user_suffix=user_suffix))
|
pools.append(pool.as_x19(user_suffix=user_suffix))
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
|
def as_avalon(self, user_suffix: str = None):
|
||||||
|
pool = self.pools[0].as_avalon(user_suffix=user_suffix)
|
||||||
|
return pool
|
||||||
|
|
||||||
def as_bos(self, user_suffix: str = None):
|
def as_bos(self, user_suffix: str = None):
|
||||||
"""Convert the data in this class to a dict usable by an BOSMiner device.
|
"""Convert the data in this class to a dict usable by an BOSMiner device.
|
||||||
|
|
||||||
@@ -269,7 +281,7 @@ class MinerConfig:
|
|||||||
"""
|
"""
|
||||||
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
|
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
|
||||||
|
|
||||||
def as_x19(self, user_suffix: str = None):
|
def as_x19(self, user_suffix: str = None) -> str:
|
||||||
"""Convert the data in this class to a config usable by an X19 device.
|
"""Convert the data in this class to a config usable by an X19 device.
|
||||||
|
|
||||||
:param user_suffix: The suffix to append to username.
|
:param user_suffix: The suffix to append to username.
|
||||||
@@ -288,7 +300,11 @@ class MinerConfig:
|
|||||||
|
|
||||||
return json.dumps(cfg)
|
return json.dumps(cfg)
|
||||||
|
|
||||||
def as_bos(self, model: str = "S9", user_suffix: str = None):
|
def as_avalon(self, user_suffix: str = None) -> str:
|
||||||
|
cfg = self.pool_groups[0].as_avalon()
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_bos(self, model: str = "S9", user_suffix: str = None) -> str:
|
||||||
"""Convert the data in this class to a config usable by an BOSMiner device.
|
"""Convert the data in this class to a config usable by an BOSMiner device.
|
||||||
|
|
||||||
:param model: The model of the miner to be used in the format portion of the config.
|
:param model: The model of the miner to be used in the format portion of the config.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from miners._types import Avalon1066 # noqa - Ignore access to _module
|
|||||||
from data import MinerData
|
from data import MinerData
|
||||||
from settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
from settings import MINER_FACTORY_GET_VERSION_RETRIES as DATA_RETRIES
|
||||||
import re
|
import re
|
||||||
|
from config import MinerConfig
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class CGMinerAvalon1066(CGMiner, Avalon1066):
|
class CGMinerAvalon1066(CGMiner, Avalon1066):
|
||||||
@@ -11,7 +13,50 @@ class CGMinerAvalon1066(CGMiner, Avalon1066):
|
|||||||
super().__init__(ip)
|
super().__init__(ip)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|
||||||
async def get_mac(self):
|
async def fault_light_on(self) -> bool:
|
||||||
|
raw_data = await self.api.send_raw("ascset|0,led,1-1")
|
||||||
|
data = self.parse_raw(raw_data)
|
||||||
|
if data["Msg"] == "ASC 0 set OK":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
raw_data = await self.api.send_raw("ascset|0,led,1-0")
|
||||||
|
data = self.parse_raw(raw_data)
|
||||||
|
if data["Msg"] == "ASC 0 set OK":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def reboot(self) -> bool:
|
||||||
|
if (await self.api.restart())["STATUS"] == "RESTART":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def send_config(self, yaml_config, ip_user: bool = False) -> None:
|
||||||
|
"""Configures miner with yaml config."""
|
||||||
|
raise NotImplementedError
|
||||||
|
logging.debug(f"{self}: Sending config.")
|
||||||
|
if ip_user:
|
||||||
|
suffix = str(self.ip).split(".")[-1]
|
||||||
|
conf = MinerConfig().from_yaml(yaml_config).as_avalon(user_suffix=suffix)
|
||||||
|
else:
|
||||||
|
conf = MinerConfig().from_yaml(yaml_config).as_avalon()
|
||||||
|
command = f"ascset|0,setpool,root,root,{conf}"
|
||||||
|
raw_data = await self.api.send_raw(command) # this should work but doesn't
|
||||||
|
data = self.parse_raw(raw_data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_raw(raw_data: str) -> dict:
|
||||||
|
data = raw_data.split("|")[0]
|
||||||
|
items = data.split(",")
|
||||||
|
ret_data = {}
|
||||||
|
for item in items:
|
||||||
|
print(item)
|
||||||
|
k, v = tuple(item.split("="))
|
||||||
|
ret_data[k] = v
|
||||||
|
return ret_data
|
||||||
|
|
||||||
|
async def get_mac(self) -> str:
|
||||||
mac = None
|
mac = None
|
||||||
version = await self.api.version()
|
version = await self.api.version()
|
||||||
if version:
|
if version:
|
||||||
|
|||||||
Reference in New Issue
Block a user