Compare commits

...

8 Commits

Author SHA1 Message Date
UpstreamData
1e5b19c149 version: bump version number. 2023-02-16 08:47:15 -07:00
UpstreamData
c9339fec2f bug: fix issues with new versions of braiins OS, and fix bugs with innosilicon miners not returning much data at all. 2023-02-16 08:46:32 -07:00
UpstreamData
018c09e84f version: bump version number. 2023-02-15 14:34:08 -07:00
UpstreamData
46e7f9a569 bug: remove a missed print statement. 2023-02-15 14:31:32 -07:00
UpstreamData
996ab58252 version: bump version number. 2023-02-15 14:19:57 -07:00
UpstreamData
04974d5287 bug: fix reboot and restart on btminer not returning data. 2023-02-15 14:17:57 -07:00
UpstreamData
1a8a5ccb0e version: bump version number. 2023-02-14 10:33:46 -07:00
UpstreamData
4c61c4c345 bug: add MAC address support for stock S9s. 2023-02-14 10:33:14 -07:00
6 changed files with 130 additions and 43 deletions

View File

@@ -173,9 +173,14 @@ If you are sure you want to use this command please use API.send_command("{comma
writer.write(data)
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
await writer.drain()
# instantiate data
ret_data = b""
ret_data = await asyncio.wait_for(reader.read(4096), timeout=timeout)
try:
# Fix for stupid whatsminer bug, reboot/restart seem to not load properly in the loop
# have to receive, save the data, check if there is more data by reading with a short timeout
# append that data if there is more, and then onto the main loop.
ret_data += await asyncio.wait_for(reader.read(1), timeout=1)
except asyncio.TimeoutError:
return ret_data
# loop to receive all the data
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving")
@@ -244,6 +249,8 @@ If you are sure you want to use this command please use API.send_command("{comma
str_data = str_data.replace("}{", "},{")
# fix an error with a bmminer return having a specific comma that breaks json.loads()
str_data = str_data.replace("[,{", "[{")
# fix an error with a btminer return having a missing comma. (2023-01-06 version)
str_data = str_data.replace('""temp0', '","temp0')
# fix an error with Avalonminers returning inf and nan
str_data = str_data.replace("info", "1nfo")
str_data = str_data.replace("inf", "0")

View File

@@ -247,21 +247,9 @@ class BTMinerAPI(BaseMinerAPI):
try:
data = await self._send_bytes(enc_command, timeout)
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
if command["cmd"] in ["reboot", "restart_btminer", "power_on", "power_off"]:
logging.info(
f"{self} - (reboot/restart_btminer/power_on/power_off) - Whatsminers currently break this. "
f"Ignoring exception. Command probably worked."
)
# FAKING IT HERE
data = (
b'{"STATUS": "S", "When": 1670966423, "Code": 131, "Msg": "API command OK", "Description": "'
+ command["cmd"].encode("utf-8")
+ b'"}'
)
else:
if ignore_errors:
return {}
raise APIError("No data was returned from the API.")
if ignore_errors:
return {}
raise APIError("No data was returned from the API.")
if not data:
if ignore_errors:

View File

@@ -483,10 +483,12 @@ class BOSMiner(BaseMiner):
api_devs = d["devs"][0]
except (KeyError, IndexError):
api_devs = None
print(api_devdetails)
print(api_devs)
print(api_temps)
if api_temps:
try:
offset = 6 if api_temps["TEMPS"][0]["ID"] in [6, 7, 8] else 0
offset = 6 if api_temps["TEMPS"][0]["ID"] in [6, 7, 8] else 1
for board in api_temps["TEMPS"]:
_id = board["ID"] - offset
@@ -499,7 +501,7 @@ class BOSMiner(BaseMiner):
if api_devdetails:
try:
offset = 6 if api_devdetails["DEVDETAILS"][0]["ID"] in [6, 7, 8] else 0
offset = 6 if api_devdetails["DEVDETAILS"][0]["ID"] in [6, 7, 8] else 1
for board in api_devdetails["DEVDETAILS"]:
_id = board["ID"] - offset
@@ -511,7 +513,7 @@ class BOSMiner(BaseMiner):
if api_devs:
try:
offset = 6 if api_devs["DEVS"][0]["ID"] in [6, 7, 8] else 0
offset = 6 if api_devs["DEVS"][0]["ID"] in [6, 7, 8] else 1
for board in api_devs["DEVS"]:
_id = board["ID"] - offset

View File

@@ -14,9 +14,54 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
import json
from typing import Optional, Union
import httpx
from pyasic.miners._backends import BMMiner # noqa - Ignore access to _module
from pyasic.miners._types import S9 # noqa - Ignore access to _module
from pyasic.settings import PyasicSettings
class BMMinerS9(BMMiner, S9):
pass
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver=api_ver)
self.ip = ip
self.uname = "root"
self.pwd = PyasicSettings().global_x19_password
async def send_web_command(
self, command: str, params: dict = None
) -> Optional[dict]:
url = f"http://{self.ip}/cgi-bin/{command}.cgi"
auth = httpx.DigestAuth(self.uname, self.pwd)
try:
async with httpx.AsyncClient() as client:
if params:
data = await client.post(url, data=params, auth=auth)
else:
data = await client.get(url, auth=auth)
except httpx.HTTPError:
pass
else:
if data.status_code == 200:
try:
return data.json()
except json.decoder.JSONDecodeError:
pass
async def get_mac(self) -> Union[str, None]:
try:
data = await self.send_web_command("get_system_info")
if data:
return data["macaddr"]
except KeyError:
pass
try:
data = await self.send_web_command("get_network_info")
if data:
return data["macaddr"]
except KeyError:
pass

View File

@@ -136,10 +136,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
async def get_mac(
self,
web_getAll: dict = None,
web_getAll: dict = None, # noqa
web_overview: dict = None, # noqa: named this way for automatic functionality
) -> Optional[str]:
web_all_data = web_getAll
web_all_data = web_getAll.get("all")
if not web_all_data and not web_overview:
try:
web_overview = await self.send_web_command("overview")
@@ -183,7 +183,7 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
api_summary: dict = None,
web_getAll: dict = None, # noqa: named this way for automatic functionality
) -> Optional[float]:
web_all_data = web_getAll
web_all_data = web_getAll.get("all")
if not api_summary and not web_all_data:
try:
api_summary = await self.api.summary()
@@ -209,7 +209,7 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
api_stats: dict = None,
web_getAll: dict = None, # noqa: named this way for automatic functionality
) -> List[HashBoard]:
web_all_data = web_getAll
web_all_data = web_getAll.get("all")
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
@@ -231,8 +231,9 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
if api_stats:
if api_stats.get("STATS"):
for idx, board in enumerate(api_stats["STATS"]):
for board in api_stats["STATS"]:
try:
idx = board["Chain ID"]
chips = board["Num active chips"]
except KeyError:
pass
@@ -242,25 +243,31 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
if web_all_data:
if web_all_data.get("chain"):
for idx, board in enumerate(web_all_data["chain"]):
temp = board.get("Temp min")
if temp:
hashboards[idx].temp = round(temp)
for board in web_all_data["chain"]:
idx = board.get("ASC")
if idx is not None:
temp = board.get("Temp min")
if temp:
hashboards[idx].temp = round(temp)
hashrate = board.get("Hash Rate H")
if hashrate:
hashboards[idx].hashrate = round(hashrate / 1000000000000, 2)
hashrate = board.get("Hash Rate H")
if hashrate:
hashboards[idx].hashrate = round(
hashrate / 1000000000000, 2
)
chip_temp = board.get("Temp max")
if chip_temp:
hashboards[idx].chip_temp = round(chip_temp)
chip_temp = board.get("Temp max")
if chip_temp:
hashboards[idx].chip_temp = round(chip_temp)
return hashboards
async def get_wattage(
self, web_getAll: dict = None
) -> Optional[int]: # noqa: named this way for automatic functionality
web_all_data = web_getAll
self,
web_getAll: dict = None,
api_stats: dict = None, # noqa: named this way for automatic functionality
) -> Optional[int]:
web_all_data = web_getAll.get("all")
if not web_all_data:
try:
web_all_data = await self.send_web_command("getAll")
@@ -275,11 +282,28 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
except KeyError:
pass
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
wattage = board["power"]
except KeyError:
pass
else:
wattage = int(wattage)
return wattage
async def get_fans(
self,
web_getAll: dict = None, # noqa: named this way for automatic functionality
) -> List[Fan]:
web_all_data = web_getAll
web_all_data = web_getAll.get("all")
if not web_all_data:
try:
web_all_data = await self.send_web_command("getAll")
@@ -350,3 +374,24 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
if not err == 0:
errors.append(InnosiliconError(error_code=err))
return errors
async def get_wattage_limit(self, web_getAll: dict = None) -> Optional[int]:
web_all_data = web_getAll.get("all")
if not web_all_data:
try:
web_all_data = await self.send_web_command("getAll")
except APIError:
pass
else:
web_all_data = web_all_data["all"]
if web_all_data:
try:
level = web_all_data["running_mode"]["level"]
except KeyError:
pass
else:
# this is very possibly not correct.
level = int(level)
limit = 1250 + (250 * level)
return limit

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.28.1"
version = "0.28.5"
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"