Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
841a546505 | ||
|
|
e65b718699 | ||
|
|
15e4338046 | ||
|
|
720d4aec3d | ||
|
|
09f9028ab5 | ||
|
|
25d971b699 | ||
|
|
cd16ef3a25 | ||
|
|
b70010272f | ||
|
|
140a457445 | ||
|
|
f4775e6311 | ||
|
|
a4ecda93a2 | ||
|
|
ba90f2f082 | ||
|
|
44ac958bbb | ||
|
|
e9bcf2ec9f | ||
|
|
c73dfad01a | ||
|
|
d222912e30 | ||
|
|
6f1c1e0290 | ||
|
|
ba0bb73aa3 | ||
|
|
13fcf1d4aa | ||
|
|
6be1e94216 | ||
|
|
709b3efa81 | ||
|
|
5ac5770331 | ||
|
|
f131ebbdf5 | ||
|
|
5441e50f73 | ||
|
|
dc6a952de4 | ||
|
|
b781d215fb | ||
|
|
086b31ba23 | ||
|
|
46b7352769 | ||
|
|
e218c5039d | ||
|
|
3bb392980e | ||
|
|
92264619d2 | ||
|
|
3e21829fae | ||
|
|
d3619b0e48 |
@@ -92,7 +92,9 @@ class BaseMinerAPI:
|
|||||||
async def send_privileged_command(self, *args, **kwargs) -> dict:
|
async def send_privileged_command(self, *args, **kwargs) -> dict:
|
||||||
return await self.send_command(*args, **kwargs)
|
return await self.send_command(*args, **kwargs)
|
||||||
|
|
||||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
async def multicommand(
|
||||||
|
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||||
|
) -> dict:
|
||||||
"""Creates and sends multiple commands as one command to the miner.
|
"""Creates and sends multiple commands as one command to the miner.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -107,7 +109,9 @@ class BaseMinerAPI:
|
|||||||
# standard format doesn't work for X19
|
# standard format doesn't work for X19
|
||||||
command = "+".join(commands)
|
command = "+".join(commands)
|
||||||
try:
|
try:
|
||||||
data = await self.send_command(command, allow_warning=allow_warning)
|
data = await self.send_command(
|
||||||
|
command, allow_warning=allow_warning, ignore_errors=ignore_errors
|
||||||
|
)
|
||||||
except APIError:
|
except APIError:
|
||||||
return {command: [{}] for command in commands}
|
return {command: [{}] for command in commands}
|
||||||
logging.debug(f"{self} - (Multicommand) - Received data")
|
logging.debug(f"{self} - (Multicommand) - Received data")
|
||||||
|
|||||||
673
pyasic/API/bfgminer.py
Normal file
673
pyasic/API/bfgminer.py
Normal file
@@ -0,0 +1,673 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 logging
|
||||||
|
|
||||||
|
from pyasic.API import APIError, BaseMinerAPI
|
||||||
|
|
||||||
|
|
||||||
|
class BFGMinerAPI(BaseMinerAPI):
|
||||||
|
"""An abstraction of the BFGMiner API.
|
||||||
|
|
||||||
|
Each method corresponds to an API command in BFGMiner.
|
||||||
|
|
||||||
|
[BFGMiner API documentation](https://github.com/luke-jr/bfgminer/blob/bfgminer/README.RPC)
|
||||||
|
|
||||||
|
This class abstracts use of the BFGMiner API, as well as the
|
||||||
|
methods for sending commands to it. The self.send_command()
|
||||||
|
function handles sending a command to the miner asynchronously, and
|
||||||
|
as such is the base for many of the functions in this class, which
|
||||||
|
rely on it to send the command for them.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
ip: The IP of the miner to reference the API on.
|
||||||
|
port: The port to reference the API on. Default is 4028.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, ip: str, api_ver: str = "0.0.0", port: int = 4028):
|
||||||
|
super().__init__(ip, port)
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
|
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||||
|
# make sure we can actually run each command, otherwise they will fail
|
||||||
|
commands = self._check_commands(*commands)
|
||||||
|
# standard multicommand format is "command1+command2"
|
||||||
|
# doesn't work for S19 which uses the backup _x19_multicommand
|
||||||
|
command = "+".join(commands)
|
||||||
|
try:
|
||||||
|
data = await self.send_command(command, allow_warning=allow_warning)
|
||||||
|
except APIError:
|
||||||
|
logging.debug(f"{self} - (Multicommand) - Handling X19 multicommand.")
|
||||||
|
data = await self._x19_multicommand(*command.split("+"))
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def _x19_multicommand(self, *commands):
|
||||||
|
data = None
|
||||||
|
try:
|
||||||
|
data = {}
|
||||||
|
# send all commands individually
|
||||||
|
for cmd in commands:
|
||||||
|
data[cmd] = []
|
||||||
|
data[cmd].append(await self.send_command(cmd, allow_warning=True))
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(
|
||||||
|
f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}"
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def version(self) -> dict:
|
||||||
|
"""Get miner version info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner version information.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("version")
|
||||||
|
|
||||||
|
async def config(self) -> dict:
|
||||||
|
"""Get some basic configuration info.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
## Some miner configuration information:
|
||||||
|
* ASC Count <- the number of ASCs
|
||||||
|
* PGA Count <- the number of PGAs
|
||||||
|
* Pool Count <- the number of Pools
|
||||||
|
* Strategy <- the current pool strategy
|
||||||
|
* Log Interval <- the interval of logging
|
||||||
|
* Device Code <- list of compiled device drivers
|
||||||
|
* OS <- the current operating system
|
||||||
|
* Failover-Only <- failover-only setting
|
||||||
|
* Scan Time <- scan-time setting
|
||||||
|
* Queue <- queue setting
|
||||||
|
* Expiry <- expiry setting
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("config")
|
||||||
|
|
||||||
|
async def summary(self) -> dict:
|
||||||
|
"""Get the status summary of the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The status summary of the miner.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("summary")
|
||||||
|
|
||||||
|
async def pools(self) -> dict:
|
||||||
|
"""Get pool information.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner pool information.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pools")
|
||||||
|
|
||||||
|
async def devs(self) -> dict:
|
||||||
|
"""Get data on each PGA/ASC with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on each PGA/ASC with their details.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("devs")
|
||||||
|
|
||||||
|
async def procs(self) -> dict:
|
||||||
|
"""Get data on each processor with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on each processor with their details.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("procs")
|
||||||
|
|
||||||
|
async def devscan(self, info: str = "") -> dict:
|
||||||
|
"""Get data on each processor with their details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
info: Info to scan for device by.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on each processor with their details.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("devscan", parameters=info)
|
||||||
|
|
||||||
|
async def pga(self, n: int) -> dict:
|
||||||
|
"""Get data from PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA number to get data from.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on the PGA n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pga", parameters=n)
|
||||||
|
|
||||||
|
async def proc(self, n: int = 0) -> dict:
|
||||||
|
"""Get data processor n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The processor to get data on.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on processor n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("proc", parameters=n)
|
||||||
|
|
||||||
|
async def pgacount(self) -> dict:
|
||||||
|
"""Get data fon all PGAs.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on the PGAs connected.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pgacount")
|
||||||
|
|
||||||
|
async def proccount(self) -> dict:
|
||||||
|
"""Get data fon all processors.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on the processors connected.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("proccount")
|
||||||
|
|
||||||
|
async def switchpool(self, n: int) -> dict:
|
||||||
|
"""Switch pools to pool n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The pool to switch to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of switching to pool n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("switchpool", parameters=n)
|
||||||
|
|
||||||
|
async def enablepool(self, n: int) -> dict:
|
||||||
|
"""Enable pool n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The pool to enable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of enabling pool n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("enablepool", parameters=n)
|
||||||
|
|
||||||
|
async def addpool(self, url: str, username: str, password: str) -> dict:
|
||||||
|
"""Add a pool to the miner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
url: The URL of the new pool to add.
|
||||||
|
username: The users username on the new pool.
|
||||||
|
password: The worker password on the new pool.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of adding the pool.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command(
|
||||||
|
"addpool", parameters=f"{url},{username},{password}"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def poolpriority(self, *n: int) -> dict:
|
||||||
|
"""Set pool priority.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
*n: Pools in order of priority.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of setting pool priority.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
pools = f"{','.join([str(item) for item in n])}"
|
||||||
|
return await self.send_command("poolpriority", parameters=pools)
|
||||||
|
|
||||||
|
async def poolquota(self, n: int, q: int) -> dict:
|
||||||
|
"""Set pool quota.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: Pool number to set quota on.
|
||||||
|
q: Quota to set the pool to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of setting pool quota.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("poolquota", parameters=f"{n},{q}")
|
||||||
|
|
||||||
|
async def disablepool(self, n: int) -> dict:
|
||||||
|
"""Disable a pool.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: Pool to disable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of diabling the pool.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("disablepool", parameters=n)
|
||||||
|
|
||||||
|
async def removepool(self, n: int) -> dict:
|
||||||
|
"""Remove a pool.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: Pool to remove.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of removing the pool.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("removepool", parameters=n)
|
||||||
|
|
||||||
|
async def save(self, filename: str = None) -> dict:
|
||||||
|
"""Save the config.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
filename: Filename to save the config as.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of saving the config.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
if filename:
|
||||||
|
return await self.send_command("save", parameters=filename)
|
||||||
|
else:
|
||||||
|
return await self.send_command("save")
|
||||||
|
|
||||||
|
async def quit(self) -> dict:
|
||||||
|
"""Quit CGMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A single "BYE" before CGMiner quits.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("quit")
|
||||||
|
|
||||||
|
async def notify(self) -> dict:
|
||||||
|
"""Notify the user of past errors.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The last status and count of each devices problem(s).
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("notify")
|
||||||
|
|
||||||
|
async def privileged(self) -> dict:
|
||||||
|
"""Check if you have privileged access.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The STATUS section with an error if you have no privileged access, or success if you have privileged access.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("privileged")
|
||||||
|
|
||||||
|
async def pgaenable(self, n: int) -> dict:
|
||||||
|
"""Enable PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA to enable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of enabling PGA n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pgaenable", parameters=n)
|
||||||
|
|
||||||
|
async def pgadisable(self, n: int) -> dict:
|
||||||
|
"""Disable PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA to disable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of disabling PGA n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pgadisable", parameters=n)
|
||||||
|
|
||||||
|
async def pgarestart(self, n: int) -> dict:
|
||||||
|
"""Restart PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA to restart.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of restarting PGA n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pgadisable", parameters=n)
|
||||||
|
|
||||||
|
async def pgaidentify(self, n: int) -> dict:
|
||||||
|
"""Identify PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA to identify.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of identifying PGA n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("pgaidentify", parameters=n)
|
||||||
|
|
||||||
|
async def procenable(self, n: int) -> dict:
|
||||||
|
"""Enable processor n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The processor to enable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of enabling processor n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("procenable", parameters=n)
|
||||||
|
|
||||||
|
async def procdisable(self, n: int) -> dict:
|
||||||
|
"""Disable processor n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The processor to disable.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of disabling processor n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("procdisable", parameters=n)
|
||||||
|
|
||||||
|
async def procrestart(self, n: int) -> dict:
|
||||||
|
"""Restart processor n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The processor to restart.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of restarting processor n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("procdisable", parameters=n)
|
||||||
|
|
||||||
|
async def procidentify(self, n: int) -> dict:
|
||||||
|
"""Identify processor n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The processor to identify.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A confirmation of identifying processor n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("procidentify", parameters=n)
|
||||||
|
|
||||||
|
async def devdetails(self) -> dict:
|
||||||
|
"""Get data on all devices with their static details.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on all devices with their static details.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("devdetails")
|
||||||
|
|
||||||
|
async def restart(self) -> dict:
|
||||||
|
"""Restart CGMiner using the API.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A reply informing of the restart.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("restart")
|
||||||
|
|
||||||
|
async def stats(self) -> dict:
|
||||||
|
"""Get stats of each device/pool with more than 1 getwork.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Stats of each device/pool with more than 1 getwork.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("stats")
|
||||||
|
|
||||||
|
async def check(self, command: str) -> dict:
|
||||||
|
"""Check if the command command exists in CGMiner.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
command: The command to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
## Information about a command:
|
||||||
|
* Exists (Y/N) <- the command exists in this version
|
||||||
|
* Access (Y/N) <- you have access to use the command
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("check", parameters=command)
|
||||||
|
|
||||||
|
async def failover_only(self, failover: bool) -> dict:
|
||||||
|
"""Set failover-only.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
failover: What to set failover-only to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Confirmation of setting failover-only.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("failover-only", parameters=failover)
|
||||||
|
|
||||||
|
async def coin(self) -> dict:
|
||||||
|
"""Get information on the current coin.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
## Information about the current coin being mined:
|
||||||
|
* Hash Method <- the hashing algorithm
|
||||||
|
* Current Block Time <- blocktime as a float, 0 means none
|
||||||
|
* Current Block Hash <- the hash of the current block, blank means none
|
||||||
|
* LP <- whether LP is in use on at least 1 pool
|
||||||
|
* Network Difficulty: the current network difficulty
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("coin")
|
||||||
|
|
||||||
|
async def debug(self, setting: str) -> dict:
|
||||||
|
"""Set a debug setting.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
setting: Which setting to switch to.
|
||||||
|
## Options are:
|
||||||
|
* Silent
|
||||||
|
* Quiet
|
||||||
|
* Verbose
|
||||||
|
* Debug
|
||||||
|
* RPCProto
|
||||||
|
* PerDevice
|
||||||
|
* WorkTime
|
||||||
|
* Normal
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Data on which debug setting was enabled or disabled.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("debug", parameters=setting)
|
||||||
|
|
||||||
|
async def setconfig(self, name: str, n: int) -> dict:
|
||||||
|
"""Set config of name to value n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
name: The name of the config setting to set.
|
||||||
|
## Options are:
|
||||||
|
* queue
|
||||||
|
* scantime
|
||||||
|
* expiry
|
||||||
|
n: The value to set the 'name' setting to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The results of setting config of name to n.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("setconfig", parameters=f"{name},{n}")
|
||||||
|
|
||||||
|
async def pgaset(self, n: int, opt: str, val: int = None) -> dict:
|
||||||
|
"""Set PGA option opt to val on PGA n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Options:
|
||||||
|
```
|
||||||
|
MMQ -
|
||||||
|
opt: clock
|
||||||
|
val: 2 - 250 (multiple of 2)
|
||||||
|
XBS -
|
||||||
|
opt: clock
|
||||||
|
val: 2 - 250 (multiple of 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA to set the options on.
|
||||||
|
opt: The option to set. Setting this to 'help' returns a help message.
|
||||||
|
val: The value to set the option to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Confirmation of setting PGA n with opt[,val].
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
if val:
|
||||||
|
return await self.send_command("pgaset", parameters=f"{n},{opt},{val}")
|
||||||
|
else:
|
||||||
|
return await self.send_command("pgaset", parameters=f"{n},{opt}")
|
||||||
|
|
||||||
|
async def pprocset(self, n: int, opt: str, val: int = None) -> dict:
|
||||||
|
"""Set processor option opt to val on processor n.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Options:
|
||||||
|
```
|
||||||
|
MMQ -
|
||||||
|
opt: clock
|
||||||
|
val: 2 - 250 (multiple of 2)
|
||||||
|
XBS -
|
||||||
|
opt: clock
|
||||||
|
val: 2 - 250 (multiple of 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
n: The PGA to set the options on.
|
||||||
|
opt: The option to set. Setting this to 'help' returns a help message.
|
||||||
|
val: The value to set the option to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Confirmation of setting PGA n with opt[,val].
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
if val:
|
||||||
|
return await self.send_command("pgaset", parameters=f"{n},{opt},{val}")
|
||||||
|
else:
|
||||||
|
return await self.send_command("pgaset", parameters=f"{n},{opt}")
|
||||||
|
|
||||||
|
async def zero(self, which: str, summary: bool) -> dict:
|
||||||
|
"""Zero a device.
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
which: Which device to zero. Setting this to 'all' zeros all devices. Setting this to 'bestshare' zeros only the bestshare values for each pool and global.
|
||||||
|
summary: Whether or not to show a full summary.
|
||||||
|
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
the STATUS section with info on the zero and optional summary.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
return await self.send_command("zero", parameters=f"{which},{summary}")
|
||||||
@@ -246,7 +246,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
logging.debug(f"{self} - (Send Privileged Command) - Sending")
|
logging.debug(f"{self} - (Send Privileged Command) - Sending")
|
||||||
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):
|
||||||
if ignore_errors:
|
if ignore_errors:
|
||||||
return {}
|
return {}
|
||||||
raise APIError("No data was returned from the API.")
|
raise APIError("No data was returned from the API.")
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
for cmd in commands:
|
for cmd in commands:
|
||||||
data[cmd] = []
|
data[cmd] = []
|
||||||
data[cmd].append(await self.send_command(cmd, allow_warning=True))
|
data[cmd].append(await self.send_command(cmd, allow_warning=True))
|
||||||
except APIError as e:
|
except APIError:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
|
|||||||
@@ -14,18 +14,24 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import time
|
import time
|
||||||
from dataclasses import asdict, dataclass, fields
|
from dataclasses import asdict, dataclass, fields
|
||||||
from typing import Dict, List, Literal
|
from enum import IntEnum
|
||||||
|
from typing import List, Literal
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
class X19PowerMode(IntEnum):
|
||||||
|
Normal = 0
|
||||||
|
Sleep = 1
|
||||||
|
LPM = 3
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class _Pool:
|
class _Pool:
|
||||||
"""A dataclass for pool information.
|
"""A dataclass for pool information.
|
||||||
@@ -85,6 +91,45 @@ 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_x15(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a dict usable by an X15 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_x5(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a dict usable by an X5 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_goldshell(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a dict usable by a goldshell 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_inno(self, user_suffix: str = None) -> dict:
|
def as_inno(self, user_suffix: str = None) -> dict:
|
||||||
"""Convert the data in this class to a dict usable by an Innosilicon device.
|
"""Convert the data in this class to a dict usable by an Innosilicon device.
|
||||||
|
|
||||||
@@ -182,30 +227,104 @@ 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_x15(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a list usable by an X15 device.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
user_suffix: The suffix to append to username.
|
||||||
|
"""
|
||||||
|
pools = {
|
||||||
|
"_ant_pool1url": "",
|
||||||
|
"_ant_pool1user": "",
|
||||||
|
"_ant_pool1pw": "",
|
||||||
|
"_ant_pool2url": "",
|
||||||
|
"_ant_pool2user": "",
|
||||||
|
"_ant_pool2pw": "",
|
||||||
|
"_ant_pool3url": "",
|
||||||
|
"_ant_pool3user": "",
|
||||||
|
"_ant_pool3pw": "",
|
||||||
|
}
|
||||||
|
for idx, pool in enumerate(self.pools[:3]):
|
||||||
|
pools[f"_ant_pool{idx+1}url"] = pool.as_x15(user_suffix=user_suffix)["url"]
|
||||||
|
pools[f"_ant_pool{idx+1}user"] = pool.as_x15(user_suffix=user_suffix)[
|
||||||
|
"user"
|
||||||
|
]
|
||||||
|
pools[f"_ant_pool{idx+1}pw"] = pool.as_x15(user_suffix=user_suffix)["pass"]
|
||||||
|
|
||||||
|
return pools
|
||||||
|
|
||||||
|
def as_x5(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a list usable by an X5 device.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
user_suffix: The suffix to append to username.
|
||||||
|
"""
|
||||||
|
pools = {
|
||||||
|
"_ant_pool1url": "",
|
||||||
|
"_ant_pool1user": "",
|
||||||
|
"_ant_pool1pw": "",
|
||||||
|
"_ant_pool2url": "",
|
||||||
|
"_ant_pool2user": "",
|
||||||
|
"_ant_pool2pw": "",
|
||||||
|
"_ant_pool3url": "",
|
||||||
|
"_ant_pool3user": "",
|
||||||
|
"_ant_pool3pw": "",
|
||||||
|
}
|
||||||
|
for idx, pool in enumerate(self.pools[:3]):
|
||||||
|
pools[f"_ant_pool{idx+1}url"] = pool.as_x5(user_suffix=user_suffix)["url"]
|
||||||
|
pools[f"_ant_pool{idx+1}user"] = pool.as_x5(user_suffix=user_suffix)["user"]
|
||||||
|
pools[f"_ant_pool{idx+1}pw"] = pool.as_x5(user_suffix=user_suffix)["pass"]
|
||||||
|
|
||||||
|
return pools
|
||||||
|
|
||||||
|
def as_goldshell(self, user_suffix: str = None) -> list:
|
||||||
|
"""Convert the data in this class to a list usable by a goldshell device.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
user_suffix: The suffix to append to username.
|
||||||
|
"""
|
||||||
|
return [pool.as_goldshell(user_suffix=user_suffix) for pool in self.pools[:3]]
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str = None) -> dict:
|
def as_inno(self, user_suffix: str = None) -> dict:
|
||||||
"""Convert the data in this class to a list usable by an Innosilicon device.
|
"""Convert the data in this class to a list usable by an Innosilicon device.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
pools = {}
|
pools = {
|
||||||
|
"Pool1": None,
|
||||||
|
"UserName1": None,
|
||||||
|
"Password1": None,
|
||||||
|
"Pool2": None,
|
||||||
|
"UserName2": None,
|
||||||
|
"Password2": None,
|
||||||
|
"Pool3": None,
|
||||||
|
"UserName3": None,
|
||||||
|
"Password3": None,
|
||||||
|
}
|
||||||
for idx, pool in enumerate(self.pools[:3]):
|
for idx, pool in enumerate(self.pools[:3]):
|
||||||
pool_data = pool.as_inno(user_suffix=user_suffix)
|
pool_data = pool.as_inno(user_suffix=user_suffix)
|
||||||
for key in pool_data:
|
for key in pool_data:
|
||||||
pools[f"{key}{idx+1}"] = pool_data[key]
|
pools[f"{key}{idx+1}"] = pool_data[key]
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> List[dict]:
|
def as_wm(self, user_suffix: str = None) -> dict:
|
||||||
"""Convert the data in this class to a list usable by a Whatsminer device.
|
"""Convert the data in this class to a list usable by a Whatsminer device.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
pools = []
|
pools = {}
|
||||||
for pool in self.pools[:3]:
|
for i in range(1, 4):
|
||||||
pools.append(pool.as_wm(user_suffix=user_suffix))
|
if i <= len(self.pools):
|
||||||
while len(pools) < 3:
|
pool_wm = self.pools[i - 1].as_wm(user_suffix)
|
||||||
pools.append({"url": None, "user": None, "pass": None})
|
pools[f"pool_{i}"] = pool_wm["url"]
|
||||||
|
pools[f"worker_{i}"] = pool_wm["user"]
|
||||||
|
pools[f"passwd_{i}"] = pool_wm["pass"]
|
||||||
|
else:
|
||||||
|
pools[f"pool_{i}"] = ""
|
||||||
|
pools[f"worker_{i}"] = ""
|
||||||
|
pools[f"passwd_{i}"] = ""
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str = None) -> str:
|
def as_avalon(self, user_suffix: str = None) -> str:
|
||||||
@@ -267,6 +386,7 @@ class MinerConfig:
|
|||||||
|
|
||||||
asicboost: bool = None
|
asicboost: bool = None
|
||||||
|
|
||||||
|
miner_mode: IntEnum = X19PowerMode.Normal
|
||||||
autotuning_enabled: bool = True
|
autotuning_enabled: bool = True
|
||||||
autotuning_mode: Literal["power", "hashrate"] = None
|
autotuning_mode: Literal["power", "hashrate"] = None
|
||||||
autotuning_wattage: int = None
|
autotuning_wattage: int = None
|
||||||
@@ -287,6 +407,8 @@ class MinerConfig:
|
|||||||
logging.debug(f"MinerConfig - (To Dict) - Dumping Dict config")
|
logging.debug(f"MinerConfig - (To Dict) - Dumping Dict config")
|
||||||
data_dict = asdict(self)
|
data_dict = asdict(self)
|
||||||
for key in asdict(self).keys():
|
for key in asdict(self).keys():
|
||||||
|
if isinstance(data_dict[key], IntEnum):
|
||||||
|
data_dict[key] = data_dict[key].value
|
||||||
if data_dict[key] is None:
|
if data_dict[key] is None:
|
||||||
del data_dict[key]
|
del data_dict[key]
|
||||||
return data_dict
|
return data_dict
|
||||||
@@ -310,6 +432,9 @@ class MinerConfig:
|
|||||||
"""
|
"""
|
||||||
logging.debug(f"MinerConfig - (From Raw) - Loading raw config")
|
logging.debug(f"MinerConfig - (From Raw) - Loading raw config")
|
||||||
pool_groups = []
|
pool_groups = []
|
||||||
|
if isinstance(data, list):
|
||||||
|
# goldshell config list
|
||||||
|
data = {"pools": data}
|
||||||
for key in data.keys():
|
for key in data.keys():
|
||||||
if key == "pools":
|
if key == "pools":
|
||||||
pool_groups.append(_PoolGroup().from_dict({"pools": data[key]}))
|
pool_groups.append(_PoolGroup().from_dict({"pools": data[key]}))
|
||||||
@@ -324,10 +449,7 @@ class MinerConfig:
|
|||||||
self.fan_speed = int(data["bitmain-fan-pwm"])
|
self.fan_speed = int(data["bitmain-fan-pwm"])
|
||||||
elif key == "bitmain-work-mode":
|
elif key == "bitmain-work-mode":
|
||||||
if data[key]:
|
if data[key]:
|
||||||
if data[key] == 1:
|
self.miner_mode = X19PowerMode(int(data[key]))
|
||||||
self.autotuning_wattage = 0
|
|
||||||
if data[key] == 2:
|
|
||||||
self.autotuning_wattage = 1200
|
|
||||||
elif key == "fan_control":
|
elif key == "fan_control":
|
||||||
for _key in data[key].keys():
|
for _key in data[key].keys():
|
||||||
if _key == "min_fans":
|
if _key == "min_fans":
|
||||||
@@ -404,8 +526,14 @@ class MinerConfig:
|
|||||||
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:
|
for key in data:
|
||||||
if hasattr(self, key) and not key == "pool_groups":
|
if (
|
||||||
|
hasattr(self, key)
|
||||||
|
and not key == "pool_groups"
|
||||||
|
and not key == "miner_mode"
|
||||||
|
):
|
||||||
setattr(self, key, data[key])
|
setattr(self, key, data[key])
|
||||||
|
if key == "miner_mode":
|
||||||
|
self.miner_mode = X19PowerMode(data[key])
|
||||||
self.pool_groups = pool_groups
|
self.pool_groups = pool_groups
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@@ -427,7 +555,7 @@ class MinerConfig:
|
|||||||
logging.debug(f"MinerConfig - (From YAML) - Loading YAML config")
|
logging.debug(f"MinerConfig - (From YAML) - Loading YAML config")
|
||||||
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
|
return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader))
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> Dict[str, int]:
|
def as_wm(self, user_suffix: str = None) -> dict:
|
||||||
"""Convert the data in this class to a config usable by a Whatsminer device.
|
"""Convert the data in this class to a config usable by a Whatsminer device.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -448,7 +576,7 @@ class MinerConfig:
|
|||||||
logging.debug(f"MinerConfig - (As Inno) - Generating Innosilicon config")
|
logging.debug(f"MinerConfig - (As Inno) - Generating Innosilicon config")
|
||||||
return self.pool_groups[0].as_inno(user_suffix=user_suffix)
|
return self.pool_groups[0].as_inno(user_suffix=user_suffix)
|
||||||
|
|
||||||
def as_x19(self, user_suffix: str = None) -> str:
|
def as_x19(self, user_suffix: str = None) -> dict:
|
||||||
"""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.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -459,13 +587,8 @@ class MinerConfig:
|
|||||||
"pools": self.pool_groups[0].as_x19(user_suffix=user_suffix),
|
"pools": self.pool_groups[0].as_x19(user_suffix=user_suffix),
|
||||||
"bitmain-fan-ctrl": False,
|
"bitmain-fan-ctrl": False,
|
||||||
"bitmain-fan-pwn": 100,
|
"bitmain-fan-pwn": 100,
|
||||||
"miner-mode": 0, # Normal Mode
|
"miner-mode": self.miner_mode.value,
|
||||||
}
|
}
|
||||||
if self.autotuning_wattage == 0:
|
|
||||||
cfg["miner-mode"] = 1 # Sleep Mode
|
|
||||||
|
|
||||||
if self.autotuning_wattage < 1800:
|
|
||||||
cfg["miner-mode"] = 2 # LPM?
|
|
||||||
|
|
||||||
if not self.temp_mode == "auto":
|
if not self.temp_mode == "auto":
|
||||||
cfg["bitmain-fan-ctrl"] = True
|
cfg["bitmain-fan-ctrl"] = True
|
||||||
@@ -473,7 +596,37 @@ class MinerConfig:
|
|||||||
if self.fan_speed:
|
if self.fan_speed:
|
||||||
cfg["bitmain-fan-ctrl"] = str(self.fan_speed)
|
cfg["bitmain-fan-ctrl"] = str(self.fan_speed)
|
||||||
|
|
||||||
return json.dumps(cfg)
|
return cfg
|
||||||
|
|
||||||
|
def as_x15(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a config usable by an X15 device.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
user_suffix: The suffix to append to username.
|
||||||
|
"""
|
||||||
|
cfg = self.pool_groups[0].as_x15(user_suffix=user_suffix)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_x5(self, user_suffix: str = None) -> dict:
|
||||||
|
"""Convert the data in this class to a config usable by an X5 device.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
user_suffix: The suffix to append to username.
|
||||||
|
"""
|
||||||
|
cfg = self.pool_groups[0].as_x5(user_suffix=user_suffix)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_goldshell(self, user_suffix: str = None) -> list:
|
||||||
|
"""Convert the data in this class to a config usable by a goldshell device.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
user_suffix: The suffix to append to username.
|
||||||
|
"""
|
||||||
|
cfg = self.pool_groups[0].as_goldshell(user_suffix=user_suffix)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str = None) -> str:
|
def as_avalon(self, user_suffix: str = None) -> str:
|
||||||
"""Convert the data in this class to a config usable by an Avalonminer device.
|
"""Convert the data in this class to a config usable by an Avalonminer device.
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ class MinerData:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def efficiency(self): # noqa - Skip PyCharm inspection
|
def efficiency(self): # noqa - Skip PyCharm inspection
|
||||||
if self.hashrate == 0:
|
if self.hashrate == 0 or self.wattage == -1:
|
||||||
return 0
|
return 0
|
||||||
return round(self.wattage / self.hashrate)
|
return round(self.wattage / self.hashrate)
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,25 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
# from pyasic.errors import PhaseBalancingError
|
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners import AnyMiner
|
from pyasic.miners import AnyMiner
|
||||||
from pyasic.miners._backends import X19, BOSMiner, BTMiner
|
from pyasic.miners.btc._backends import ( # noqa - Ignore access to _module
|
||||||
from pyasic.miners._types import S9, S17, T17, S17e, S17Plus, S17Pro, T17e, T17Plus
|
X19,
|
||||||
|
BOSMiner,
|
||||||
# from pprint import pprint as print
|
BTMiner,
|
||||||
|
)
|
||||||
|
from pyasic.miners.btc._types import ( # noqa - Ignore access to _module
|
||||||
|
S9,
|
||||||
|
S17,
|
||||||
|
T17,
|
||||||
|
S17e,
|
||||||
|
S17Plus,
|
||||||
|
S17Pro,
|
||||||
|
T17e,
|
||||||
|
T17Plus,
|
||||||
|
)
|
||||||
|
|
||||||
FAN_USAGE = 50 # 50 W per fan
|
FAN_USAGE = 50 # 50 W per fan
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ from pyasic.errors import APIError
|
|||||||
class BaseMiner(ABC):
|
class BaseMiner(ABC):
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
self.ip = None
|
self.ip = None
|
||||||
self.uname = "root"
|
|
||||||
self.pwd = "admin"
|
|
||||||
self.api = None
|
self.api = None
|
||||||
|
self.web = None
|
||||||
|
self.uname = None
|
||||||
|
self.pwd = None
|
||||||
self.api_type = None
|
self.api_type = None
|
||||||
self.api_ver = None
|
self.api_ver = None
|
||||||
self.fw_ver = None
|
self.fw_ver = None
|
||||||
@@ -396,8 +397,13 @@ class BaseMiner(ABC):
|
|||||||
|
|
||||||
web_data = {}
|
web_data = {}
|
||||||
for command in web_params:
|
for command in web_params:
|
||||||
data = await self.send_web_command(command) # noqa: web only anyway
|
try:
|
||||||
web_data[command] = data
|
cmd_func = getattr(self.web, command)
|
||||||
|
data = await cmd_func() # noqa: web only anyway
|
||||||
|
except (LookupError, APIError):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
web_data[command] = data
|
||||||
for data_name in data_to_get:
|
for data_name in data_to_get:
|
||||||
function = getattr(self, "get_" + data_name)
|
function = getattr(self, "get_" + data_name)
|
||||||
sig = inspect.signature(function)
|
sig = inspect.signature(function)
|
||||||
|
|||||||
19
pyasic/miners/btc/__init__.py
Normal file
19
pyasic/miners/btc/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 .antminer import *
|
||||||
|
from .avalonminer import *
|
||||||
|
from .innosilicon import *
|
||||||
|
from .whatsminer import *
|
||||||
@@ -15,47 +15,23 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from pyasic.API import APIError
|
from pyasic.API import APIError
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig, X19PowerMode
|
||||||
from pyasic.data.error_codes import MinerErrorData, X19Error
|
from pyasic.data.error_codes import MinerErrorData, X19Error
|
||||||
from pyasic.miners._backends import BMMiner # noqa - Ignore access to _module
|
from pyasic.miners.btc._backends import BMMiner # noqa - Ignore access to _module
|
||||||
from pyasic.settings import PyasicSettings
|
from pyasic.web.X19 import X19WebAPI
|
||||||
|
|
||||||
|
|
||||||
class X19(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
|
||||||
self.uname = "root"
|
self.web = X19WebAPI(ip)
|
||||||
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_config(self) -> MinerConfig:
|
async def get_config(self) -> MinerConfig:
|
||||||
data = await self.send_web_command("get_miner_conf")
|
data = await self.web.get_miner_conf()
|
||||||
if data:
|
if data:
|
||||||
self.config = MinerConfig().from_raw(data)
|
self.config = MinerConfig().from_raw(data)
|
||||||
return self.config
|
return self.config
|
||||||
@@ -63,9 +39,7 @@ class X19(BMMiner):
|
|||||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
conf = config.as_x19(user_suffix=user_suffix)
|
conf = config.as_x19(user_suffix=user_suffix)
|
||||||
await self.send_web_command(
|
await self.web.set_miner_conf(conf)
|
||||||
"set_miner_conf", params=conf # noqa: ignore conf being a str
|
|
||||||
)
|
|
||||||
|
|
||||||
for i in range(7):
|
for i in range(7):
|
||||||
data = await self.get_config()
|
data = await self.get_config()
|
||||||
@@ -74,46 +48,40 @@ class X19(BMMiner):
|
|||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def fault_light_on(self) -> bool:
|
async def fault_light_on(self) -> bool:
|
||||||
data = await self.send_web_command(
|
data = await self.web.blink(blink=True)
|
||||||
"blink",
|
|
||||||
params=json.dumps({"blink": "true"}), # noqa - ignore params being a str
|
|
||||||
)
|
|
||||||
if data:
|
if data:
|
||||||
if data.get("code") == "B000":
|
if data.get("code") == "B000":
|
||||||
self.light = True
|
self.light = True
|
||||||
return self.light
|
return self.light
|
||||||
|
|
||||||
async def fault_light_off(self) -> bool:
|
async def fault_light_off(self) -> bool:
|
||||||
data = await self.send_web_command(
|
data = await self.web.blink(blink=False)
|
||||||
"blink",
|
|
||||||
params=json.dumps({"blink": "false"}), # noqa - ignore params being a str
|
|
||||||
)
|
|
||||||
if data:
|
if data:
|
||||||
if data.get("code") == "B100":
|
if data.get("code") == "B100":
|
||||||
self.light = True
|
self.light = True
|
||||||
return self.light
|
return self.light
|
||||||
|
|
||||||
async def reboot(self) -> bool:
|
async def reboot(self) -> bool:
|
||||||
data = await self.send_web_command("reboot")
|
data = await self.web.reboot()
|
||||||
if data:
|
if data:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def stop_mining(self) -> bool:
|
async def stop_mining(self) -> bool:
|
||||||
cfg = await self.get_config()
|
cfg = await self.get_config()
|
||||||
cfg.autotuning_wattage = 0
|
cfg.miner_mode = X19PowerMode.Sleep
|
||||||
await self.send_config(cfg)
|
await self.send_config(cfg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def resume_mining(self) -> bool:
|
async def resume_mining(self) -> bool:
|
||||||
cfg = await self.get_config()
|
cfg = await self.get_config()
|
||||||
cfg.autotuning_wattage = 3600
|
cfg.miner_mode = X19PowerMode.Normal
|
||||||
await self.send_config(cfg)
|
await self.send_config(cfg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def get_hostname(self) -> Union[str, None]:
|
async def get_hostname(self) -> Union[str, None]:
|
||||||
try:
|
try:
|
||||||
data = await self.send_web_command("get_system_info")
|
data = await self.web.get_system_info()
|
||||||
if data:
|
if data:
|
||||||
return data["hostname"]
|
return data["hostname"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -121,14 +89,14 @@ class X19(BMMiner):
|
|||||||
|
|
||||||
async def get_mac(self) -> Union[str, None]:
|
async def get_mac(self) -> Union[str, None]:
|
||||||
try:
|
try:
|
||||||
data = await self.send_web_command("get_system_info")
|
data = await self.web.get_system_info()
|
||||||
if data:
|
if data:
|
||||||
return data["macaddr"]
|
return data["macaddr"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = await self.send_web_command("get_network_info")
|
data = await self.web.get_network_info()
|
||||||
if data:
|
if data:
|
||||||
return data["macaddr"]
|
return data["macaddr"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -136,7 +104,7 @@ class X19(BMMiner):
|
|||||||
|
|
||||||
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.web.summary()
|
||||||
if data:
|
if data:
|
||||||
try:
|
try:
|
||||||
for item in data["SUMMARY"][0]["status"]:
|
for item in data["SUMMARY"][0]["status"]:
|
||||||
@@ -153,7 +121,7 @@ class X19(BMMiner):
|
|||||||
if self.light:
|
if self.light:
|
||||||
return self.light
|
return self.light
|
||||||
try:
|
try:
|
||||||
data = await self.send_web_command("get_blink_status")
|
data = await self.web.get_blink_status()
|
||||||
if data:
|
if data:
|
||||||
self.light = data["blink"]
|
self.light = data["blink"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -193,42 +161,34 @@ class X19(BMMiner):
|
|||||||
):
|
):
|
||||||
if not hostname:
|
if not hostname:
|
||||||
hostname = await self.get_hostname()
|
hostname = await self.get_hostname()
|
||||||
payload = {
|
await self.web.set_network_conf(
|
||||||
"ipAddress": ip,
|
ip=ip,
|
||||||
"ipDns": dns,
|
dns=dns,
|
||||||
"ipGateway": gateway,
|
gateway=gateway,
|
||||||
"ipHost": hostname,
|
subnet_mask=subnet_mask,
|
||||||
"ipPro": 2, # static
|
hostname=hostname,
|
||||||
"ipSub": subnet_mask,
|
protocol=2,
|
||||||
}
|
)
|
||||||
await self.send_web_command("set_network_conf", params=payload)
|
|
||||||
|
|
||||||
async def set_dhcp(self, hostname: str = None):
|
async def set_dhcp(self, hostname: str = None):
|
||||||
if not hostname:
|
if not hostname:
|
||||||
hostname = await self.get_hostname()
|
hostname = await self.get_hostname()
|
||||||
payload = {
|
await self.web.set_network_conf(
|
||||||
"ipAddress": "",
|
ip="", dns="", gateway="", subnet_mask="", hostname=hostname, protocol=1
|
||||||
"ipDns": "",
|
)
|
||||||
"ipGateway": "",
|
|
||||||
"ipHost": hostname,
|
|
||||||
"ipPro": 1, # DHCP
|
|
||||||
"ipSub": "",
|
|
||||||
}
|
|
||||||
await self.send_web_command("set_network_conf", params=payload)
|
|
||||||
|
|
||||||
async def set_hostname(self, hostname: str):
|
async def set_hostname(self, hostname: str):
|
||||||
cfg = await self.send_web_command("get_network_info")
|
cfg = await self.web.get_network_info()
|
||||||
dns = cfg["conf_dnsservers"]
|
dns = cfg["conf_dnsservers"]
|
||||||
gateway = cfg["conf_gateway"]
|
gateway = cfg["conf_gateway"]
|
||||||
ip = cfg["conf_ipaddress"]
|
ip = cfg["conf_ipaddress"]
|
||||||
subnet_mask = cfg["conf_netmask"]
|
subnet_mask = cfg["conf_netmask"]
|
||||||
protocol = 1 if cfg["conf_nettype"] == "DHCP" else 2
|
protocol = 1 if cfg["conf_nettype"] == "DHCP" else 2
|
||||||
payload = {
|
await self.web.set_network_conf(
|
||||||
"ipAddress": ip,
|
ip=ip,
|
||||||
"ipDns": dns,
|
dns=dns,
|
||||||
"ipGateway": gateway,
|
gateway=gateway,
|
||||||
"ipHost": hostname,
|
subnet_mask=subnet_mask,
|
||||||
"ipPro": protocol,
|
hostname=hostname,
|
||||||
"ipSub": subnet_mask,
|
protocol=protocol,
|
||||||
}
|
)
|
||||||
await self.send_web_command("set_network_conf", params=payload)
|
|
||||||
@@ -17,17 +17,16 @@
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import List, Optional, Tuple, Union
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import asyncssh
|
import asyncssh
|
||||||
|
|
||||||
from pyasic.API.bmminer import BMMinerAPI
|
from pyasic.API.bmminer import BMMinerAPI
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, MinerData
|
from pyasic.data import Fan, HashBoard
|
||||||
from pyasic.data.error_codes import MinerErrorData
|
from pyasic.data.error_codes import MinerErrorData
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners.base import BaseMiner
|
from pyasic.miners.base import BaseMiner
|
||||||
from pyasic.settings import PyasicSettings
|
|
||||||
|
|
||||||
|
|
||||||
class BMMiner(BaseMiner):
|
class BMMiner(BaseMiner):
|
||||||
@@ -26,11 +26,10 @@ import toml
|
|||||||
|
|
||||||
from pyasic.API.bosminer import BOSMinerAPI
|
from pyasic.API.bosminer import BOSMinerAPI
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, MinerData
|
from pyasic.data import Fan, HashBoard
|
||||||
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
|
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners.base import BaseMiner
|
from pyasic.miners.base import BaseMiner
|
||||||
from pyasic.settings import PyasicSettings
|
|
||||||
|
|
||||||
|
|
||||||
class BOSMiner(BaseMiner):
|
class BOSMiner(BaseMiner):
|
||||||
@@ -690,14 +689,15 @@ class BOSMiner(BaseMiner):
|
|||||||
# need to use get_config, as this will never read perfectly as there are some bad edge cases
|
# need to use get_config, as this will never read perfectly as there are some bad edge cases
|
||||||
groups = []
|
groups = []
|
||||||
cfg = await self.get_config()
|
cfg = await self.get_config()
|
||||||
for group in cfg.pool_groups:
|
if cfg:
|
||||||
pools = {"quota": group.quota}
|
for group in cfg.pool_groups:
|
||||||
for _i, _pool in enumerate(group.pools):
|
pools = {"quota": group.quota}
|
||||||
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
|
for _i, _pool in enumerate(group.pools):
|
||||||
"stratum+tcp://", ""
|
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
|
||||||
).replace("stratum2+tcp://", "")
|
"stratum+tcp://", ""
|
||||||
pools[f"pool_{_i + 1}_user"] = _pool.username
|
).replace("stratum2+tcp://", "")
|
||||||
groups.append(pools)
|
pools[f"pool_{_i + 1}_user"] = _pool.username
|
||||||
|
groups.append(pools)
|
||||||
return groups
|
return groups
|
||||||
else:
|
else:
|
||||||
groups[0][f"pool_{i + 1}_url"] = (
|
groups[0][f"pool_{i + 1}_url"] = (
|
||||||
@@ -839,7 +839,6 @@ class BOSMiner(BaseMiner):
|
|||||||
api_devs = await self.api.devs()
|
api_devs = await self.api.devs()
|
||||||
except APIError:
|
except APIError:
|
||||||
pass
|
pass
|
||||||
nom_hr = 0
|
|
||||||
|
|
||||||
if api_devs:
|
if api_devs:
|
||||||
try:
|
try:
|
||||||
@@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from typing import List, Optional, Tuple
|
||||||
from typing import List, Optional, Tuple, Union
|
|
||||||
|
|
||||||
import asyncssh
|
import asyncssh
|
||||||
|
|
||||||
@@ -180,5 +179,5 @@ class BOSMinerOld(BaseMiner):
|
|||||||
async def get_nominal_hashrate(self) -> Optional[float]:
|
async def get_nominal_hashrate(self) -> Optional[float]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
async def get_data(self, allow_warning: bool = False, **kwargs) -> MinerData:
|
||||||
return MinerData(ip=str(self.ip))
|
return MinerData(ip=str(self.ip))
|
||||||
@@ -18,15 +18,14 @@ import ipaddress
|
|||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import List, Optional, Tuple, Union
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
from pyasic.API.btminer import BTMinerAPI
|
from pyasic.API.btminer import BTMinerAPI
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, MinerData
|
from pyasic.data import Fan, HashBoard
|
||||||
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
|
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners.base import BaseMiner
|
from pyasic.miners.base import BaseMiner
|
||||||
from pyasic.settings import PyasicSettings
|
|
||||||
|
|
||||||
|
|
||||||
class BTMiner(BaseMiner):
|
class BTMiner(BaseMiner):
|
||||||
@@ -122,17 +121,7 @@ class BTMiner(BaseMiner):
|
|||||||
pools_conf = conf["pools"]
|
pools_conf = conf["pools"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.api.update_pools(
|
await self.api.update_pools(**pools_conf)
|
||||||
pools_conf[0]["url"],
|
|
||||||
pools_conf[0]["user"],
|
|
||||||
pools_conf[0]["pass"],
|
|
||||||
pools_conf[1]["url"],
|
|
||||||
pools_conf[1]["user"],
|
|
||||||
pools_conf[1]["pass"],
|
|
||||||
pools_conf[2]["url"],
|
|
||||||
pools_conf[2]["user"],
|
|
||||||
pools_conf[2]["pass"],
|
|
||||||
)
|
|
||||||
except APIError:
|
except APIError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
@@ -153,15 +142,16 @@ class BTMiner(BaseMiner):
|
|||||||
except APIError as e:
|
except APIError as e:
|
||||||
logging.warning(e)
|
logging.warning(e)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
# somethings wrong with the miner
|
|
||||||
warnings.warn(
|
|
||||||
f"Failed to gather pool config for miner: {self}, miner did not return pool information."
|
|
||||||
)
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if pools:
|
if pools:
|
||||||
if "POOLS" in pools:
|
if "POOLS" in pools:
|
||||||
cfg = cfg.from_api(pools["POOLS"])
|
cfg = cfg.from_api(pools["POOLS"])
|
||||||
|
else:
|
||||||
|
# somethings wrong with the miner
|
||||||
|
warnings.warn(
|
||||||
|
f"Failed to gather pool config for miner: {self}, miner did not return pool information."
|
||||||
|
)
|
||||||
if summary:
|
if summary:
|
||||||
if "SUMMARY" in summary:
|
if "SUMMARY" in summary:
|
||||||
if wattage := summary["SUMMARY"][0].get("Power Limit"):
|
if wattage := summary["SUMMARY"][0].get("Power Limit"):
|
||||||
@@ -545,8 +535,6 @@ class BTMiner(BaseMiner):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def get_fault_light(self, api_get_miner_info: dict = None) -> bool:
|
async def get_fault_light(self, api_get_miner_info: dict = None) -> bool:
|
||||||
data = None
|
|
||||||
|
|
||||||
if not api_get_miner_info:
|
if not api_get_miner_info:
|
||||||
try:
|
try:
|
||||||
api_get_miner_info = await self.api.get_miner_info()
|
api_get_miner_info = await self.api.get_miner_info()
|
||||||
@@ -17,17 +17,16 @@
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import logging
|
import logging
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import List, Optional, Tuple, Union
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import asyncssh
|
import asyncssh
|
||||||
|
|
||||||
from pyasic.API.cgminer import CGMinerAPI
|
from pyasic.API.cgminer import CGMinerAPI
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, MinerData
|
from pyasic.data import Fan, HashBoard
|
||||||
from pyasic.data.error_codes import MinerErrorData
|
from pyasic.data.error_codes import MinerErrorData
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners.base import BaseMiner
|
from pyasic.miners.base import BaseMiner
|
||||||
from pyasic.settings import PyasicSettings
|
|
||||||
|
|
||||||
|
|
||||||
class CGMiner(BaseMiner):
|
class CGMiner(BaseMiner):
|
||||||
@@ -129,12 +128,8 @@ class CGMiner(BaseMiner):
|
|||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def get_config(self, api_pools: dict = None) -> MinerConfig:
|
async def get_config(self) -> MinerConfig:
|
||||||
# get pool data
|
api_pools = await self.api.pools()
|
||||||
try:
|
|
||||||
api_pools = await self.api.pools()
|
|
||||||
except APIError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if api_pools:
|
if api_pools:
|
||||||
self.config = MinerConfig().from_api(api_pools["POOLS"])
|
self.config = MinerConfig().from_api(api_pools["POOLS"])
|
||||||
@@ -14,20 +14,15 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import ipaddress
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from collections import namedtuple
|
from typing import List, Optional
|
||||||
from typing import List, Optional, Tuple, Union
|
|
||||||
|
|
||||||
from pyasic.API.cgminer import CGMinerAPI
|
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, MinerData
|
from pyasic.data import Fan, HashBoard
|
||||||
from pyasic.data.error_codes import MinerErrorData
|
from pyasic.data.error_codes import MinerErrorData
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners._backends import CGMiner
|
from pyasic.miners.btc._backends import CGMiner
|
||||||
from pyasic.miners.base import BaseMiner
|
|
||||||
from pyasic.settings import PyasicSettings
|
|
||||||
|
|
||||||
|
|
||||||
class CGMinerAvalon(CGMiner):
|
class CGMinerAvalon(CGMiner):
|
||||||
@@ -79,7 +74,7 @@ class CGMinerAvalon(CGMiner):
|
|||||||
logging.debug(f"{self}: Sending config.") # noqa - This doesnt work...
|
logging.debug(f"{self}: Sending config.") # noqa - This doesnt work...
|
||||||
conf = config.as_avalon(user_suffix=user_suffix)
|
conf = config.as_avalon(user_suffix=user_suffix)
|
||||||
try:
|
try:
|
||||||
data = await self.api.ascset(
|
data = await self.api.ascset( # noqa
|
||||||
0, "setpool", f"root,root,{conf}"
|
0, "setpool", f"root,root,{conf}"
|
||||||
) # this should work but doesn't
|
) # this should work but doesn't
|
||||||
except APIError:
|
except APIError:
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
|
||||||
from pyasic.miners._backends import BMMiner
|
from pyasic.miners.btc._backends import BMMiner
|
||||||
|
|
||||||
|
|
||||||
class Hiveon(BMMiner):
|
class Hiveon(BMMiner):
|
||||||
@@ -14,92 +14,19 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
from typing import Optional
|
||||||
from typing import Optional, Union
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.miners._backends.bmminer import BMMiner
|
from pyasic.miners.btc._backends.bmminer import BMMiner
|
||||||
from pyasic.settings import PyasicSettings
|
from pyasic.web.vnish import VNishWebAPI
|
||||||
|
|
||||||
|
|
||||||
class VNish(BMMiner):
|
class VNish(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)
|
super().__init__(ip, api_ver)
|
||||||
self.api_type = "VNish"
|
self.api_type = "VNish"
|
||||||
self.uname = "root"
|
self.web = VNishWebAPI(ip)
|
||||||
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]:
|
async def get_model(self, api_stats: dict = None) -> Optional[str]:
|
||||||
# check if model is cached
|
# check if model is cached
|
||||||
@@ -122,16 +49,26 @@ class VNish(BMMiner):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
async def restart_backend(self) -> bool:
|
async def restart_backend(self) -> bool:
|
||||||
data = await self.send_web_command("mining/restart", method="POST")
|
data = await self.web.restart_vnish()
|
||||||
return data
|
if data:
|
||||||
|
try:
|
||||||
|
return data["success"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
async def reboot(self) -> bool:
|
async def reboot(self) -> bool:
|
||||||
data = await self.send_web_command("system/reboot", method="POST")
|
data = await self.web.reboot()
|
||||||
return data
|
if data:
|
||||||
|
try:
|
||||||
|
return data["success"]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
|
||||||
async def get_mac(self, web_summary: dict = None) -> str:
|
async def get_mac(self, web_summary: dict = None) -> str:
|
||||||
if not web_summary:
|
if not web_summary:
|
||||||
web_info = await self.send_web_command("info")
|
web_info = await self.web.info()
|
||||||
|
|
||||||
if web_info:
|
if web_info:
|
||||||
try:
|
try:
|
||||||
@@ -149,7 +86,7 @@ class VNish(BMMiner):
|
|||||||
|
|
||||||
async def get_hostname(self, web_summary: dict = None) -> str:
|
async def get_hostname(self, web_summary: dict = None) -> str:
|
||||||
if not web_summary:
|
if not web_summary:
|
||||||
web_info = await self.send_web_command("info")
|
web_info = await self.web.info()
|
||||||
|
|
||||||
if web_info:
|
if web_info:
|
||||||
try:
|
try:
|
||||||
@@ -167,7 +104,7 @@ class VNish(BMMiner):
|
|||||||
|
|
||||||
async def get_wattage(self, web_summary: dict = None) -> Optional[int]:
|
async def get_wattage(self, web_summary: dict = None) -> Optional[int]:
|
||||||
if not web_summary:
|
if not web_summary:
|
||||||
web_summary = await self.send_web_command("summary")
|
web_summary = await self.web.summary()
|
||||||
|
|
||||||
if web_summary:
|
if web_summary:
|
||||||
try:
|
try:
|
||||||
@@ -196,7 +133,7 @@ class VNish(BMMiner):
|
|||||||
|
|
||||||
async def get_wattage_limit(self, web_settings: dict = None) -> Optional[int]:
|
async def get_wattage_limit(self, web_settings: dict = None) -> Optional[int]:
|
||||||
if not web_settings:
|
if not web_settings:
|
||||||
web_settings = await self.send_web_command("summary")
|
web_settings = await self.web.summary()
|
||||||
|
|
||||||
if web_settings:
|
if web_settings:
|
||||||
try:
|
try:
|
||||||
@@ -207,7 +144,7 @@ class VNish(BMMiner):
|
|||||||
|
|
||||||
async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]:
|
async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]:
|
||||||
if not web_summary:
|
if not web_summary:
|
||||||
web_summary = await self.send_web_command("summary")
|
web_summary = await self.web.summary()
|
||||||
|
|
||||||
if web_summary:
|
if web_summary:
|
||||||
try:
|
try:
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S17(AntMiner): # noqa - ignore ABC method implementation
|
class S17(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S17Plus(AntMiner): # noqa - ignore ABC method implementation
|
class S17Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S17Pro(AntMiner): # noqa - ignore ABC method implementation
|
class S17Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S17e(AntMiner): # noqa - ignore ABC method implementation
|
class S17e(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class T17(AntMiner): # noqa - ignore ABC method implementation
|
class T17(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class T17Plus(AntMiner): # noqa - ignore ABC method implementation
|
class T17Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class T17e(AntMiner): # noqa - ignore ABC method implementation
|
class T17e(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19(AntMiner): # noqa - ignore ABC method implementation
|
class S19(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19Pro(AntMiner): # noqa - ignore ABC method implementation
|
class S19Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19XP(AntMiner): # noqa - ignore ABC method implementation
|
class S19XP(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19a(AntMiner): # noqa - ignore ABC method implementation
|
class S19a(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19aPro(AntMiner): # noqa - ignore ABC method implementation
|
class S19aPro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19j(AntMiner): # noqa - ignore ABC method implementation
|
class S19j(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S19jPro(AntMiner): # noqa - ignore ABC method implementation
|
class S19jPro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class T19(AntMiner): # noqa - ignore ABC method implementation
|
class T19(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S9(AntMiner): # noqa - ignore ABC method implementation
|
class S9(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class S9i(AntMiner): # noqa - ignore ABC method implementation
|
class S9i(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AntMiner
|
from pyasic.miners.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class T9(AntMiner): # noqa - ignore ABC method implementation
|
class T9(AntMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -13,7 +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. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from .X9 import *
|
from .X9 import *
|
||||||
from .X17 import *
|
from .X17 import *
|
||||||
from .X19 import *
|
from .X19 import *
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon1026(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon1026(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon1047(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon1047(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon1066(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon1066(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon721(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon721(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon741(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon741(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon761(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon761(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon821(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon821(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon841(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon841(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon851(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon851(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import AvalonMiner
|
from pyasic.miners.makes import AvalonMiner
|
||||||
|
|
||||||
|
|
||||||
class Avalon921(AvalonMiner): # noqa - ignore ABC method implementation
|
class Avalon921(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import InnosiliconMiner
|
from pyasic.miners.makes import InnosiliconMiner
|
||||||
|
|
||||||
|
|
||||||
class InnosiliconT3HPlus(InnosiliconMiner): # noqa - ignore ABC method implementation
|
class InnosiliconT3HPlus(InnosiliconMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M20V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M20V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M20SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M20SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M20SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M20SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M21SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
class M21SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M21SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
class M21SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M29V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M29V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
|
class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M31SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M31SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M31SEV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M31SEV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M31SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M32V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M32V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M32S(WhatsMiner): # noqa - ignore ABC method implementation
|
class M32S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M33V10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M33V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M33SVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M33SVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M33SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
class M33SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M33SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
class M33SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M34SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M34SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M36SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M36SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M36SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M36SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M39V20(WhatsMiner): # noqa - ignore ABC method implementation
|
class M39V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -48,10 +48,7 @@ class M50VH20(WhatsMiner): # noqa - ignore ABC method implementation
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.model = "M50 VH20"
|
self.model = "M50 VH20"
|
||||||
self.nominal_chips = 0
|
self.nominal_chips = 111
|
||||||
warnings.warn(
|
|
||||||
"Unknown chip count for miner type M50 VH20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
|
||||||
)
|
|
||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
@@ -72,10 +69,7 @@ class M50VH40(WhatsMiner): # noqa - ignore ABC method implementation
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.model = "M50 VH40"
|
self.model = "M50 VH40"
|
||||||
self.nominal_chips = 0
|
self.nominal_chips = 84
|
||||||
warnings.warn(
|
|
||||||
"Unknown chip count for miner type M50 VH40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
|
|
||||||
)
|
|
||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M50SVJ10(WhatsMiner): # noqa - ignore ABC method implementation
|
class M50SVJ10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M50SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M50SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M53VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M53VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M53SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M53SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M53SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M53SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M56VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M56VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M56SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M56SVH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M56SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M56SPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from pyasic.miners._types.makes import WhatsMiner
|
from pyasic.miners.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M59VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
class M59VH30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user