Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67b3e2f312 | ||
|
|
82006de30f | ||
|
|
5bde9d7fe6 | ||
|
|
f7cd428366 | ||
|
|
d5e797de9e | ||
|
|
b9d5e7b206 | ||
|
|
2d8c7eb4fd | ||
|
|
f69e07fe68 | ||
|
|
84aab38954 | ||
|
|
dcf37481bd | ||
|
|
1a9cca84d5 | ||
|
|
c5272d67de | ||
|
|
3bcfb14177 | ||
|
|
566280f280 | ||
|
|
a814f7eefb | ||
|
|
097b8ed534 | ||
|
|
da47d72749 | ||
|
|
abd4d18a01 | ||
|
|
2adbce3c21 | ||
|
|
c41324b324 | ||
|
|
151a4f6c2d | ||
|
|
d23777a83f | ||
|
|
bb0aee8337 | ||
|
|
2a23acb73a | ||
|
|
ad5eb0cef6 | ||
|
|
07dd8f55fe | ||
|
|
bafa91cc47 | ||
|
|
16399510fa | ||
|
|
e9f54eec0a | ||
|
|
fbbbc9f215 | ||
|
|
69e4f575c0 | ||
|
|
e95659d2e0 | ||
|
|
35f34310ec | ||
|
|
acc18e20fd | ||
|
|
72460eac08 | ||
|
|
3e5f9d4eca | ||
|
|
e873fa252c | ||
|
|
ff2c083a19 | ||
|
|
a30a84c34b | ||
|
|
97d2023298 | ||
|
|
1ce8430a14 | ||
|
|
1c0b638818 | ||
|
|
e852588eeb | ||
|
|
08b9bfd854 | ||
|
|
7ee2f3a29a | ||
|
|
5ee6a38f39 |
@@ -46,7 +46,7 @@ from pyasic.network import MinerNetwork
|
|||||||
async def scan_and_get_data():
|
async def scan_and_get_data():
|
||||||
# Define network range to be used for scanning
|
# Define network range to be used for scanning
|
||||||
# This can take a list of IPs, a constructor string, or an IP and subnet mask
|
# This can take a list of IPs, a constructor string, or an IP and subnet mask
|
||||||
# The standard mask is /24, and you can pass any IP address in the subnet
|
# The standard mask is /24 (x.x.x.0-255), and you can pass any IP address in the subnet
|
||||||
net = MinerNetwork("192.168.1.69", mask=24)
|
net = MinerNetwork("192.168.1.69", mask=24)
|
||||||
# Scan the network for miners
|
# Scan the network for miners
|
||||||
# This function returns a list of miners of the correct type as a class
|
# This function returns a list of miners of the correct type as a class
|
||||||
@@ -93,6 +93,10 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
If needed, this library exposes a wrapper for the miner API that can be used for advanced data gathering.
|
If needed, this library exposes a wrapper for the miner API that can be used for advanced data gathering.
|
||||||
|
|
||||||
|
You can see more information on basic usage of the APIs past this example in the docs [here](https://pyasic.readthedocs.io/en/latest/API/api/).
|
||||||
|
|
||||||
|
Please see the appropriate API documentation page (pyasic docs -> Advanced -> Miner APIs -> your API type) for a link to that specific miner's API documentation page and more information.
|
||||||
|
|
||||||
#### List available API commands
|
#### List available API commands
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -105,7 +109,8 @@ async def get_api_commands(miner_ip: str):
|
|||||||
miner = await get_miner(miner_ip)
|
miner = await get_miner(miner_ip)
|
||||||
|
|
||||||
# List all available commands
|
# List all available commands
|
||||||
print(miner.api.get_commands())
|
# Can also be called explicitly with the function miner.api.get_commands()
|
||||||
|
print(miner.api.commands)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -117,10 +117,11 @@ These functions are
|
|||||||
[`get_hostname`](#get-hostname),
|
[`get_hostname`](#get-hostname),
|
||||||
[`get_model`](#get-model),
|
[`get_model`](#get-model),
|
||||||
[`reboot`](#reboot),
|
[`reboot`](#reboot),
|
||||||
[`restart_backend`](#restart-backend), and
|
[`restart_backend`](#restart-backend),
|
||||||
[`stop_mining`](#stop-mining), and
|
[`stop_mining`](#stop-mining),
|
||||||
[`resume_mining`](#resume-mining), and
|
[`resume_mining`](#resume-mining),
|
||||||
[`send_config`](#send-config).
|
[`send_config`](#send-config), and
|
||||||
|
[`set_power_limit`](#set-power-limit).
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -228,6 +229,14 @@ These functions are
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
### Set Power Limit
|
||||||
|
::: pyasic.miners.BaseMiner.set_power_limit
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
## [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
## [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
||||||
|
|
||||||
Pyasic implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
Pyasic implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ details {
|
|||||||
<ul>
|
<ul>
|
||||||
<li><a href="../whatsminer/M3X/#m30svf20">VF20</a></li>
|
<li><a href="../whatsminer/M3X/#m30svf20">VF20</a></li>
|
||||||
<li><a href="../whatsminer/M3X/#m30sve40">VE40</a></li>
|
<li><a href="../whatsminer/M3X/#m30sve40">VE40</a></li>
|
||||||
|
<li><a href="../whatsminer/M3X/#m30svg40">VG40</a></li>
|
||||||
<li><a href="../whatsminer/M3X/#m30svg60">VG60</a></li>
|
<li><a href="../whatsminer/M3X/#m30svg60">VG60</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
@@ -90,6 +91,10 @@ details {
|
|||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary><a href="../whatsminer/M3X/#m31s">M31S</a></summary>
|
<summary><a href="../whatsminer/M3X/#m31s">M31S</a></summary>
|
||||||
|
<summary><a href="../whatsminer/M3X/#m31sv10">M31SV10</a></summary>
|
||||||
|
<summary><a href="../whatsminer/M3X/#m31sv20">M31SV20</a></summary>
|
||||||
|
<summary><a href="../whatsminer/M3X/#m31sv60">M31SV60</a></summary>
|
||||||
|
<summary><a href="../whatsminer/M3X/#m31sv70">M31SV70</a></summary>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
<summary><a href="../whatsminer/M3X/#m31s_1">M31S+</a></summary>
|
<summary><a href="../whatsminer/M3X/#m31s_1">M31S+</a></summary>
|
||||||
|
|||||||
@@ -65,6 +65,14 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## M30S+VG40
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG40
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## M30S+VG60
|
## M30S+VG60
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG60
|
::: pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG60
|
||||||
@@ -114,6 +122,38 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
|
## M31SV10
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV10
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M31SV20
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV20
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M31SV60
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV60
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## M31SV70
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31SV70
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
## M31S+
|
## M31S+
|
||||||
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlus
|
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlus
|
||||||
|
|||||||
@@ -35,6 +35,81 @@ class BaseMinerAPI:
|
|||||||
raise TypeError(f"Only children of '{cls.__name__}' may be instantiated")
|
raise TypeError(f"Only children of '{cls.__name__}' may be instantiated")
|
||||||
return object.__new__(cls)
|
return object.__new__(cls)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{self.__class__.__name__}: {str(self.ip)}"
|
||||||
|
|
||||||
|
async def send_command(
|
||||||
|
self,
|
||||||
|
command: Union[str, bytes],
|
||||||
|
parameters: Union[str, int, bool] = None,
|
||||||
|
ignore_errors: bool = False,
|
||||||
|
allow_warning: bool = True,
|
||||||
|
) -> dict:
|
||||||
|
"""Send an API command to the miner and return the result.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
command: The command to sent to the miner.
|
||||||
|
parameters: Any additional parameters to be sent with the command.
|
||||||
|
ignore_errors: Whether to raise APIError when the command returns an error.
|
||||||
|
allow_warning: Whether to warn if the command fails.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The return data from the API command parsed from JSON into a dict.
|
||||||
|
"""
|
||||||
|
logging.debug(f"{self} - (Send Privileged Command) - {command} " + f'with args {parameters}' if parameters else '')
|
||||||
|
# create the command
|
||||||
|
cmd = {"command": command}
|
||||||
|
if parameters:
|
||||||
|
cmd["parameter"] = parameters
|
||||||
|
|
||||||
|
# send the command
|
||||||
|
data = await self._send_bytes(json.dumps(cmd).encode("utf-8"))
|
||||||
|
|
||||||
|
data = self._load_api_data(data)
|
||||||
|
|
||||||
|
# check for if the user wants to allow errors to return
|
||||||
|
if not ignore_errors:
|
||||||
|
# validate the command succeeded
|
||||||
|
validation = self._validate_command_output(data)
|
||||||
|
if not validation[0]:
|
||||||
|
if allow_warning:
|
||||||
|
logging.warning(
|
||||||
|
f"{self.ip}: API Command Error: {command}: {validation[1]}"
|
||||||
|
)
|
||||||
|
raise APIError(validation[1])
|
||||||
|
|
||||||
|
logging.debug(f"{self} - (Send Command) - Received data.")
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# Privileged command handler, only used by whatsminers, defined here for consistency.
|
||||||
|
async def send_privileged_command(self, *args, **kwargs) -> dict:
|
||||||
|
return await self.send_command(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
||||||
|
"""Creates and sends multiple commands as one command to the miner.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
*commands: The commands to send as a multicommand to the miner.
|
||||||
|
allow_warning: A boolean to supress APIWarnings.
|
||||||
|
"""
|
||||||
|
# make sure we can actually run each command, otherwise they will fail
|
||||||
|
commands = self._check_commands(*commands)
|
||||||
|
# standard multicommand format is "command1+command2"
|
||||||
|
# standard format doesn't work for X19
|
||||||
|
command = "+".join(commands)
|
||||||
|
try:
|
||||||
|
data = await self.send_command(command, allow_warning=allow_warning)
|
||||||
|
except APIError:
|
||||||
|
return {}
|
||||||
|
logging.debug(f"{self} - (Multicommand) - Received data")
|
||||||
|
return data
|
||||||
|
|
||||||
|
@property
|
||||||
|
def commands(self) -> list:
|
||||||
|
return self.get_commands()
|
||||||
|
|
||||||
def get_commands(self) -> list:
|
def get_commands(self) -> list:
|
||||||
"""Get a list of command accessible to a specific type of API on the miner.
|
"""Get a list of command accessible to a specific type of API on the miner.
|
||||||
|
|
||||||
@@ -46,6 +121,7 @@ class BaseMinerAPI:
|
|||||||
for func in
|
for func in
|
||||||
# each function in self
|
# each function in self
|
||||||
dir(self)
|
dir(self)
|
||||||
|
if not func == "commands"
|
||||||
if callable(getattr(self, func)) and
|
if callable(getattr(self, func)) and
|
||||||
# no __ or _ methods
|
# no __ or _ methods
|
||||||
not func.startswith("__") and not func.startswith("_") and
|
not func.startswith("__") and not func.startswith("_") and
|
||||||
@@ -72,103 +148,49 @@ If you are sure you want to use this command please use API.send_command("{comma
|
|||||||
)
|
)
|
||||||
return return_commands
|
return return_commands
|
||||||
|
|
||||||
async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict:
|
async def _send_bytes(self, data: bytes, timeout: int=100) -> bytes:
|
||||||
"""Creates and sends multiple commands as one command to the miner.
|
logging.debug(f"{self} - ([Hidden] Send Bytes) - Sending")
|
||||||
|
|
||||||
Parameters:
|
|
||||||
*commands: The commands to send as a multicommand to the miner.
|
|
||||||
allow_warning: A boolean to supress APIWarnings.
|
|
||||||
"""
|
|
||||||
logging.debug(f"{self.ip}: Sending multicommand: {[*commands]}")
|
|
||||||
# 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:
|
|
||||||
return {}
|
|
||||||
logging.debug(f"{self.ip}: Received multicommand data.")
|
|
||||||
return data
|
|
||||||
|
|
||||||
async def _send_bytes(self, data: bytes) -> bytes:
|
|
||||||
try:
|
try:
|
||||||
# get reader and writer streams
|
# get reader and writer streams
|
||||||
reader, writer = await asyncio.open_connection(str(self.ip), self.port)
|
reader, writer = await asyncio.open_connection(str(self.ip), self.port)
|
||||||
# handle OSError 121
|
# handle OSError 121
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
if getattr(e, "winerror") == "121":
|
if getattr(e, "winerror") == "121":
|
||||||
logging.warning("Semaphore Timeout has Expired.")
|
logging.warning(f"{self} - ([Hidden] Send Bytes) - Semaphore timeout expired.")
|
||||||
return b"{}"
|
return b"{}"
|
||||||
|
|
||||||
# send the command
|
# send the command
|
||||||
|
logging.debug(f"{self} - ([Hidden] Send Bytes) - Writing")
|
||||||
writer.write(data)
|
writer.write(data)
|
||||||
|
logging.debug(f"{self} - ([Hidden] Send Bytes) - Draining")
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
|
|
||||||
# instantiate data
|
# instantiate data
|
||||||
ret_data = b""
|
ret_data = b""
|
||||||
|
|
||||||
# loop to receive all the data
|
# loop to receive all the data
|
||||||
|
logging.debug(f"{self} - ([Hidden] Send Bytes) - Receiving")
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
d = await reader.read(4096)
|
try:
|
||||||
if not d:
|
d = await asyncio.wait_for(reader.read(4096), timeout=timeout)
|
||||||
break
|
if not d:
|
||||||
ret_data += d
|
break
|
||||||
|
ret_data += d
|
||||||
|
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||||
|
raise e
|
||||||
|
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||||
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"{self.ip}: API Command Error: - {e}")
|
logging.warning(f"{self} - ([Hidden] Send Bytes) - API Command Error {e}")
|
||||||
|
|
||||||
# close the connection
|
# close the connection
|
||||||
|
logging.debug(f"{self} - ([Hidden] Send Bytes) - Closing")
|
||||||
writer.close()
|
writer.close()
|
||||||
await writer.wait_closed()
|
await writer.wait_closed()
|
||||||
|
|
||||||
return ret_data
|
return ret_data
|
||||||
|
|
||||||
async def send_command(
|
|
||||||
self,
|
|
||||||
command: Union[str, bytes],
|
|
||||||
parameters: Union[str, int, bool] = None,
|
|
||||||
ignore_errors: bool = False,
|
|
||||||
allow_warning: bool = True,
|
|
||||||
) -> dict:
|
|
||||||
"""Send an API command to the miner and return the result.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
command: The command to sent to the miner.
|
|
||||||
parameters: Any additional parameters to be sent with the command.
|
|
||||||
ignore_errors: Whether to raise APIError when the command returns an error.
|
|
||||||
allow_warning: Whether to warn if the command fails.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The return data from the API command parsed from JSON into a dict.
|
|
||||||
"""
|
|
||||||
# create the command
|
|
||||||
cmd = {"command": command}
|
|
||||||
if parameters:
|
|
||||||
cmd["parameter"] = parameters
|
|
||||||
|
|
||||||
# send the command
|
|
||||||
data = await self._send_bytes(json.dumps(cmd).encode("utf-8"))
|
|
||||||
|
|
||||||
data = self._load_api_data(data)
|
|
||||||
|
|
||||||
# check for if the user wants to allow errors to return
|
|
||||||
if not ignore_errors:
|
|
||||||
# validate the command succeeded
|
|
||||||
validation = self._validate_command_output(data)
|
|
||||||
if not validation[0]:
|
|
||||||
if allow_warning:
|
|
||||||
logging.warning(
|
|
||||||
f"{self.ip}: API Command Error: {command}: {validation[1]}"
|
|
||||||
)
|
|
||||||
raise APIError(validation[1])
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
async def send_privileged_command(self, *args, **kwargs) -> dict:
|
|
||||||
return await self.send_command(*args, **kwargs)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _validate_command_output(data: dict) -> tuple:
|
def _validate_command_output(data: dict) -> tuple:
|
||||||
# check if the data returned is correct or an error
|
# check if the data returned is correct or an error
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
port: The port to reference the API on. Default is 4028.
|
port: The port to reference the API on. Default is 4028.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip: str, port: int = 4028) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0", port: int = 4028) -> None:
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port=port)
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
async def multicommand(
|
async def multicommand(
|
||||||
self, *commands: str, allow_warning: bool = True
|
self, *commands: str, allow_warning: bool = True
|
||||||
) -> dict:
|
) -> dict:
|
||||||
logging.debug(f"{self.ip}: Sending multicommand: {[*commands]}")
|
|
||||||
# make sure we can actually run each command, otherwise they will fail
|
# make sure we can actually run each command, otherwise they will fail
|
||||||
commands = self._check_commands(*commands)
|
commands = self._check_commands(*commands)
|
||||||
# standard multicommand format is "command1+command2"
|
# standard multicommand format is "command1+command2"
|
||||||
@@ -49,9 +49,8 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
try:
|
try:
|
||||||
data = await self.send_command(command, allow_warning=allow_warning)
|
data = await self.send_command(command, allow_warning=allow_warning)
|
||||||
except APIError:
|
except APIError:
|
||||||
logging.debug(f"{self.ip}: Handling X19 multicommand.")
|
logging.debug(f"{self} - (Multicommand) - Handling X19 multicommand.")
|
||||||
data = await self._x19_multicommand(*command.split("+"))
|
data = await self._x19_multicommand(*command.split("+"))
|
||||||
logging.debug(f"{self.ip}: Received multicommand data.")
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def _x19_multicommand(self, *commands):
|
async def _x19_multicommand(self, *commands):
|
||||||
@@ -65,7 +64,7 @@ class BMMinerAPI(BaseMinerAPI):
|
|||||||
except APIError as e:
|
except APIError as e:
|
||||||
raise APIError(e)
|
raise APIError(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"{self.ip}: API Multicommand Error: {e}")
|
logging.warning(f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def version(self) -> dict:
|
async def version(self) -> dict:
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ class BOSMinerAPI(BaseMinerAPI):
|
|||||||
port: The port to reference the API on. Default is 4028.
|
port: The port to reference the API on. Default is 4028.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip: str, port: int = 4028):
|
def __init__(self, ip: str, api_ver: str = "1.0.0", port: int = 4028) -> None:
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port=port)
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
|
|
||||||
async def asccount(self) -> dict:
|
async def asccount(self) -> dict:
|
||||||
"""Get data on the number of ASC devices and their info.
|
"""Get data on the number of ASC devices and their info.
|
||||||
@@ -166,7 +168,7 @@ class BOSMinerAPI(BaseMinerAPI):
|
|||||||
return await self.send_command("estats")
|
return await self.send_command("estats")
|
||||||
|
|
||||||
async def check(self, command: str) -> dict:
|
async def check(self, command: str) -> dict:
|
||||||
"""Check if the command command exists in BOSMiner.
|
"""Check if the command `command` exists in BOSMiner.
|
||||||
<details>
|
<details>
|
||||||
<summary>Expand</summary>
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -26,13 +27,14 @@ from passlib.handlers.md5_crypt import md5_crypt
|
|||||||
from pyasic.API import BaseMinerAPI
|
from pyasic.API import BaseMinerAPI
|
||||||
from pyasic.errors import APIError
|
from pyasic.errors import APIError
|
||||||
from pyasic.settings import PyasicSettings
|
from pyasic.settings import PyasicSettings
|
||||||
|
from pyasic.misc import api_min_version
|
||||||
|
|
||||||
### IMPORTANT ###
|
### IMPORTANT ###
|
||||||
# you need to change the password of the miners using the Whatsminer
|
# you need to change the password of the miners using the Whatsminer
|
||||||
# tool, then you can set them back to admin with this tool, but they
|
# tool, then you can set them back to admin with this tool, but they
|
||||||
# must be changed to something else and set back to admin with this
|
# must be changed to something else and set back to admin with this
|
||||||
# or the privileged API will not work using admin as the password. If
|
# or the privileged API will not work using admin as the password. If
|
||||||
# you change the password, you can pass that to the this class as pwd,
|
# you change the password, you can pass that to this class as pwd,
|
||||||
# or add it as the Whatsminer_pwd in the settings.toml file.
|
# or add it as the Whatsminer_pwd in the settings.toml file.
|
||||||
|
|
||||||
|
|
||||||
@@ -81,7 +83,7 @@ def _add_to_16(string: str) -> bytes:
|
|||||||
def parse_btminer_priviledge_data(token_data: dict, data: dict):
|
def parse_btminer_priviledge_data(token_data: dict, data: dict):
|
||||||
"""Parses data returned from the BTMiner privileged API.
|
"""Parses data returned from the BTMiner privileged API.
|
||||||
|
|
||||||
Parses data from the BTMiner privileged API using the the token
|
Parses data from the BTMiner privileged API using the token
|
||||||
from the API in an AES format.
|
from the API in an AES format.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -123,6 +125,7 @@ def create_privileged_cmd(token_data: dict, command: dict) -> bytes:
|
|||||||
Returns:
|
Returns:
|
||||||
The encrypted privileged command to be sent to the miner.
|
The encrypted privileged command to be sent to the miner.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"(Create Prilileged Command) - Creating Privileged Command")
|
||||||
# add token to command
|
# add token to command
|
||||||
command["token"] = token_data["host_sign"]
|
command["token"] = token_data["host_sign"]
|
||||||
# encode host_passwd data and get hexdigest
|
# encode host_passwd data and get hexdigest
|
||||||
@@ -178,25 +181,39 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
ip: str,
|
ip: str,
|
||||||
|
api_ver: str = "1.0.0",
|
||||||
port: int = 4028,
|
port: int = 4028,
|
||||||
pwd: str = PyasicSettings().global_whatsminer_password,
|
pwd: str = PyasicSettings().global_whatsminer_password,
|
||||||
):
|
):
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port)
|
||||||
self.pwd = pwd
|
self.pwd = pwd
|
||||||
self.current_token = None
|
self.current_token = None
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
|
|
||||||
async def send_privileged_command(
|
async def send_privileged_command(
|
||||||
self, command: Union[str, bytes], ignore_errors: bool = False, **kwargs
|
self, command: Union[str, bytes], ignore_errors: bool = False, timeout: int = 10, **kwargs
|
||||||
) -> dict:
|
) -> dict:
|
||||||
command = {"cmd": command}
|
logging.debug(f"{self} - (Send Privileged Command) - {command} " + f'with args {kwargs}' if len(kwargs) > 0 else '')
|
||||||
for kwarg in kwargs:
|
command = {"cmd": command, **kwargs}
|
||||||
if kwargs[kwarg]:
|
|
||||||
command[kwarg] = kwargs[kwarg]
|
|
||||||
|
|
||||||
token_data = await self.get_token()
|
token_data = await self.get_token()
|
||||||
enc_command = create_privileged_cmd(token_data, command)
|
enc_command = create_privileged_cmd(token_data, command)
|
||||||
|
|
||||||
data = await self._send_bytes(enc_command)
|
logging.debug(f"{self} - (Send Privileged Command) - Sending")
|
||||||
|
try:
|
||||||
|
data = await self._send_bytes(enc_command, timeout)
|
||||||
|
except (asyncio.CancelledError, asyncio.TimeoutError) as e:
|
||||||
|
if command['cmd'] in ['reboot', 'restart']:
|
||||||
|
logging.info(f"{self} - (reboot/restart) - Whatsminers currently break this. "
|
||||||
|
f"Ignoring exception. Command probably worked.")
|
||||||
|
# FAKING IT HERE
|
||||||
|
data = b'{"STATUS": "S", "When": 1670966423, "Code": 131, "Msg": "API command OK", "Description": "Reboot"}'
|
||||||
|
else:
|
||||||
|
raise APIError("No data was returned from the API.")
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
raise APIError("No data was returned from the API.")
|
||||||
data = self._load_api_data(data)
|
data = self._load_api_data(data)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -222,6 +239,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
An encoded token and md5 password, which are used for the privileged API.
|
An encoded token and md5 password, which are used for the privileged API.
|
||||||
</details>
|
</details>
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"{self} - (Get Token) - Getting token")
|
||||||
# get the token
|
# get the token
|
||||||
data = await self.send_command("get_token")
|
data = await self.send_command("get_token")
|
||||||
|
|
||||||
@@ -244,6 +262,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
"host_sign": host_sign,
|
"host_sign": host_sign,
|
||||||
"host_passwd_md5": host_passwd_md5,
|
"host_passwd_md5": host_passwd_md5,
|
||||||
}
|
}
|
||||||
|
logging.debug(f"{self} - (Get Token) - Gathered token data: {self.current_token}")
|
||||||
return self.current_token
|
return self.current_token
|
||||||
|
|
||||||
#### PRIVILEGED COMMANDS ####
|
#### PRIVILEGED COMMANDS ####
|
||||||
@@ -364,8 +383,8 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
self,
|
self,
|
||||||
auto: bool = True,
|
auto: bool = True,
|
||||||
color: str = "red",
|
color: str = "red",
|
||||||
period: int = 60,
|
period: int = 400,
|
||||||
duration: int = 20,
|
duration: int = 200,
|
||||||
start: int = 0,
|
start: int = 0,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Set the LED on the miner using the API.
|
"""Set the LED on the miner using the API.
|
||||||
@@ -387,7 +406,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
</details>
|
</details>
|
||||||
"""
|
"""
|
||||||
if auto:
|
if auto:
|
||||||
return await self.send_privileged_command("set_led", param=auto)
|
return await self.send_privileged_command("set_led", param="auto")
|
||||||
return await self.send_privileged_command(
|
return await self.send_privileged_command(
|
||||||
"set_led", color=color, period=period, duration=duration, start=start
|
"set_led", color=color, period=period, duration=duration, start=start
|
||||||
)
|
)
|
||||||
@@ -413,7 +432,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
# requires a file stream in bytes
|
# requires a file stream in bytes
|
||||||
return NotImplementedError
|
return NotImplementedError
|
||||||
|
|
||||||
async def reboot(self) -> dict:
|
async def reboot(self, timeout: int = 10) -> dict:
|
||||||
"""Reboot the miner using the API.
|
"""Reboot the miner using the API.
|
||||||
<details>
|
<details>
|
||||||
<summary>Expand</summary>
|
<summary>Expand</summary>
|
||||||
@@ -423,7 +442,12 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
A reply informing of the status of the reboot.
|
A reply informing of the status of the reboot.
|
||||||
</details>
|
</details>
|
||||||
"""
|
"""
|
||||||
return await self.send_privileged_command("reboot")
|
try:
|
||||||
|
d = await asyncio.wait_for(self.send_privileged_command("reboot"), timeout=timeout)
|
||||||
|
except (asyncio.CancelledError, asyncio.TimeoutError):
|
||||||
|
return {}
|
||||||
|
else:
|
||||||
|
return d
|
||||||
|
|
||||||
async def factory_reset(self) -> dict:
|
async def factory_reset(self) -> dict:
|
||||||
"""Reset the miner to factory defaults.
|
"""Reset the miner to factory defaults.
|
||||||
@@ -581,7 +605,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
return await self.send_privileged_command("set_hostname", hostname=hostname)
|
return await self.send_privileged_command("set_hostname", hostname=hostname)
|
||||||
|
|
||||||
async def set_power_pct(self, percent: int) -> dict:
|
async def set_power_pct(self, percent: int) -> dict:
|
||||||
"""Set the power percentage of the miner.
|
"""Set the power percentage of the miner based on current power. Used for temporary adjustment.
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Expand</summary>
|
<summary>Expand</summary>
|
||||||
@@ -641,6 +665,124 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
"pre_power_on", complete="false", msg=msg
|
"pre_power_on", complete="false", msg=msg
|
||||||
)
|
)
|
||||||
|
|
||||||
|
### ADDED IN V2.0.5 Whatsminer API ###
|
||||||
|
@api_min_version("2.0.5")
|
||||||
|
async def set_temp_offset(self, temp_offset: int):
|
||||||
|
"""Set the offset of miner hash board target temperature.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Set the offset of miner hash board target temperature, only works after
|
||||||
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
temp_offset: Target temperature offset.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting temp offset.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
"""
|
||||||
|
if not -30 < temp_offset < 0:
|
||||||
|
raise APIError(
|
||||||
|
f"Temp offset is outside of the allowed "
|
||||||
|
f"range. Please set a number between -30 and "
|
||||||
|
f"0."
|
||||||
|
)
|
||||||
|
|
||||||
|
return await self.send_privileged_command("set_temp_offset", temp_offset=temp_offset)
|
||||||
|
|
||||||
|
@api_min_version("2.0.5")
|
||||||
|
async def adjust_power_limit(self, power_limit: int):
|
||||||
|
"""Set the upper limit of the miner's power. Cannot be higher than the ordinary power of the machine.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Set the upper limit of the miner's power, only works after
|
||||||
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
The miner will reboot after this is set.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
power_limit: New power limit.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting power limit.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
"""
|
||||||
|
return await self.send_privileged_command("adjust_power_limit", power_limit=power_limit)
|
||||||
|
|
||||||
|
|
||||||
|
@api_min_version("2.0.5")
|
||||||
|
async def adjust_upfreq_speed(self, upfreq_speed: int):
|
||||||
|
"""Set the upfreq speed, 0 is the normal speed, 9 is the fastest speed.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Set the upfreq speed, 0 is the normal speed, 9 is the fastest speed, only works after
|
||||||
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
The faster the speed, the greater the final hash rate and power deviation, and the stability
|
||||||
|
may be impacted. Fast boot mode cannot be used at the same time.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
upfreq_speed: New upfreq speed.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting upfreq speed.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
if not 0 < upfreq_speed < 9:
|
||||||
|
raise APIError(
|
||||||
|
f"Upfreq speed is outside of the allowed "
|
||||||
|
f"range. Please set a number between 0 (Normal) and "
|
||||||
|
f"9 (Fastest)."
|
||||||
|
)
|
||||||
|
return await self.send_privileged_command("adjust_upfreq_speed", upfreq_speed=upfreq_speed)
|
||||||
|
|
||||||
|
@api_min_version("2.0.5")
|
||||||
|
async def set_poweroff_cool(self, poweroff_cool: bool):
|
||||||
|
"""Set whether to cool the machine when mining is stopped.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Set whether to cool the machine when mining is stopped, only works after
|
||||||
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
poweroff_cool: Whether to cool the miner during power off mode.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting power off cooling mode.
|
||||||
|
</details>
|
||||||
|
"""
|
||||||
|
|
||||||
|
return await self.send_privileged_command("set_poweroff_cool", poweroff_cool=int(poweroff_cool))
|
||||||
|
|
||||||
|
@api_min_version("2.0.5")
|
||||||
|
async def set_fan_zero_speed(self, fan_zero_speed: bool):
|
||||||
|
"""Sets whether the fan speed supports the lowest 0 speed.
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand</summary>
|
||||||
|
|
||||||
|
Sets whether the fan speed supports the lowest 0 speed, only works after
|
||||||
|
changing the password of the miner using the Whatsminer tool.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
fan_zero_speed: Whether the fan is allowed to support 0 speed.
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
A reply informing of the status of setting fan minimum speed.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
"""
|
||||||
|
return await self.send_privileged_command("set_fan_zero_speed", fan_zero_speed=int(fan_zero_speed))
|
||||||
|
|
||||||
|
|
||||||
#### END privileged COMMANDS ####
|
#### END privileged COMMANDS ####
|
||||||
|
|
||||||
async def summary(self) -> dict:
|
async def summary(self) -> dict:
|
||||||
@@ -767,6 +909,7 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
"""
|
"""
|
||||||
return await self.send_command("get_miner_info", allow_warning=False)
|
return await self.send_command("get_miner_info", allow_warning=False)
|
||||||
|
|
||||||
|
@api_min_version("2.0.1")
|
||||||
async def get_error_code(self) -> dict:
|
async def get_error_code(self) -> dict:
|
||||||
"""Get a list of error codes from the miner.
|
"""Get a list of error codes from the miner.
|
||||||
|
|
||||||
|
|||||||
@@ -35,24 +35,24 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
port: The port to reference the API on. Default is 4028.
|
port: The port to reference the API on. Default is 4028.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip: str, port: int = 4028):
|
def __init__(self, ip: str, api_ver: str = "1.0.0", port: int = 4028):
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port)
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
|
|
||||||
async def multicommand(
|
async def multicommand(
|
||||||
self, *commands: str, allow_warning: bool = True
|
self, *commands: str, allow_warning: bool = True
|
||||||
) -> dict:
|
) -> dict:
|
||||||
logging.debug(f"{self.ip}: Sending multicommand: {[*commands]}")
|
|
||||||
# make sure we can actually run each command, otherwise they will fail
|
# make sure we can actually run each command, otherwise they will fail
|
||||||
commands = self._check_commands(*commands)
|
commands = self._check_commands(*commands)
|
||||||
# standard multicommand format is "command1+command2"
|
# standard multicommand format is "command1+command2"
|
||||||
# doesnt work for S19 which uses the backup _x19_multicommand
|
# doesn't work for S19 which uses the backup _x19_multicommand
|
||||||
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)
|
||||||
except APIError:
|
except APIError:
|
||||||
logging.debug(f"{self.ip}: Handling X19 multicommand.")
|
logging.debug(f"{self} - (Multicommand) - Handling X19 multicommand.")
|
||||||
data = await self._x19_multicommand(*command.split("+"))
|
data = await self._x19_multicommand(*command.split("+"))
|
||||||
logging.debug(f"{self.ip}: Received multicommand data.")
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def _x19_multicommand(self, *commands):
|
async def _x19_multicommand(self, *commands):
|
||||||
@@ -66,7 +66,7 @@ class CGMinerAPI(BaseMinerAPI):
|
|||||||
except APIError as e:
|
except APIError as e:
|
||||||
raise APIError(e)
|
raise APIError(e)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning(f"{self.ip}: API Multicommand Error: {e}")
|
logging.warning(f"{self} - ([Hidden] X19 Multicommand) - API Command Error {e}")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
async def version(self) -> dict:
|
async def version(self) -> dict:
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ from pyasic.API import BaseMinerAPI
|
|||||||
class UnknownAPI(BaseMinerAPI):
|
class UnknownAPI(BaseMinerAPI):
|
||||||
"""An abstraction of an API for a miner which is unknown.
|
"""An abstraction of an API for a miner which is unknown.
|
||||||
|
|
||||||
This class is designed to try to be a intersection of as many miner APIs
|
This class is designed to try to be an intersection of as many miner APIs
|
||||||
and API commands as possible (API ⋂ API), to ensure that it can be used
|
and API commands as possible (API ⋂ API), to ensure that it can be used
|
||||||
with as many APIs as possible.
|
with as many APIs as possible.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, ip, port=4028):
|
def __init__(self, ip, api_ver: str = "1.0.0", port: int = 4028):
|
||||||
super().__init__(ip, port)
|
super().__init__(ip, port)
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
|
|
||||||
async def asccount(self) -> dict:
|
async def asccount(self) -> dict:
|
||||||
return await self.send_command("asccount")
|
return await self.send_command("asccount")
|
||||||
|
|||||||
@@ -13,11 +13,12 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
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 List, Literal
|
from typing import Dict, List, Literal
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
import yaml
|
import yaml
|
||||||
@@ -193,7 +194,7 @@ class _PoolGroup:
|
|||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> List[dict]:
|
def as_wm(self, user_suffix: str = None) -> List[dict]:
|
||||||
"""Convert the data in this class to a list usable by an 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.
|
||||||
@@ -277,6 +278,7 @@ class MinerConfig:
|
|||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
"""Convert the data in this class to a dict."""
|
"""Convert the data in this class to a dict."""
|
||||||
|
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 data_dict[key] is None:
|
if data_dict[key] is None:
|
||||||
@@ -285,10 +287,12 @@ class MinerConfig:
|
|||||||
|
|
||||||
def as_toml(self) -> str:
|
def as_toml(self) -> str:
|
||||||
"""Convert the data in this class to toml."""
|
"""Convert the data in this class to toml."""
|
||||||
|
logging.debug(f"MinerConfig - (To TOML) - Dumping TOML config")
|
||||||
return toml.dumps(self.as_dict())
|
return toml.dumps(self.as_dict())
|
||||||
|
|
||||||
def as_yaml(self) -> str:
|
def as_yaml(self) -> str:
|
||||||
"""Convert the data in this class to yaml."""
|
"""Convert the data in this class to yaml."""
|
||||||
|
logging.debug(f"MinerConfig - (To YAML) - Dumping YAML config")
|
||||||
return yaml.dump(self.as_dict(), sort_keys=False)
|
return yaml.dump(self.as_dict(), sort_keys=False)
|
||||||
|
|
||||||
def from_raw(self, data: dict):
|
def from_raw(self, data: dict):
|
||||||
@@ -298,6 +302,7 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
data: The raw config data to convert.
|
data: The raw config data to convert.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (From Raw) - Loading raw config")
|
||||||
pool_groups = []
|
pool_groups = []
|
||||||
for key in data.keys():
|
for key in data.keys():
|
||||||
if key == "pools":
|
if key == "pools":
|
||||||
@@ -360,6 +365,12 @@ class MinerConfig:
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def from_api(self, pools: list):
|
def from_api(self, pools: list):
|
||||||
|
"""Convert list output from the `AnyMiner.api.pools()` command into a usable data and save it to this class.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
pools: The list of pool data to convert.
|
||||||
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (From API) - Loading API config")
|
||||||
_pools = []
|
_pools = []
|
||||||
for pool in pools:
|
for pool in pools:
|
||||||
url = pool.get("URL")
|
url = pool.get("URL")
|
||||||
@@ -374,6 +385,7 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
data: The dict config data to convert.
|
data: The dict config data to convert.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (From Dict) - Loading Dict config")
|
||||||
pool_groups = []
|
pool_groups = []
|
||||||
for group in data["pool_groups"]:
|
for group in data["pool_groups"]:
|
||||||
pool_groups.append(_PoolGroup().from_dict(group))
|
pool_groups.append(_PoolGroup().from_dict(group))
|
||||||
@@ -389,6 +401,7 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
data: The toml config data to convert.
|
data: The toml config data to convert.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (From TOML) - Loading TOML config")
|
||||||
return self.from_dict(toml.loads(data))
|
return self.from_dict(toml.loads(data))
|
||||||
|
|
||||||
def from_yaml(self, data: str):
|
def from_yaml(self, data: str):
|
||||||
@@ -397,15 +410,17 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
data: The yaml config data to convert.
|
data: The yaml config data to convert.
|
||||||
"""
|
"""
|
||||||
|
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) -> List[dict]:
|
def as_wm(self, user_suffix: str = None) -> Dict[str, int]:
|
||||||
"""Convert the data in this class to a config usable by an Whatsminer device.
|
"""Convert the data in this class to a config usable by a Whatsminer device.
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
return self.pool_groups[0].as_wm(user_suffix=user_suffix)
|
logging.debug(f"MinerConfig - (As Whatsminer) - Generating Whatsminer config")
|
||||||
|
return {"pools": self.pool_groups[0].as_wm(user_suffix=user_suffix), "wattage": self.autotuning_wattage}
|
||||||
|
|
||||||
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 config usable by an Innosilicon device.
|
"""Convert the data in this class to a config usable by an Innosilicon device.
|
||||||
@@ -413,6 +428,7 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
|
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) -> str:
|
||||||
@@ -421,6 +437,7 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (As X19) - Generating X19 config")
|
||||||
cfg = {
|
cfg = {
|
||||||
"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,
|
||||||
@@ -444,6 +461,7 @@ class MinerConfig:
|
|||||||
Parameters:
|
Parameters:
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (As Avalon) - Generating AvalonMiner config")
|
||||||
cfg = self.pool_groups[0].as_avalon(user_suffix=user_suffix)
|
cfg = self.pool_groups[0].as_avalon(user_suffix=user_suffix)
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
@@ -454,6 +472,7 @@ class MinerConfig:
|
|||||||
model: The model of the miner to be used in the format portion of the config.
|
model: The model of the miner to be used in the format portion of the config.
|
||||||
user_suffix: The suffix to append to username.
|
user_suffix: The suffix to append to username.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerConfig - (As BOS) - Generating BOSMiner config")
|
||||||
cfg = {
|
cfg = {
|
||||||
"format": {
|
"format": {
|
||||||
"version": "1.2+",
|
"version": "1.2+",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
from dataclasses import asdict, dataclass, field, fields
|
from dataclasses import asdict, dataclass, field, fields
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@@ -41,6 +42,9 @@ class MinerData:
|
|||||||
ip: The IP of the miner as a str.
|
ip: The IP of the miner as a str.
|
||||||
datetime: The time and date this data was generated.
|
datetime: The time and date this data was generated.
|
||||||
model: The model of the miner as a str.
|
model: The model of the miner as a str.
|
||||||
|
make: The make of the miner as a str.
|
||||||
|
api_ver: The current api version on the miner as a str.
|
||||||
|
fw_ver: The current firmware version on the miner as a str.
|
||||||
hostname: The network hostname of the miner as a str.
|
hostname: The network hostname of the miner as a str.
|
||||||
hashrate: The hashrate of the miner in TH/s as a float.
|
hashrate: The hashrate of the miner in TH/s as a float.
|
||||||
left_board_hashrate: The hashrate of the left board of the miner in TH/s as a float.
|
left_board_hashrate: The hashrate of the left board of the miner in TH/s as a float.
|
||||||
@@ -82,6 +86,9 @@ class MinerData:
|
|||||||
datetime: datetime = None
|
datetime: datetime = None
|
||||||
mac: str = "00:00:00:00:00:00"
|
mac: str = "00:00:00:00:00:00"
|
||||||
model: str = "Unknown"
|
model: str = "Unknown"
|
||||||
|
make: str = "Unknown"
|
||||||
|
api_ver: str = "Unknown"
|
||||||
|
fw_ver: str = "Unknown"
|
||||||
hostname: str = "Unknown"
|
hostname: str = "Unknown"
|
||||||
hashrate: float = 0
|
hashrate: float = 0
|
||||||
hashboards: List[HashBoard] = field(default_factory=list)
|
hashboards: List[HashBoard] = field(default_factory=list)
|
||||||
@@ -122,6 +129,7 @@ class MinerData:
|
|||||||
fault_light: Union[bool, None] = None
|
fault_light: Union[bool, None] = None
|
||||||
efficiency: int = field(init=False)
|
efficiency: int = field(init=False)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls):
|
def fields(cls):
|
||||||
return [f.name for f in fields(cls)]
|
return [f.name for f in fields(cls)]
|
||||||
@@ -371,6 +379,7 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A dictionary version of this class.
|
A dictionary version of this class.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerData - (To Dict) - Dumping Dict data")
|
||||||
return asdict(self)
|
return asdict(self)
|
||||||
|
|
||||||
def as_json(self) -> str:
|
def as_json(self) -> str:
|
||||||
@@ -379,6 +388,7 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A JSON version of this class.
|
A JSON version of this class.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerData - (To JSON) - Dumping JSON data")
|
||||||
data = self.asdict()
|
data = self.asdict()
|
||||||
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
||||||
return json.dumps(data)
|
return json.dumps(data)
|
||||||
@@ -389,6 +399,7 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A CSV version of this class with no headers.
|
A CSV version of this class with no headers.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerData - (To CSV) - Dumping CSV data")
|
||||||
data = self.asdict()
|
data = self.asdict()
|
||||||
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
||||||
errs = []
|
errs = []
|
||||||
@@ -407,6 +418,7 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A influxdb line protocol version of this class.
|
A influxdb line protocol version of this class.
|
||||||
"""
|
"""
|
||||||
|
logging.debug(f"MinerData - (To InfluxDB) - Dumping InfluxDB data")
|
||||||
tag_data = [measurement_name]
|
tag_data = [measurement_name]
|
||||||
field_data = []
|
field_data = []
|
||||||
|
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ from .bosminer import BOSMiner
|
|||||||
from .btminer import BTMiner
|
from .btminer import BTMiner
|
||||||
from .cgminer import CGMiner
|
from .cgminer import CGMiner
|
||||||
from .hiveon import Hiveon
|
from .hiveon import Hiveon
|
||||||
|
from .cgminer_avalon import CGMinerAvalon
|
||||||
|
|||||||
@@ -27,11 +27,12 @@ from pyasic.settings import PyasicSettings
|
|||||||
class BMMiner(BaseMiner):
|
class BMMiner(BaseMiner):
|
||||||
"""Base handler for BMMiner based miners."""
|
"""Base handler for BMMiner based miners."""
|
||||||
|
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip)
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
self.api = BMMinerAPI(ip)
|
self.api = BMMinerAPI(ip, api_ver)
|
||||||
self.api_type = "BMMiner"
|
self.api_type = "BMMiner"
|
||||||
|
self.api_ver = api_ver
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = "admin"
|
self.pwd = "admin"
|
||||||
|
|
||||||
@@ -167,6 +168,24 @@ class BMMiner(BaseMiner):
|
|||||||
async def fault_light_on(self) -> bool:
|
async def fault_light_on(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def get_version(self) -> dict:
|
||||||
|
"""Get miner firmware version.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner api & firmware version or None.
|
||||||
|
"""
|
||||||
|
# check if version is cached
|
||||||
|
if self.fw_ver and self.api_ver:
|
||||||
|
logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||||
|
return {'api_ver': self.api_ver,'fw_ver': self.fw_ver}
|
||||||
|
# Now get the API version
|
||||||
|
version = await self.api.version()
|
||||||
|
self.api_ver = version['VERSION'][0]['API']
|
||||||
|
self.fw_ver = version['VERSION'][0]['CompileTime']
|
||||||
|
self.api.api_ver = self.api_ver
|
||||||
|
return {'api_ver': self.api_ver,'fw_ver': self.fw_ver}
|
||||||
|
|
||||||
|
|
||||||
async def get_errors(self) -> List[MinerErrorData]:
|
async def get_errors(self) -> List[MinerErrorData]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -211,6 +230,13 @@ class BMMiner(BaseMiner):
|
|||||||
if mac:
|
if mac:
|
||||||
data.mac = mac
|
data.mac = mac
|
||||||
|
|
||||||
|
if self.make:
|
||||||
|
data.make = self.make
|
||||||
|
|
||||||
|
await self.get_version()
|
||||||
|
data.api_ver = self.api_ver
|
||||||
|
data.fw_ver = self.fw_ver
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
for error in errors:
|
for error in errors:
|
||||||
data.errors.append(error)
|
data.errors.append(error)
|
||||||
@@ -349,3 +375,7 @@ class BMMiner(BaseMiner):
|
|||||||
data.pool_split = str(quota)
|
data.pool_split = str(quota)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
|
|
||||||
import toml
|
|
||||||
import httpx
|
import httpx
|
||||||
|
import toml
|
||||||
|
|
||||||
from pyasic.API.bosminer import BOSMinerAPI
|
from pyasic.API.bosminer import BOSMinerAPI
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
@@ -30,11 +30,12 @@ from pyasic.settings import PyasicSettings
|
|||||||
|
|
||||||
|
|
||||||
class BOSMiner(BaseMiner):
|
class BOSMiner(BaseMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip)
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
self.api = BOSMinerAPI(ip)
|
self.api = BOSMinerAPI(ip, api_ver)
|
||||||
self.api_type = "BOSMiner"
|
self.api_type = "BOSMiner"
|
||||||
|
self.api_ver = api_ver
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = "admin"
|
self.pwd = "admin"
|
||||||
self.config = None
|
self.config = None
|
||||||
@@ -228,21 +229,25 @@ class BOSMiner(BaseMiner):
|
|||||||
logging.warning(f"Failed to get model for miner: {self}")
|
logging.warning(f"Failed to get model for miner: {self}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_version(self) -> Union[str, None]:
|
async def get_version(self) -> Union[dict, None]:
|
||||||
"""Get miner firmware version.
|
"""Get miner firmware version.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Miner firmware version or None.
|
Miner api & firmware version or None.
|
||||||
"""
|
"""
|
||||||
# check if version is cached
|
# check if version is cached
|
||||||
if self.version:
|
if self.fw_ver and self.api_ver:
|
||||||
logging.debug(f"Found version for {self.ip}: {self.version}")
|
logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||||
return self.version
|
return {'api_ver': self.api_ver,'fw_ver': self.fw_ver}
|
||||||
version_data = None
|
version_data = None
|
||||||
# try to get data from graphql
|
# try to get data from graphql
|
||||||
data = await self.send_graphql_query("{bos{info{version{full}}}}")
|
data = await self.send_graphql_query("{bos{info{version{full}}}}")
|
||||||
if data:
|
if data:
|
||||||
version_data = data["bos"]["info"]["version"]["full"]
|
try:
|
||||||
|
version_data = data["bos"]["info"]["version"]["full"]
|
||||||
|
except KeyError:
|
||||||
|
version_data = data["data"]["bos"]["info"]["version"]["full"]
|
||||||
|
|
||||||
|
|
||||||
if not version_data:
|
if not version_data:
|
||||||
# try version data file
|
# try version data file
|
||||||
@@ -250,9 +255,14 @@ class BOSMiner(BaseMiner):
|
|||||||
|
|
||||||
# if we get the version data, parse it
|
# if we get the version data, parse it
|
||||||
if version_data:
|
if version_data:
|
||||||
self.version = version_data.split("-")[5]
|
self.fw_ver = version_data.split("-")[5]
|
||||||
logging.debug(f"Found version for {self.ip}: {self.version}")
|
logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||||
return self.version
|
|
||||||
|
# Now get the API version
|
||||||
|
version = await self.api.version()
|
||||||
|
self.api_ver = version['VERSION'][0]['API']
|
||||||
|
self.api.api_ver = self.api_ver
|
||||||
|
return {'api_ver': self.api_ver,'fw_ver': self.fw_ver}
|
||||||
|
|
||||||
# if we fail to get version, log a failed attempt
|
# if we fail to get version, log a failed attempt
|
||||||
logging.warning(f"Failed to get model for miner: {self}")
|
logging.warning(f"Failed to get model for miner: {self}")
|
||||||
@@ -327,7 +337,7 @@ class BOSMiner(BaseMiner):
|
|||||||
if board["Status"] not in [
|
if board["Status"] not in [
|
||||||
"Stable",
|
"Stable",
|
||||||
"Testing performance profile",
|
"Testing performance profile",
|
||||||
"Tuning individual chips"
|
"Tuning individual chips",
|
||||||
]:
|
]:
|
||||||
_error = board["Status"].split(" {")[0]
|
_error = board["Status"].split(" {")[0]
|
||||||
_error = _error[0].lower() + _error[1:]
|
_error = _error[0].lower() + _error[1:]
|
||||||
@@ -359,6 +369,7 @@ class BOSMiner(BaseMiner):
|
|||||||
model = await self.get_model()
|
model = await self.get_model()
|
||||||
hostname = await self.get_hostname()
|
hostname = await self.get_hostname()
|
||||||
mac = await self.get_mac()
|
mac = await self.get_mac()
|
||||||
|
await self.get_version()
|
||||||
|
|
||||||
if model:
|
if model:
|
||||||
data.model = model
|
data.model = model
|
||||||
@@ -369,6 +380,11 @@ class BOSMiner(BaseMiner):
|
|||||||
if mac:
|
if mac:
|
||||||
data.mac = mac
|
data.mac = mac
|
||||||
|
|
||||||
|
data.api_ver = self.api_ver
|
||||||
|
data.fw_ver = self.fw_ver
|
||||||
|
|
||||||
|
data.make = self.make
|
||||||
|
|
||||||
data.fault_light = await self.check_light()
|
data.fault_light = await self.check_light()
|
||||||
|
|
||||||
miner_data = None
|
miner_data = None
|
||||||
@@ -471,6 +487,16 @@ class BOSMiner(BaseMiner):
|
|||||||
data.pool_2_user = pool_2_user
|
data.pool_2_user = pool_2_user
|
||||||
|
|
||||||
if quota:
|
if quota:
|
||||||
|
if not quota == "0":
|
||||||
|
cfg = await self.get_config()
|
||||||
|
if cfg:
|
||||||
|
if len(cfg.pool_groups) > 1:
|
||||||
|
quota = (
|
||||||
|
str(cfg.pool_groups[0].quota)
|
||||||
|
+ "/"
|
||||||
|
+ str(cfg.pool_groups[1].quota)
|
||||||
|
)
|
||||||
|
|
||||||
data.pool_split = str(quota)
|
data.pool_split = str(quota)
|
||||||
|
|
||||||
if tunerstatus:
|
if tunerstatus:
|
||||||
@@ -548,17 +574,29 @@ class BOSMiner(BaseMiner):
|
|||||||
if not query_data:
|
if not query_data:
|
||||||
return None
|
return None
|
||||||
query_data = query_data["data"]
|
query_data = query_data["data"]
|
||||||
|
if not query_data:
|
||||||
|
return None
|
||||||
|
|
||||||
data.mac = await self.get_mac()
|
data.mac = await self.get_mac()
|
||||||
data.model = await self.get_model()
|
data.model = await self.get_model()
|
||||||
|
await self.get_version()
|
||||||
|
|
||||||
|
data.api_ver = self.api_ver
|
||||||
|
data.fw_ver = self.fw_ver
|
||||||
|
data.make = self.make
|
||||||
|
|
||||||
if query_data.get("bos"):
|
if query_data.get("bos"):
|
||||||
if query_data["bos"].get("hostname"):
|
if query_data["bos"].get("hostname"):
|
||||||
data.hostname = query_data["bos"]["hostname"]
|
data.hostname = query_data["bos"]["hostname"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if query_data["bosminer"]["info"]["workSolver"]["realHashrate"].get("mhs1M"):
|
if query_data["bosminer"]["info"]["workSolver"]["realHashrate"].get(
|
||||||
|
"mhs1M"
|
||||||
|
):
|
||||||
data.hashrate = round(
|
data.hashrate = round(
|
||||||
query_data["bosminer"]["info"]["workSolver"]["realHashrate"]["mhs1M"]
|
query_data["bosminer"]["info"]["workSolver"]["realHashrate"][
|
||||||
|
"mhs1M"
|
||||||
|
]
|
||||||
/ 1000000,
|
/ 1000000,
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
@@ -569,9 +607,13 @@ class BOSMiner(BaseMiner):
|
|||||||
if query_data.get("bosminer"):
|
if query_data.get("bosminer"):
|
||||||
if query_data["bosminer"].get("info"):
|
if query_data["bosminer"].get("info"):
|
||||||
if query_data["bosminer"]["info"].get("workSolver"):
|
if query_data["bosminer"]["info"].get("workSolver"):
|
||||||
boards = query_data["bosminer"]["info"]["workSolver"].get("childSolvers")
|
boards = query_data["bosminer"]["info"]["workSolver"].get(
|
||||||
|
"childSolvers"
|
||||||
|
)
|
||||||
if boards:
|
if boards:
|
||||||
offset = 6 if int(boards[0]["name"]) in [6, 7, 8] else int(boards[0]["name"])
|
offset = (
|
||||||
|
6 if int(boards[0]["name"]) in [6, 7, 8] else int(boards[0]["name"])
|
||||||
|
)
|
||||||
for hb in boards:
|
for hb in boards:
|
||||||
_id = int(hb["name"]) - offset
|
_id = int(hb["name"]) - offset
|
||||||
|
|
||||||
@@ -598,24 +640,33 @@ class BOSMiner(BaseMiner):
|
|||||||
if hb["tuner"]["statusMessages"][0] not in [
|
if hb["tuner"]["statusMessages"][0] not in [
|
||||||
"Stable",
|
"Stable",
|
||||||
"Testing performance profile",
|
"Testing performance profile",
|
||||||
"Tuning individual chips"
|
"Tuning individual chips",
|
||||||
]:
|
]:
|
||||||
data.errors.append(
|
data.errors.append(
|
||||||
BraiinsOSError(f"Slot {_id} {hb['tuner']['statusMessages'][0]}")
|
BraiinsOSError(
|
||||||
|
f"Slot {_id} {hb['tuner']['statusMessages'][0]}"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
data.wattage = query_data["bosminer"]["info"]["workSolver"]["power"]["approxConsumptionW"]
|
data.wattage = query_data["bosminer"]["info"]["workSolver"]["power"][
|
||||||
|
"approxConsumptionW"
|
||||||
|
]
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
data.wattage = 0
|
data.wattage = 0
|
||||||
try:
|
try:
|
||||||
data.wattage_limit = query_data["bosminer"]["info"]["workSolver"]["power"]["limitW"]
|
data.wattage_limit = query_data["bosminer"]["info"]["workSolver"]["power"][
|
||||||
|
"limitW"
|
||||||
|
]
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
for n in range(self.fan_count):
|
for n in range(self.fan_count):
|
||||||
try:
|
try:
|
||||||
setattr(data, f"fan_{n + 1}", query_data["bosminer"]["info"]["fans"][n]["rpm"])
|
setattr(
|
||||||
|
data,
|
||||||
|
f"fan_{n + 1}",
|
||||||
|
query_data["bosminer"]["info"]["fans"][n]["rpm"],
|
||||||
|
)
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -630,7 +681,11 @@ class BOSMiner(BaseMiner):
|
|||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
data.pool_1_url = groups[0]["pools"][0]["url"]
|
data.pool_1_url = (
|
||||||
|
groups[0]["pools"][0]["url"]
|
||||||
|
.replace("stratum+tcp://", "")
|
||||||
|
.replace("stratum2+tcp://", "")
|
||||||
|
)
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
@@ -638,7 +693,11 @@ class BOSMiner(BaseMiner):
|
|||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
data.pool_2_url = groups[0]["pools"][1]["url"]
|
data.pool_2_url = (
|
||||||
|
groups[0]["pools"][1]["url"]
|
||||||
|
.replace("stratum+tcp://", "")
|
||||||
|
.replace("stratum2+tcp://", "")
|
||||||
|
)
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
data.quota = 0
|
data.quota = 0
|
||||||
@@ -648,7 +707,11 @@ class BOSMiner(BaseMiner):
|
|||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
data.pool_1_url = groups[0]["pools"][0]["url"]
|
data.pool_1_url = (
|
||||||
|
groups[0]["pools"][0]["url"]
|
||||||
|
.replace("stratum+tcp://", "")
|
||||||
|
.replace("stratum2+tcp://", "")
|
||||||
|
)
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
@@ -656,11 +719,19 @@ class BOSMiner(BaseMiner):
|
|||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
data.pool_2_url = groups[1]["pools"][0]["url"]
|
data.pool_2_url = (
|
||||||
|
groups[1]["pools"][0]["url"]
|
||||||
|
.replace("stratum+tcp://", "")
|
||||||
|
.replace("stratum2+tcp://", "")
|
||||||
|
)
|
||||||
except (TypeError, KeyError, ValueError, IndexError):
|
except (TypeError, KeyError, ValueError, IndexError):
|
||||||
pass
|
pass
|
||||||
if groups[0]["strategy"].get("quota"):
|
if groups[0]["strategy"].get("quota"):
|
||||||
data.quota = str(groups[0]["strategy"]["quota"]) + "/" + str(groups[1]["strategy"]["quota"])
|
data.pool_split = (
|
||||||
|
str(groups[0]["strategy"]["quota"])
|
||||||
|
+ "/"
|
||||||
|
+ str(groups[1]["strategy"]["quota"])
|
||||||
|
)
|
||||||
|
|
||||||
data.fault_light = await self.check_light()
|
data.fault_light = await self.check_light()
|
||||||
|
|
||||||
@@ -669,3 +740,14 @@ class BOSMiner(BaseMiner):
|
|||||||
async def get_mac(self):
|
async def get_mac(self):
|
||||||
result = await self.send_ssh_command("cat /sys/class/net/eth0/address")
|
result = await self.send_ssh_command("cat /sys/class/net/eth0/address")
|
||||||
return result.upper().strip()
|
return result.upper().strip()
|
||||||
|
|
||||||
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
|
try:
|
||||||
|
cfg = await self.get_config()
|
||||||
|
cfg.autotuning_wattage = wattage
|
||||||
|
await self.send_config(cfg)
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"{self} set_power_limit: {e}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ from pyasic.miners.base import BaseMiner
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerOld(BaseMiner):
|
class BOSMinerOld(BaseMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip)
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
self.api = BOSMinerAPI(ip)
|
self.api = BOSMinerAPI(ip, api_ver)
|
||||||
self.api_type = "BOSMiner"
|
self.api_type = "BOSMiner"
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = "admin"
|
self.pwd = "admin"
|
||||||
@@ -106,3 +106,6 @@ class BOSMinerOld(BaseMiner):
|
|||||||
|
|
||||||
async def get_data(self, **kwargs) -> MinerData:
|
async def get_data(self, **kwargs) -> MinerData:
|
||||||
return MinerData(ip=str(self.ip))
|
return MinerData(ip=str(self.ip))
|
||||||
|
|
||||||
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -26,11 +26,12 @@ from pyasic.settings import PyasicSettings
|
|||||||
|
|
||||||
|
|
||||||
class BTMiner(BaseMiner):
|
class BTMiner(BaseMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip)
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
self.api = BTMinerAPI(ip)
|
self.api = BTMinerAPI(ip, api_ver)
|
||||||
self.api_type = "BTMiner"
|
self.api_type = "BTMiner"
|
||||||
|
self.api_ver = api_ver
|
||||||
|
|
||||||
async def get_model(self) -> Union[str, None]:
|
async def get_model(self) -> Union[str, None]:
|
||||||
"""Get miner model.
|
"""Get miner model.
|
||||||
@@ -137,12 +138,14 @@ class BTMiner(BaseMiner):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def fault_light_on(self) -> bool:
|
async def fault_light_on(self, flash: list = []) -> bool:
|
||||||
|
if flash == []:
|
||||||
|
# If no flash pattern is provided, use a red-green semi-slow alternating flash
|
||||||
|
flash = [{"color": "green", "start":0, "period":400, "duration":200},
|
||||||
|
{"color": "red", "start":200, "period":400, "duration":200}]
|
||||||
try:
|
try:
|
||||||
data = await self.api.set_led(auto=False)
|
for x in flash:
|
||||||
await self.api.set_led(
|
data = await self.api.set_led(auto=False, **x)
|
||||||
auto=False, color="green", start=0, period=1, duration=0
|
|
||||||
)
|
|
||||||
except APIError:
|
except APIError:
|
||||||
return False
|
return False
|
||||||
if data:
|
if data:
|
||||||
@@ -196,14 +199,20 @@ class BTMiner(BaseMiner):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def stop_mining(self) -> bool:
|
async def stop_mining(self) -> bool:
|
||||||
data = await self.api.power_off(respbefore=True)
|
try:
|
||||||
|
data = await self.api.power_off(respbefore=True)
|
||||||
|
except APIError:
|
||||||
|
return False
|
||||||
if data.get("Msg"):
|
if data.get("Msg"):
|
||||||
if data["Msg"] == "API command OK":
|
if data["Msg"] == "API command OK":
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def resume_mining(self) -> bool:
|
async def resume_mining(self) -> bool:
|
||||||
data = await self.api.power_on()
|
try:
|
||||||
|
data = await self.api.power_on()
|
||||||
|
except APIError:
|
||||||
|
return False
|
||||||
if data.get("Msg"):
|
if data.get("Msg"):
|
||||||
if data["Msg"] == "API command OK":
|
if data["Msg"] == "API command OK":
|
||||||
return True
|
return True
|
||||||
@@ -211,31 +220,45 @@ class BTMiner(BaseMiner):
|
|||||||
|
|
||||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||||
conf = config.as_wm(user_suffix=user_suffix)
|
conf = config.as_wm(user_suffix=user_suffix)
|
||||||
|
pools_conf = conf["pools"]
|
||||||
|
|
||||||
await self.api.update_pools(
|
await self.api.update_pools(
|
||||||
conf[0]["url"],
|
pools_conf[0]["url"],
|
||||||
conf[0]["user"],
|
pools_conf[0]["user"],
|
||||||
conf[0]["pass"],
|
pools_conf[0]["pass"],
|
||||||
conf[1]["url"],
|
pools_conf[1]["url"],
|
||||||
conf[1]["user"],
|
pools_conf[1]["user"],
|
||||||
conf[1]["pass"],
|
pools_conf[1]["pass"],
|
||||||
conf[2]["url"],
|
pools_conf[2]["url"],
|
||||||
conf[2]["user"],
|
pools_conf[2]["user"],
|
||||||
conf[2]["pass"],
|
pools_conf[2]["pass"],
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
await self.api.adjust_power_limit(conf["wattage"])
|
||||||
|
except APIError:
|
||||||
|
# cannot set wattage
|
||||||
|
pass
|
||||||
|
|
||||||
async def get_config(self) -> MinerConfig:
|
async def get_config(self) -> MinerConfig:
|
||||||
pools = None
|
pools = None
|
||||||
|
summary = None
|
||||||
cfg = MinerConfig()
|
cfg = MinerConfig()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pools = await self.api.pools()
|
data = await self.api.multicommand("pools", "summary")
|
||||||
|
pools = data["pools"][0]
|
||||||
|
summary = data["summary"][0]
|
||||||
except APIError as e:
|
except APIError as e:
|
||||||
logging.warning(e)
|
logging.warning(e)
|
||||||
|
|
||||||
if pools:
|
if pools:
|
||||||
if "POOLS" in pools.keys():
|
if "POOLS" in pools:
|
||||||
cfg = cfg.from_api(pools["POOLS"])
|
cfg = cfg.from_api(pools["POOLS"])
|
||||||
|
if summary:
|
||||||
|
if "SUMMARY" in summary:
|
||||||
|
if wattage := summary["SUMMARY"][0].get("Power Limit"):
|
||||||
|
cfg.autotuning_wattage = wattage
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
async def get_data(self, allow_warning: bool = True) -> MinerData:
|
async def get_data(self, allow_warning: bool = True) -> MinerData:
|
||||||
@@ -269,33 +292,44 @@ class BTMiner(BaseMiner):
|
|||||||
if model:
|
if model:
|
||||||
data.model = model
|
data.model = model
|
||||||
|
|
||||||
|
if self.make:
|
||||||
|
data.make = self.make
|
||||||
|
|
||||||
|
await self.get_version()
|
||||||
|
data.api_ver = self.api_ver
|
||||||
|
data.fw_ver = self.fw_ver
|
||||||
|
|
||||||
if hostname:
|
if hostname:
|
||||||
data.hostname = hostname
|
data.hostname = hostname
|
||||||
|
|
||||||
data.fault_light = await self.check_light()
|
data.fault_light = await self.check_light()
|
||||||
|
|
||||||
miner_data = None
|
miner_data = None
|
||||||
|
err_data = None
|
||||||
for i in range(PyasicSettings().miner_get_data_retries):
|
for i in range(PyasicSettings().miner_get_data_retries):
|
||||||
try:
|
try:
|
||||||
miner_data = await self.api.multicommand("summary", "devs", "pools", allow_warning=allow_warning)
|
miner_data = await self.api.multicommand("summary", "devs", "pools", allow_warning=allow_warning)
|
||||||
if miner_data:
|
if miner_data:
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
err_data = await self.api.get_error_code()
|
||||||
except APIError:
|
except APIError:
|
||||||
pass
|
pass
|
||||||
if not miner_data:
|
if not (miner_data or err_data):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
summary = miner_data.get("summary")[0]
|
summary = miner_data["summary"][0] if miner_data.get("summary") else None
|
||||||
devs = miner_data.get("devs")[0]
|
devs = miner_data["devs"][0] if miner_data.get("devs") else None
|
||||||
pools = miner_data.get("pools")[0]
|
pools = miner_data["pools"][0] if miner_data.get("pools") else None
|
||||||
try:
|
try:
|
||||||
psu_data = await self.api.get_psu()
|
psu_data = await self.api.get_psu()
|
||||||
except APIError:
|
except APIError:
|
||||||
psu_data = None
|
psu_data = None
|
||||||
try:
|
if not err_data:
|
||||||
err_data = await self.api.get_error_code()
|
try:
|
||||||
except APIError:
|
err_data = await self.api.get_error_code()
|
||||||
err_data = None
|
except APIError:
|
||||||
|
err_data = None
|
||||||
|
|
||||||
if summary:
|
if summary:
|
||||||
summary_data = summary.get("SUMMARY")
|
summary_data = summary.get("SUMMARY")
|
||||||
@@ -314,8 +348,9 @@ class BTMiner(BaseMiner):
|
|||||||
if summary_data[0].get("Power Fanspeed"):
|
if summary_data[0].get("Power Fanspeed"):
|
||||||
data.fan_psu = summary_data[0]["Power Fanspeed"]
|
data.fan_psu = summary_data[0]["Power Fanspeed"]
|
||||||
|
|
||||||
data.fan_1 = summary_data[0]["Fan Speed In"]
|
if self.fan_count > 0:
|
||||||
data.fan_2 = summary_data[0]["Fan Speed Out"]
|
data.fan_1 = summary_data[0]["Fan Speed In"]
|
||||||
|
data.fan_2 = summary_data[0]["Fan Speed Out"]
|
||||||
|
|
||||||
hr = summary_data[0].get("MHS 1m")
|
hr = summary_data[0].get("MHS 1m")
|
||||||
if hr:
|
if hr:
|
||||||
@@ -432,3 +467,30 @@ class BTMiner(BaseMiner):
|
|||||||
data.mac = mac
|
data.mac = mac
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
async def get_version(self) -> Union[dict, bool]:
|
||||||
|
"""Get miner firmware version.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner api & firmware version or None.
|
||||||
|
"""
|
||||||
|
# Check to see if the version info is already cached
|
||||||
|
if self.api_ver and self.fw_ver:
|
||||||
|
return {"api_ver": self.api_ver, "fw_ver": self.fw_ver}
|
||||||
|
data = await self.api.get_version()
|
||||||
|
if "Code" in data.keys():
|
||||||
|
if data["Code"] == 131:
|
||||||
|
self.api_ver = data["Msg"]["api_ver"].replace("whatsminer v", "")
|
||||||
|
self.fw_ver = data["Msg"]["fw_ver"]
|
||||||
|
self.api.api_ver = self.api_ver
|
||||||
|
return {"api_ver": self.api_ver, "fw_ver": self.fw_ver}
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
|
try:
|
||||||
|
await self.api.adjust_power_limit(wattage)
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"{self} set_power_limit: {e}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|||||||
@@ -26,10 +26,11 @@ from pyasic.settings import PyasicSettings
|
|||||||
|
|
||||||
|
|
||||||
class CGMiner(BaseMiner):
|
class CGMiner(BaseMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip)
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
self.api = CGMinerAPI(ip)
|
self.api = CGMinerAPI(ip, api_ver)
|
||||||
|
self.api_ver = api_ver
|
||||||
self.api_type = "CGMiner"
|
self.api_type = "CGMiner"
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = "admin"
|
self.pwd = "admin"
|
||||||
@@ -170,6 +171,23 @@ class CGMiner(BaseMiner):
|
|||||||
async def get_mac(self) -> str:
|
async def get_mac(self) -> str:
|
||||||
return "00:00:00:00:00:00"
|
return "00:00:00:00:00:00"
|
||||||
|
|
||||||
|
async def get_version(self) -> dict:
|
||||||
|
"""Get miner firmware version.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Miner api & firmware version or None.
|
||||||
|
"""
|
||||||
|
# check if version is cached
|
||||||
|
if self.fw_ver and self.api_ver:
|
||||||
|
logging.debug(f"Found version for {self.ip}: {self.fw_ver}")
|
||||||
|
return {'api_ver': self.api_ver,'fw_ver': self.fw_ver}
|
||||||
|
# Now get the API version
|
||||||
|
version = await self.api.version()
|
||||||
|
self.api_ver = version['VERSION'][0]['API']
|
||||||
|
self.fw_ver = version['VERSION'][0]['CGMiner']
|
||||||
|
self.api.api_ver = self.api_ver
|
||||||
|
return {'api_ver': self.api_ver,'fw_ver': self.fw_ver}
|
||||||
|
|
||||||
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
async def get_data(self, allow_warning: bool = False) -> MinerData:
|
||||||
"""Get data from the miner.
|
"""Get data from the miner.
|
||||||
|
|
||||||
@@ -335,3 +353,6 @@ class CGMiner(BaseMiner):
|
|||||||
data.pool_split = str(quota)
|
data.pool_split = str(quota)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
|
return False
|
||||||
|
|||||||
276
pyasic/miners/_backends/cgminer_avalon.py
Normal file
276
pyasic/miners/_backends/cgminer_avalon.py
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
# 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 ipaddress
|
||||||
|
import logging
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
|
from pyasic.API.cgminer import CGMinerAPI
|
||||||
|
from pyasic.config import MinerConfig
|
||||||
|
from pyasic.data import HashBoard, MinerData
|
||||||
|
from pyasic.data.error_codes import MinerErrorData
|
||||||
|
from pyasic.errors import APIError
|
||||||
|
from pyasic.miners.base import BaseMiner
|
||||||
|
from pyasic.settings import PyasicSettings
|
||||||
|
from pyasic.miners._backends import CGMiner
|
||||||
|
|
||||||
|
|
||||||
|
class CGMinerAvalon(CGMiner):
|
||||||
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
|
super().__init__(ip, api_ver)
|
||||||
|
self.ip = ip
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
if self.light:
|
||||||
|
return self.light
|
||||||
|
data = await self.api.ascset(0, "led", "1-255")
|
||||||
|
if data["STATUS"][0]["Msg"] == "ASC 0 set info: LED[1]":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_on(self) -> bool:
|
||||||
|
data = await self.api.ascset(0, "led", "1-1")
|
||||||
|
if data["STATUS"][0]["Msg"] == "ASC 0 set OK":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
data = await self.api.ascset(0, "led", "1-0")
|
||||||
|
if data["STATUS"][0]["Msg"] == "ASC 0 set OK":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def reboot(self) -> bool:
|
||||||
|
if (await self.api.restart())["STATUS"] == "RESTART":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def stop_mining(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def resume_mining(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||||
|
"""Configures miner with yaml config."""
|
||||||
|
raise NotImplementedError
|
||||||
|
logging.debug(f"{self}: Sending config.")
|
||||||
|
conf = config.as_avalon(user_suffix=user_suffix)
|
||||||
|
data = await self.api.ascset(
|
||||||
|
0, "setpool", f"root,root,{conf}"
|
||||||
|
) # this should work but doesn't
|
||||||
|
return data
|
||||||
|
|
||||||
|
async def get_mac(self) -> str:
|
||||||
|
mac = None
|
||||||
|
version = await self.api.version()
|
||||||
|
if version:
|
||||||
|
if "VERSION" in version.keys():
|
||||||
|
if "MAC" in version["VERSION"][0].keys():
|
||||||
|
base_mac = version["VERSION"][0]["MAC"].upper()
|
||||||
|
# parse the MAC into a recognizable form
|
||||||
|
mac = ":".join(
|
||||||
|
[base_mac[i : (i + 2)] for i in range(0, len(base_mac), 2)]
|
||||||
|
)
|
||||||
|
return mac
|
||||||
|
|
||||||
|
async def get_data(self, allow_warning: bool = True):
|
||||||
|
data = MinerData(
|
||||||
|
ip=str(self.ip),
|
||||||
|
ideal_chips=self.nominal_chips * self.ideal_hashboards,
|
||||||
|
ideal_hashboards=self.ideal_hashboards,
|
||||||
|
hashboards=[
|
||||||
|
HashBoard(slot=i, expected_chips=self.nominal_chips)
|
||||||
|
for i in range(self.ideal_hashboards)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
model = await self.get_model()
|
||||||
|
mac = None
|
||||||
|
|
||||||
|
if model:
|
||||||
|
data.model = model
|
||||||
|
|
||||||
|
if self.make:
|
||||||
|
data.make = self.make
|
||||||
|
|
||||||
|
await self.get_version()
|
||||||
|
data.api_ver = self.api_ver
|
||||||
|
data.fw_ver = self.fw_ver
|
||||||
|
|
||||||
|
data.fault_light = await self.check_light()
|
||||||
|
|
||||||
|
miner_data = None
|
||||||
|
for i in range(PyasicSettings().miner_get_data_retries):
|
||||||
|
miner_data = await self.api.multicommand(
|
||||||
|
"version", "summary", "pools", "stats", allow_warning=allow_warning
|
||||||
|
)
|
||||||
|
if miner_data:
|
||||||
|
break
|
||||||
|
if not miner_data:
|
||||||
|
hostname = await self.get_hostname()
|
||||||
|
mac = await self.get_mac()
|
||||||
|
|
||||||
|
if hostname and not hostname == "?":
|
||||||
|
data.hostname = hostname
|
||||||
|
elif mac:
|
||||||
|
data.hostname = f"Avalon{mac.replace(':', '')[-6:]}"
|
||||||
|
if mac:
|
||||||
|
data.mac = mac
|
||||||
|
return data
|
||||||
|
|
||||||
|
summary = miner_data.get("summary")
|
||||||
|
version = miner_data.get("version")
|
||||||
|
pools = miner_data.get("pools")
|
||||||
|
stats = miner_data.get("stats")
|
||||||
|
|
||||||
|
if summary:
|
||||||
|
hr = summary[0].get("SUMMARY")
|
||||||
|
if hr:
|
||||||
|
if len(hr) > 0:
|
||||||
|
hr = hr[0].get("MHS 1m")
|
||||||
|
if hr:
|
||||||
|
data.hashrate = round(hr / 1000000, 2)
|
||||||
|
|
||||||
|
if version:
|
||||||
|
if "VERSION" in version[0].keys():
|
||||||
|
if "MAC" in version[0]["VERSION"][0].keys():
|
||||||
|
base_mac = version[0]["VERSION"][0]["MAC"].upper()
|
||||||
|
# parse the MAC into a recognizable form
|
||||||
|
mac = ":".join(
|
||||||
|
[base_mac[i : (i + 2)] for i in range(0, len(base_mac), 2)]
|
||||||
|
)
|
||||||
|
|
||||||
|
if stats:
|
||||||
|
stats_data = stats[0].get("STATS")
|
||||||
|
if stats_data:
|
||||||
|
for key in stats_data[0].keys():
|
||||||
|
if key.startswith("MM ID"):
|
||||||
|
raw_data = self.parse_stats(stats_data[0][key])
|
||||||
|
for fan in range(self.fan_count):
|
||||||
|
if f"Fan{fan+1}" in raw_data:
|
||||||
|
setattr(
|
||||||
|
data,
|
||||||
|
f"fan_{fan+1}",
|
||||||
|
int(raw_data[f"Fan{fan+1}"]),
|
||||||
|
)
|
||||||
|
for board in range(self.ideal_hashboards):
|
||||||
|
chip_temp = raw_data.get("MTmax")
|
||||||
|
if chip_temp:
|
||||||
|
data.hashboards[board].chip_temp = chip_temp[board]
|
||||||
|
|
||||||
|
temp = raw_data.get("MTavg")
|
||||||
|
if temp:
|
||||||
|
data.hashboards[board].temp = temp[board]
|
||||||
|
|
||||||
|
chips = raw_data.get(f"PVT_T{board}")
|
||||||
|
if chips:
|
||||||
|
data.hashboards[board].chips = len(
|
||||||
|
[item for item in chips if not item == "0"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if pools:
|
||||||
|
pool_1 = None
|
||||||
|
pool_2 = None
|
||||||
|
pool_1_user = None
|
||||||
|
pool_2_user = None
|
||||||
|
pool_1_quota = 1
|
||||||
|
pool_2_quota = 1
|
||||||
|
quota = 0
|
||||||
|
for pool in pools[0].get("POOLS"):
|
||||||
|
if not pool_1_user:
|
||||||
|
pool_1_user = pool.get("User")
|
||||||
|
pool_1 = pool["URL"]
|
||||||
|
pool_1_quota = pool["Quota"]
|
||||||
|
elif not pool_2_user:
|
||||||
|
pool_2_user = pool.get("User")
|
||||||
|
pool_2 = pool["URL"]
|
||||||
|
pool_2_quota = pool["Quota"]
|
||||||
|
if not pool.get("User") == pool_1_user:
|
||||||
|
if not pool_2_user == pool.get("User"):
|
||||||
|
pool_2_user = pool.get("User")
|
||||||
|
pool_2 = pool["URL"]
|
||||||
|
pool_2_quota = pool["Quota"]
|
||||||
|
if pool_2_user and not pool_2_user == pool_1_user:
|
||||||
|
quota = f"{pool_1_quota}/{pool_2_quota}"
|
||||||
|
|
||||||
|
if pool_1:
|
||||||
|
pool_1 = pool_1.replace("stratum+tcp://", "").replace(
|
||||||
|
"stratum2+tcp://", ""
|
||||||
|
)
|
||||||
|
data.pool_1_url = pool_1
|
||||||
|
|
||||||
|
if pool_1_user:
|
||||||
|
data.pool_1_user = pool_1_user
|
||||||
|
|
||||||
|
if pool_2:
|
||||||
|
pool_2 = pool_2.replace("stratum+tcp://", "").replace(
|
||||||
|
"stratum2+tcp://", ""
|
||||||
|
)
|
||||||
|
data.pool_2_url = pool_2
|
||||||
|
|
||||||
|
if pool_2_user:
|
||||||
|
data.pool_2_user = pool_2_user
|
||||||
|
|
||||||
|
if quota:
|
||||||
|
data.pool_split = str(quota)
|
||||||
|
|
||||||
|
hostname = await self.get_hostname()
|
||||||
|
|
||||||
|
if mac:
|
||||||
|
data.mac = mac
|
||||||
|
else:
|
||||||
|
mac = await self.get_mac()
|
||||||
|
if mac:
|
||||||
|
data.mac = mac
|
||||||
|
if hostname and not hostname == "?":
|
||||||
|
data.hostname = hostname
|
||||||
|
elif mac:
|
||||||
|
data.hostname = f"Avalon{mac.replace(':', '')[-6:]}"
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_stats(stats):
|
||||||
|
_stats_items = re.findall(".+?\[*?]", stats)
|
||||||
|
stats_items = []
|
||||||
|
stats_dict = {}
|
||||||
|
for item in _stats_items:
|
||||||
|
if ":" in item:
|
||||||
|
data = item.replace("]", "").split("[")
|
||||||
|
data_list = [i.split(": ") for i in data[1].strip().split(", ")]
|
||||||
|
data_dict = {}
|
||||||
|
for key, val in [tuple(item) for item in data_list]:
|
||||||
|
data_dict[key] = val
|
||||||
|
raw_data = [data[0].strip(), data_dict]
|
||||||
|
else:
|
||||||
|
raw_data = [
|
||||||
|
value
|
||||||
|
for value in item.replace("[", " ")
|
||||||
|
.replace("]", " ")
|
||||||
|
.split(" ")[:-1]
|
||||||
|
if value != ""
|
||||||
|
]
|
||||||
|
if len(raw_data) == 1:
|
||||||
|
raw_data.append("")
|
||||||
|
if raw_data[0] == "":
|
||||||
|
raw_data = raw_data[1:]
|
||||||
|
|
||||||
|
if len(raw_data) == 2:
|
||||||
|
stats_dict[raw_data[0]] = raw_data[1]
|
||||||
|
else:
|
||||||
|
stats_dict[raw_data[0]] = raw_data[1:]
|
||||||
|
stats_items.append(raw_data)
|
||||||
|
|
||||||
|
return stats_dict
|
||||||
@@ -18,8 +18,8 @@ from pyasic.miners._backends import BMMiner
|
|||||||
|
|
||||||
|
|
||||||
class Hiveon(BMMiner):
|
class Hiveon(BMMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver)
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
self.api_type = "Hiveon"
|
self.api_type = "Hiveon"
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
@@ -61,3 +61,6 @@ class Hiveon(BMMiner):
|
|||||||
bad_boards[board] = []
|
bad_boards[board] = []
|
||||||
bad_boards[board].append(chain)
|
bad_boards[board].append(chain)
|
||||||
return bad_boards
|
return bad_boards
|
||||||
|
|
||||||
|
async def set_power_limit(self, wattage: int) -> bool:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S17(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S17(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S17Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S17Plus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S17Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S17Pro(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S17e(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S17e(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class T17(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class T17(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class T17Plus(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class T17Plus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class T17e(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class T17e(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S19(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19Pro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S19Pro(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19XP(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S19XP(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19a(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S19a(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
23
pyasic/miners/_types/antminer/X19/S19a_Pro.py
Normal file
23
pyasic/miners/_types/antminer/X19/S19a_Pro.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2022 Upstream Data Inc
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19aPro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__()
|
||||||
|
self.ip = ip
|
||||||
|
self.model = "S19a Pro"
|
||||||
|
self.nominal_chips = 100
|
||||||
|
self.fan_count = 4
|
||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19j(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S19j(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S19jPro(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S19jPro(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
|
||||||
class T19(BaseMiner): # noqa - ignore ABC method implementation
|
class T19(AntMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from .S19 import S19
|
|||||||
from .S19_Pro import S19Pro
|
from .S19_Pro import S19Pro
|
||||||
from .S19_XP import S19XP
|
from .S19_XP import S19XP
|
||||||
from .S19a import S19a
|
from .S19a import S19a
|
||||||
|
from .S19a_Pro import S19aPro
|
||||||
from .S19j import S19j
|
from .S19j import S19j
|
||||||
from .S19j_Pro import S19jPro
|
from .S19j_Pro import S19jPro
|
||||||
from .T19 import T19
|
from .T19 import T19
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S9(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S9(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class S9i(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class S9i(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AntMiner
|
||||||
|
|
||||||
|
class T9(AntMiner): # noqa - ignore ABC method implementation
|
||||||
class T9(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon1026(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon1026(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon1047(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon1047(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon1066(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon1066(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon721(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon721(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon741(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon741(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon761(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon761(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon821(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon821(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon841(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon841(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon851(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon851(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import AvalonMiner
|
||||||
|
|
||||||
|
class Avalon921(AvalonMiner): # noqa - ignore ABC method implementation
|
||||||
class Avalon921(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import InnosiliconMiner
|
||||||
|
|
||||||
|
class InnosiliconT3HPlus(InnosiliconMiner): # noqa - ignore ABC method implementation
|
||||||
class InnosiliconT3HPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
35
pyasic/miners/_types/makes.py
Normal file
35
pyasic/miners/_types/makes.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Copyright 2022 Upstream Data Inc
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from pyasic.miners.base import BaseMiner
|
||||||
|
|
||||||
|
class WhatsMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.make = "WhatsMiner"
|
||||||
|
|
||||||
|
class AntMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.make = "AntMiner"
|
||||||
|
|
||||||
|
class AvalonMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.make = "AvalonMiner"
|
||||||
|
|
||||||
|
class InnosiliconMiner(BaseMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.make = "Innosilicon"
|
||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M20(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M20(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M20V10(BaseMiner): # noqa - ignore ABC method implementation
|
class M20V10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M20S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M20S(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M20S(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M20SV10(BaseMiner): # noqa - ignore ABC method implementation
|
class M20SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -33,7 +32,7 @@ class M20SV10(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M20SV20(BaseMiner): # noqa - ignore ABC method implementation
|
class M20SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M20SPlus(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M20SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M21(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M21(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M21S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M21S(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M21S(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M21SV60(BaseMiner): # noqa - ignore ABC method implementation
|
class M21SV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -33,7 +32,7 @@ class M21SV60(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M21SV20(BaseMiner): # noqa - ignore ABC method implementation
|
class M21SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M21SPlus(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M21SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M30S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M30S(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M30S(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SV50(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SV50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -33,7 +32,7 @@ class M30SV50(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SVG20(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SVG20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -42,7 +41,7 @@ class M30SVG20(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SVE20(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SVE20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -51,7 +50,7 @@ class M30SVE20(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SVE10(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M30SPlus(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M30SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M30SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusVG60(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SPlusVG60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -32,8 +31,16 @@ class M30SPlusVG60(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.nominal_chips = 86
|
self.nominal_chips = 86
|
||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
class M30SPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__()
|
||||||
|
self.ip = ip
|
||||||
|
self.model = "M30S+ VG40"
|
||||||
|
self.nominal_chips = 105
|
||||||
|
self.fan_count = 2
|
||||||
|
|
||||||
class M30SPlusVE40(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
|
class M30SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -42,7 +49,7 @@ class M30SPlusVE40(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusVF20(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M30SPlusPlus(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M30SPlusPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M30SPlusPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusPlusVG30(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SPlusPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -33,7 +32,7 @@ class M30SPlusPlusVG30(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusPlusVG40(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -42,7 +41,7 @@ class M30SPlusPlusVG40(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M30SPlusPlusVH60(BaseMiner): # noqa - ignore ABC method implementation
|
class M30SPlusPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,13 +12,46 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M31S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M31S(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.model = "M31S"
|
self.model = "M31S"
|
||||||
# TODO: Add chip count for this miner (per board) - self.nominal_chips
|
self.nominal_chips = 111
|
||||||
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
|
class M31SV10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__()
|
||||||
|
self.ip = ip
|
||||||
|
self.model = "M31S V10"
|
||||||
|
self.nominal_chips = 105
|
||||||
|
self.fan_count = 2
|
||||||
|
|
||||||
|
class M31SV20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__()
|
||||||
|
self.ip = ip
|
||||||
|
self.model = "M31S V20"
|
||||||
|
self.nominal_chips = 111
|
||||||
|
self.fan_count = 2
|
||||||
|
|
||||||
|
class M31SV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__()
|
||||||
|
self.ip = ip
|
||||||
|
self.model = "M31S V60"
|
||||||
|
self.nominal_chips = 105
|
||||||
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
|
class M31SV70(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
|
def __init__(self, ip: str):
|
||||||
|
super().__init__()
|
||||||
|
self.ip = ip
|
||||||
|
self.model = "M31S V70"
|
||||||
|
self.nominal_chips = 111
|
||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M31SPlus(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M31SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M31SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusVE20(BaseMiner): # noqa - ignore ABC method implementation
|
class M31SPlusVE20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -33,7 +32,7 @@ class M31SPlusVE20(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusV30(BaseMiner): # noqa - ignore ABC method implementation
|
class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -42,7 +41,7 @@ class M31SPlusV30(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusV40(BaseMiner): # noqa - ignore ABC method implementation
|
class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -51,7 +50,7 @@ class M31SPlusV40(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusV60(BaseMiner): # noqa - ignore ABC method implementation
|
class M31SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -60,7 +59,7 @@ class M31SPlusV60(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusV80(BaseMiner): # noqa - ignore ABC method implementation
|
class M31SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -69,7 +68,7 @@ class M31SPlusV80(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M31SPlusV90(BaseMiner): # noqa - ignore ABC method implementation
|
class M31SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M32(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M32(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +23,7 @@ class M32(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M32V20(BaseMiner): # noqa - ignore ABC method implementation
|
class M32V20(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M32S(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M32S(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -12,10 +12,9 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
class M34SPlus(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
class M34SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -25,7 +24,7 @@ class M34SPlus(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 0
|
self.fan_count = 0
|
||||||
|
|
||||||
|
|
||||||
class M34SPlusVE10(BaseMiner): # noqa - ignore ABC method implementation
|
class M34SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -13,14 +13,14 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from .M30S import M30S, M30SV50, M30SVE10, M30SVE20, M30SVG20
|
from .M30S import M30S, M30SV50, M30SVE10, M30SVE20, M30SVG20
|
||||||
from .M30S_Plus import M30SPlus, M30SPlusVE40, M30SPlusVF20, M30SPlusVG60
|
from .M30S_Plus import M30SPlus, M30SPlusVE40, M30SPlusVF20, M30SPlusVG60, M30SPlusVG40
|
||||||
from .M30S_Plus_Plus import (
|
from .M30S_Plus_Plus import (
|
||||||
M30SPlusPlus,
|
M30SPlusPlus,
|
||||||
M30SPlusPlusVG30,
|
M30SPlusPlusVG30,
|
||||||
M30SPlusPlusVG40,
|
M30SPlusPlusVG40,
|
||||||
M30SPlusPlusVH60,
|
M30SPlusPlusVH60,
|
||||||
)
|
)
|
||||||
from .M31S import M31S
|
from .M31S import M31S, M31SV10, M31SV20, M31SV60, M31SV70
|
||||||
from .M31S_Plus import (
|
from .M31S_Plus import (
|
||||||
M31SPlus,
|
M31SPlus,
|
||||||
M31SPlusV30,
|
M31SPlusV30,
|
||||||
|
|||||||
@@ -12,10 +12,10 @@
|
|||||||
# 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 pyasic.miners.base import BaseMiner
|
from pyasic.miners._types.makes import WhatsMiner
|
||||||
|
|
||||||
|
|
||||||
class M50(BaseMiner): # noqa - ignore ABC method implementation
|
class M50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
@@ -24,7 +24,7 @@ class M50(BaseMiner): # noqa - ignore ABC method implementation
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
|
|
||||||
|
|
||||||
class M50VH50(BaseMiner): # noqa - ignore ABC method implementation
|
class M50VH50(WhatsMiner): # noqa - ignore ABC method implementation
|
||||||
def __init__(self, ip: str):
|
def __init__(self, ip: str):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS17(BMMinerX17, S17):
|
class BMMinerS17(BMMinerX17, S17):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS17Plus(BMMinerX17, S17Plus):
|
class BMMinerS17Plus(BMMinerX17, S17Plus):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS17Pro(BMMinerX17, S17Pro):
|
class BMMinerS17Pro(BMMinerX17, S17Pro):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS17e(BMMinerX17, S17e):
|
class BMMinerS17e(BMMinerX17, S17e):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerT17(BMMinerX17, T17):
|
class BMMinerT17(BMMinerX17, T17):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerT17Plus(BMMinerX17, T17Plus):
|
class BMMinerT17Plus(BMMinerX17, T17Plus):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X17 import BMMinerX17
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerT17e(BMMinerX17, T17e):
|
class BMMinerT17e(BMMinerX17, T17e):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ from pyasic.settings import PyasicSettings
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerX17(BMMiner):
|
class BMMinerX17(BMMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = PyasicSettings().global_x17_password
|
self.pwd = PyasicSettings().global_x17_password
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS19(BMMinerX19, S19):
|
class BMMinerS19(BMMinerX19, S19):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS19Pro(BMMinerX19, S19Pro):
|
class BMMinerS19Pro(BMMinerX19, S19Pro):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS19XP(BMMinerX19, S19XP):
|
class BMMinerS19XP(BMMinerX19, S19XP):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS19a(BMMinerX19, S19a):
|
class BMMinerS19a(BMMinerX19, S19a):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
23
pyasic/miners/antminer/bmminer/X19/S19a_Pro.py
Normal file
23
pyasic/miners/antminer/bmminer/X19/S19a_Pro.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Copyright 2022 Upstream Data Inc
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
from pyasic.miners._types import S19aPro # noqa - Ignore access to _module
|
||||||
|
|
||||||
|
from .X19 import BMMinerX19
|
||||||
|
|
||||||
|
|
||||||
|
class BMMinerS19aPro(BMMinerX19, S19aPro):
|
||||||
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
|
super().__init__(ip, api_ver=api_ver)
|
||||||
|
self.ip = ip
|
||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS19j(BMMinerX19, S19j):
|
class BMMinerS19j(BMMinerX19, S19j):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS19jPro(BMMinerX19, S19jPro):
|
class BMMinerS19jPro(BMMinerX19, S19jPro):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -18,6 +18,6 @@ from .X19 import BMMinerX19
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerT19(BMMinerX19, T19):
|
class BMMinerT19(BMMinerX19, T19):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ from pyasic.settings import PyasicSettings
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerX19(BMMiner):
|
class BMMinerX19(BMMiner):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = PyasicSettings().global_x19_password
|
self.pwd = PyasicSettings().global_x19_password
|
||||||
@@ -139,7 +139,10 @@ class BMMinerX19(BMMiner):
|
|||||||
async with httpx.AsyncClient() as client:
|
async with httpx.AsyncClient() as client:
|
||||||
data = await client.get(url, auth=auth)
|
data = await client.get(url, auth=auth)
|
||||||
if data:
|
if data:
|
||||||
data = data.json()
|
try:
|
||||||
|
data = data.json()
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
return []
|
||||||
if "SUMMARY" in data.keys():
|
if "SUMMARY" in data.keys():
|
||||||
if "status" in data["SUMMARY"][0].keys():
|
if "status" in data["SUMMARY"][0].keys():
|
||||||
for item in data["SUMMARY"][0]["status"]:
|
for item in data["SUMMARY"][0]["status"]:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from .S19 import BMMinerS19
|
|||||||
from .S19_Pro import BMMinerS19Pro
|
from .S19_Pro import BMMinerS19Pro
|
||||||
from .S19_XP import BMMinerS19XP
|
from .S19_XP import BMMinerS19XP
|
||||||
from .S19a import BMMinerS19a
|
from .S19a import BMMinerS19a
|
||||||
|
from .S19a_Pro import BMMinerS19aPro
|
||||||
from .S19j import BMMinerS19j
|
from .S19j import BMMinerS19j
|
||||||
from .S19j_Pro import BMMinerS19jPro
|
from .S19j_Pro import BMMinerS19jPro
|
||||||
from .T19 import BMMinerT19
|
from .T19 import BMMinerT19
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S9 # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS9(BMMiner, S9):
|
class BMMinerS9(BMMiner, S9):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S9i # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerS9i(BMMiner, S9i):
|
class BMMinerS9i(BMMiner, S9i):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import T9 # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BMMinerT9(BMMiner, T9):
|
class BMMinerT9(BMMiner, T9):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S17 # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS17(BOSMiner, S17):
|
class BOSMinerS17(BOSMiner, S17):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S17Plus # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS17Plus(BOSMiner, S17Plus):
|
class BOSMinerS17Plus(BOSMiner, S17Plus):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S17Pro # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS17Pro(BOSMiner, S17Pro):
|
class BOSMinerS17Pro(BOSMiner, S17Pro):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S17e # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS17e(BOSMiner, S17e):
|
class BOSMinerS17e(BOSMiner, S17e):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import T17 # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerT17(BOSMiner, T17):
|
class BOSMinerT17(BOSMiner, T17):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import T17Plus # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerT17Plus(BOSMiner, T17Plus):
|
class BOSMinerT17Plus(BOSMiner, T17Plus):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import T17e # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerT17e(BOSMiner, T17e):
|
class BOSMinerT17e(BOSMiner, T17e):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S19 # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS19(BOSMiner, S19):
|
class BOSMinerS19(BOSMiner, S19):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S19Pro # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS19Pro(BOSMiner, S19Pro):
|
class BOSMinerS19Pro(BOSMiner, S19Pro):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S19j # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS19j(BOSMiner, S19j):
|
class BOSMinerS19j(BOSMiner, S19j):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import S19jPro # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerS19jPro(BOSMiner, S19jPro):
|
class BOSMinerS19jPro(BOSMiner, S19jPro):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ from pyasic.miners._types import T19 # noqa - Ignore access to _module
|
|||||||
|
|
||||||
|
|
||||||
class BOSMinerT19(BOSMiner, T19):
|
class BOSMinerT19(BOSMiner, T19):
|
||||||
def __init__(self, ip: str) -> None:
|
def __init__(self, ip: str, api_ver: str = "1.0.0") -> None:
|
||||||
super().__init__(ip)
|
super().__init__(ip, api_ver=api_ver)
|
||||||
self.ip = ip
|
self.ip = ip
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user