Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6424acf5f | ||
|
|
34389e9133 | ||
|
|
b0f7629607 | ||
|
|
6e50f1f769 | ||
|
|
708b506f6f | ||
|
|
673d63daf0 | ||
|
|
ea55fed8d1 | ||
|
|
f5fd539eba | ||
|
|
51a56f441c | ||
|
|
54310b1d79 | ||
|
|
cb30b761dc | ||
|
|
cb50a7cac8 | ||
|
|
738af245cb | ||
|
|
71e9af1b91 | ||
|
|
1cd2566d0a | ||
|
|
1f1e5f23a2 | ||
|
|
3394234e4f | ||
|
|
e9882124ff | ||
|
|
1e5b19c149 | ||
|
|
c9339fec2f | ||
|
|
018c09e84f | ||
|
|
46e7f9a569 | ||
|
|
996ab58252 | ||
|
|
04974d5287 | ||
|
|
1a8a5ccb0e | ||
|
|
4c61c4c345 | ||
|
|
a6d6bfe73d | ||
|
|
f159e9ccb4 | ||
|
|
8ae2932274 | ||
|
|
52786ba954 |
@@ -173,9 +173,14 @@ If you are sure you want to use this command please use API.send_command("{comma
|
|||||||
writer.write(data)
|
writer.write(data)
|
||||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
|
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
ret_data = await asyncio.wait_for(reader.read(4096), timeout=timeout)
|
||||||
# instantiate data
|
try:
|
||||||
ret_data = b""
|
# 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
|
# loop to receive all the data
|
||||||
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving")
|
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("}{", "},{")
|
str_data = str_data.replace("}{", "},{")
|
||||||
# fix an error with a bmminer return having a specific comma that breaks json.loads()
|
# fix an error with a bmminer return having a specific comma that breaks json.loads()
|
||||||
str_data = str_data.replace("[,{", "[{")
|
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
|
# fix an error with Avalonminers returning inf and nan
|
||||||
str_data = str_data.replace("info", "1nfo")
|
str_data = str_data.replace("info", "1nfo")
|
||||||
str_data = str_data.replace("inf", "0")
|
str_data = str_data.replace("inf", "0")
|
||||||
|
|||||||
@@ -13,18 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from pyasic.API import APIError, BaseMinerAPI
|
from pyasic.API import APIError, BaseMinerAPI
|
||||||
|
|||||||
@@ -247,21 +247,9 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
try:
|
try:
|
||||||
data = await self._send_bytes(enc_command, timeout)
|
data = await self._send_bytes(enc_command, timeout)
|
||||||
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||||
if command["cmd"] in ["reboot", "restart_btminer", "power_on", "power_off"]:
|
if ignore_errors:
|
||||||
logging.info(
|
return {}
|
||||||
f"{self} - (reboot/restart_btminer/power_on/power_off) - Whatsminers currently break this. "
|
raise APIError("No data was returned from the API.")
|
||||||
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 not data:
|
if not data:
|
||||||
if ignore_errors:
|
if ignore_errors:
|
||||||
|
|||||||
@@ -13,18 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
from pyasic.API.bmminer import BMMinerAPI
|
from pyasic.API.bmminer import BMMinerAPI
|
||||||
from pyasic.API.bosminer import BOSMinerAPI
|
from pyasic.API.bosminer import BOSMinerAPI
|
||||||
from pyasic.API.btminer import BTMinerAPI
|
from pyasic.API.btminer import BTMinerAPI
|
||||||
|
|||||||
@@ -245,7 +245,9 @@ class MinerConfig:
|
|||||||
fan_speed: Manual fan speed to run the fan at (only if temp_mode == "manual").
|
fan_speed: Manual fan speed to run the fan at (only if temp_mode == "manual").
|
||||||
asicboost: Whether or not to enable asicboost.
|
asicboost: Whether or not to enable asicboost.
|
||||||
autotuning_enabled: Whether or not to enable autotuning.
|
autotuning_enabled: Whether or not to enable autotuning.
|
||||||
|
autotuning_mode: Autotuning mode, either "wattage" or "hashrate".
|
||||||
autotuning_wattage: The wattage to use when autotuning.
|
autotuning_wattage: The wattage to use when autotuning.
|
||||||
|
autotuning_hashrate: The hashrate to use when autotuning.
|
||||||
dps_enabled: Whether or not to enable dynamic power scaling.
|
dps_enabled: Whether or not to enable dynamic power scaling.
|
||||||
dps_power_step: The amount of power to reduce autotuning by when the miner reaches dangerous temp.
|
dps_power_step: The amount of power to reduce autotuning by when the miner reaches dangerous temp.
|
||||||
dps_min_power: The minimum power to reduce autotuning to.
|
dps_min_power: The minimum power to reduce autotuning to.
|
||||||
@@ -266,7 +268,9 @@ class MinerConfig:
|
|||||||
asicboost: bool = None
|
asicboost: bool = None
|
||||||
|
|
||||||
autotuning_enabled: bool = True
|
autotuning_enabled: bool = True
|
||||||
autotuning_wattage: int = 900
|
autotuning_mode: Literal["power", "hashrate"] = None
|
||||||
|
autotuning_wattage: int = None
|
||||||
|
autotuning_hashrate: int = None
|
||||||
|
|
||||||
dps_enabled: bool = None
|
dps_enabled: bool = None
|
||||||
dps_power_step: int = None
|
dps_power_step: int = None
|
||||||
@@ -349,14 +353,20 @@ class MinerConfig:
|
|||||||
self.autotuning_enabled = data[key][_key]
|
self.autotuning_enabled = data[key][_key]
|
||||||
elif _key == "psu_power_limit":
|
elif _key == "psu_power_limit":
|
||||||
self.autotuning_wattage = data[key][_key]
|
self.autotuning_wattage = data[key][_key]
|
||||||
|
elif _key == "power_target":
|
||||||
|
self.autotuning_wattage = data[key][_key]
|
||||||
|
elif _key == "hashrate_target":
|
||||||
|
self.autotuning_hashrate = data[key][_key]
|
||||||
|
elif _key == "mode":
|
||||||
|
self.autotuning_mode = data[key][_key].replace("_target", "")
|
||||||
|
|
||||||
if key == "power_scaling":
|
if key in ["power_scaling", "performance_scaling"]:
|
||||||
for _key in data[key].keys():
|
for _key in data[key].keys():
|
||||||
if _key == "enabled":
|
if _key == "enabled":
|
||||||
self.dps_enabled = data[key][_key]
|
self.dps_enabled = data[key][_key]
|
||||||
elif _key == "power_step":
|
elif _key == "power_step":
|
||||||
self.dps_power_step = data[key][_key]
|
self.dps_power_step = data[key][_key]
|
||||||
elif _key == "min_psu_power_limit":
|
elif _key in ["min_psu_power_limit", "min_power_target"]:
|
||||||
self.dps_min_power = data[key][_key]
|
self.dps_min_power = data[key][_key]
|
||||||
elif _key == "shutdown_enabled":
|
elif _key == "shutdown_enabled":
|
||||||
self.dps_shutdown_enabled = data[key][_key]
|
self.dps_shutdown_enabled = data[key][_key]
|
||||||
@@ -391,8 +401,8 @@ class MinerConfig:
|
|||||||
pool_groups = []
|
pool_groups = []
|
||||||
for group in data["pool_groups"]:
|
for group in data["pool_groups"]:
|
||||||
pool_groups.append(_PoolGroup().from_dict(group))
|
pool_groups.append(_PoolGroup().from_dict(group))
|
||||||
for key in data.keys():
|
for key in data:
|
||||||
if getattr(self, key) and not key == "pool_groups":
|
if hasattr(self, key) and not key == "pool_groups":
|
||||||
setattr(self, key, data[key])
|
setattr(self, key, data[key])
|
||||||
self.pool_groups = pool_groups
|
self.pool_groups = pool_groups
|
||||||
return self
|
return self
|
||||||
@@ -481,8 +491,8 @@ class MinerConfig:
|
|||||||
cfg = {
|
cfg = {
|
||||||
"format": {
|
"format": {
|
||||||
"version": "1.2+",
|
"version": "1.2+",
|
||||||
"model": f"Antminer {model}",
|
"model": f"Antminer {model.replace('j', 'J')}",
|
||||||
"generator": "Upstream Config Utility",
|
"generator": "pyasic",
|
||||||
"timestamp": int(time.time()),
|
"timestamp": int(time.time()),
|
||||||
},
|
},
|
||||||
"group": [
|
"group": [
|
||||||
@@ -499,9 +509,19 @@ class MinerConfig:
|
|||||||
if self.autotuning_enabled or self.autotuning_wattage:
|
if self.autotuning_enabled or self.autotuning_wattage:
|
||||||
cfg["autotuning"] = {}
|
cfg["autotuning"] = {}
|
||||||
if self.autotuning_enabled:
|
if self.autotuning_enabled:
|
||||||
cfg["autotuning"]["enabled"] = self.autotuning_enabled
|
cfg["autotuning"]["enabled"] = True
|
||||||
if self.autotuning_wattage:
|
else:
|
||||||
cfg["autotuning"]["psu_power_limit"] = self.autotuning_wattage
|
cfg["autotuning"]["enabled"] = False
|
||||||
|
if self.autotuning_mode:
|
||||||
|
cfg["format"]["version"] = "2.0"
|
||||||
|
cfg["autotuning"]["mode"] = self.autotuning_mode + "_target"
|
||||||
|
if self.autotuning_wattage:
|
||||||
|
cfg["autotuning"]["power_target"] = self.autotuning_wattage
|
||||||
|
elif self.autotuning_hashrate:
|
||||||
|
cfg["autotuning"]["hashrate_target"] = self.autotuning_hashrate
|
||||||
|
else:
|
||||||
|
if self.autotuning_wattage:
|
||||||
|
cfg["autotuning"]["psu_power_limit"] = self.autotuning_wattage
|
||||||
|
|
||||||
if self.asicboost:
|
if self.asicboost:
|
||||||
cfg["hash_chain_global"] = {}
|
cfg["hash_chain_global"] = {}
|
||||||
@@ -525,7 +545,10 @@ class MinerConfig:
|
|||||||
if self.dps_power_step:
|
if self.dps_power_step:
|
||||||
cfg["power_scaling"]["power_step"] = self.dps_power_step
|
cfg["power_scaling"]["power_step"] = self.dps_power_step
|
||||||
if self.dps_min_power:
|
if self.dps_min_power:
|
||||||
cfg["power_scaling"]["min_psu_power_limit"] = self.dps_min_power
|
if cfg["format"]["version"] == "2.0":
|
||||||
|
cfg["power_scaling"]["min_power_target"] = self.dps_min_power
|
||||||
|
else:
|
||||||
|
cfg["power_scaling"]["min_psu_power_limit"] = self.dps_min_power
|
||||||
if self.dps_shutdown_enabled:
|
if self.dps_shutdown_enabled:
|
||||||
cfg["power_scaling"]["shutdown_enabled"] = self.dps_shutdown_enabled
|
cfg["power_scaling"]["shutdown_enabled"] = self.dps_shutdown_enabled
|
||||||
if self.dps_shutdown_duration:
|
if self.dps_shutdown_duration:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from pyasic.miners._backends import BMMiner # noqa - Ignore access to _module
|
|||||||
from pyasic.settings import PyasicSettings
|
from pyasic.settings import PyasicSettings
|
||||||
|
|
||||||
|
|
||||||
class BMMinerX19(BMMiner):
|
class X19(BMMiner):
|
||||||
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||||
super().__init__(ip, api_ver=api_ver)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -126,6 +126,13 @@ class BMMinerX19(BMMiner):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = await self.send_web_command("get_network_info")
|
||||||
|
if data:
|
||||||
|
return data["macaddr"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
async def get_errors(self) -> List[MinerErrorData]:
|
async def get_errors(self) -> List[MinerErrorData]:
|
||||||
errors = []
|
errors = []
|
||||||
data = await self.send_web_command("summary")
|
data = await self.send_web_command("summary")
|
||||||
@@ -20,3 +20,5 @@ from .btminer import BTMiner
|
|||||||
from .cgminer import CGMiner
|
from .cgminer import CGMiner
|
||||||
from .cgminer_avalon import CGMinerAvalon
|
from .cgminer_avalon import CGMinerAvalon
|
||||||
from .hiveon import Hiveon
|
from .hiveon import Hiveon
|
||||||
|
from .vnish import VNish
|
||||||
|
from .X19 import X19
|
||||||
|
|||||||
@@ -198,11 +198,8 @@ class BOSMiner(BaseMiner):
|
|||||||
return self.config
|
return self.config
|
||||||
if conn:
|
if conn:
|
||||||
async with conn:
|
async with conn:
|
||||||
logging.debug(f"{self}: Opening SFTP connection.")
|
# good ol' BBB compatibility :/
|
||||||
async with conn.start_sftp_client() as sftp:
|
toml_data = toml.loads((await conn.run("cat /etc/bosminer.toml")).stdout)
|
||||||
logging.debug(f"{self}: Reading config file.")
|
|
||||||
async with sftp.open("/etc/bosminer.toml") as file:
|
|
||||||
toml_data = toml.loads(await file.read())
|
|
||||||
logging.debug(f"{self}: Converting config file.")
|
logging.debug(f"{self}: Converting config file.")
|
||||||
cfg = MinerConfig().from_raw(toml_data)
|
cfg = MinerConfig().from_raw(toml_data)
|
||||||
self.config = cfg
|
self.config = cfg
|
||||||
@@ -219,14 +216,28 @@ class BOSMiner(BaseMiner):
|
|||||||
except (asyncssh.Error, OSError):
|
except (asyncssh.Error, OSError):
|
||||||
return None
|
return None
|
||||||
async with conn:
|
async with conn:
|
||||||
await conn.run("/etc/init.d/bosminer stop")
|
# BBB check because bitmain suxx
|
||||||
logging.debug(f"{self}: Opening SFTP connection.")
|
bbb_check = await conn.run("if [ ! -f /etc/init.d/bosminer ]; then echo '1'; else echo '0'; fi;")
|
||||||
async with conn.start_sftp_client() as sftp:
|
|
||||||
logging.debug(f"{self}: Opening config file.")
|
bbb = bbb_check.stdout.strip() == "1"
|
||||||
async with sftp.open("/etc/bosminer.toml", "w+") as file:
|
|
||||||
await file.write(toml_conf)
|
if not bbb:
|
||||||
logging.debug(f"{self}: Restarting BOSMiner")
|
await conn.run("/etc/init.d/bosminer stop")
|
||||||
await conn.run("/etc/init.d/bosminer start")
|
logging.debug(f"{self}: Opening SFTP connection.")
|
||||||
|
async with conn.start_sftp_client() as sftp:
|
||||||
|
logging.debug(f"{self}: Opening config file.")
|
||||||
|
async with sftp.open("/etc/bosminer.toml", "w+") as file:
|
||||||
|
await file.write(toml_conf)
|
||||||
|
logging.debug(f"{self}: Restarting BOSMiner")
|
||||||
|
await conn.run("/etc/init.d/bosminer start")
|
||||||
|
|
||||||
|
# I really hate BBB, please get rid of it if you have it
|
||||||
|
else:
|
||||||
|
await conn.run("/etc/init.d/S99bosminer stop")
|
||||||
|
logging.debug(f"{self}: BBB sending config")
|
||||||
|
await conn.run("echo '" + toml_conf + "' > /etc/bosminer.toml")
|
||||||
|
logging.debug(f"{self}: BBB restarting bosminer.")
|
||||||
|
await conn.run("/etc/init.d/S99bosminer start")
|
||||||
|
|
||||||
async def set_power_limit(self, wattage: int) -> bool:
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
try:
|
try:
|
||||||
@@ -483,10 +494,9 @@ class BOSMiner(BaseMiner):
|
|||||||
api_devs = d["devs"][0]
|
api_devs = d["devs"][0]
|
||||||
except (KeyError, IndexError):
|
except (KeyError, IndexError):
|
||||||
api_devs = None
|
api_devs = None
|
||||||
|
|
||||||
if api_temps:
|
if api_temps:
|
||||||
try:
|
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"]:
|
for board in api_temps["TEMPS"]:
|
||||||
_id = board["ID"] - offset
|
_id = board["ID"] - offset
|
||||||
@@ -499,7 +509,7 @@ class BOSMiner(BaseMiner):
|
|||||||
|
|
||||||
if api_devdetails:
|
if api_devdetails:
|
||||||
try:
|
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"]:
|
for board in api_devdetails["DEVDETAILS"]:
|
||||||
_id = board["ID"] - offset
|
_id = board["ID"] - offset
|
||||||
@@ -511,7 +521,7 @@ class BOSMiner(BaseMiner):
|
|||||||
|
|
||||||
if api_devs:
|
if api_devs:
|
||||||
try:
|
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"]:
|
for board in api_devs["DEVS"]:
|
||||||
_id = board["ID"] - offset
|
_id = board["ID"] - offset
|
||||||
|
|||||||
218
pyasic/miners/_backends/vnish.py
Normal file
218
pyasic/miners/_backends/vnish.py
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import warnings
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from pyasic.errors import APIError
|
||||||
|
from pyasic.miners._backends.bmminer import BMMiner
|
||||||
|
from pyasic.settings import PyasicSettings
|
||||||
|
|
||||||
|
|
||||||
|
class VNish(BMMiner):
|
||||||
|
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
|
||||||
|
super().__init__(ip, api_ver)
|
||||||
|
self.api_type = "VNish"
|
||||||
|
self.uname = "root"
|
||||||
|
self.pwd = PyasicSettings().global_vnish_password
|
||||||
|
self.jwt = None
|
||||||
|
|
||||||
|
async def auth(self):
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
try:
|
||||||
|
auth = await client.post(
|
||||||
|
f"http://{self.ip}/api/v1/unlock",
|
||||||
|
json={"pw": self.pwd},
|
||||||
|
)
|
||||||
|
except httpx.HTTPError:
|
||||||
|
warnings.warn(f"Could not authenticate web token with miner: {self}")
|
||||||
|
else:
|
||||||
|
if not auth.status_code == 200:
|
||||||
|
warnings.warn(
|
||||||
|
f"Could not authenticate web token with miner: {self}"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
json_auth = auth.json()
|
||||||
|
self.jwt = json_auth["token"]
|
||||||
|
return self.jwt
|
||||||
|
|
||||||
|
async def send_web_command(
|
||||||
|
self, command: str, data: Union[dict, None] = None, method: str = "GET"
|
||||||
|
):
|
||||||
|
if not self.jwt:
|
||||||
|
await self.auth()
|
||||||
|
if not data:
|
||||||
|
data = {}
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
for i in range(PyasicSettings().miner_get_data_retries):
|
||||||
|
try:
|
||||||
|
auth = self.jwt
|
||||||
|
if command.startswith("system"):
|
||||||
|
auth = "Bearer " + self.jwt
|
||||||
|
if method == "GET":
|
||||||
|
response = await client.get(
|
||||||
|
f"http://{self.ip}/api/v1/{command}",
|
||||||
|
headers={"Authorization": auth},
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
elif method == "POST":
|
||||||
|
if data:
|
||||||
|
response = await client.post(
|
||||||
|
f"http://{self.ip}/api/v1/{command}",
|
||||||
|
headers={"Authorization": auth},
|
||||||
|
timeout=5,
|
||||||
|
json=data,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
response = await client.post(
|
||||||
|
f"http://{self.ip}/api/v1/{command}",
|
||||||
|
headers={"Authorization": auth},
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise APIError("Bad method type.")
|
||||||
|
if not response.status_code == 200:
|
||||||
|
# refresh the token, retry
|
||||||
|
await self.auth()
|
||||||
|
continue
|
||||||
|
json_data = response.json()
|
||||||
|
if json_data:
|
||||||
|
return json_data
|
||||||
|
return True
|
||||||
|
except httpx.HTTPError:
|
||||||
|
pass
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_model(self, api_stats: dict = None) -> Optional[str]:
|
||||||
|
# check if model is cached
|
||||||
|
if self.model:
|
||||||
|
logging.debug(f"Found model for {self.ip}: {self.model} (VNish)")
|
||||||
|
return self.model + " (VNish)"
|
||||||
|
|
||||||
|
if not api_stats:
|
||||||
|
try:
|
||||||
|
api_stats = await self.api.stats()
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if api_stats:
|
||||||
|
try:
|
||||||
|
m_type = api_stats["STATS"][0]["Type"]
|
||||||
|
self.model = m_type.split(" ")[1]
|
||||||
|
return self.model
|
||||||
|
except (KeyError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def restart_backend(self) -> bool:
|
||||||
|
data = await self.send_web_command("mining/restart", method="POST")
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def reboot(self) -> bool:
|
||||||
|
data = await self.send_web_command("system/reboot", method="POST")
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def get_mac(self, web_summary: dict = None) -> str:
|
||||||
|
if not web_summary:
|
||||||
|
web_info = await self.send_web_command("info")
|
||||||
|
|
||||||
|
if web_info:
|
||||||
|
try:
|
||||||
|
mac = web_info["system"]["network_status"]["mac"]
|
||||||
|
return mac
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if web_summary:
|
||||||
|
try:
|
||||||
|
mac = web_summary["system"]["network_status"]["mac"]
|
||||||
|
return mac
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_hostname(self, web_summary: dict = None) -> str:
|
||||||
|
if not web_summary:
|
||||||
|
web_info = await self.send_web_command("info")
|
||||||
|
|
||||||
|
if web_info:
|
||||||
|
try:
|
||||||
|
hostname = web_info["system"]["network_status"]["hostname"]
|
||||||
|
return hostname
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if web_summary:
|
||||||
|
try:
|
||||||
|
hostname = web_summary["system"]["network_status"]["hostname"]
|
||||||
|
return hostname
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_wattage(self, web_summary: dict = None) -> Optional[int]:
|
||||||
|
if not web_summary:
|
||||||
|
web_summary = await self.send_web_command("summary")
|
||||||
|
|
||||||
|
if web_summary:
|
||||||
|
try:
|
||||||
|
wattage = web_summary["miner"]["power_usage"]
|
||||||
|
wattage = round(wattage * 1000)
|
||||||
|
return wattage
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]:
|
||||||
|
# get hr from API
|
||||||
|
if not api_summary:
|
||||||
|
try:
|
||||||
|
api_summary = await self.api.summary()
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if api_summary:
|
||||||
|
try:
|
||||||
|
return round(
|
||||||
|
float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2
|
||||||
|
)
|
||||||
|
except (IndexError, KeyError, ValueError, TypeError) as e:
|
||||||
|
print(e)
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_wattage_limit(self, web_settings: dict = None) -> Optional[int]:
|
||||||
|
if not web_settings:
|
||||||
|
web_settings = await self.send_web_command("summary")
|
||||||
|
|
||||||
|
if web_settings:
|
||||||
|
try:
|
||||||
|
wattage_limit = web_settings["miner"]["overclock"]["preset"]
|
||||||
|
return int(wattage_limit)
|
||||||
|
except (KeyError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]:
|
||||||
|
if not web_summary:
|
||||||
|
web_summary = await self.send_web_command("summary")
|
||||||
|
|
||||||
|
if web_summary:
|
||||||
|
try:
|
||||||
|
fw_ver = web_summary["miner"]["miner_type"]
|
||||||
|
fw_ver = fw_ver.split("(Vnish ")[1].replace(")", "")
|
||||||
|
return fw_ver
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
@@ -174,10 +174,7 @@ class M30SPlusPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.model = "M30S++ VH50"
|
self.model = "M30S++ VH50"
|
||||||
self.nominal_chips = 0
|
self.nominal_chips = 74
|
||||||
warnings.warn(
|
|
||||||
"Unknown chip count for miner type M30S++ VH50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
|
||||||
)
|
|
||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
@@ -195,10 +192,7 @@ class M30SPlusPlusVH70(WhatsMiner): # noqa - ignore ABC method implementation
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.model = "M30S++ VH70"
|
self.model = "M30S++ VH70"
|
||||||
self.nominal_chips = 0
|
self.nominal_chips = 70
|
||||||
warnings.warn(
|
|
||||||
"Unknown chip count for miner type M30S++ VH70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
|
||||||
)
|
|
||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ from .bmminer import *
|
|||||||
from .bosminer import *
|
from .bosminer import *
|
||||||
from .cgminer import *
|
from .cgminer import *
|
||||||
from .hiveon import *
|
from .hiveon import *
|
||||||
|
from .vnish import *
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19 # noqa - Ignore access to _module
|
from pyasic.miners._types import S19 # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19(BMMinerX19, S19):
|
class BMMinerS19(X19, S19):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19Pro # noqa - Ignore access to _module
|
from pyasic.miners._types import S19Pro # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19Pro(BMMinerX19, S19Pro):
|
class BMMinerS19Pro(X19, S19Pro):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19XP # noqa - Ignore access to _module
|
from pyasic.miners._types import S19XP # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19XP(BMMinerX19, S19XP):
|
class BMMinerS19XP(X19, S19XP):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19a # noqa - Ignore access to _module
|
from pyasic.miners._types import S19a # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19a(BMMinerX19, S19a):
|
class BMMinerS19a(X19, S19a):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19aPro # noqa - Ignore access to _module
|
from pyasic.miners._types import S19aPro # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19aPro(BMMinerX19, S19aPro):
|
class BMMinerS19aPro(X19, S19aPro):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19j # noqa - Ignore access to _module
|
from pyasic.miners._types import S19j # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19j(BMMinerX19, S19j):
|
class BMMinerS19j(X19, S19j):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import S19jPro # noqa - Ignore access to _module
|
from pyasic.miners._types import S19jPro # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerS19jPro(BMMinerX19, S19jPro):
|
class BMMinerS19jPro(X19, S19jPro):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import X19
|
||||||
from pyasic.miners._types import T19 # noqa - Ignore access to _module
|
from pyasic.miners._types import T19 # noqa - Ignore access to _module
|
||||||
|
|
||||||
from .X19 import BMMinerX19
|
# noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
class BMMinerT19(BMMinerX19, T19):
|
class BMMinerT19(X19, T19):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,9 +14,54 @@
|
|||||||
# limitations under the License. -
|
# 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._backends import BMMiner # noqa - Ignore access to _module
|
||||||
from pyasic.miners._types import S9 # 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):
|
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
|
||||||
|
|||||||
22
pyasic/miners/antminer/vnish/X19/S19.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19 # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19(VNish, S19):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/S19_Pro.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19_Pro.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19Pro # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19Pro(VNish, S19Pro):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/S19_XP.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19_XP.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19XP # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19XP(VNish, S19XP):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/S19a.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19a.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19a # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19a(VNish, S19a):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/S19a_Pro.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19a_Pro.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19aPro # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19aPro(VNish, S19aPro):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/S19j.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19j.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19j # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19j(VNish, S19j):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/S19j_Pro.py
Normal file
22
pyasic/miners/antminer/vnish/X19/S19j_Pro.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import S19jPro # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishS19jPro(VNish, S19jPro):
|
||||||
|
pass
|
||||||
22
pyasic/miners/antminer/vnish/X19/T19.py
Normal file
22
pyasic/miners/antminer/vnish/X19/T19.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners._backends import VNish # noqa - Ignore access to _module
|
||||||
|
from pyasic.miners._types import T19 # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
|
||||||
|
class VNishT19(VNish, T19):
|
||||||
|
pass
|
||||||
24
pyasic/miners/antminer/vnish/X19/__init__.py
Normal file
24
pyasic/miners/antminer/vnish/X19/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .S19 import VNishS19
|
||||||
|
from .S19_Pro import VNishS19Pro
|
||||||
|
from .S19_XP import VNishS19XP
|
||||||
|
from .S19a import VNishS19a
|
||||||
|
from .S19a_Pro import VNishS19aPro
|
||||||
|
from .S19j import VNishS19j
|
||||||
|
from .S19j_Pro import VNishS19jPro
|
||||||
|
from .T19 import VNishT19
|
||||||
17
pyasic/miners/antminer/vnish/__init__.py
Normal file
17
pyasic/miners/antminer/vnish/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .X19 import *
|
||||||
@@ -162,7 +162,7 @@ class BaseMiner(ABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def resume_mining(self) -> bool:
|
async def resume_mining(self) -> bool:
|
||||||
"""Stop the mining process of the miner.
|
"""Resume the mining process of the miner.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A boolean value of the success of resuming the mining process.
|
A boolean value of the success of resuming the mining process.
|
||||||
|
|||||||
@@ -13,18 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
@@ -148,10 +136,10 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
|
|
||||||
async def get_mac(
|
async def get_mac(
|
||||||
self,
|
self,
|
||||||
web_getAll: dict = None,
|
web_getAll: dict = None, # noqa
|
||||||
web_overview: dict = None, # noqa: named this way for automatic functionality
|
web_overview: dict = None, # noqa: named this way for automatic functionality
|
||||||
) -> Optional[str]:
|
) -> Optional[str]:
|
||||||
web_all_data = web_getAll
|
web_all_data = web_getAll.get("all")
|
||||||
if not web_all_data and not web_overview:
|
if not web_all_data and not web_overview:
|
||||||
try:
|
try:
|
||||||
web_overview = await self.send_web_command("overview")
|
web_overview = await self.send_web_command("overview")
|
||||||
@@ -195,7 +183,7 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
api_summary: dict = None,
|
api_summary: dict = None,
|
||||||
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
||||||
) -> Optional[float]:
|
) -> Optional[float]:
|
||||||
web_all_data = web_getAll
|
web_all_data = web_getAll.get("all")
|
||||||
if not api_summary and not web_all_data:
|
if not api_summary and not web_all_data:
|
||||||
try:
|
try:
|
||||||
api_summary = await self.api.summary()
|
api_summary = await self.api.summary()
|
||||||
@@ -221,7 +209,7 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
api_stats: dict = None,
|
api_stats: dict = None,
|
||||||
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
||||||
) -> List[HashBoard]:
|
) -> List[HashBoard]:
|
||||||
web_all_data = web_getAll
|
web_all_data = web_getAll.get("all")
|
||||||
hashboards = [
|
hashboards = [
|
||||||
HashBoard(slot=i, expected_chips=self.nominal_chips)
|
HashBoard(slot=i, expected_chips=self.nominal_chips)
|
||||||
for i in range(self.ideal_hashboards)
|
for i in range(self.ideal_hashboards)
|
||||||
@@ -243,8 +231,9 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
|
|
||||||
if api_stats:
|
if api_stats:
|
||||||
if api_stats.get("STATS"):
|
if api_stats.get("STATS"):
|
||||||
for idx, board in enumerate(api_stats["STATS"]):
|
for board in api_stats["STATS"]:
|
||||||
try:
|
try:
|
||||||
|
idx = board["Chain ID"]
|
||||||
chips = board["Num active chips"]
|
chips = board["Num active chips"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
@@ -254,25 +243,31 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
|
|
||||||
if web_all_data:
|
if web_all_data:
|
||||||
if web_all_data.get("chain"):
|
if web_all_data.get("chain"):
|
||||||
for idx, board in enumerate(web_all_data["chain"]):
|
for board in web_all_data["chain"]:
|
||||||
temp = board.get("Temp min")
|
idx = board.get("ASC")
|
||||||
if temp:
|
if idx is not None:
|
||||||
hashboards[idx].temp = round(temp)
|
temp = board.get("Temp min")
|
||||||
|
if temp:
|
||||||
|
hashboards[idx].temp = round(temp)
|
||||||
|
|
||||||
hashrate = board.get("Hash Rate H")
|
hashrate = board.get("Hash Rate H")
|
||||||
if hashrate:
|
if hashrate:
|
||||||
hashboards[idx].hashrate = round(hashrate / 1000000000000, 2)
|
hashboards[idx].hashrate = round(
|
||||||
|
hashrate / 1000000000000, 2
|
||||||
|
)
|
||||||
|
|
||||||
chip_temp = board.get("Temp max")
|
chip_temp = board.get("Temp max")
|
||||||
if chip_temp:
|
if chip_temp:
|
||||||
hashboards[idx].chip_temp = round(chip_temp)
|
hashboards[idx].chip_temp = round(chip_temp)
|
||||||
|
|
||||||
return hashboards
|
return hashboards
|
||||||
|
|
||||||
async def get_wattage(
|
async def get_wattage(
|
||||||
self, web_getAll: dict = None
|
self,
|
||||||
) -> Optional[int]: # noqa: named this way for automatic functionality
|
web_getAll: dict = None,
|
||||||
web_all_data = web_getAll
|
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:
|
if not web_all_data:
|
||||||
try:
|
try:
|
||||||
web_all_data = await self.send_web_command("getAll")
|
web_all_data = await self.send_web_command("getAll")
|
||||||
@@ -287,11 +282,28 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
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(
|
async def get_fans(
|
||||||
self,
|
self,
|
||||||
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
web_getAll: dict = None, # noqa: named this way for automatic functionality
|
||||||
) -> List[Fan]:
|
) -> List[Fan]:
|
||||||
web_all_data = web_getAll
|
web_all_data = web_getAll.get("all")
|
||||||
if not web_all_data:
|
if not web_all_data:
|
||||||
try:
|
try:
|
||||||
web_all_data = await self.send_web_command("getAll")
|
web_all_data = await self.send_web_command("getAll")
|
||||||
@@ -362,3 +374,24 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
|
|||||||
if not err == 0:
|
if not err == 0:
|
||||||
errors.append(InnosiliconError(error_code=err))
|
errors.append(InnosiliconError(error_code=err))
|
||||||
return errors
|
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
|
||||||
|
|||||||
@@ -106,24 +106,28 @@ MINER_CLASSES = {
|
|||||||
"BOSMiner+": BOSMinerS19,
|
"BOSMiner+": BOSMinerS19,
|
||||||
"BMMiner": BMMinerS19,
|
"BMMiner": BMMinerS19,
|
||||||
"CGMiner": CGMinerS19,
|
"CGMiner": CGMinerS19,
|
||||||
|
"VNish": VNishS19,
|
||||||
},
|
},
|
||||||
"ANTMINER S19 PRO": {
|
"ANTMINER S19 PRO": {
|
||||||
"Default": BMMinerS19Pro,
|
"Default": BMMinerS19Pro,
|
||||||
"BOSMiner+": BOSMinerS19Pro,
|
"BOSMiner+": BOSMinerS19Pro,
|
||||||
"BMMiner": BMMinerS19Pro,
|
"BMMiner": BMMinerS19Pro,
|
||||||
"CGMiner": CGMinerS19Pro,
|
"CGMiner": CGMinerS19Pro,
|
||||||
|
"VNish": VNishS19Pro,
|
||||||
},
|
},
|
||||||
"ANTMINER S19J": {
|
"ANTMINER S19J": {
|
||||||
"Default": BMMinerS19j,
|
"Default": BMMinerS19j,
|
||||||
"BOSMiner+": BOSMinerS19j,
|
"BOSMiner+": BOSMinerS19j,
|
||||||
"BMMiner": BMMinerS19j,
|
"BMMiner": BMMinerS19j,
|
||||||
"CGMiner": CGMinerS19j,
|
"CGMiner": CGMinerS19j,
|
||||||
|
"VNish": VNishS19j,
|
||||||
},
|
},
|
||||||
"ANTMINER S19J PRO": {
|
"ANTMINER S19J PRO": {
|
||||||
"Default": BMMinerS19jPro,
|
"Default": BMMinerS19jPro,
|
||||||
"BOSMiner+": BOSMinerS19jPro,
|
"BOSMiner+": BOSMinerS19jPro,
|
||||||
"BMMiner": BMMinerS19jPro,
|
"BMMiner": BMMinerS19jPro,
|
||||||
"CGMiner": CGMinerS19jPro,
|
"CGMiner": CGMinerS19jPro,
|
||||||
|
"VNish": VNishS19jPro,
|
||||||
},
|
},
|
||||||
"ANTMINER S19 XP": {
|
"ANTMINER S19 XP": {
|
||||||
"Default": BMMinerS19XP,
|
"Default": BMMinerS19XP,
|
||||||
@@ -132,16 +136,19 @@ MINER_CLASSES = {
|
|||||||
"ANTMINER S19A": {
|
"ANTMINER S19A": {
|
||||||
"Default": BMMinerS19a,
|
"Default": BMMinerS19a,
|
||||||
"BMMiner": BMMinerS19a,
|
"BMMiner": BMMinerS19a,
|
||||||
|
"VNish": VNishS19a,
|
||||||
},
|
},
|
||||||
"ANTMINER S19A PRO": {
|
"ANTMINER S19A PRO": {
|
||||||
"Default": BMMinerS19aPro,
|
"Default": BMMinerS19aPro,
|
||||||
"BMMiner": BMMinerS19aPro,
|
"BMMiner": BMMinerS19aPro,
|
||||||
|
"VNish": VNishS19aPro,
|
||||||
},
|
},
|
||||||
"ANTMINER T19": {
|
"ANTMINER T19": {
|
||||||
"Default": BMMinerT19,
|
"Default": BMMinerT19,
|
||||||
"BOSMiner+": BOSMinerT19,
|
"BOSMiner+": BOSMinerT19,
|
||||||
"BMMiner": BMMinerT19,
|
"BMMiner": BMMinerT19,
|
||||||
"CGMiner": CGMinerT19,
|
"CGMiner": CGMinerT19,
|
||||||
|
"VNish": VNishT19,
|
||||||
},
|
},
|
||||||
"M20": {"Default": BTMinerM20V10, "BTMiner": BTMinerM20V10, "10": BTMinerM20V10},
|
"M20": {"Default": BTMinerM20V10, "BTMiner": BTMinerM20V10, "10": BTMinerM20V10},
|
||||||
"M20S": {
|
"M20S": {
|
||||||
@@ -781,6 +788,8 @@ class MinerFactory(metaclass=Singleton):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
if "VNISH" in _model:
|
||||||
|
api = "VNish"
|
||||||
for split_point in [" BB", " XILINX", " (VNISH"]:
|
for split_point in [" BB", " XILINX", " (VNISH"]:
|
||||||
if split_point in _model:
|
if split_point in _model:
|
||||||
_model = _model.split(split_point)[0]
|
_model = _model.split(split_point)[0]
|
||||||
|
|||||||
@@ -13,18 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
from pyasic.API import APIError
|
from pyasic.API import APIError
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -108,10 +108,6 @@ class MinerNetwork:
|
|||||||
# clear cached miners
|
# clear cached miners
|
||||||
MinerFactory().clear_cached_miners()
|
MinerFactory().clear_cached_miners()
|
||||||
|
|
||||||
# create a list of tasks and miner IPs
|
|
||||||
scan_tasks = []
|
|
||||||
miners = []
|
|
||||||
|
|
||||||
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
|
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
|
||||||
miners = await asyncio.gather(
|
miners = await asyncio.gather(
|
||||||
*[self.ping_and_get_miner(host, limit) for host in local_network.hosts()]
|
*[self.ping_and_get_miner(host, limit) for host in local_network.hosts()]
|
||||||
@@ -140,8 +136,6 @@ class MinerNetwork:
|
|||||||
local_network = self.get_network()
|
local_network = self.get_network()
|
||||||
|
|
||||||
# create a list of scan tasks
|
# create a list of scan tasks
|
||||||
scan_tasks = []
|
|
||||||
|
|
||||||
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
|
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
|
||||||
miners = asyncio.as_completed(
|
miners = asyncio.as_completed(
|
||||||
[
|
[
|
||||||
@@ -213,14 +207,13 @@ async def ping_miner(
|
|||||||
except asyncio.exceptions.TimeoutError:
|
except asyncio.exceptions.TimeoutError:
|
||||||
# ping failed if we time out
|
# ping failed if we time out
|
||||||
continue
|
continue
|
||||||
except ConnectionRefusedError:
|
except (ConnectionRefusedError, OSError):
|
||||||
# handle for other connection errors
|
# handle for other connection errors
|
||||||
logging.debug(f"{str(ip)}: Connection Refused.")
|
logging.debug(f"{str(ip)}: Connection Refused.")
|
||||||
raise ConnectionRefusedError
|
raise ConnectionRefusedError
|
||||||
# ping failed, likely with an exception
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"{str(ip)}: {e}")
|
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
|
||||||
continue
|
raise ConnectionRefusedError
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -228,8 +221,8 @@ async def ping_and_get_miner(
|
|||||||
ip: ipaddress.ip_address, port=4028
|
ip: ipaddress.ip_address, port=4028
|
||||||
) -> Union[None, AnyMiner]:
|
) -> Union[None, AnyMiner]:
|
||||||
for i in range(PyasicSettings().network_ping_retries):
|
for i in range(PyasicSettings().network_ping_retries):
|
||||||
connection_fut = asyncio.open_connection(str(ip), port)
|
|
||||||
try:
|
try:
|
||||||
|
connection_fut = asyncio.open_connection(str(ip), port)
|
||||||
# get the read and write streams from the connection
|
# get the read and write streams from the connection
|
||||||
reader, writer = await asyncio.wait_for(
|
reader, writer = await asyncio.wait_for(
|
||||||
connection_fut, timeout=PyasicSettings().network_ping_timeout
|
connection_fut, timeout=PyasicSettings().network_ping_timeout
|
||||||
@@ -243,13 +236,11 @@ async def ping_and_get_miner(
|
|||||||
except asyncio.exceptions.TimeoutError:
|
except asyncio.exceptions.TimeoutError:
|
||||||
# ping failed if we time out
|
# ping failed if we time out
|
||||||
continue
|
continue
|
||||||
except ConnectionRefusedError as e:
|
except (ConnectionRefusedError, OSError):
|
||||||
# handle for other connection errors
|
# handle for other connection errors
|
||||||
logging.debug(f"{str(ip)}: Connection Refused.")
|
logging.debug(f"{str(ip)}: Connection Refused.")
|
||||||
raise e
|
raise ConnectionRefusedError
|
||||||
# ping failed, likely with an exception
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
|
logging.warning(f"{str(ip)}: Ping And Get Miner Exception: {e}")
|
||||||
raise e
|
raise ConnectionRefusedError
|
||||||
continue
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class PyasicSettings(metaclass=Singleton):
|
|||||||
global_innosilicon_password = "admin"
|
global_innosilicon_password = "admin"
|
||||||
global_x19_password = "root"
|
global_x19_password = "root"
|
||||||
global_x17_password = "root"
|
global_x17_password = "root"
|
||||||
|
global_vnish_password = "admin"
|
||||||
|
|
||||||
debug: bool = False
|
debug: bool = False
|
||||||
logfile: bool = False
|
logfile: bool = False
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pyasic"
|
name = "pyasic"
|
||||||
version = "0.27.7"
|
version = "0.29.5"
|
||||||
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
|
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>"]
|
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||||
repository = "https://github.com/UpstreamData/pyasic"
|
repository = "https://github.com/UpstreamData/pyasic"
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from tests.config_tests import ConfigTest
|
|
||||||
from tests.miners_tests import MinerFactoryTest, MinersTest
|
from tests.miners_tests import MinerFactoryTest, MinersTest
|
||||||
from tests.network_tests import NetworkTest
|
from tests.network_tests import NetworkTest
|
||||||
|
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Copyright 2022 Upstream Data Inc -
|
|
||||||
# -
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
|
||||||
# you may not use this file except in compliance with the License. -
|
|
||||||
# You may obtain a copy of the License at -
|
|
||||||
# -
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
|
||||||
# -
|
|
||||||
# Unless required by applicable law or agreed to in writing, software -
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
|
||||||
# See the License for the specific language governing permissions and -
|
|
||||||
# limitations under the License. -
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from pyasic.config import MinerConfig, _Pool, _PoolGroup # noqa
|
|
||||||
from tests.test_data import (
|
|
||||||
bosminer_api_pools,
|
|
||||||
bosminer_config_pools,
|
|
||||||
x19_api_pools,
|
|
||||||
x19_web_pools,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ConfigTest(unittest.TestCase):
|
|
||||||
def setUp(self) -> None:
|
|
||||||
self.test_config = MinerConfig(
|
|
||||||
pool_groups=[
|
|
||||||
_PoolGroup(
|
|
||||||
quota=1,
|
|
||||||
group_name="TEST",
|
|
||||||
pools=[
|
|
||||||
_Pool(
|
|
||||||
url="stratum+tcp://pyasic.testpool_1.pool:3333",
|
|
||||||
username="pyasic.test",
|
|
||||||
password="123",
|
|
||||||
),
|
|
||||||
_Pool(
|
|
||||||
url="stratum+tcp://pyasic.testpool_2.pool:3333",
|
|
||||||
username="pyasic.test",
|
|
||||||
password="123",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
temp_mode="auto",
|
|
||||||
temp_target=70.0,
|
|
||||||
temp_hot=80.0,
|
|
||||||
temp_dangerous=100.0,
|
|
||||||
fan_speed=None,
|
|
||||||
autotuning_enabled=True,
|
|
||||||
autotuning_wattage=900,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_config_from_raw(self):
|
|
||||||
bos_config = MinerConfig().from_raw(bosminer_config_pools)
|
|
||||||
bos_config.pool_groups[0].group_name = "TEST"
|
|
||||||
|
|
||||||
with self.subTest(
|
|
||||||
msg="Testing BOSMiner config file config.", bos_config=bos_config
|
|
||||||
):
|
|
||||||
self.assertEqual(bos_config, self.test_config)
|
|
||||||
|
|
||||||
x19_cfg = MinerConfig().from_raw(x19_web_pools)
|
|
||||||
x19_cfg.pool_groups[0].group_name = "TEST"
|
|
||||||
|
|
||||||
with self.subTest(msg="Testing X19 API config.", x19_cfg=x19_cfg):
|
|
||||||
self.assertEqual(x19_cfg, self.test_config)
|
|
||||||
|
|
||||||
def test_config_from_api(self):
|
|
||||||
bos_cfg = MinerConfig().from_api(bosminer_api_pools["POOLS"])
|
|
||||||
bos_cfg.pool_groups[0].group_name = "TEST"
|
|
||||||
|
|
||||||
with self.subTest(msg="Testing BOSMiner API config.", bos_cfg=bos_cfg):
|
|
||||||
self.assertEqual(bos_cfg, self.test_config)
|
|
||||||
|
|
||||||
x19_cfg = MinerConfig().from_api(x19_api_pools["POOLS"])
|
|
||||||
x19_cfg.pool_groups[0].group_name = "TEST"
|
|
||||||
|
|
||||||
with self.subTest(msg="Testing X19 API config.", x19_cfg=x19_cfg):
|
|
||||||
self.assertEqual(x19_cfg, self.test_config)
|
|
||||||
|
|
||||||
def test_config_as_types(self):
|
|
||||||
cfg = MinerConfig().from_api(bosminer_api_pools["POOLS"])
|
|
||||||
cfg.pool_groups[0].group_name = "TEST"
|
|
||||||
|
|
||||||
commands = [
|
|
||||||
func
|
|
||||||
for func in
|
|
||||||
# each function in self
|
|
||||||
dir(cfg)
|
|
||||||
if callable(getattr(cfg, func)) and
|
|
||||||
# no __ methods
|
|
||||||
not func.startswith("__")
|
|
||||||
]
|
|
||||||
|
|
||||||
for command in [cmd for cmd in commands if cmd.startswith("as_")]:
|
|
||||||
with self.subTest():
|
|
||||||
output = getattr(cfg, command)()
|
|
||||||
self.assertEqual(output, getattr(self.test_config, command)())
|
|
||||||
if f"from_{command.split('as_')[1]}" in commands:
|
|
||||||
self.assertEqual(
|
|
||||||
getattr(MinerConfig(), f"from_{command.split('as_')[1]}")(
|
|
||||||
output
|
|
||||||
),
|
|
||||||
self.test_config,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
@@ -13,18 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
@@ -13,18 +13,6 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
bosminer_api_pools = {
|
bosminer_api_pools = {
|
||||||
"STATUS": [
|
"STATUS": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user