update documentation and make BaseMiner and BaseMinerAPI unable to be instantiated directly. Add more unittests for miners.
This commit is contained in:
@@ -2,11 +2,12 @@
|
|||||||
## Miner APIs
|
## Miner APIs
|
||||||
Each miner has a unique API that is used to communicate with it.
|
Each miner has a unique API that is used to communicate with it.
|
||||||
Each of these API types has commands that differ between them, and some commands have data that others do not.
|
Each of these API types has commands that differ between them, and some commands have data that others do not.
|
||||||
Each miner that is a subclass of `BaseMiner` should have an API linked to it as `Miner.api`.
|
Each miner that is a subclass of [`BaseMiner`][pyasic.miners.BaseMiner] should have an API linked to it as `Miner.api`.
|
||||||
|
|
||||||
All API implementations inherit from [`BaseMinerAPI`][pyasic.API.BaseMinerAPI], which implements the basic communications protocols.
|
All API implementations inherit from [`BaseMinerAPI`][pyasic.API.BaseMinerAPI], which implements the basic communications protocols.
|
||||||
|
|
||||||
BaseMinerAPI should never be used unless inheriting to create a new miner API class for a new type of miner (which should be exceedingly rare).
|
[`BaseMinerAPI`][pyasic.API.BaseMinerAPI] should never be used unless inheriting to create a new miner API class for a new type of miner (which should be exceedingly rare).
|
||||||
|
[`BaseMinerAPI`][pyasic.API.BaseMinerAPI] cannot be instantiated directly, it will raise a `TypeError`.
|
||||||
Use these instead -
|
Use these instead -
|
||||||
|
|
||||||
#### [BMMiner API][pyasic.API.bmminer.BMMinerAPI]
|
#### [BMMiner API][pyasic.API.bmminer.BMMinerAPI]
|
||||||
|
|||||||
25
docs/data/error_codes.md
Normal file
25
docs/data/error_codes.md
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# pyasic
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Whatsminer Error Codes
|
||||||
|
::: pyasic.data.error_codes.WhatsminerError
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## Braiins OS Error Codes
|
||||||
|
::: pyasic.data.error_codes.BraiinsOSError
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## X19 Error Codes
|
||||||
|
::: pyasic.data.error_codes.X19Error
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
10
docs/miners/base_miner.md
Normal file
10
docs/miners/base_miner.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# pyasic
|
||||||
|
## Base Miner
|
||||||
|
[`BaseMiner`][pyasic.miners.BaseMiner] is the basis for all miner classes, they all subclass (usually indirectly) from this class.
|
||||||
|
|
||||||
|
You may not instantiate this class on its own, only subclass from it. Trying to instantiate an instance of this class will raise `TypeError`.
|
||||||
|
|
||||||
|
::: pyasic.miners.BaseMiner
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
@@ -6,3 +6,15 @@
|
|||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## AnyMiner
|
||||||
|
::: pyasic.miners.miner_factory.AnyMiner
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
[`AnyMiner`][pyasic.miners.miner_factory.AnyMiner] is a placeholder type variable used for typing returns of functions.
|
||||||
|
A function returning [`AnyMiner`][pyasic.miners.miner_factory.AnyMiner] will always return a subclass of [`BaseMiner`][pyasic.miners.BaseMiner],
|
||||||
|
and is used to specify a function returning some arbitrary type of miner class instance.
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
||||||
|
|
||||||
## Miner List
|
|
||||||
|
|
||||||
##### pyasic currently supports the following miners and subtypes:
|
##### pyasic currently supports the following miners and subtypes:
|
||||||
* Braiins OS+ Devices:
|
* Braiins OS+ Devices:
|
||||||
* All devices supported by BraiinsOS+ are supported here.
|
* All devices supported by BraiinsOS+ are supported here.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## Miner Network Range
|
## Miner Network Range
|
||||||
|
|
||||||
`MinerNetworkRange` is a class used by [`MinerNetwork`][pyasic.network.MinerNetwork] to handle any constructor stings.
|
[`MinerNetworkRange`][pyasic.network.net_range.MinerNetworkRange] is a class used by [`MinerNetwork`][pyasic.network.MinerNetwork] to handle any constructor stings.
|
||||||
The goal is to emulate what is produced by `ipaddress.ip_network` by allowing [`MinerNetwork`][pyasic.network.MinerNetwork] to get a list of hosts.
|
The goal is to emulate what is produced by `ipaddress.ip_network` by allowing [`MinerNetwork`][pyasic.network.MinerNetwork] to get a list of hosts.
|
||||||
This allows this class to be the [`MinerNetwork.network`][pyasic.network.MinerNetwork] and hence be used for scanning.
|
This allows this class to be the [`MinerNetwork.network`][pyasic.network.MinerNetwork] and hence be used for scanning.
|
||||||
|
|
||||||
|
|||||||
@@ -28,17 +28,21 @@ nav:
|
|||||||
- Miner Network Range: "network/miner_network_range.md"
|
- Miner Network Range: "network/miner_network_range.md"
|
||||||
- Data:
|
- Data:
|
||||||
- Miner Data: "data/miner_data.md"
|
- Miner Data: "data/miner_data.md"
|
||||||
|
- Error Codes: "data/error_codes.md"
|
||||||
- Config:
|
- Config:
|
||||||
- Miner Config: "config/miner_config.md"
|
- Miner Config: "config/miner_config.md"
|
||||||
- Advanced:
|
- Advanced:
|
||||||
- Miner APIs:
|
- Miner APIs:
|
||||||
- Base: "API/api.md"
|
- Intro: "API/api.md"
|
||||||
- BMMiner: "API/bmminer.md"
|
- BMMiner: "API/bmminer.md"
|
||||||
- BOSMiner: "API/bosminer.md"
|
- BOSMiner: "API/bosminer.md"
|
||||||
- BTMiner: "API/btminer.md"
|
- BTMiner: "API/btminer.md"
|
||||||
- CGMiner: "API/cgminer.md"
|
- CGMiner: "API/cgminer.md"
|
||||||
- Unknown: "API/unknown.md"
|
- Unknown: "API/unknown.md"
|
||||||
|
|
||||||
|
- Base Miner: "miners/base_miner.md"
|
||||||
|
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- mkdocstrings
|
- mkdocstrings
|
||||||
- search
|
- search
|
||||||
|
|||||||
@@ -55,6 +55,11 @@ class BaseMinerAPI:
|
|||||||
# ip address of the miner
|
# ip address of the miner
|
||||||
self.ip = ipaddress.ip_address(ip)
|
self.ip = ipaddress.ip_address(ip)
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if cls is BaseMinerAPI:
|
||||||
|
raise TypeError(f"Only children of '{cls.__name__}' may be instantiated")
|
||||||
|
return object.__new__(cls)
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import hashlib
|
|||||||
import binascii
|
import binascii
|
||||||
import base64
|
import base64
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from passlib.handlers.md5_crypt import md5_crypt
|
from passlib.handlers.md5_crypt import md5_crypt
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
@@ -187,8 +188,8 @@ class BTMinerAPI(BaseMinerAPI):
|
|||||||
|
|
||||||
async def send_command(
|
async def send_command(
|
||||||
self,
|
self,
|
||||||
command: str or bytes,
|
command: Union[str, bytes],
|
||||||
parameters: str or int or bool = None,
|
parameters: Union[str, int, bool] = None,
|
||||||
ignore_errors: bool = False,
|
ignore_errors: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
|
|||||||
@@ -287,6 +287,15 @@ class MinerConfig:
|
|||||||
self.pool_groups = pool_groups
|
self.pool_groups = pool_groups
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def from_api(self, pools: list):
|
||||||
|
_pools = []
|
||||||
|
for pool in pools:
|
||||||
|
url = pool.get("URL")
|
||||||
|
user = pool.get("User")
|
||||||
|
_pools.append({"url": url, "user": user, "pass": "123"})
|
||||||
|
self.pool_groups = [_PoolGroup().from_dict({"pools": _pools})]
|
||||||
|
return self
|
||||||
|
|
||||||
def from_dict(self, data: dict):
|
def from_dict(self, data: dict):
|
||||||
"""Convert an output dict of this class back into usable data and save it to this class.
|
"""Convert an output dict of this class back into usable data and save it to this class.
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,12 @@
|
|||||||
# 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 typing import Union
|
from typing import Union, List
|
||||||
from dataclasses import dataclass, field, asdict
|
from dataclasses import dataclass, field, asdict
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from .error_codes import X19Error, WhatsminerError, BraiinsOSError
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MinerData:
|
class MinerData:
|
||||||
@@ -95,7 +97,9 @@ class MinerData:
|
|||||||
pool_1_user: str = "Unknown"
|
pool_1_user: str = "Unknown"
|
||||||
pool_2_url: str = ""
|
pool_2_url: str = ""
|
||||||
pool_2_user: str = ""
|
pool_2_user: str = ""
|
||||||
errors: list = field(default_factory=list)
|
errors: List[Union[WhatsminerError, BraiinsOSError, X19Error]] = field(
|
||||||
|
default_factory=list
|
||||||
|
)
|
||||||
fault_light: Union[bool, None] = None
|
fault_light: Union[bool, None] = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ from dataclasses import dataclass, asdict
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class X19Error:
|
class X19Error:
|
||||||
"""A Dataclass to handle error codes of X19 miners."""
|
"""A Dataclass to handle error codes of X19 miners.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
error_message: The error message as a string.
|
||||||
|
"""
|
||||||
|
|
||||||
error_message: str
|
error_message: str
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,11 @@ from dataclasses import dataclass, asdict
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class BraiinsOSError:
|
class BraiinsOSError:
|
||||||
"""A Dataclass to handle error codes of BraiinsOS+ miners."""
|
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
error_message: The error message as a string.
|
||||||
|
"""
|
||||||
|
|
||||||
error_message: str
|
error_message: str
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ from dataclasses import dataclass, field, asdict
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WhatsminerError:
|
class WhatsminerError:
|
||||||
"""A Dataclass to handle error codes of Whatsminers."""
|
"""A Dataclass to handle error codes of Whatsminers.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
error_code: The error code as an int.
|
||||||
|
error_message: The error message as a string. Automatically found from the error code.
|
||||||
|
"""
|
||||||
|
|
||||||
error_code: int
|
error_code: int
|
||||||
error_message: str = field(init=False)
|
error_message: str = field(init=False)
|
||||||
|
|||||||
@@ -15,11 +15,13 @@
|
|||||||
import asyncssh
|
import asyncssh
|
||||||
import logging
|
import logging
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
from pyasic.data import MinerData
|
from pyasic.data import MinerData
|
||||||
|
from pyasic.config import MinerConfig
|
||||||
|
|
||||||
|
|
||||||
class BaseMiner:
|
class BaseMiner(ABC):
|
||||||
def __init__(self, *args) -> None:
|
def __init__(self, *args) -> None:
|
||||||
self.ip = None
|
self.ip = None
|
||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
@@ -34,6 +36,11 @@ class BaseMiner:
|
|||||||
self.fan_count = 2
|
self.fan_count = 2
|
||||||
self.config = None
|
self.config = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if cls is BaseMiner:
|
||||||
|
raise TypeError(f"Only children of '{cls.__name__}' may be instantiated")
|
||||||
|
return object.__new__(cls)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"{'' if not self.api_type else self.api_type} {'' if not self.model else self.model}: {str(self.ip)}"
|
return f"{'' if not self.api_type else self.api_type} {'' if not self.model else self.model}: {str(self.ip)}"
|
||||||
|
|
||||||
@@ -75,45 +82,56 @@ class BaseMiner:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
async def fault_light_on(self) -> bool:
|
async def fault_light_on(self) -> bool:
|
||||||
return False
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
async def fault_light_off(self) -> bool:
|
async def fault_light_off(self) -> bool:
|
||||||
return False
|
pass
|
||||||
|
|
||||||
async def send_file(self, src, dest):
|
# async def send_file(self, src, dest):
|
||||||
async with (await self._get_ssh_connection()) as conn:
|
# async with (await self._get_ssh_connection()) as conn:
|
||||||
await asyncssh.scp(src, (conn, dest))
|
# await asyncssh.scp(src, (conn, dest))
|
||||||
|
|
||||||
async def check_light(self):
|
@abstractmethod
|
||||||
return self.light
|
async def check_light(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# @abstractmethod
|
||||||
async def get_board_info(self):
|
async def get_board_info(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_config(self):
|
@abstractmethod
|
||||||
return None
|
async def get_config(self) -> MinerConfig:
|
||||||
|
pass
|
||||||
|
|
||||||
async def get_hostname(self):
|
@abstractmethod
|
||||||
return None
|
async def get_hostname(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
async def get_model(self):
|
@abstractmethod
|
||||||
return None
|
async def get_model(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
async def reboot(self):
|
@abstractmethod
|
||||||
return False
|
async def reboot(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
async def restart_backend(self):
|
@abstractmethod
|
||||||
return False
|
async def restart_backend(self) -> bool:
|
||||||
|
pass
|
||||||
|
|
||||||
async def send_config(self, *args, **kwargs):
|
async def send_config(self, *args, **kwargs):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_mac(self):
|
@abstractmethod
|
||||||
return None
|
async def get_mac(self) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
async def get_errors(self):
|
@abstractmethod
|
||||||
return None
|
async def get_errors(self) -> list:
|
||||||
|
pass
|
||||||
|
|
||||||
async def get_data(self) -> MinerData:
|
async def get_data(self) -> MinerData:
|
||||||
return MinerData(ip=str(self.ip))
|
return MinerData(ip=str(self.ip))
|
||||||
|
|||||||
@@ -154,6 +154,26 @@ class BMMiner(BaseMiner):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
if not self.light:
|
||||||
|
self.light = False
|
||||||
|
return self.light
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_on(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_errors(self) -> list:
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_mac(self) -> str:
|
||||||
|
return "00:00:00:00:00:00"
|
||||||
|
|
||||||
|
async def restart_backend(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
async def get_data(self) -> MinerData:
|
async def get_data(self) -> MinerData:
|
||||||
"""Get data from the miner.
|
"""Get data from the miner.
|
||||||
|
|
||||||
|
|||||||
@@ -240,6 +240,49 @@ class BOSMiner(BaseMiner):
|
|||||||
logging.debug(f"{self}: Restarting BOSMiner")
|
logging.debug(f"{self}: Restarting BOSMiner")
|
||||||
await conn.run("/etc/init.d/bosminer start")
|
await conn.run("/etc/init.d/bosminer start")
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
if not self.light:
|
||||||
|
self.light = False
|
||||||
|
return self.light
|
||||||
|
|
||||||
|
async def get_errors(self) -> list:
|
||||||
|
tunerstatus = None
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
tunerstatus = await self.api.tunerstatus()
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(e)
|
||||||
|
|
||||||
|
if tunerstatus:
|
||||||
|
tuner = tunerstatus[0].get("TUNERSTATUS")
|
||||||
|
if tuner:
|
||||||
|
if len(tuner) > 0:
|
||||||
|
chain_status = tuner[0].get("TunerChainStatus")
|
||||||
|
if chain_status and len(chain_status) > 0:
|
||||||
|
board_map = {
|
||||||
|
0: "Left board",
|
||||||
|
1: "Center board",
|
||||||
|
2: "Right board",
|
||||||
|
}
|
||||||
|
offset = (
|
||||||
|
6
|
||||||
|
if chain_status[0]["HashchainIndex"] in [6, 7, 8]
|
||||||
|
else chain_status[0]["HashchainIndex"]
|
||||||
|
)
|
||||||
|
for board in chain_status:
|
||||||
|
_id = board["HashchainIndex"] - offset
|
||||||
|
if board["Status"] not in [
|
||||||
|
"Stable",
|
||||||
|
"Testing performance profile",
|
||||||
|
]:
|
||||||
|
_error = board["Status"]
|
||||||
|
_error = _error[0].lower() + _error[1:]
|
||||||
|
errors.append(
|
||||||
|
BraiinsOSError(f"{board_map[_id]} {_error}")
|
||||||
|
)
|
||||||
|
return errors
|
||||||
|
|
||||||
async def get_data(self) -> MinerData:
|
async def get_data(self) -> MinerData:
|
||||||
"""Get data from the miner.
|
"""Get data from the miner.
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
from pyasic.API.bosminer import BOSMinerAPI
|
from pyasic.API.bosminer import BOSMinerAPI
|
||||||
from pyasic.miners import BaseMiner
|
from pyasic.miners import BaseMiner
|
||||||
@@ -29,7 +30,7 @@ class BOSMinerOld(BaseMiner):
|
|||||||
self.uname = "root"
|
self.uname = "root"
|
||||||
self.pwd = "admin"
|
self.pwd = "admin"
|
||||||
|
|
||||||
async def send_ssh_command(self, cmd: str) -> str or None:
|
async def send_ssh_command(self, cmd: str) -> Union[str, None]:
|
||||||
"""Send a command to the miner over ssh.
|
"""Send a command to the miner over ssh.
|
||||||
|
|
||||||
:return: Result of the command or None.
|
:return: Result of the command or None.
|
||||||
@@ -61,3 +62,33 @@ class BOSMinerOld(BaseMiner):
|
|||||||
async def update_to_plus(self):
|
async def update_to_plus(self):
|
||||||
result = await self.send_ssh_command("opkg update && opkg install bos_plus")
|
result = await self.send_ssh_command("opkg update && opkg install bos_plus")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_on(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_config(self) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_errors(self) -> list:
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_hostname(self) -> str:
|
||||||
|
return "?"
|
||||||
|
|
||||||
|
async def get_mac(self) -> str:
|
||||||
|
return "00:00:00:00:00:00"
|
||||||
|
|
||||||
|
async def get_model(self) -> str:
|
||||||
|
return "S9"
|
||||||
|
|
||||||
|
async def reboot(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def restart_backend(self) -> bool:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from pyasic.API import APIError
|
|||||||
|
|
||||||
from pyasic.data import MinerData
|
from pyasic.data import MinerData
|
||||||
from pyasic.data.error_codes import WhatsminerError
|
from pyasic.data.error_codes import WhatsminerError
|
||||||
|
from pyasic.config import MinerConfig
|
||||||
|
|
||||||
from pyasic.settings import PyasicSettings
|
from pyasic.settings import PyasicSettings
|
||||||
|
|
||||||
@@ -99,6 +100,40 @@ class BTMiner(BaseMiner):
|
|||||||
|
|
||||||
return str(mac).upper()
|
return str(mac).upper()
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
if not self.light:
|
||||||
|
self.light = False
|
||||||
|
return self.light
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_on(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_errors(self) -> list:
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def reboot(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def restart_backend(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_config(self) -> MinerConfig:
|
||||||
|
pools = None
|
||||||
|
cfg = MinerConfig()
|
||||||
|
|
||||||
|
try:
|
||||||
|
pools = await self.api.pools()
|
||||||
|
except APIError as e:
|
||||||
|
logging.warning(e)
|
||||||
|
|
||||||
|
if pools:
|
||||||
|
if "POOLS" in pools.keys():
|
||||||
|
cfg = cfg.from_api(pools["POOLS"])
|
||||||
|
return cfg
|
||||||
|
|
||||||
async def get_data(self) -> MinerData:
|
async def get_data(self) -> MinerData:
|
||||||
"""Get data from the miner.
|
"""Get data from the miner.
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,23 @@ class CGMiner(BaseMiner):
|
|||||||
self.config = result.stdout
|
self.config = result.stdout
|
||||||
return self.config
|
return self.config
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
if not self.light:
|
||||||
|
self.light = False
|
||||||
|
return self.light
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_on(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_errors(self) -> list:
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_mac(self) -> str:
|
||||||
|
return "00:00:00:00:00:00"
|
||||||
|
|
||||||
async def get_data(self) -> MinerData:
|
async def get_data(self) -> MinerData:
|
||||||
"""Get data from the miner.
|
"""Get data from the miner.
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ MINER_CLASSES = {
|
|||||||
"Default": CGMinerAvalon1066,
|
"Default": CGMinerAvalon1066,
|
||||||
"CGMiner": CGMinerAvalon1066,
|
"CGMiner": CGMinerAvalon1066,
|
||||||
},
|
},
|
||||||
|
"Unknown": {"Default": UnknownMiner},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,3 +31,29 @@ class UnknownMiner(BaseMiner):
|
|||||||
|
|
||||||
async def get_hostname(self):
|
async def get_hostname(self):
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
|
|
||||||
|
async def check_light(self) -> bool:
|
||||||
|
if not self.light:
|
||||||
|
self.light = False
|
||||||
|
return self.light
|
||||||
|
|
||||||
|
async def fault_light_off(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def fault_light_on(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_config(self) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_errors(self) -> list:
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_mac(self) -> str:
|
||||||
|
return "00:00:00:00:00:00"
|
||||||
|
|
||||||
|
async def reboot(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def restart_backend(self) -> bool:
|
||||||
|
return False
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from pyasic.tests.miners_tests import MinersTest
|
||||||
|
from pyasic.tests.network_tests import NetworkTest
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
54
pyasic/tests/miners_tests/__init__.py
Normal file
54
pyasic/tests/miners_tests/__init__.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# 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 unittest
|
||||||
|
|
||||||
|
from pyasic.miners.miner_factory import MINER_CLASSES
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class MinersTest(unittest.TestCase):
|
||||||
|
def test_miner_model_creation(self):
|
||||||
|
for miner_model in MINER_CLASSES.keys():
|
||||||
|
for miner_api in MINER_CLASSES[miner_model].keys():
|
||||||
|
with self.subTest(miner_model=miner_model, miner_api=miner_api):
|
||||||
|
miner = MINER_CLASSES[miner_model][miner_api]("0.0.0.0")
|
||||||
|
self.assertTrue(
|
||||||
|
isinstance(miner, MINER_CLASSES[miner_model][miner_api])
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_miner_backend_backup_creation(self):
|
||||||
|
backends = inspect.getmembers(
|
||||||
|
sys.modules["pyasic.miners._backends"], inspect.isclass
|
||||||
|
)
|
||||||
|
for backend in backends:
|
||||||
|
miner_class = backend[1]
|
||||||
|
with self.subTest(miner_class=miner_class):
|
||||||
|
miner = miner_class("0.0.0.0")
|
||||||
|
self.assertTrue(isinstance(miner, miner_class))
|
||||||
|
|
||||||
|
def test_miner_type_creation_failure(self):
|
||||||
|
backends = inspect.getmembers(
|
||||||
|
sys.modules["pyasic.miners._types"], inspect.isclass
|
||||||
|
)
|
||||||
|
for backend in backends:
|
||||||
|
miner_class = backend[1]
|
||||||
|
with self.subTest(miner_class=miner_class):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
miner_class("0.0.0.0")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user