Compare commits

...

6 Commits

Author SHA1 Message Date
UpstreamData
7a1936d095 improve how the fault light looks on whatsminers 2022-08-11 11:59:19 -06:00
UpstreamData
ee45f2342e improve how the fault light looks on whatsminers 2022-08-11 11:58:42 -06:00
UpstreamData
1f59ef025d bump version number 2022-08-10 16:26:24 -06:00
UpstreamData
d6a153144f remove print statement from btminer configuration 2022-08-10 16:25:54 -06:00
UpstreamData
99001e2e13 added the ability to configure whatsminer via API 2022-08-10 16:21:47 -06:00
UpstreamData
92b847656e add light functions for btminer, and add a way to reset to admin password for btminers to allow unlocking of priviledged API. 2022-08-10 15:31:42 -06:00
5 changed files with 128 additions and 18 deletions

View File

@@ -183,7 +183,7 @@ class BTMinerAPI(BaseMinerAPI):
pwd: str = PyasicSettings().global_whatsminer_password,
):
super().__init__(ip, port)
self.admin_pwd = pwd
self.pwd = pwd
self.current_token = None
async def send_command(
@@ -260,7 +260,7 @@ class BTMinerAPI(BaseMinerAPI):
data = await self.send_command("get_token")
# encrypt the admin password with the salt
pwd = _crypt(self.admin_pwd, "$1$" + data["Msg"]["salt"] + "$")
pwd = _crypt(self.pwd, "$1$" + data["Msg"]["salt"] + "$")
pwd = pwd.split("$")
# take the 4th item from the pwd split
@@ -437,9 +437,10 @@ class BTMinerAPI(BaseMinerAPI):
async def set_led(
self,
auto: bool = True,
color: str = "red",
period: int = 2000,
duration: int = 1000,
period: int = 60,
duration: int = 20,
start: int = 0,
) -> dict:
"""Set the LED on the miner using the API.
@@ -450,6 +451,7 @@ class BTMinerAPI(BaseMinerAPI):
changing the password of the miner using the Whatsminer tool.
Parameters:
auto: Whether or not to reset the LED to auto mode.
color: The LED color to set, either 'red' or 'green'.
period: The flash cycle in ms.
duration: LED on time in the cycle in ms.
@@ -459,16 +461,19 @@ class BTMinerAPI(BaseMinerAPI):
A reply informing of the status of setting the LED.
</details>
"""
command = {
"cmd": "set_led",
"color": color,
"period": period,
"duration": duration,
"start": start,
}
if not auto:
command = {
"cmd": "set_led",
"color": color,
"period": period,
"duration": duration,
"start": start,
}
else:
command = {"cmd": "set_led", "param": "auto"}
token_data = await self.get_token()
enc_command = create_privileged_cmd(token_data, command)
return await self.send_command(enc_command)
return await self.send_command(enc_command, ignore_errors=True)
async def set_low_power(self) -> dict:
"""Set low power mode on the miner using the API.
@@ -542,6 +547,7 @@ class BTMinerAPI(BaseMinerAPI):
A reply informing of the status of setting the password.
"""
self.pwd = old_pwd
# check if password length is greater than 8 bytes
if len(new_pwd.encode("utf-8")) > 8:
raise APIError(
@@ -551,7 +557,12 @@ class BTMinerAPI(BaseMinerAPI):
command = {"cmd": "update_pwd", "old": old_pwd, "new": new_pwd}
token_data = await self.get_token()
enc_command = create_privileged_cmd(token_data, command)
return await self.send_command(enc_command)
try:
data = await self.send_command(enc_command)
except APIError as e:
raise e
self.pwd = new_pwd
return data
async def set_target_freq(self, percent: int) -> dict:
"""Update the target frequency.

View File

@@ -52,6 +52,19 @@ class _Pool:
self.password = data[key]
return self
def as_wm(self, user_suffix: str = None) -> dict:
"""Convert the data in this class to a dict usable by an Whatsminer device.
Parameters:
user_suffix: The suffix to append to username.
"""
username = self.username
if user_suffix:
username = f"{username}{user_suffix}"
pool = {"url": self.url, "user": username, "pass": self.password}
return pool
def as_x19(self, user_suffix: str = None) -> dict:
"""Convert the data in this class to a dict usable by an X19 device.
@@ -141,6 +154,19 @@ class _PoolGroup:
pools.append(pool.as_x19(user_suffix=user_suffix))
return pools
def as_wm(self, user_suffix: str = None) -> List[dict]:
"""Convert the data in this class to a list usable by an Whatsminer device.
Parameters:
user_suffix: The suffix to append to username.
"""
pools = []
for pool in self.pools[:3]:
pools.append(pool.as_wm(user_suffix=user_suffix))
while len(pools) < 3:
pools.append({"url": None, "user": None, "pass": None})
return pools
def as_avalon(self, user_suffix: str = None) -> str:
"""Convert the data in this class to a dict usable by an Avalonminer device.
@@ -327,6 +353,14 @@ class MinerConfig:
"""
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
def as_wm(self, user_suffix: str = None) -> List[dict]:
"""Convert the data in this class to a config usable by an Whatsminer device.
Parameters:
user_suffix: The suffix to append to username.
"""
return self.pool_groups[0].as_x19(user_suffix=user_suffix)
def as_x19(self, user_suffix: str = None) -> str:
"""Convert the data in this class to a config usable by an X19 device.

View File

@@ -72,11 +72,11 @@ class BOSMiner(BaseMiner):
async def fault_light_on(self) -> bool:
"""Sends command to turn on fault light on the miner."""
logging.debug(f"{self}: Sending fault_light on command.")
self.light = True
_ret = await self.send_ssh_command("miner fault_light on")
logging.debug(f"{self}: fault_light on command completed.")
if isinstance(_ret, str):
return True
self.light = True
return self.light
return False
async def fault_light_off(self) -> bool:
@@ -86,6 +86,7 @@ class BOSMiner(BaseMiner):
_ret = await self.send_ssh_command("miner fault_light off")
logging.debug(f"{self}: fault_light off command completed.")
if isinstance(_ret, str):
self.light = False
return True
return False

View File

@@ -100,15 +100,60 @@ class BTMiner(BaseMiner):
return str(mac).upper()
async def _reset_api_pwd_to_admin(self, pwd: str):
try:
data = await self.api.update_pwd(pwd, "admin")
except APIError:
return False
if data:
if "Code" in data.keys():
if data["Code"] == 131:
return True
print(data)
return False
async def check_light(self) -> bool:
if not self.light:
self.light = False
data = None
try:
data = await self.api.get_miner_info()
except APIError:
if not self.light:
self.light = False
if data:
if "Msg" in data.keys():
if "ledstat" in data["Msg"].keys():
if not data["Msg"]["ledstat"] == "auto":
self.light = True
if data["Msg"]["ledstat"] == "auto":
self.light = False
return self.light
async def fault_light_off(self) -> bool:
try:
data = await self.api.set_led(auto=True)
except APIError:
return False
if data:
if "Code" in data.keys():
if data["Code"] == 131:
self.light = False
return True
return False
async def fault_light_on(self) -> bool:
try:
data = await self.api.set_led(auto=False)
await self.api.set_led(
auto=False, color="green", start=0, period=1, duration=0
)
except APIError:
return False
if data:
if "Code" in data.keys():
if data["Code"] == 131:
self.light = True
return True
return False
async def get_errors(self) -> list:
@@ -120,6 +165,25 @@ class BTMiner(BaseMiner):
async def restart_backend(self) -> bool:
return False
async def send_config(self, yaml_config, ip_user: bool = False):
if ip_user:
suffix = str(self.ip).split(".")[-1]
conf = MinerConfig().from_yaml(yaml_config).as_wm(user_suffix=suffix)
else:
conf = MinerConfig().from_yaml(yaml_config).as_wm()
await self.api.update_pools(
conf[0]["url"],
conf[0]["user"],
conf[0]["pass"],
conf[1]["url"],
conf[1]["user"],
conf[1]["pass"],
conf[2]["url"],
conf[2]["user"],
conf[2]["pass"],
)
async def get_config(self) -> MinerConfig:
pools = None
cfg = MinerConfig()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.15.1"
version = "0.15.3"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"