Compare commits

..

31 Commits

Author SHA1 Message Date
UpstreamData
e7f05f7a28 version: bump version number. 2024-01-05 16:22:03 -07:00
UpstreamData
2d229be9fd feature: add board serial numbers to whatsminers. 2024-01-05 16:18:03 -07:00
UpstreamData
de5038e57a feature: add AntminerModern serial numbers to Hashboard data. 2024-01-05 15:57:26 -07:00
UpstreamData
8ad1b3f72a refactor: fix formatting issue. 2024-01-05 08:49:44 -07:00
b-rowan
070fb26dbc version: bump version number. 2024-01-04 20:58:44 -07:00
b-rowan
80d9d7df1d bug: fix possible empty command when getting small data points. 2024-01-04 20:58:15 -07:00
UpstreamData
928c24f56f version: bump version number. 2024-01-04 13:07:13 -07:00
UpstreamData
6e7442f90d Update data locations to be typed with dataclasses and enums. (#82)
* feature: swap AntminerModern to new data location style.

* bug: fix a bunch of missed instances of `nominal_` naming.

* feature: add support for S19 Pro Hydro.

* version: bump version number.

* dependencies: bump httpx version

* version: bump version number.

* feature: implement data locations for all remaining miners.

* refactor: remove some unused docstrings.

* feature: swap AntminerModern to new data location style.

* feature: implement data locations for all remaining miners.

* refactor: remove some unused docstrings.

* bug: fix misnamed data locations, and update base miner get_data to use new data locations.

* bug: fix include/exclude implementation on get_data.

* bug: swap ePIC to BaseMiner subclass.

* feature: add DataOptions to __all__

* tests: update data tests with new data locations method.

* bug: remove bad command from bosminer commands.

* dependencies: update dependencies.

* bug: fix some typing issues with python 3.8, and remove useless semaphore and scan threads.

* bug: fix KeyError when pools rpc command returns broken data.
2024-01-04 13:03:45 -07:00
b-rowan
936474ed3b Merge pull request #84 from jpcomps/master 2023-12-23 13:07:47 -07:00
John-Paul Compagnone
2e28060e05 fixes, changes, and formatting 2023-12-23 15:01:42 -05:00
John-Paul Compagnone
07f92557c6 cover chiptune case 2023-12-22 23:35:13 -05:00
John-Paul Compagnone
6f6f5743cf add get_config to ePIC backend 2023-12-22 23:35:13 -05:00
Upstream Data
b89ea1fa92 version: bump version number. 2023-12-22 16:29:03 -07:00
Upstream Data
3588197741 dependencies: bump httpx version 2023-12-22 16:28:46 -07:00
Upstream Data
8adc3d2adf version: bump version number. 2023-12-22 15:47:25 -07:00
Upstream Data
040c0b6842 feature: add support for S19 Pro Hydro. 2023-12-22 15:40:23 -07:00
Upstream Data
550b4a97a1 bug: fix a bunch of missed instances of nominal_ naming. 2023-12-22 15:32:01 -07:00
UpstreamData
d84d95fe5f version: bump version number. 2023-12-21 15:25:57 -07:00
UpstreamData
0e5b811fb9 Add config attribute to data and refactor data naming (#81)
* feature: add config to MinerData.  Remove related attributes.

* feature: rename ideal and nominal to expected to make data naming consistent across files.

* refactor: run isort on all files.

* docs: update docstrings.
2023-12-21 15:20:50 -07:00
UpstreamData
3d31179562 feature: add more BOS+ supported types. 2023-12-19 08:40:31 -07:00
UpstreamData
69f39bef0c docs: update tagline 2023-12-19 08:18:06 -07:00
UpstreamData
1076dab7f5 Update README.md 2023-12-19 08:17:39 -07:00
UpstreamData
3ae1f700c2 docs: update README.md 2023-12-18 14:48:19 -07:00
UpstreamData
dc3f061b9b docs: update shields. 2023-12-18 14:44:43 -07:00
UpstreamData
52758dd8b3 docs: update README. 2023-12-18 14:33:42 -07:00
UpstreamData
0e492f1cfd tests: add more tests for miners. 2023-12-18 14:11:16 -07:00
UpstreamData
659dc55f3c bug: add missing key to epic data locations. 2023-12-18 14:07:46 -07:00
UpstreamData
eb9b29aca1 tests: add tests for config and update tests. 2023-12-18 14:00:40 -07:00
UpstreamData
b045abe76e bug: reorder config information and fix bad key. 2023-12-18 13:59:56 -07:00
Upstream Data
7a75818a20 version: bump version number. 2023-12-17 09:09:00 -07:00
Upstream Data
d2be68d35e bug: fix MinerConfig default values for 3.11+. Add MinerConfig.as_epic default implementation. 2023-12-17 09:08:14 -07:00
131 changed files with 2225 additions and 1865 deletions

289
README.md
View File

@@ -1,142 +1,249 @@
# pyasic
*A simplified and standardized interface for Bitcoin ASICs.*
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![python](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/)
[![GitHub](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic)
## Documentation and Supported Miners
Documentation is located on Read the Docs as [pyasic](https://pyasic.readthedocs.io/en/latest/).
[![PyPI - Version](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/pyasic)](https://pypi.org/project/pyasic/)
Supported miners are listed in the docs, [here](https://pyasic.readthedocs.io/en/latest/miners/supported_types/).
[![Python - Supported Versions](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![CodeFactor - Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic)
[![Commit Activity - master](https://img.shields.io/github/commit-activity/y/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/commits/master/)
## Installation
You can install pyasic directly from pip with the command `pip install pyasic`.
[![Code Style - Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Read The Docs - Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/)
[![License - Apache 2.0](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
For those of you who aren't comfortable with code and developer tools, there are windows builds of GUI applications that use this library [here](https://drive.google.com/drive/folders/1DjR8UOS_g0ehfiJcgmrV0FFoqFvE9akW?usp=sharing).
---
## Intro
## Developer Setup
It is highly reccommended that you contribute to this project through [`pyasic-super`](https://github.com/UpstreamData/pyasic-super) using its submodules. This allows testing in conjunction with other `pyasic` related programs.
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
<br>
[Click here to view supported miner types](miners/supported_types.md)
This repo uses poetry for dependencies, which can be installed by following the guide on their website [here](https://python-poetry.org/docs/#installation).
---
## Getting started
After you have poetry installed, run `poetry install --with dev`, or `poetry install --with dev,docs` if you want to include packages required for documentation.
Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
Finally, initialize pre-commit hooks with `poetry run pre-commit install`.
### Documentation Testing
Testing the documentation can be done by running `poetry run mkdocs serve`, whcih will serve the documentation locally on port 8000.
## Interfacing with miners programmatically
There are 2 main ways to get a miner (and the functions attached to it), via scanning or via the `MinerFactory()`.
#### Scanning for miners
##### Scanning for miners
To scan for miners in `pyasic`, we use the class `MinerNetwork`, which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
The command `MinerNetwork.scan()` returns a list that contains any miners found.
```python
import asyncio
from pyasic.network import MinerNetwork
import asyncio # asyncio for handling the async part
from pyasic.network import MinerNetwork # miner network handles the scanning
# define asynchronous function to scan for miners
async def scan_and_get_data():
# Define network range to be used for scanning
# This can take a list of IPs, a constructor string, or an IP and subnet mask
# 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)
# Scan the network for miners
# This function returns a list of miners of the correct type as a class
miners: list = await net.scan_network_for_miners()
async def scan_miners(): # define async scan function to allow awaiting
# create a miner network
# you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
# We can now get data from any of these miners
# To do them all we have to create a list of tasks and gather them
tasks = [miner.get_data() for miner in miners]
# Gather all tasks asynchronously and run them
data = await asyncio.gather(*tasks)
# Data is now a list of MinerData, and we can reference any part of that
# Print out all data for now
for item in data:
print(item)
# scan for miners asynchronously
# this will return the correct type of miners if they are supported with all functionality.
miners = await network.scan()
print(miners)
if __name__ == "__main__":
asyncio.run(scan_and_get_data())
asyncio.run(scan_miners()) # run the scan asynchronously with asyncio.run()
```
---
##### Creating miners based on IP
If you already know the IP address of your miner or miners, you can use the `MinerFactory` to communicate and identify the miners, or an abstraction of its functionality, `get_miner()`.
The function `get_miner()` will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner.
```python
import asyncio # asyncio for handling the async part
from pyasic import get_miner # handles miner creation
#### Getting a miner if you know the IP
async def get_miners(): # define async scan function to allow awaiting
# get the miner with the miner factory
# the miner factory is a singleton, and will always use the same object and cache
# this means you can always call it as MinerFactory().get_miner(), or just get_miner()
miner_1 = await get_miner("192.168.1.75")
miner_2 = await get_miner("192.168.1.76")
print(miner_1, miner_2)
# can also gather these, since they are async
# gathering them will get them both at the same time
# this makes it much faster to get a lot of miners at a time
tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")]
miners = await asyncio.gather(*tasks)
print(miners)
if __name__ == "__main__":
asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run()
```
---
## Data gathering
Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`.
This function will return an instance of the dataclass `MinerData` with all data it can gather from the miner.
Each piece of data in a `MinerData` instance can be referenced by getting it as an attribute, such as `MinerData().hashrate`.
##### One miner
```python
import asyncio
from pyasic import get_miner
async def gather_miner_data():
miner = await get_miner("192.168.1.75")
if miner is not None:
miner_data = await miner.get_data()
print(miner_data) # all data from the dataclass
print(miner_data.hashrate) # hashrate of the miner in TH/s
if __name__ == "__main__":
asyncio.run(gather_miner_data())
```
---
##### Multiple miners
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
```python
import asyncio # asyncio for handling the async part
from pyasic.network import MinerNetwork # miner network handles the scanning
async def gather_miner_data(): # define async scan function to allow awaiting
network = MinerNetwork.from_subnet("192.168.1.50/24")
miners = await network.scan()
# we need to asyncio.gather() all the miners get_data() functions to make them run together
all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners])
for miner_data in all_miner_data:
print(miner_data) # print out all the data one by one
if __name__ == "__main__":
asyncio.run(gather_miner_data())
```
---
## Miner control
`pyasic` exposes a standard interface for each miner using control functions.
Every miner class in `pyasic` must implement all the control functions defined in `BaseMiner`.
These functions are
`check_light`,
`fault_light_off`,
`fault_light_on`,
`get_config`,
`get_data`,
`get_errors`,
`get_hostname`,
`get_model`,
`reboot`,
`restart_backend`,
`stop_mining`,
`resume_mining`,
`is_mining`,
`send_config`, and
`set_power_limit`.
##### Usage
```python
import asyncio
from pyasic import get_miner
# define asynchronous function to get miner and data
async def get_miner_data(miner_ip: str):
# Use MinerFactory to get miner
# MinerFactory is a singleton, so we can just get the instance in place
miner = await get_miner(miner_ip)
async def set_fault_light():
miner = await get_miner("192.168.1.20")
# Get data from the miner
data = await miner.get_data()
print(data)
# call control function
await miner.fault_light_on()
if __name__ == "__main__":
asyncio.run(get_miner_data("192.168.1.69"))
asyncio.run(set_fault_light())
```
### Advanced data gathering
---
## Helper dataclasses
If needed, this library exposes a wrapper for the miner API that can be used for advanced data gathering.
##### `MinerConfig` and `MinerData`
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/).
`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()`.
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
##### MinerData
`MinerData` is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns.
You can call `MinerData.as_dict()` to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
`MinerData` instances can also be added to each other to combine their data and can be divided by a number to divide all their data, allowing you to get average data from many miners by doing -
```python
from pyasic import MinerData
# examples of miner data
d1 = MinerData("192.168.1.1")
d2 = MinerData("192.168.1.2")
list_of_miner_data = [d1, d2]
average_data = sum(list_of_miner_data, start=MinerData("0.0.0.0"))/len(list_of_miner_data)
```
---
##### MinerConfig
`MinerConfig` is `pyasic`'s way to represent a configuration file from a miner.
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`](#get-config).
Each miner has a unique way to convert the `MinerConfig` to their specific type, there are helper functions in the class.
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig` and will do the conversion to the right type for you.
You can use the `MinerConfig` as follows:
```python
import asyncio
from pyasic import get_miner
async def get_api_commands(miner_ip: str):
# Get the miner
miner = await get_miner(miner_ip)
async def set_fault_light():
miner = await get_miner("192.168.1.20")
# List all available commands
# Can also be called explicitly with the function miner.api.get_commands()
print(miner.api.commands)
# get config
cfg = await miner.get_config()
# send config
await miner.send_config(cfg)
if __name__ == "__main__":
asyncio.run(get_api_commands("192.168.1.69"))
asyncio.run(set_fault_light())
```
#### Use miner API commands to gather data
---
## Settings
The miner API commands will raise an `APIError` if they fail with a bad status code, to bypass this you must send them manually by using `miner.api.send_command(command, ignore_errors=True)`
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows:
```python
import asyncio
from pyasic import settings
from pyasic import get_miner
async def get_api_commands(miner_ip: str):
# Get the miner
miner = await get_miner(miner_ip)
# Run the devdetails command
# This is equivalent to await miner.api.send_command("devdetails")
devdetails: dict = await miner.api.devdetails()
print(devdetails)
if __name__ == "__main__":
asyncio.run(get_api_commands("192.168.1.69"))
settings.update("default_antminer_password", "my_pwd")
```
##### Default values:
```
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"factory_get_retries": 1,
"factory_get_timeout": 3,
"get_data_retries": 1,
"api_function_timeout": 5,
"default_whatsminer_password": "admin",
"default_innosilicon_password": "admin",
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
"default_goldshell_password": "123456789",
# ADVANCED
# Only use this if you know what you are doing
"socket_linger_time": 1000,
```

View File

@@ -1,12 +1,15 @@
# pyasic
*A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH.*
*A simplified and standardized interface for Bitcoin ASICs.*
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![python](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/)
[![GitHub](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
[![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic)
[![PyPI - Version](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![PyPI - Downloads](https://img.shields.io/pypi/dm/pyasic)](https://pypi.org/project/pyasic/)
[![Python - Supported Versions](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![CodeFactor - Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic)
[![Commit Activity - master](https://img.shields.io/github/commit-activity/y/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/commits/master/)
[![Code Style - Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Read The Docs - Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/)
[![License - Apache 2.0](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
---
## Intro

View File

@@ -263,6 +263,12 @@ If you are sure you want to use this command please use API.send_command("{comma
else:
return False, data["STATUS"][0]["Msg"]
elif isinstance(data["STATUS"], dict):
# new style X19 command
if data["STATUS"]["STATUS"] not in ["S", "I"]:
return False, data["STATUS"]["Msg"]
return True, None
if data["STATUS"] not in ["S", "I"]:
return False, data["Msg"]
else:

View File

@@ -48,10 +48,10 @@ PrePowerOnMessage = Union[
def _crypt(word: str, salt: str) -> str:
"""Encrypts a word with a salt, using a standard salt format.
r"""Encrypts a word with a salt, using a standard salt format.
Encrypts a word using a salt with the format
'\s*\$(\d+)\$([\w\./]*)\$'. If this format is not used, a
\s*\$(\d+)\$([\w\./]*)\$. If this format is not used, a
ValueError is raised.
Parameters:
@@ -62,7 +62,7 @@ def _crypt(word: str, salt: str) -> str:
An MD5 hash of the word with the salt.
"""
# compile a standard format for the salt
standard_salt = re.compile("\s*\$(\d+)\$([\w\./]*)\$")
standard_salt = re.compile(r"\s*\$(\d+)\$([\w\./]*)\$")
# check if the salt matches
match = standard_salt.match(salt)
# if the matching fails, the salt is incorrect

View File

@@ -29,7 +29,7 @@ from pyasic.data import (
)
from pyasic.errors import APIError, APIWarning
from pyasic.miners import get_miner
from pyasic.miners.base import AnyMiner
from pyasic.miners.base import AnyMiner, DataOptions
from pyasic.miners.miner_factory import MinerFactory, miner_factory
from pyasic.miners.miner_listener import MinerListener
from pyasic.network import MinerNetwork
@@ -50,6 +50,7 @@ __all__ = [
"APIWarning",
"get_miner",
"AnyMiner",
"DataOptions",
"MinerFactory",
"miner_factory",
"MinerListener",

View File

@@ -14,7 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from copy import deepcopy
from dataclasses import asdict, dataclass
from dataclasses import asdict, dataclass, field
from pyasic.config.fans import FanModeConfig
from pyasic.config.mining import MiningModeConfig
@@ -25,11 +25,13 @@ from pyasic.config.temperature import TemperatureConfig
@dataclass
class MinerConfig:
pools: PoolConfig = PoolConfig.default()
fan_mode: FanModeConfig = FanModeConfig.default()
temperature: TemperatureConfig = TemperatureConfig.default()
mining_mode: MiningModeConfig = MiningModeConfig.default()
power_scaling: PowerScalingConfig = PowerScalingConfig.default()
pools: PoolConfig = field(default_factory=PoolConfig.default)
fan_mode: FanModeConfig = field(default_factory=FanModeConfig.default)
temperature: TemperatureConfig = field(default_factory=TemperatureConfig.default)
mining_mode: MiningModeConfig = field(default_factory=MiningModeConfig.default)
power_scaling: PowerScalingConfig = field(
default_factory=PowerScalingConfig.default
)
def as_dict(self) -> dict:
return asdict(self)
@@ -106,6 +108,15 @@ class MinerConfig:
**self.power_scaling.as_bos_grpc(),
}
def as_epic(self, user_suffix: str = None) -> dict:
return {
**self.fan_mode.as_epic(),
**self.temperature.as_epic(),
**self.mining_mode.as_epic(),
**self.pools.as_epic(user_suffix=user_suffix),
**self.power_scaling.as_epic(),
}
@classmethod
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
return cls(
@@ -150,6 +161,15 @@ class MinerConfig:
power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
)
@classmethod
def from_epic(cls, web_conf: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_epic(web_conf),
fan_mode=FanModeConfig.from_epic(web_conf),
temperature=TemperatureConfig.from_epic(web_conf),
mining_mode=MiningModeConfig.from_epic(web_conf),
)
def merge(a: dict, b: dict) -> dict:
result = deepcopy(a)

View File

@@ -47,6 +47,9 @@ class MinerConfigOption(Enum):
def as_bos_grpc(self) -> dict:
return self.value.as_bos_grpc()
def as_epic(self) -> dict:
return self.value.as_epic()
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
@@ -87,3 +90,6 @@ class MinerConfigValue:
def as_bos_grpc(self) -> dict:
return {}
def as_epic(self) -> dict:
return {}

View File

@@ -37,16 +37,16 @@ class FanModeNormal(MinerConfigValue):
@dataclass
class FanModeManual(MinerConfigValue):
mode: str = field(init=False, default="manual")
minimum_fans: int = 1
speed: int = 100
minimum_fans: int = 1
@classmethod
def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeManual":
cls_conf = {}
if dict_conf.get("min_fans") is not None:
cls_conf["minimum_fans"] = dict_conf["minimum_fans"]
if dict_conf.get("speed") is not None:
cls_conf["speed"] = dict_conf["speed"]
if dict_conf.get("minimum_fans") is not None:
cls_conf["minimum_fans"] = dict_conf["minimum_fans"]
return cls(**cls_conf)
@classmethod
@@ -116,6 +116,17 @@ class FanModeConfig(MinerConfigOption):
else:
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
try:
fan_mode = web_conf["Fans"]["Fan Mode"]
if fan_mode.get("Manual") is not None:
return cls.manual(speed=fan_mode.get("Manual"))
else:
return cls.normal()
except KeyError:
return cls.default()
@classmethod
def from_bosminer(cls, toml_conf: dict):
if toml_conf.get("temp_control") is None:

View File

@@ -14,7 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import dataclass, field
from typing import Union
from typing import Dict, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@@ -132,7 +132,7 @@ class MiningModeManual(MinerConfigValue):
global_freq: float
global_volt: float
boards: dict[int, ManualBoardSettings] = field(default_factory=dict)
boards: Dict[int, ManualBoardSettings] = field(default_factory=dict)
@classmethod
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual":
@@ -186,6 +186,29 @@ class MiningModeConfig(MinerConfigOption):
return cls.low()
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
try:
work_mode = web_conf["PerpetualTune"]["Running"]
if work_mode:
if (
web_conf["PerpetualTune"]["Algorithm"].get("VoltageOptimizer")
is not None
):
return cls.hashrate_tuning(
web_conf["PerpetualTune"]["Algorithm"]["VoltageOptimizer"][
"Target"
]
)
else:
return cls.hashrate_tuning(
web_conf["PerpetualTune"]["Algorithm"]["ChipTune"]["Target"]
)
else:
return cls.normal()
except KeyError:
return cls.default()
@classmethod
def from_bosminer(cls, toml_conf: dict):
if toml_conf.get("autotuning") is None:

View File

@@ -16,7 +16,7 @@
import random
import string
from dataclasses import dataclass, field
from typing import Union
from typing import Dict, List, Union
from pyasic.config.base import MinerConfigValue
@@ -108,6 +108,12 @@ class Pool(MinerConfigValue):
def from_api(cls, api_pool: dict) -> "Pool":
return cls(url=api_pool["URL"], user=api_pool["User"], password="x")
@classmethod
def from_epic(cls, api_pool: dict) -> "Pool":
return cls(
url=api_pool["pool"], user=api_pool["login"], password=api_pool["password"]
)
@classmethod
def from_am_modern(cls, web_pool: dict) -> "Pool":
return cls(
@@ -138,7 +144,7 @@ class Pool(MinerConfigValue):
@dataclass
class PoolGroup(MinerConfigValue):
pools: list[Pool] = field(default_factory=list)
pools: List[Pool] = field(default_factory=list)
quota: int = 1
name: str = None
@@ -237,6 +243,13 @@ class PoolGroup(MinerConfigValue):
pools.append(Pool.from_api(pool))
return cls(pools=pools)
@classmethod
def from_epic(cls, api_pool_list: list) -> "PoolGroup":
pools = []
for pool in api_pool_list:
pools.append(Pool.from_epic(pool))
return cls(pools=pools)
@classmethod
def from_am_modern(cls, web_pool_list: list) -> "PoolGroup":
pools = []
@@ -265,7 +278,7 @@ class PoolGroup(MinerConfigValue):
@dataclass
class PoolConfig(MinerConfigValue):
groups: list[PoolGroup] = field(default_factory=list)
groups: List[PoolGroup] = field(default_factory=list)
@classmethod
def default(cls) -> "PoolConfig":
@@ -279,7 +292,7 @@ class PoolConfig(MinerConfigValue):
return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]])
@classmethod
def simple(cls, pools: list[Union[Pool, dict[str, str]]]) -> "PoolConfig":
def simple(cls, pools: List[Union[Pool, Dict[str, str]]]) -> "PoolConfig":
group_pools = []
for pool in pools:
if isinstance(pool, dict):
@@ -329,11 +342,19 @@ class PoolConfig(MinerConfigValue):
@classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig":
pool_data = api_pools["POOLS"]
try:
pool_data = api_pools["POOLS"]
except KeyError:
return PoolConfig.default()
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
return cls([PoolGroup.from_api(pool_data)])
@classmethod
def from_epic(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["StratumConfigs"]
return cls([PoolGroup.from_epic(pool_data)])
@classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"]

View File

@@ -56,3 +56,18 @@ class TemperatureConfig(MinerConfigValue):
hot=temp_control.get("hot_temp"),
danger=temp_control.get("dangerous_temp"),
)
@classmethod
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
dangerous_temp = None
try:
hot_temp = web_conf["Misc"]["Shutdown Temp"]
except KeyError:
hot_temp = None
# Need to do this in two blocks to avoid KeyError if one is missing
try:
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
except KeyError:
target_temp = None
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp)

View File

@@ -22,6 +22,9 @@ from dataclasses import asdict, dataclass, field, fields
from datetime import datetime, timezone
from typing import Any, List, Union
from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
@@ -35,7 +38,7 @@ class HashBoard:
temp: The temperature of the PCB as an int.
chip_temp: The temperature of the chips as an int.
chips: The chip count of the board as an int.
expected_chips: The ideal chip count of the board as an int.
expected_chips: The expected chip count of the board as an int.
missing: Whether the board is returned from the miners data as a bool.
"""
@@ -45,6 +48,7 @@ class HashBoard:
chip_temp: int = None
chips: int = None
expected_chips: int = None
serial_number: str = None
missing: bool = True
def get(self, __key: str, default: Any = None):
@@ -105,7 +109,7 @@ class MinerData:
hostname: The network hostname of the miner as a str.
hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically.
_hashrate: Backup for hashrate found via API instead of hashboards.
nominal_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
hashboards: A list of hashboards on the miner with their statistics.
temperature_avg: The average temperature across the boards. Calculated automatically.
env_temp: The environment temps as a float.
@@ -114,10 +118,10 @@ class MinerData:
fans: A list of fans on the miner with their speeds.
fan_psu: The speed of the PSU on the fan if the miner collects it.
total_chips: The total number of chips on all boards. Calculated automatically.
ideal_chips: The ideal number of chips in the miner as an int.
percent_ideal_chips: The percent of total chips out of the ideal count. Calculated automatically.
percent_ideal_hashrate: The percent of total hashrate out of the ideal hashrate. Calculated automatically.
percent_ideal_wattage: The percent of total wattage out of the ideal wattage. Calculated automatically.
expected_chips: The expected number of chips in the miner as an int.
percent_expected_chips: The percent of total chips out of the expected count. Calculated automatically.
percent_expected_hashrate: The percent of total hashrate out of the expected hashrate. Calculated automatically.
percent_expected_wattage: The percent of total wattage out of the expected wattage. Calculated automatically.
nominal: Whether the number of chips in the miner is nominal. Calculated automatically.
pool_split: The pool split as a str.
pool_1_url: The first pool url on the miner as a str.
@@ -140,27 +144,24 @@ class MinerData:
fw_ver: str = None
hostname: str = None
hashrate: float = field(init=False)
_hashrate: float = None
nominal_hashrate: float = None
_hashrate: float = field(repr=False, default=None)
expected_hashrate: float = None
hashboards: List[HashBoard] = field(default_factory=list)
ideal_hashboards: int = None
expected_hashboards: int = None
temperature_avg: int = field(init=False)
env_temp: float = None
wattage: int = None
wattage_limit: int = None
wattage_limit: int = field(init=False)
_wattage_limit: int = field(repr=False, default=None)
fans: List[Fan] = field(default_factory=list)
fan_psu: int = None
total_chips: int = field(init=False)
ideal_chips: int = None
percent_ideal_chips: float = field(init=False)
percent_ideal_hashrate: float = field(init=False)
percent_ideal_wattage: float = field(init=False)
expected_chips: int = None
percent_expected_chips: float = field(init=False)
percent_expected_hashrate: float = field(init=False)
percent_expected_wattage: float = field(init=False)
nominal: bool = field(init=False)
pool_split: str = "0"
pool_1_url: str = "Unknown"
pool_1_user: str = "Unknown"
pool_2_url: str = ""
pool_2_user: str = ""
config: MinerConfig = None
errors: List[
Union[WhatsminerError, BraiinsOSError, X19Error, InnosiliconError]
] = field(default_factory=list)
@@ -170,7 +171,11 @@ class MinerData:
@classmethod
def fields(cls):
return [f.name for f in fields(cls)]
return [f.name for f in fields(cls) if not f.name.startswith("_")]
@staticmethod
def dict_factory(x):
return {k: v for (k, v) in x if not k.startswith("_")}
def __post_init__(self):
self.datetime = datetime.now(timezone.utc).astimezone()
@@ -248,6 +253,17 @@ class MinerData:
def hashrate(self, val):
self._hashrate = val
@property
def wattage_limit(self): # noqa - Skip PyCharm inspection
if self.config is not None:
if isinstance(self.config.mining_mode, MiningModePowerTune):
return self.config.mining_mode.power
return self._wattage_limit
@wattage_limit.setter
def wattage_limit(self, val: int):
self._wattage_limit = val
@property
def total_chips(self): # noqa - Skip PyCharm inspection
if len(self.hashboards) > 0:
@@ -265,48 +281,48 @@ class MinerData:
@property
def nominal(self): # noqa - Skip PyCharm inspection
if self.total_chips is None or self.ideal_chips is None:
if self.total_chips is None or self.expected_chips is None:
return None
return self.ideal_chips == self.total_chips
return self.expected_chips == self.total_chips
@nominal.setter
def nominal(self, val):
pass
@property
def percent_ideal_chips(self): # noqa - Skip PyCharm inspection
if self.total_chips is None or self.ideal_chips is None:
def percent_expected_chips(self): # noqa - Skip PyCharm inspection
if self.total_chips is None or self.expected_chips is None:
return None
if self.total_chips == 0 or self.ideal_chips == 0:
if self.total_chips == 0 or self.expected_chips == 0:
return 0
return round((self.total_chips / self.ideal_chips) * 100)
return round((self.total_chips / self.expected_chips) * 100)
@percent_ideal_chips.setter
def percent_ideal_chips(self, val):
@percent_expected_chips.setter
def percent_expected_chips(self, val):
pass
@property
def percent_ideal_hashrate(self): # noqa - Skip PyCharm inspection
if self.hashrate is None or self.nominal_hashrate is None:
def percent_expected_hashrate(self): # noqa - Skip PyCharm inspection
if self.hashrate is None or self.expected_hashrate is None:
return None
if self.hashrate == 0 or self.nominal_hashrate == 0:
if self.hashrate == 0 or self.expected_hashrate == 0:
return 0
return round((self.hashrate / self.nominal_hashrate) * 100)
return round((self.hashrate / self.expected_hashrate) * 100)
@percent_ideal_hashrate.setter
def percent_ideal_hashrate(self, val):
@percent_expected_hashrate.setter
def percent_expected_hashrate(self, val):
pass
@property
def percent_ideal_wattage(self): # noqa - Skip PyCharm inspection
def percent_expected_wattage(self): # noqa - Skip PyCharm inspection
if self.wattage_limit is None or self.wattage is None:
return None
if self.wattage_limit == 0 or self.wattage == 0:
return 0
return round((self.wattage / self.wattage_limit) * 100)
@percent_ideal_wattage.setter
def percent_ideal_wattage(self, val):
@percent_expected_wattage.setter
def percent_expected_wattage(self, val):
pass
@property
@@ -339,7 +355,7 @@ class MinerData:
def asdict(self) -> dict:
logging.debug(f"MinerData - (To Dict) - Dumping Dict data")
return asdict(self)
return asdict(self, dict_factory=self.dict_factory)
def as_dict(self) -> dict:
"""Get this dataclass as a dictionary.

View File

@@ -137,10 +137,10 @@ class _MinerPhaseBalancer:
for miner in self.miners
]
)
pct_ideal_list = [d.percent_ideal for d in data]
pct_expected_list = [d.percent_ideal for d in data]
pct_ideal = 0
if len(pct_ideal_list) > 0:
pct_ideal = sum(pct_ideal_list) / len(pct_ideal_list)
if len(pct_expected_list) > 0:
pct_ideal = sum(pct_expected_list) / len(pct_expected_list)
wattage = round(wattage * 1 / (pct_ideal / 100))

View File

@@ -17,7 +17,7 @@
from .bmminer import *
from .bosminer import *
from .cgminer import *
from .epic import *
from .hiveon import *
from .luxos import *
from .vnish import *
from .epic import *

View File

@@ -27,6 +27,7 @@ from pyasic.miners.types import (
S19jPro,
S19Plus,
S19Pro,
S19ProHydro,
S19ProPlus,
)
@@ -77,3 +78,7 @@ class BMMinerS19jPro(AntminerModern, S19jPro):
class BMMinerS19L(AntminerModern, S19L):
pass
class BMMinerS19ProHydro(AntminerModern, S19ProHydro):
pass

View File

@@ -25,6 +25,7 @@ from .S19 import (
BMMinerS19L,
BMMinerS19Plus,
BMMinerS19Pro,
BMMinerS19ProHydro,
BMMinerS19ProPlus,
BMMinerS19XP,
)

View File

@@ -15,17 +15,37 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSMiner
from pyasic.miners.types import S19, S19j, S19jNoPIC, S19jPro, S19kProNoPIC, S19Pro
from pyasic.miners.types import (
S19,
S19XP,
S19a,
S19aPro,
S19j,
S19jNoPIC,
S19jPro,
S19jProPlus,
S19kProNoPIC,
S19Plus,
S19Pro,
)
class BOSMinerS19(BOSMiner, S19):
pass
class BOSMinerS19Plus(BOSMiner, S19Plus):
pass
class BOSMinerS19Pro(BOSMiner, S19Pro):
pass
class BOSMinerS19a(BOSMiner, S19a):
pass
class BOSMinerS19j(BOSMiner, S19j):
pass
@@ -40,3 +60,15 @@ class BOSMinerS19jPro(BOSMiner, S19jPro):
class BOSMinerS19kProNoPIC(BOSMiner, S19kProNoPIC):
pass
class BOSMinerS19aPro(BOSMiner, S19aPro):
pass
class BOSMinerS19jProPlus(BOSMiner, S19jProPlus):
pass
class BOSMinerS19XP(BOSMiner, S19XP):
pass

View File

@@ -16,10 +16,15 @@
from .S19 import (
BOSMinerS19,
BOSMinerS19a,
BOSMinerS19aPro,
BOSMinerS19j,
BOSMinerS19jNoPIC,
BOSMinerS19jPro,
BOSMinerS19jProPlus,
BOSMinerS19kProNoPIC,
BOSMinerS19Plus,
BOSMinerS19Pro,
BOSMinerS19XP,
)
from .T19 import BOSMinerT19

View File

@@ -16,10 +16,10 @@
from .S19 import (
ePICS19,
ePICS19Pro,
ePICS19j,
ePICS19jPro,
ePICS19jProPlus,
ePICS19kPro,
ePICS19Pro,
ePICS19XP,
)

View File

@@ -54,7 +54,7 @@ class HiveonT9(Hiveon, T9):
hashboards = []
for board in board_map:
hashboard = HashBoard(slot=board, expected_chips=self.nominal_chips)
hashboard = HashBoard(slot=board, expected_chips=self.expected_chips)
hashrate = 0
chips = 0
for chipset in board_map[board]:

View File

@@ -21,8 +21,8 @@ from .bosminer import BOSMiner
from .btminer import BTMiner
from .cgminer import CGMiner
from .cgminer_avalon import CGMinerAvalon
from .epic import ePIC
from .hiveon import Hiveon
from .luxminer import LUXMiner
from .vnish import VNish
from .epic import ePIC
from .whatsminer import M2X, M3X, M5X, M6X

View File

@@ -14,7 +14,6 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
import asyncio
from typing import List, Optional, Union
from pyasic.API import APIError
@@ -23,46 +22,60 @@ from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.backends.cgminer import CGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
ANTMINER_MODERN_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {
"cmd": "is_mining",
"kwargs": {"web_get_conf": {"web": "get_miner_conf"}},
},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_stats": {"api": "stats"}},
},
}
ANTMINER_MODERN_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_get_system_info", "get_system_info")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_get_system_info", "get_system_info")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction("get_hashboards", []),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"get_errors", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [WebAPICommand("web_get_conf", "get_miner_conf")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class AntminerModern(BMMiner):
@@ -181,6 +194,42 @@ class AntminerModern(BMMiner):
pass
return errors
async def get_hashboards(self) -> List[HashBoard]:
hashboards = [
HashBoard(idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
try:
api_stats = await self.api.send_command("stats", new_api=True)
except APIError:
return hashboards
if api_stats:
try:
for board in api_stats["STATS"][0]["chain"]:
hashboards[board["index"]].hashrate = round(
board["rate_real"] / 1000, 2
)
hashboards[board["index"]].chips = board["asic_num"]
board_temp_data = list(
filter(lambda x: not x == 0, board["temp_pcb"])
)
hashboards[board["index"]].temp = sum(board_temp_data) / len(
board_temp_data
)
chip_temp_data = list(
filter(lambda x: not x == 0, board["temp_chip"])
)
hashboards[board["index"]].chip_temp = sum(chip_temp_data) / len(
chip_temp_data
)
hashboards[board["index"]].serial_number = board["sn"]
hashboards[board["index"]].missing = False
except LookupError:
pass
return hashboards
async def get_fault_light(self, web_get_blink_status: dict = None) -> bool:
if self.light:
return self.light
@@ -198,7 +247,7 @@ class AntminerModern(BMMiner):
pass
return self.light
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
if not api_stats:
try:
api_stats = await self.api.stats()
@@ -207,17 +256,17 @@ class AntminerModern(BMMiner):
if api_stats:
try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
expected_rate = api_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
if rate_unit == "GH":
return round(ideal_rate / 1000, 2)
return round(expected_rate / 1000, 2)
if rate_unit == "MH":
return round(ideal_rate / 1000000, 2)
return round(expected_rate / 1000000, 2)
else:
return round(ideal_rate, 2)
return round(expected_rate, 2)
except (KeyError, IndexError):
pass
@@ -294,44 +343,49 @@ class AntminerModern(BMMiner):
pass
ANTMINER_OLD_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {
"cmd": "get_model",
"kwargs": {},
},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"web_get_system_info": {"web": "get_system_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {
"cmd": "is_mining",
"kwargs": {"web_get_conf": {"web": "get_miner_conf"}},
},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_stats": {"api": "stats"}},
},
}
ANTMINER_OLD_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_get_system_info", "get_system_info")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [WebAPICommand("web_get_conf", "get_miner_conf")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class AntminerOld(CGMiner):
@@ -472,9 +526,11 @@ class AntminerOld(CGMiner):
if board_offset == -1:
board_offset = 1
for i in range(board_offset, board_offset + self.ideal_hashboards):
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.nominal_chips
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")

View File

@@ -22,31 +22,48 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BFGMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
}
BFGMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BFGMiner(BaseMiner):
@@ -197,9 +214,11 @@ class BFGMiner(BaseMiner):
if board_offset == -1:
board_offset = 1
for i in range(board_offset, board_offset + self.ideal_hashboards):
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.nominal_chips
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
@@ -265,39 +284,13 @@ class BFGMiner(BaseMiner):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []
async def get_fault_light(self) -> bool:
return False
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
# X19 method, not sure compatibility
if not api_stats:
try:
@@ -307,17 +300,17 @@ class BFGMiner(BaseMiner):
if api_stats:
try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
expected_rate = api_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
if rate_unit == "GH":
return round(ideal_rate / 1000, 2)
return round(expected_rate / 1000, 2)
if rate_unit == "MH":
return round(ideal_rate / 1000000, 2)
return round(expected_rate / 1000000, 2)
else:
return round(ideal_rate, 2)
return round(expected_rate, 2)
except (KeyError, IndexError):
pass

View File

@@ -20,37 +20,55 @@ from pyasic.data import HashBoard
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends import BFGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.goldshell import GoldshellWebAPI
GOLDSHELL_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"web_setting": {"web": "setting"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_status": {"web": "status"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {
"api_devs": {"api": "devs"},
"api_devdetails": {"api": "devdetails"},
},
},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
}
GOLDSHELL_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_setting", "setting")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [WebAPICommand("web_status", "status")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards",
[
RPCAPICommand("api_devs", "devs"),
RPCAPICommand("api_devdetails", "devdetails"),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BFGMinerGoldshell(BFGMiner):
@@ -128,8 +146,8 @@ class BFGMinerGoldshell(BFGMiner):
pass
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if api_devs:

View File

@@ -23,34 +23,50 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BMMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_stats": {"api": "stats"}},
},
}
BMMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BMMiner(BaseMiner):
@@ -247,12 +263,12 @@ class BMMiner(BaseMiner):
if len(real_slots) < 3:
real_slots = list(
range(board_offset, board_offset + self.ideal_hashboards)
range(board_offset, board_offset + self.expected_hashboards)
)
for i in real_slots:
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.nominal_chips
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
@@ -317,39 +333,13 @@ class BMMiner(BaseMiner):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []
async def get_fault_light(self) -> bool:
return False
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
# X19 method, not sure compatibility
if not api_stats:
try:
@@ -359,17 +349,17 @@ class BMMiner(BaseMiner):
if api_stats:
try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
expected_rate = api_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
if rate_unit == "GH":
return round(ideal_rate / 1000, 2)
return round(expected_rate / 1000, 2)
if rate_unit == "MH":
return round(ideal_rate / 1000000, 2)
return round(expected_rate / 1000000, 2)
else:
return round(ideal_rate, 2)
return round(expected_rate, 2)
except (KeyError, IndexError):
pass

View File

@@ -27,162 +27,157 @@ from pyasic.config.mining import MiningModePowerTune
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
GraphQLCommand,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.bosminer import BOSMinerWebAPI
BOSMINER_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {
"web_net_conf": {"web": "/cgi-bin/luci/admin/network/iface_status/lan"}
},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {
"cmd": "get_api_ver",
"kwargs": {"api_version": {"api": "version"}},
},
"fw_ver": {
"cmd": "get_fw_ver",
"kwargs": {
"graphql_version": {"web": {"bos": {"info": {"version": {"full": None}}}}}
},
},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"graphql_hostname": {"web": {"bos": {"hostname": None}}}},
},
"hashrate": {
"cmd": "get_hashrate",
"kwargs": {
"api_summary": {"api": "summary"},
"graphql_hashrate": {
"web": {
"bosminer": {
"info": {"workSolver": {"realHashrate": {"mhs1M": None}}}
}
},
},
},
},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_devs": {"api": "devs"}},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {
"api_temps": {"api": "temps"},
"api_devdetails": {"api": "devdetails"},
"api_devs": {"api": "devs"},
"graphql_boards": {
"web": {
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"realHashrate": {"mhs1M": None},
"hwDetails": {"chips": None},
"temperatures": {"degreesC": None},
BOSMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac",
[
WebAPICommand(
"web_net_conf", "/cgi-bin/luci/admin/network/iface_status/lan"
)
],
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver",
[
GraphQLCommand(
"graphql_version", {"bos": {"info": {"version": {"full": None}}}}
)
],
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname",
[GraphQLCommand("graphql_hostname", {"bos": {"hostname": None}})],
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate",
[
RPCAPICommand("api_summary", "summary"),
GraphQLCommand(
"graphql_hashrate",
{
"bosminer": {
"info": {"workSolver": {"realHashrate": {"mhs1M": None}}}
}
},
),
],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_devs", "devs")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards",
[
RPCAPICommand("api_temps", "temps"),
RPCAPICommand("api_devdetails", "devdetails"),
RPCAPICommand("api_devs", "devs"),
GraphQLCommand(
"graphql_boards",
{
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"realHashrate": {"mhs1M": None},
"hwDetails": {"chips": None},
"temperatures": {"degreesC": None},
}
}
}
}
}
},
},
},
},
"wattage": {
"cmd": "get_wattage",
"kwargs": {
"api_tunerstatus": {"api": "tunerstatus"},
"graphql_wattage": {
"web": {
"bosminer": {
"info": {"workSolver": {"power": {"approxConsumptionW": None}}}
}
}
},
},
},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {
"api_tunerstatus": {"api": "tunerstatus"},
"graphql_wattage_limit": {
"web": {
"bosminer": {"info": {"workSolver": {"power": {"limitW": None}}}}
}
},
},
},
"fans": {
"cmd": "get_fans",
"kwargs": {
"api_fans": {"api": "fans"},
"graphql_fans": {
"web": {"bosminer": {"info": {"fans": {"name": None, "rpm": None}}}}
},
},
},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"errors": {
"cmd": "get_errors",
"kwargs": {
"api_tunerstatus": {"api": "tunerstatus"},
"graphql_errors": {
"web": {
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"tuner": {"statusMessages": None},
},
),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage",
[
RPCAPICommand("api_tunerstatus", "tunerstatus"),
GraphQLCommand(
"graphql_wattage",
{
"bosminer": {
"info": {
"workSolver": {"power": {"approxConsumptionW": None}}
}
}
},
),
],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit",
[
RPCAPICommand("api_tunerstatus", "tunerstatus"),
GraphQLCommand(
"graphql_wattage_limit",
{"bosminer": {"info": {"workSolver": {"power": {"limitW": None}}}}},
),
],
),
str(DataOptions.FANS): DataFunction(
"get_fans",
[
RPCAPICommand("api_fans", "fans"),
GraphQLCommand(
"graphql_fans",
{"bosminer": {"info": {"fans": {"name": None, "rpm": None}}}},
),
],
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"get_errors",
[
RPCAPICommand("api_tunerstatus", "tunerstatus"),
GraphQLCommand(
"graphql_errors",
{
"bosminer": {
"info": {
"workSolver": {
"childSolvers": {
"name": None,
"tuner": {"statusMessages": None},
}
}
}
}
}
}
},
},
},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"graphql_fault_light": {"web": {"bos": {"faultLight": None}}}},
},
"pools": {
"cmd": "get_pools",
"kwargs": {
"api_pools": {"api": "pools"},
"graphql_pools": {
"web": {
"bosminer": {
"config": {
"... on BosminerConfig": {
"groups": {
"pools": {"url": None, "user": None},
"strategy": {
"... on QuotaStrategy": {"quota": None}
},
}
}
}
}
}
},
},
},
"is_mining": {
"cmd": "is_mining",
"kwargs": {"api_devdetails": {"api": "devdetails"}},
},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_summary": {"api": "summary"}},
},
}
},
),
],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[GraphQLCommand("graphql_fault_light", {"bos": {"faultLight": None}})],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [RPCAPICommand("api_devdetails", "devdetails")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BOSMiner(BaseMiner):
@@ -236,7 +231,6 @@ class BOSMiner(BaseMiner):
return result
async def fault_light_on(self) -> bool:
"""Sends command to turn on fault light on the miner."""
logging.debug(f"{self}: Sending fault_light on command.")
ret = await self.send_ssh_command("miner fault_light on")
logging.debug(f"{self}: fault_light on command completed.")
@@ -246,7 +240,6 @@ class BOSMiner(BaseMiner):
return False
async def fault_light_off(self) -> bool:
"""Sends command to turn off fault light on the miner."""
logging.debug(f"{self}: Sending fault_light off command.")
self.light = False
ret = await self.send_ssh_command("miner fault_light off")
@@ -257,11 +250,9 @@ class BOSMiner(BaseMiner):
return False
async def restart_backend(self) -> bool:
"""Restart bosminer hashing process. Wraps [`restart_bosminer`][pyasic.miners.backends.bosminer.BOSMiner.restart_bosminer] to standardize."""
return await self.restart_bosminer()
async def restart_bosminer(self) -> bool:
"""Restart bosminer hashing process."""
logging.debug(f"{self}: Sending bosminer restart command.")
ret = await self.send_ssh_command("/etc/init.d/bosminer restart")
logging.debug(f"{self}: bosminer restart command completed.")
@@ -290,7 +281,6 @@ class BOSMiner(BaseMiner):
return False
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.")
ret = await self.send_ssh_command("/sbin/reboot")
logging.debug(f"{self}: Reboot command completed.")
@@ -542,7 +532,7 @@ class BOSMiner(BaseMiner):
pass
try:
async with (await self._get_ssh_connection()) as conn:
async with await self._get_ssh_connection() as conn:
if conn is not None:
data = await conn.run("cat /proc/sys/kernel/hostname")
host = data.stdout.strip()
@@ -601,8 +591,8 @@ class BOSMiner(BaseMiner):
graphql_boards: dict = None,
):
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if not graphql_boards and not (api_devs or api_temps or api_devdetails):
@@ -838,95 +828,6 @@ class BOSMiner(BaseMiner):
async def get_fan_psu(self) -> Optional[int]:
return None
async def get_pools(
self, api_pools: dict = None, graphql_pools: dict = None
) -> List[dict]:
if not graphql_pools and not api_pools:
try:
graphql_pools = await self.web.send_command(
{
"bosminer": {
"config": {
"... on BosminerConfig": {
"groups": {
"pools": {"urluser"},
"strategy": {"... on QuotaStrategy": {"quota"}},
}
}
}
}
}
)
except APIError:
pass
if graphql_pools:
groups = []
try:
g = graphql_pools["data"]["bosminer"]["config"]["groups"]
for group in g:
pools = {"quota": group["strategy"]["quota"]}
for i, pool in enumerate(group["pools"]):
pools[f"pool_{i + 1}_url"] = (
pool["url"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["user"]
groups.append(pools)
return groups
except (KeyError, TypeError):
pass
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
seen = []
groups = [{"quota": "0"}]
if api_pools.get("POOLS"):
for i, pool in enumerate(api_pools["POOLS"]):
if len(seen) == 0:
seen.append(pool["User"])
if not pool["User"] in seen:
# need to use get_config, as this will never read perfectly as there are some bad edge cases
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
else:
groups[0][f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
groups[0][f"pool_{i + 1}_user"] = pool["User"]
else:
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
return groups
async def get_errors(
self, api_tunerstatus: dict = None, graphql_errors: dict = None
) -> List[MinerErrorData]:
@@ -1063,7 +964,7 @@ class BOSMiner(BaseMiner):
except (TypeError, AttributeError):
return self.light
async def get_nominal_hashrate(self, api_devs: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_devs: dict = None) -> Optional[float]:
if not api_devs:
try:
api_devs = await self.api.devs()
@@ -1077,14 +978,14 @@ class BOSMiner(BaseMiner):
for board in api_devs["DEVS"]:
_id = board["ID"] - offset
nominal_hashrate = round(float(board["Nominal MHS"] / 1000000), 2)
if nominal_hashrate:
hr_list.append(nominal_hashrate)
expected_hashrate = round(float(board["Nominal MHS"] / 1000000), 2)
if expected_hashrate:
hr_list.append(expected_hashrate)
if len(hr_list) == 0:
return 0
else:
return round(
(sum(hr_list) / len(hr_list)) * self.ideal_hashboards, 2
(sum(hr_list) / len(hr_list)) * self.expected_hashboards, 2
)
except (IndexError, KeyError):
pass

View File

@@ -136,16 +136,13 @@ class BOSMinerOld(BOSMiner):
async def get_fw_ver(self, *args, **kwargs) -> Optional[str]:
return None
async def get_pools(self, *args, **kwargs) -> List[dict]:
return []
async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
return []
async def get_fault_light(self, *args, **kwargs) -> bool:
return False
async def get_nominal_hashrate(self, *args, **kwargs) -> Optional[float]:
async def get_expected_hashrate(self, *args, **kwargs) -> Optional[float]:
return None
async def get_data(self, allow_warning: bool = False, **kwargs) -> MinerData:

View File

@@ -23,76 +23,86 @@ from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
BTMINER_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_miner_info": {"api": "get_miner_info"},
},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {
"cmd": "get_api_ver",
"kwargs": {"api_get_version": {"api": "get_version"}},
},
"fw_ver": {
"cmd": "get_fw_ver",
"kwargs": {
"api_get_version": {"api": "get_version"},
"api_summary": {"api": "summary"},
},
},
"hostname": {
"cmd": "get_hostname",
"kwargs": {"api_get_miner_info": {"api": "get_miner_info"}},
},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_summary": {"api": "summary"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_devs": {"api": "devs"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {"api_summary": {"api": "summary"}}},
"wattage": {"cmd": "get_wattage", "kwargs": {"api_summary": {"api": "summary"}}},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {"api_summary": {"api": "summary"}},
},
"fans": {
"cmd": "get_fans",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_psu": {"api": "get_psu"},
},
},
"fan_psu": {
"cmd": "get_fan_psu",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_psu": {"api": "get_psu"},
},
},
"errors": {
"cmd": "get_errors",
"kwargs": {
"api_summary": {"api": "summary"},
"api_get_error_code": {"api": "get_error_code"},
},
},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"api_get_miner_info": {"api": "get_miner_info"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {"api_status": {"api": "status"}}},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_summary": {"api": "summary"}},
},
}
BTMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac",
[
RPCAPICommand("api_summary", "summary"),
RPCAPICommand("api_get_miner_info", "get_miner_info"),
],
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_get_version", "get_version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver",
[
RPCAPICommand("api_get_version", "get_version"),
RPCAPICommand("api_summary", "summary"),
],
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [RPCAPICommand("api_get_miner_info", "get_miner_info")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_devs", "devs")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"get_env_temp", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.FANS): DataFunction(
"get_fans",
[
RPCAPICommand("api_summary", "summary"),
RPCAPICommand("api_get_psu", "get_psu"),
],
),
str(DataOptions.FAN_PSU): DataFunction(
"get_fan_psu",
[
RPCAPICommand("api_summary", "summary"),
RPCAPICommand("api_get_psu", "get_psu"),
],
),
str(DataOptions.ERRORS): DataFunction(
"get_errors", [RPCAPICommand("api_get_error_code", "get_error_code")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light",
[RPCAPICommand("api_get_miner_info", "get_miner_info")],
),
str(DataOptions.IS_MINING): DataFunction(
"is_mining", [RPCAPICommand("api_status", "status")]
),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class BTMiner(BaseMiner):
@@ -410,8 +420,8 @@ class BTMiner(BaseMiner):
async def get_hashboards(self, api_devs: dict = None) -> List[HashBoard]:
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if not api_devs:
@@ -426,16 +436,17 @@ class BTMiner(BaseMiner):
if len(hashboards) < board["ASC"] + 1:
hashboards.append(
HashBoard(
slot=board["ASC"], expected_chips=self.nominal_chips
slot=board["ASC"], expected_chips=self.expected_chips
)
)
self.ideal_hashboards += 1
self.expected_hashboards += 1
hashboards[board["ASC"]].chip_temp = round(board["Chip Temp Avg"])
hashboards[board["ASC"]].temp = round(board["Temperature"])
hashboards[board["ASC"]].hashrate = round(
float(board["MHS 1m"] / 1000000), 2
)
hashboards[board["ASC"]].chips = board["Effective Chips"]
hashboards[board["ASC"]].serial_number = board["PCB SN"]
hashboards[board["ASC"]].missing = False
except (KeyError, IndexError):
pass
@@ -531,32 +542,6 @@ class BTMiner(BaseMiner):
except (KeyError, TypeError):
pass
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(
self, api_summary: dict = None, api_get_error_code: dict = None
) -> List[MinerErrorData]:
@@ -592,7 +577,7 @@ class BTMiner(BaseMiner):
return errors
async def get_nominal_hashrate(self, api_summary: dict = None):
async def get_expected_hashrate(self, api_summary: dict = None):
if not api_summary:
try:
api_summary = await self.api.summary()
@@ -601,9 +586,9 @@ class BTMiner(BaseMiner):
if api_summary:
try:
nominal_hashrate = api_summary["SUMMARY"][0]["Factory GHS"]
if nominal_hashrate:
return round(nominal_hashrate / 1000, 2)
expected_hashrate = api_summary["SUMMARY"][0]["Factory GHS"]
if expected_hashrate:
return round(expected_hashrate / 1000, 2)
except (KeyError, IndexError):
pass

View File

@@ -23,34 +23,50 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
CGMINER_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_stats": {"api": "stats"}},
},
}
CGMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class CGMiner(BaseMiner):
@@ -96,11 +112,9 @@ class CGMiner(BaseMiner):
return result
async def restart_backend(self) -> bool:
"""Restart cgminer hashing process. Wraps [`restart_cgminer`][pyasic.miners.backends.cgminer.CGMiner.restart_cgminer] to standardize."""
return await self.restart_cgminer()
async def restart_cgminer(self) -> bool:
"""Restart cgminer hashing process."""
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
commands = ";".join(commands)
ret = await self.send_ssh_command(commands)
@@ -109,7 +123,6 @@ class CGMiner(BaseMiner):
return True
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.")
ret = await self.send_ssh_command("reboot")
if ret is None:
@@ -259,9 +272,11 @@ class CGMiner(BaseMiner):
if board_offset == -1:
board_offset = 1
for i in range(board_offset, board_offset + self.ideal_hashboards):
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.nominal_chips
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
@@ -328,39 +343,13 @@ class CGMiner(BaseMiner):
async def get_fan_psu(self) -> Optional[int]:
return None
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []
async def get_fault_light(self) -> bool:
return False
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
# X19 method, not sure compatibility
if not api_stats:
try:
@@ -370,17 +359,17 @@ class CGMiner(BaseMiner):
if api_stats:
try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
expected_rate = api_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
if rate_unit == "GH":
return round(ideal_rate / 1000, 2)
return round(expected_rate / 1000, 2)
if rate_unit == "MH":
return round(ideal_rate / 1000000, 2)
return round(expected_rate / 1000000, 2)
else:
return round(ideal_rate, 2)
return round(expected_rate, 2)
except (KeyError, IndexError):
pass

View File

@@ -23,36 +23,52 @@ from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.base import DataFunction, DataLocations, DataOptions, RPCAPICommand
AVALON_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"api_version": {"api": "version"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {"mac": {"api": "version"}}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_devs": {"api": "devs"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {"api_stats": {"api": "stats"}}},
"wattage": {"cmd": "get_wattage", "kwargs": {}},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {"api_stats": {"api": "stats"}},
},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"api_stats": {"api": "stats"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
}
AVALON_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_devs", "devs")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"get_env_temp", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.WATTAGE): DataFunction("get_wattage"),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class CGMinerAvalon(CGMiner):
@@ -115,7 +131,7 @@ class CGMinerAvalon(CGMiner):
@staticmethod
def parse_stats(stats):
_stats_items = re.findall(".+?\[*?]", stats)
_stats_items = re.findall(".+?\\[*?]", stats)
stats_items = []
stats_dict = {}
for item in _stats_items:
@@ -201,8 +217,8 @@ class CGMinerAvalon(CGMiner):
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if not api_stats:
@@ -218,7 +234,7 @@ class CGMinerAvalon(CGMiner):
except (IndexError, KeyError, ValueError, TypeError):
return hashboards
for board in range(self.ideal_hashboards):
for board in range(self.expected_hashboards):
try:
hashboards[board].chip_temp = int(parsed_stats["MTmax"][board])
except LookupError:
@@ -247,7 +263,7 @@ class CGMinerAvalon(CGMiner):
return hashboards
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
if not api_stats:
try:
api_stats = await self.api.stats()
@@ -317,32 +333,6 @@ class CGMinerAvalon(CGMiner):
pass
return fans_data
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(self) -> List[MinerErrorData]:
return []

View File

@@ -14,49 +14,74 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import List, Optional, Tuple, Union
from typing import List, Optional, Tuple
from pyasic import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
WebAPICommand,
)
from pyasic.web.epic import ePICWebAPI
EPIC_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "network"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"web_summary": {"web": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"web_summary": {"web": "summary"}},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {
"web_summary": {"web": "summary"},
"web_hashrate": {"web": "hashrate"},
},
},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}},
"fans": {"cmd": "get_fans", "kwargs": {"web_summary": {"web": "summary"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {"web_summary": {"web": "summary"}},
},
"pools": {"cmd": "get_pools", "kwargs": {"web_summary": {"web": "summary"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {"web_summary": {"web": "summary"}}},
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
}
EPIC_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_network", "network")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction("get_api_ver"),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards",
[
WebAPICommand("web_summary", "summary"),
WebAPICommand("web_hashrate", "hashrate"),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"get_errors", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"get_fault_light", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class ePIC(BMMiner):
class ePIC(BaseMiner):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver)
# interfaces
@@ -72,6 +97,23 @@ class ePIC(BMMiner):
return self.model + " (ePIC)"
return "? (ePIC)"
async def get_config(self) -> MinerConfig:
summary = None
try:
summary = await self.web.summary()
except APIError as e:
logger.warning(e)
except LookupError:
pass
if summary is not None:
cfg = MinerConfig.from_epic(summary)
else:
cfg = MinerConfig()
self.config = cfg
return self.config
async def restart_backend(self) -> bool:
data = await self.web.restart_epic()
if data:
@@ -108,13 +150,13 @@ class ePIC(BMMiner):
pass
return False
async def get_mac(self, web_summary: dict = None) -> str:
if not web_summary:
web_summary = await self.web.network()
if web_summary:
async def get_mac(self, web_network: dict = None) -> str:
if not web_network:
web_network = await self.web.network()
if web_network:
try:
for network in web_summary:
mac = web_summary[network]["mac_address"]
for network in web_network:
mac = web_network[network]["mac_address"]
return mac
except KeyError:
pass
@@ -160,7 +202,7 @@ class ePIC(BMMiner):
logger.error(e)
pass
async def get_nominal_hashrate(self, web_summary: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, web_summary: dict = None) -> Optional[float]:
# get hr from API
if not web_summary:
try:
@@ -227,8 +269,8 @@ class ePIC(BMMiner):
except APIError:
pass
hb_list = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if web_summary["HBs"] != None:
for hb in web_summary["HBs"]:
@@ -247,32 +289,6 @@ class ePIC(BMMiner):
async def is_mining(self, *args, **kwargs) -> Optional[bool]:
return None
async def get_pools(self, web_summary: dict = None) -> List[dict]:
groups = []
if not web_summary:
try:
web_summary = await self.api.summary()
except APIError:
pass
if web_summary:
try:
pools = {}
for i, pool in enumerate(web_summary["StratumConfigs"]):
pools[f"pool_{i + 1}_url"] = (
pool["pool"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["login"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_uptime(self, web_summary: dict = None) -> Optional[int]:
if not web_summary:
web_summary = await self.web.summary()
@@ -308,3 +324,33 @@ class ePIC(BMMiner):
except KeyError:
pass
return errors
def fault_light_off(self) -> bool:
return False
def fault_light_on(self) -> bool:
return False
def get_api_ver(self, *args, **kwargs) -> Optional[str]:
pass
def get_config(self) -> MinerConfig:
return self.config
def get_env_temp(self, *args, **kwargs) -> Optional[float]:
pass
def get_fan_psu(self, *args, **kwargs) -> Optional[int]:
pass
def get_version(self, *args, **kwargs) -> Tuple[Optional[str], Optional[str]]:
pass
def get_wattage_limit(self, *args, **kwargs) -> Optional[int]:
pass
def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
pass
def set_power_limit(self, wattage: int) -> bool:
return False

View File

@@ -26,74 +26,52 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner
from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.bosminer import BOSMinerWebAPI
LUXMINER_DATA_LOC = {
"mac": {
"cmd": "get_mac",
"kwargs": {"api_config": {"api": "config"}},
},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {
"cmd": "get_api_ver",
"kwargs": {},
},
"fw_ver": {
"cmd": "get_fw_ver",
"kwargs": {},
},
"hostname": {
"cmd": "get_hostname",
"kwargs": {},
},
"hashrate": {
"cmd": "get_hashrate",
"kwargs": {},
},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {},
},
"hashboards": {
"cmd": "get_hashboards",
"kwargs": {},
},
"wattage": {
"cmd": "get_wattage",
"kwargs": {},
},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {},
},
"fans": {
"cmd": "get_fans",
"kwargs": {},
},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"errors": {
"cmd": "get_errors",
"kwargs": {},
},
"fault_light": {
"cmd": "get_fault_light",
"kwargs": {},
},
"pools": {
"cmd": "get_pools",
"kwargs": {},
},
"is_mining": {
"cmd": "is_mining",
"kwargs": {},
},
"uptime": {
"cmd": "get_uptime",
"kwargs": {"api_stats": {"api": "stats"}},
},
}
LUXMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [RPCAPICommand("api_config", "config")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction("get_api_ver"),
str(DataOptions.FW_VERSION): DataFunction("get_fw_ver"),
str(DataOptions.HOSTNAME): DataFunction("get_hostname"),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [RPCAPICommand("api_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [RPCAPICommand("api_power", "power")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction("get_wattage_limit"),
str(DataOptions.FANS): DataFunction(
"get_fans", [RPCAPICommand("api_fans", "fans")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction(
"get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class LUXMiner(BaseMiner):
@@ -129,7 +107,6 @@ class LUXMiner(BaseMiner):
return
async def fault_light_on(self) -> bool:
"""Sends command to turn on fault light on the miner."""
try:
session_id = await self._get_session()
if session_id:
@@ -140,7 +117,6 @@ class LUXMiner(BaseMiner):
return False
async def fault_light_off(self) -> bool:
"""Sends command to turn off fault light on the miner."""
try:
session_id = await self._get_session()
if session_id:
@@ -151,11 +127,9 @@ class LUXMiner(BaseMiner):
return False
async def restart_backend(self) -> bool:
"""Restart luxminer hashing process. Wraps [`restart_luxminer`][pyasic.miners.backends.luxminer.LUXMiner.restart_luxminer] to standardize."""
return await self.restart_luxminer()
async def restart_luxminer(self) -> bool:
"""Restart luxminer hashing process."""
try:
session_id = await self._get_session()
if session_id:
@@ -185,7 +159,6 @@ class LUXMiner(BaseMiner):
pass
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
try:
session_id = await self._get_session()
if session_id:
@@ -277,9 +250,11 @@ class LUXMiner(BaseMiner):
if board_offset == -1:
board_offset = 1
for i in range(board_offset, board_offset + self.ideal_hashboards):
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.nominal_chips
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
@@ -345,63 +320,13 @@ class LUXMiner(BaseMiner):
async def get_fan_psu(self) -> Optional[int]:
return None
async def get_pools(self, api_pools: dict = None) -> List[dict]:
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
seen = []
groups = [{"quota": "0"}]
if api_pools.get("POOLS"):
for i, pool in enumerate(api_pools["POOLS"]):
if len(seen) == 0:
seen.append(pool["User"])
if not pool["User"] in seen:
# need to use get_config, as this will never read perfectly as there are some bad edge cases
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
else:
groups[0][f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
groups[0][f"pool_{i + 1}_user"] = pool["User"]
else:
groups = []
cfg = await self.get_config()
if cfg:
for group in cfg.pool_groups:
pools = {"quota": group.quota}
for _i, _pool in enumerate(group.pools):
pools[f"pool_{_i + 1}_url"] = _pool.url.replace(
"stratum+tcp://", ""
).replace("stratum2+tcp://", "")
pools[f"pool_{_i + 1}_user"] = _pool.username
groups.append(pools)
return groups
return groups
async def get_errors(self) -> List[MinerErrorData]:
pass
async def get_fault_light(self) -> bool:
pass
async def get_nominal_hashrate(self, api_stats: dict = None) -> Optional[float]:
async def get_expected_hashrate(self, api_stats: dict = None) -> Optional[float]:
if not api_stats:
try:
api_stats = await self.api.stats()
@@ -410,17 +335,17 @@ class LUXMiner(BaseMiner):
if api_stats:
try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"]
expected_rate = api_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
if rate_unit == "GH":
return round(ideal_rate / 1000, 2)
return round(expected_rate / 1000, 2)
if rate_unit == "MH":
return round(ideal_rate / 1000000, 2)
return round(expected_rate / 1000000, 2)
else:
return round(ideal_rate, 2)
return round(expected_rate, 2)
except (KeyError, IndexError):
pass

View File

@@ -19,34 +19,57 @@ from typing import Optional
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.vnish import VNishWebAPI
VNISH_DATA_LOC = {
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "summary"}}},
"model": {"cmd": "get_model", "kwargs": {}},
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}},
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}},
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}},
"nominal_hashrate": {
"cmd": "get_nominal_hashrate",
"kwargs": {"api_stats": {"api": "stats"}},
},
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}},
"env_temp": {"cmd": "get_env_temp", "kwargs": {}},
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}},
"wattage_limit": {
"cmd": "get_wattage_limit",
"kwargs": {"web_settings": {"web": "settings"}},
},
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}},
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}},
"errors": {"cmd": "get_errors", "kwargs": {}},
"fault_light": {"cmd": "get_fault_light", "kwargs": {}},
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}},
"is_mining": {"cmd": "is_mining", "kwargs": {}},
"uptime": {"cmd": "get_uptime", "kwargs": {}},
}
VNISH_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"get_mac", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.MODEL): DataFunction("get_model"),
str(DataOptions.API_VERSION): DataFunction(
"get_api_ver", [RPCAPICommand("api_version", "version")]
),
str(DataOptions.FW_VERSION): DataFunction(
"get_fw_ver", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HOSTNAME): DataFunction(
"get_hostname", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.HASHRATE): DataFunction(
"get_hashrate", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.HASHBOARDS): DataFunction(
"get_hashboards", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"get_wattage", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"get_wattage_limit", [WebAPICommand("web_settings", "settings")]
),
str(DataOptions.FANS): DataFunction(
"get_fans", [WebAPICommand("web_summary", "summary")]
),
str(DataOptions.FAN_PSU): DataFunction("get_fan_psu"),
str(DataOptions.ERRORS): DataFunction("get_errors"),
str(DataOptions.FAULT_LIGHT): DataFunction("get_fault_light"),
str(DataOptions.IS_MINING): DataFunction("is_mining"),
str(DataOptions.UPTIME): DataFunction("get_uptime"),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class VNish(BMMiner):

View File

@@ -17,7 +17,9 @@ import asyncio
import ipaddress
import logging
from abc import ABC, abstractmethod
from typing import List, Optional, Tuple, TypeVar
from dataclasses import dataclass, field, make_dataclass
from enum import Enum
from typing import List, Optional, Tuple, TypeVar, Union
import asyncssh
@@ -27,6 +29,70 @@ from pyasic.data.error_codes import MinerErrorData
from pyasic.logger import logger
class DataOptions(Enum):
MAC = "mac"
MODEL = "model"
API_VERSION = "api_ver"
FW_VERSION = "fw_ver"
HOSTNAME = "hostname"
HASHRATE = "hashrate"
EXPECTED_HASHRATE = "expected_hashrate"
HASHBOARDS = "hashboards"
ENVIRONMENT_TEMP = "env_temp"
WATTAGE = "wattage"
WATTAGE_LIMIT = "wattage_limit"
FANS = "fans"
FAN_PSU = "fan_psu"
ERRORS = "errors"
FAULT_LIGHT = "fault_light"
IS_MINING = "is_mining"
UPTIME = "uptime"
CONFIG = "config"
def __str__(self):
return self.value
@dataclass
class RPCAPICommand:
name: str
cmd: str
@dataclass
class WebAPICommand:
name: str
cmd: str
@dataclass
class GRPCCommand(WebAPICommand):
name: str
cmd: str
@dataclass
class GraphQLCommand(WebAPICommand):
name: str
cmd: dict
@dataclass
class DataFunction:
cmd: str
kwargs: list[
Union[RPCAPICommand, WebAPICommand, GRPCCommand, GraphQLCommand]
] = field(default_factory=list)
DataLocations = make_dataclass(
"DataLocations",
[(enum_value.value, str) for enum_value in DataOptions],
)
# add default value with
# [(enum_value.value, str, , DataFunction(enum_value.value)) for enum_value in DataOptions],
class BaseMiner(ABC):
def __init__(self, ip: str, *args, **kwargs) -> None:
# interfaces
@@ -42,11 +108,11 @@ class BaseMiner(ABC):
self.make = None
self.model = None
# physical attributes
self.ideal_hashboards = 3
self.nominal_chips = 0
self.expected_hashboards = 3
self.expected_chips = 0
self.fan_count = 2
# data gathering locations
self.data_locations = None
self.data_locations: DataLocations = None
# autotuning/shutdown support
self.supports_autotuning = False
self.supports_shutdown = False
@@ -173,7 +239,7 @@ class BaseMiner(ABC):
@abstractmethod
async def get_config(self) -> MinerConfig:
# Not a data gathering function, since this is used for configuration and not MinerData
# Not a data gathering function, since this is used for configuration
"""Get the mining configuration of the miner and return it as a [`MinerConfig`][pyasic.config.MinerConfig].
Returns:
@@ -359,15 +425,6 @@ class BaseMiner(ABC):
"""
pass
@abstractmethod
async def get_pools(self, *args, **kwargs) -> List[dict]:
"""Get pool information from the miner.
Returns:
Pool groups and quotas in a list of dicts.
"""
pass
@abstractmethod
async def get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
"""Get a list of the errors the miner is experiencing.
@@ -387,7 +444,7 @@ class BaseMiner(ABC):
pass
@abstractmethod
async def get_nominal_hashrate(self, *args, **kwargs) -> Optional[float]:
async def get_expected_hashrate(self, *args, **kwargs) -> Optional[float]:
"""Get the nominal hashrate from factory if available.
Returns:
@@ -414,28 +471,33 @@ class BaseMiner(ABC):
pass
async def _get_data(
self, allow_warning: bool, include: list = None, exclude: list = None
self,
allow_warning: bool,
include: List[Union[str, DataOptions]] = None,
exclude: List[Union[str, DataOptions]] = None,
) -> dict:
if include is None:
if include is not None:
include = [str(i) for i in include]
else:
# everything
include = list(self.data_locations.keys())
include = [str(enum_value.value) for enum_value in DataOptions]
if exclude is not None:
for item in exclude:
if item in include:
include.remove(item)
if str(item) in include:
include.remove(str(item))
api_multicommand = set()
web_multicommand = []
for data_name in include:
try:
fn_args = self.data_locations[data_name]["kwargs"]
for arg_name in fn_args:
if fn_args[arg_name].get("api"):
api_multicommand.add(fn_args[arg_name]["api"])
if fn_args[arg_name].get("web"):
if not fn_args[arg_name]["web"] in web_multicommand:
web_multicommand.append(fn_args[arg_name]["web"])
fn_args = getattr(self.data_locations, data_name).kwargs
for arg in fn_args:
if isinstance(arg, RPCAPICommand):
api_multicommand.add(arg.cmd)
if isinstance(arg, WebAPICommand):
if arg.cmd not in web_multicommand:
web_multicommand.append(arg.cmd)
except KeyError as e:
logger.error(e, data_name)
continue
@@ -465,59 +527,36 @@ class BaseMiner(ABC):
for data_name in include:
try:
fn_args = self.data_locations[data_name]["kwargs"]
args_to_send = {k: None for k in fn_args}
for arg_name in fn_args:
fn_args = getattr(self.data_locations, data_name).kwargs
args_to_send = {k.name: None for k in fn_args}
for arg in fn_args:
try:
if fn_args[arg_name].get("api"):
if isinstance(arg, RPCAPICommand):
if api_command_data.get("multicommand"):
args_to_send[arg_name] = api_command_data[
fn_args[arg_name]["api"]
][0]
args_to_send[arg.name] = api_command_data[arg.cmd][0]
else:
args_to_send[arg_name] = api_command_data
if fn_args[arg_name].get("web"):
args_to_send[arg.name] = api_command_data
if isinstance(arg, WebAPICommand):
if web_command_data is not None:
if web_command_data.get("multicommand"):
args_to_send[arg_name] = web_command_data[
fn_args[arg_name]["web"]
]
args_to_send[arg.name] = web_command_data[arg.cmd]
else:
if not web_command_data == {"multicommand": False}:
args_to_send[arg_name] = web_command_data
args_to_send[arg.name] = web_command_data
except LookupError:
args_to_send[arg_name] = None
args_to_send[arg.name] = None
except LookupError:
continue
function = getattr(self, self.data_locations[data_name]["cmd"])
if not data_name == "pools":
miner_data[data_name] = await function(**args_to_send)
else:
pools_data = await function(**args_to_send)
if pools_data:
try:
miner_data["pool_1_url"] = pools_data[0]["pool_1_url"]
miner_data["pool_1_user"] = pools_data[0]["pool_1_user"]
except KeyError:
pass
if len(pools_data) > 1:
miner_data["pool_2_url"] = pools_data[1]["pool_1_url"]
miner_data["pool_2_user"] = pools_data[1]["pool_1_user"]
miner_data[
"pool_split"
] = f"{pools_data[0]['quota']}/{pools_data[1]['quota']}"
else:
try:
miner_data["pool_2_url"] = pools_data[0]["pool_2_url"]
miner_data["pool_2_user"] = pools_data[0]["pool_2_user"]
miner_data["quota"] = "0"
except KeyError:
pass
function = getattr(self, getattr(self.data_locations, data_name).cmd)
miner_data[data_name] = await function(**args_to_send)
return miner_data
async def get_data(
self, allow_warning: bool = False, include: list = None, exclude: list = None
self,
allow_warning: bool = False,
include: List[Union[str, DataOptions]] = None,
exclude: List[Union[str, DataOptions]] = None,
) -> MinerData:
"""Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].
@@ -532,11 +571,11 @@ class BaseMiner(ABC):
data = MinerData(
ip=str(self.ip),
make=self.make,
ideal_chips=self.nominal_chips * self.ideal_hashboards,
ideal_hashboards=self.ideal_hashboards,
expected_chips=self.expected_chips * self.expected_hashboards,
expected_hashboards=self.expected_hashboards,
hashboards=[
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
],
)

View File

@@ -152,8 +152,8 @@ class CGMinerA10X(CGMiner, A10X):
self, api_stats: dict = None, web_get_all: dict = None
) -> List[HashBoard]:
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if web_get_all:
web_get_all = web_get_all["all"]
@@ -267,32 +267,6 @@ class CGMinerA10X(CGMiner, A10X):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(
self, web_get_error_detail: dict = None
) -> List[MinerErrorData]: # noqa: named this way for automatic functionality

View File

@@ -133,8 +133,8 @@ class CGMinerT3HPlus(CGMiner, T3HPlus):
web_get_all = web_get_all["all"]
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if not api_stats:
@@ -246,32 +246,6 @@ class CGMinerT3HPlus(CGMiner, T3HPlus):
return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]:
groups = []
if not api_pools:
try:
api_pools = await self.api.pools()
except APIError:
pass
if api_pools:
try:
pools = {}
for i, pool in enumerate(api_pools["POOLS"]):
pools[f"pool_{i + 1}_url"] = (
pool["URL"]
.replace("stratum+tcp://", "")
.replace("stratum2+tcp://", "")
)
pools[f"pool_{i + 1}_user"] = pool["User"]
pools["quota"] = pool["Quota"] if pool.get("Quota") else "0"
groups.append(pools)
except KeyError:
pass
return groups
async def get_errors(
self, web_get_error_detail: dict = None
) -> List[MinerErrorData]: # noqa: named this way for automatic functionality

View File

@@ -92,6 +92,7 @@ MINER_CLASSES = {
"ANTMINER S19 XP": BMMinerS19XP,
"ANTMINER S19A": BMMinerS19a,
"ANTMINER S19A PRO": BMMinerS19aPro,
"ANTMINER S19 PRO HYD.": BMMinerS19ProHydro,
"ANTMINER T19": BMMinerT19,
},
MinerTypes.WHATSMINER: {
@@ -346,13 +347,18 @@ MINER_CLASSES = {
"ANTMINER T17+": BOSMinerT17Plus,
"ANTMINER T17E": BOSMinerT17e,
"ANTMINER S19": BOSMinerS19,
"ANTMINER S19+": BOSMinerS19Plus,
"ANTMINER S19 PRO": BOSMinerS19Pro,
"ANTMINER S19A": BOSMinerS19a,
"ANTMINER S19A Pro": BOSMinerS19aPro,
"ANTMINER S19J": BOSMinerS19j,
"ANTMINER S19J88NOPIC": BOSMinerS19jNoPIC,
"ANTMINER S19J PRO": BOSMinerS19jPro,
"ANTMINER S19J PRO NOPIC": BOSMinerS19jPro,
"ANTMINER T19": BOSMinerT19,
"ANTMINER S19J PRO+": BOSMinerS19jProPlus,
"ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC,
"ANTMINER S19XP": BOSMinerS19XP,
"ANTMINER T19": BOSMinerT19,
},
MinerTypes.VNISH: {
None: VNish,

View File

@@ -22,5 +22,5 @@ class Z15(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Z15"
self.nominal_chips = 3
self.expected_chips = 3
self.fan_count = 2

View File

@@ -22,7 +22,7 @@ class S17(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S17"
self.nominal_chips = 48
self.expected_chips = 48
self.fan_count = 4
@@ -30,7 +30,7 @@ class S17Plus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.model = "S17+"
self.nominal_chips = 65
self.expected_chips = 65
self.fan_count = 4
@@ -39,7 +39,7 @@ class S17Pro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S17 Pro"
self.nominal_chips = 48
self.expected_chips = 48
self.fan_count = 4
@@ -48,5 +48,5 @@ class S17e(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S17e"
self.nominal_chips = 135
self.expected_chips = 135
self.fan_count = 4

View File

@@ -22,7 +22,7 @@ class T17(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "T17"
self.nominal_chips = 30
self.expected_chips = 30
self.fan_count = 4
@@ -31,7 +31,7 @@ class T17Plus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "T17+"
self.nominal_chips = 44
self.expected_chips = 44
self.fan_count = 4
@@ -40,5 +40,5 @@ class T17e(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "T17e"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 4

View File

@@ -22,7 +22,7 @@ class S19(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19"
self.nominal_chips = 76
self.expected_chips = 76
self.fan_count = 4
@@ -31,7 +31,7 @@ class S19NoPIC(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 No PIC"
self.nominal_chips = 88
self.expected_chips = 88
self.fan_count = 4
@@ -40,7 +40,7 @@ class S19Pro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 Pro"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 4
@@ -49,7 +49,7 @@ class S19i(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19i"
self.nominal_chips = 80
self.expected_chips = 80
self.fan_count = 4
@@ -58,7 +58,7 @@ class S19Plus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19+"
self.nominal_chips = 80
self.expected_chips = 80
self.fan_count = 4
@@ -67,7 +67,7 @@ class S19ProPlus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 Pro+"
self.nominal_chips = 120
self.expected_chips = 120
self.fan_count = 4
@@ -76,7 +76,7 @@ class S19XP(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 XP"
self.nominal_chips = 110
self.expected_chips = 110
self.fan_count = 4
@@ -85,7 +85,7 @@ class S19a(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19a"
self.nominal_chips = 72
self.expected_chips = 72
self.fan_count = 4
@@ -94,7 +94,7 @@ class S19aPro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19a Pro"
self.nominal_chips = 100
self.expected_chips = 100
self.fan_count = 4
@@ -103,7 +103,7 @@ class S19j(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 4
@@ -112,7 +112,7 @@ class S19jNoPIC(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j No PIC"
self.nominal_chips = 88
self.expected_chips = 88
self.fan_count = 4
@@ -121,7 +121,7 @@ class S19jPro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j Pro"
self.nominal_chips = 126
self.expected_chips = 126
self.fan_count = 4
@@ -130,7 +130,7 @@ class S19jProPlus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19j Pro+"
self.nominal_chips = 120
self.expected_chips = 120
self.fan_count = 4
@@ -139,7 +139,7 @@ class S19kPro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19k Pro"
self.nominal_chips = 77
self.expected_chips = 77
self.fan_count = 4
@@ -148,7 +148,7 @@ class S19L(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19L"
self.nominal_chips = 76
self.expected_chips = 76
self.fan_count = 4
@@ -157,5 +157,15 @@ class S19kProNoPIC(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19k Pro No PIC"
self.nominal_chips = 77
self.expected_chips = 77
self.fan_count = 4
class S19ProHydro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19 Pro Hydro"
self.expected_chips = 180
self.expected_hashboards = 4
self.fan_count = 0

View File

@@ -22,5 +22,5 @@ class T19(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "T19"
self.nominal_chips = 76
self.expected_chips = 76
self.fan_count = 4

View File

@@ -30,6 +30,7 @@ from .S19 import (
S19NoPIC,
S19Plus,
S19Pro,
S19ProHydro,
S19ProPlus,
)
from .T19 import T19

View File

@@ -22,6 +22,6 @@ class D3(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "D3"
self.nominal_chips = 60
self.ideal_hashboards = 3
self.expected_chips = 60
self.expected_hashboards = 3
self.fan_count = 2

View File

@@ -22,6 +22,6 @@ class HS3(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "HS3"
self.nominal_chips = 92
self.ideal_hashboards = 3
self.expected_chips = 92
self.expected_hashboards = 3
self.fan_count = 2

View File

@@ -21,5 +21,5 @@ class L3Plus(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "L3+"
self.nominal_chips = 72
self.expected_chips = 72
self.fan_count = 2

View File

@@ -22,6 +22,6 @@ class DR5(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "DR5"
self.nominal_chips = 72
self.ideal_hashboards = 3
self.expected_chips = 72
self.expected_hashboards = 3
self.fan_count = 2

View File

@@ -21,5 +21,5 @@ class L7(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "L7"
self.nominal_chips = 120
self.expected_chips = 120
self.fan_count = 4

View File

@@ -22,6 +22,6 @@ class E9Pro(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "E9Pro"
self.nominal_chips = 8
self.ideal_hashboards = 2
self.expected_chips = 8
self.expected_hashboards = 2
self.fan_count = 4

View File

@@ -22,7 +22,7 @@ class S9(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S9"
self.nominal_chips = 63
self.expected_chips = 63
self.fan_count = 2
@@ -31,7 +31,7 @@ class S9i(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S9i"
self.nominal_chips = 63
self.expected_chips = 63
self.fan_count = 2
@@ -40,5 +40,5 @@ class S9j(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S9j"
self.nominal_chips = 63
self.expected_chips = 63
self.fan_count = 2

View File

@@ -22,5 +22,5 @@ class T9(AntMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "T9"
self.nominal_chips = 54
self.expected_chips = 54
self.fan_count = 2

View File

@@ -22,5 +22,5 @@ class Avalon1026(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 1026"
self.nominal_chips = 80
self.expected_chips = 80
self.fan_count = 2

View File

@@ -22,5 +22,5 @@ class Avalon1047(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 1047"
self.nominal_chips = 80
self.expected_chips = 80
self.fan_count = 2

View File

@@ -22,5 +22,5 @@ class Avalon1066(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 1066"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 4

View File

@@ -23,5 +23,5 @@ class Avalon1166Pro(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 1166 Pro"
self.nominal_chips = 120
self.expected_chips = 120
self.fan_count = 4

View File

@@ -23,5 +23,5 @@ class Avalon1246(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 1246"
self.nominal_chips = 120
self.expected_chips = 120
self.fan_count = 4

View File

@@ -22,6 +22,6 @@ class Avalon721(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 721"
self.ideal_hashboards = 4
self.nominal_chips = 18
self.expected_hashboards = 4
self.expected_chips = 18
self.fan_count = 1

View File

@@ -22,6 +22,6 @@ class Avalon741(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 741"
self.ideal_hashboards = 4
self.nominal_chips = 22
self.expected_hashboards = 4
self.expected_chips = 22
self.fan_count = 1

View File

@@ -22,6 +22,6 @@ class Avalon761(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 761"
self.ideal_hashboards = 4
self.nominal_chips = 18
self.expected_hashboards = 4
self.expected_chips = 18
self.fan_count = 1

View File

@@ -22,6 +22,6 @@ class Avalon821(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 821"
self.ideal_hashboards = 4
self.nominal_chips = 26
self.expected_hashboards = 4
self.expected_chips = 26
self.fan_count = 1

View File

@@ -22,6 +22,6 @@ class Avalon841(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 841"
self.ideal_hashboards = 4
self.nominal_chips = 26
self.expected_hashboards = 4
self.expected_chips = 26
self.fan_count = 1

View File

@@ -22,6 +22,6 @@ class Avalon851(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 851"
self.ideal_hashboards = 4
self.nominal_chips = 26
self.expected_hashboards = 4
self.expected_chips = 26
self.fan_count = 1

View File

@@ -22,6 +22,6 @@ class Avalon921(AvalonMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "Avalon 921"
self.ideal_hashboards = 4
self.nominal_chips = 26
self.expected_hashboards = 4
self.expected_chips = 26
self.fan_count = 1

View File

@@ -21,6 +21,6 @@ class CK5(GoldshellMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "CK5"
self.ideal_hashboards = 4
self.nominal_chips = 46
self.expected_hashboards = 4
self.expected_chips = 46
self.fan_count = 4

View File

@@ -21,6 +21,6 @@ class HS5(GoldshellMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "HS5"
self.ideal_hashboards = 4
self.nominal_chips = 46
self.expected_hashboards = 4
self.expected_chips = 46
self.fan_count = 4

View File

@@ -21,6 +21,6 @@ class KD5(GoldshellMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "KD5"
self.ideal_hashboards = 4
self.nominal_chips = 46
self.expected_hashboards = 4
self.expected_chips = 46
self.fan_count = 4

View File

@@ -21,6 +21,6 @@ class KDMax(GoldshellMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "KD Max"
self.ideal_hashboards = 3
self.nominal_chips = 84
self.expected_hashboards = 3
self.expected_chips = 84
self.fan_count = 4

View File

@@ -22,5 +22,5 @@ class T3HPlus(InnosiliconMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "T3H+"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 4

View File

@@ -22,5 +22,5 @@ class M20V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20 V10"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2

View File

@@ -22,7 +22,7 @@ class M20PV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20P V10"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -31,5 +31,5 @@ class M20PV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20P V30"
self.nominal_chips = 148
self.expected_chips = 148
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M20SV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20S V10"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -33,7 +33,7 @@ class M20SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20S V20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -42,5 +42,5 @@ class M20SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20S V30"
self.nominal_chips = 140
self.expected_chips = 140
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M20SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20S+ V30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M20S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -24,5 +24,5 @@ class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21 V10"
self.nominal_chips = 33
self.expected_chips = 33
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M21SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21S V20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -33,7 +33,7 @@ class M21SV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21S V60"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -42,5 +42,5 @@ class M21SV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21S V70"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M21SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M21S+ V20"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M21S+ V20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -24,5 +24,5 @@ class M29V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M29 V10"
self.nominal_chips = 50
self.expected_chips = 50
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30 V10"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -33,5 +33,5 @@ class M30V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30 V20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2

View File

@@ -24,6 +24,6 @@ class M30KV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30K V10"
self.ideal_hashboards = 4
self.nominal_chips = 240
self.expected_hashboards = 4
self.expected_chips = 240
self.fan_count = 2

View File

@@ -25,5 +25,5 @@ class M30LV10(WhatsMiner): # noqa - ignore ABC method implementation
self.ip = ip
self.model = "M30L V10"
self.board_num = 4
self.nominal_chips = 144
self.expected_chips = 144
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V10"
self.nominal_chips = 148
self.expected_chips = 148
self.fan_count = 2
@@ -33,7 +33,7 @@ class M30SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V20"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -42,7 +42,7 @@ class M30SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V30"
self.nominal_chips = 164
self.expected_chips = 164
self.fan_count = 2
@@ -51,7 +51,7 @@ class M30SV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V40"
self.nominal_chips = 172
self.expected_chips = 172
self.fan_count = 2
@@ -60,7 +60,7 @@ class M30SV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V50"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -69,7 +69,7 @@ class M30SV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V60"
self.nominal_chips = 164
self.expected_chips = 164
self.fan_count = 2
@@ -78,7 +78,7 @@ class M30SV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -90,7 +90,7 @@ class M30SV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S V80"
self.nominal_chips = 129
self.expected_chips = 129
self.fan_count = 2
@@ -99,7 +99,7 @@ class M30SVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE10"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -108,7 +108,7 @@ class M30SVE20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -117,7 +117,7 @@ class M30SVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -126,7 +126,7 @@ class M30SVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE40"
self.nominal_chips = 123
self.expected_chips = 123
self.fan_count = 2
@@ -135,7 +135,7 @@ class M30SVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE50"
self.nominal_chips = 129
self.expected_chips = 129
self.fan_count = 2
@@ -144,7 +144,7 @@ class M30SVE60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE60"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -156,7 +156,7 @@ class M30SVE70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VE70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -168,7 +168,7 @@ class M30SVF10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF10"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -177,7 +177,7 @@ class M30SVF20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF20"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -186,7 +186,7 @@ class M30SVF30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VF30"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -195,7 +195,7 @@ class M30SVG10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG10"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -204,7 +204,7 @@ class M30SVG20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG20"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -213,7 +213,7 @@ class M30SVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG30"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -222,7 +222,7 @@ class M30SVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VG40"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -231,7 +231,7 @@ class M30SVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH10"
self.nominal_chips = 64
self.expected_chips = 64
self.fan_count = 2
@@ -240,7 +240,7 @@ class M30SVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -249,7 +249,7 @@ class M30SVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -261,7 +261,7 @@ class M30SVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH40"
self.nominal_chips = 64
self.expected_chips = 64
self.fan_count = 2
@@ -270,7 +270,7 @@ class M30SVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH50"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -279,7 +279,7 @@ class M30SVH60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VH60"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30SVH60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -291,5 +291,5 @@ class M30SVI20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S VI20"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V10"
self.nominal_chips = 215
self.expected_chips = 215
self.fan_count = 2
@@ -33,7 +33,7 @@ class M30SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V20"
self.nominal_chips = 255
self.expected_chips = 255
self.fan_count = 2
@@ -42,7 +42,7 @@ class M30SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -54,7 +54,7 @@ class M30SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V40"
self.nominal_chips = 235
self.expected_chips = 235
self.fan_count = 2
@@ -63,7 +63,7 @@ class M30SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V50"
self.nominal_chips = 225
self.expected_chips = 225
self.fan_count = 2
@@ -72,7 +72,7 @@ class M30SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V60"
self.nominal_chips = 245
self.expected_chips = 245
self.fan_count = 2
@@ -81,7 +81,7 @@ class M30SPlusV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V70"
self.nominal_chips = 235
self.expected_chips = 235
self.fan_count = 2
@@ -90,7 +90,7 @@ class M30SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V80"
self.nominal_chips = 245
self.expected_chips = 245
self.fan_count = 2
@@ -99,7 +99,7 @@ class M30SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V90"
self.nominal_chips = 225
self.expected_chips = 225
self.fan_count = 2
@@ -108,7 +108,7 @@ class M30SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ V100"
self.nominal_chips = 215
self.expected_chips = 215
self.fan_count = 2
@@ -117,7 +117,7 @@ class M30SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE30"
self.nominal_chips = 148
self.expected_chips = 148
self.fan_count = 2
@@ -126,7 +126,7 @@ class M30SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE40"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -135,7 +135,7 @@ class M30SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE50"
self.nominal_chips = 164
self.expected_chips = 164
self.fan_count = 2
@@ -144,7 +144,7 @@ class M30SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE60"
self.nominal_chips = 172
self.expected_chips = 172
self.fan_count = 2
@@ -153,7 +153,7 @@ class M30SPlusVE70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -165,7 +165,7 @@ class M30SPlusVE80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE80"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -177,7 +177,7 @@ class M30SPlusVE90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE90"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -189,7 +189,7 @@ class M30SPlusVE100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VE100"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -201,7 +201,7 @@ class M30SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VF20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -210,7 +210,7 @@ class M30SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VF30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -219,7 +219,7 @@ class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG20"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -228,7 +228,7 @@ class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG30"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -237,7 +237,7 @@ class M30SPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG40"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -246,7 +246,7 @@ class M30SPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG50"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -255,7 +255,7 @@ class M30SPlusVG60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VG60"
self.nominal_chips = 86
self.expected_chips = 86
self.fan_count = 2
@@ -264,7 +264,7 @@ class M30SPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH10"
self.nominal_chips = 64
self.expected_chips = 64
self.fan_count = 2
@@ -273,7 +273,7 @@ class M30SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -282,7 +282,7 @@ class M30SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH30"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -291,7 +291,7 @@ class M30SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH40"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -300,7 +300,7 @@ class M30SPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH50"
self.nominal_chips = 64
self.expected_chips = 64
self.fan_count = 2
@@ -309,5 +309,5 @@ class M30SPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S+ VH60"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2

View File

@@ -24,8 +24,8 @@ class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ V10"
self.ideal_hashboards = 4
self.nominal_chips = 255
self.expected_hashboards = 4
self.expected_chips = 255
self.fan_count = 2
@@ -34,8 +34,8 @@ class M30SPlusPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ V20"
self.ideal_hashboards = 4
self.nominal_chips = 255
self.expected_hashboards = 4
self.expected_chips = 255
self.fan_count = 2
@@ -44,7 +44,7 @@ class M30SPlusPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE30"
self.nominal_chips = 215
self.expected_chips = 215
self.fan_count = 2
@@ -53,7 +53,7 @@ class M30SPlusPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE40"
self.nominal_chips = 225
self.expected_chips = 225
self.fan_count = 2
@@ -62,7 +62,7 @@ class M30SPlusPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VE50"
self.nominal_chips = 235
self.expected_chips = 235
self.fan_count = 2
@@ -71,7 +71,7 @@ class M30SPlusPlusVF40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VF40"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -80,7 +80,7 @@ class M30SPlusPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VG30"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -89,7 +89,7 @@ class M30SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VG40"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -98,7 +98,7 @@ class M30SPlusPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VG50"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VG50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -110,7 +110,7 @@ class M30SPlusPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH10"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -119,7 +119,7 @@ class M30SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH20"
self.nominal_chips = 86
self.expected_chips = 86
self.fan_count = 2
@@ -128,7 +128,7 @@ class M30SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH30"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -137,7 +137,7 @@ class M30SPlusPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH40"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -146,7 +146,7 @@ class M30SPlusPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH50"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -155,7 +155,7 @@ class M30SPlusPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH60"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -164,7 +164,7 @@ class M30SPlusPlusVH70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH70"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -173,7 +173,7 @@ class M30SPlusPlusVH80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH80"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -182,7 +182,7 @@ class M30SPlusPlusVH90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH90"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -191,7 +191,7 @@ class M30SPlusPlusVH100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VH100"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -200,7 +200,7 @@ class M30SPlusPlusVJ20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VJ20"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -212,7 +212,7 @@ class M30SPlusPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M30S++ VJ30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -24,7 +24,7 @@ class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31 V10"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -33,5 +33,5 @@ class M31V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31 V20"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M31HV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31H V10"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 0
@@ -33,6 +33,6 @@ class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31H V40"
self.ideal_hashboards = 4
self.nominal_chips = 136
self.expected_hashboards = 4
self.expected_chips = 136
self.fan_count = 0

View File

@@ -24,5 +24,5 @@ class M31LV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31L V10"
self.nominal_chips = 114
self.expected_chips = 114
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M31SV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V10"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -33,7 +33,7 @@ class M31SV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -42,7 +42,7 @@ class M31SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -51,7 +51,7 @@ class M31SV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V40"
self.nominal_chips = 123
self.expected_chips = 123
self.fan_count = 2
@@ -60,7 +60,7 @@ class M31SV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V50"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -69,7 +69,7 @@ class M31SV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V60"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -78,7 +78,7 @@ class M31SV70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V70"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -87,7 +87,7 @@ class M31SV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V80"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SV80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -99,7 +99,7 @@ class M31SV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S V90"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -108,7 +108,7 @@ class M31SVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S VE10"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -117,7 +117,7 @@ class M31SVE20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S VE20"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2
@@ -126,7 +126,7 @@ class M31SVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S VE30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M31SVE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -24,7 +24,7 @@ class M31SEV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31SE V10"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -33,7 +33,7 @@ class M31SEV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31SE V20"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -42,5 +42,5 @@ class M31SEV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31SE V30"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M31SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V10"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -33,7 +33,7 @@ class M31SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -42,7 +42,7 @@ class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -51,7 +51,7 @@ class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V40"
self.nominal_chips = 123
self.expected_chips = 123
self.fan_count = 2
@@ -60,7 +60,7 @@ class M31SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V50"
self.nominal_chips = 148
self.expected_chips = 148
self.fan_count = 2
@@ -69,7 +69,7 @@ class M31SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V60"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -78,7 +78,7 @@ class M31SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V80"
self.nominal_chips = 129
self.expected_chips = 129
self.fan_count = 2
@@ -87,7 +87,7 @@ class M31SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V90"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -96,7 +96,7 @@ class M31SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V100"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -105,7 +105,7 @@ class M31SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE10"
self.nominal_chips = 82
self.expected_chips = 82
self.fan_count = 2
@@ -114,7 +114,7 @@ class M31SPlusVE20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE20"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -123,7 +123,7 @@ class M31SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE30"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -132,7 +132,7 @@ class M31SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE40"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -141,7 +141,7 @@ class M31SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE50"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -150,7 +150,7 @@ class M31SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE60"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -162,7 +162,7 @@ class M31SPlusVE80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VE80"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -174,7 +174,7 @@ class M31SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VF20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -183,7 +183,7 @@ class M31SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VF30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S+ VF30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -195,7 +195,7 @@ class M31SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VG20"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 2
@@ -204,7 +204,7 @@ class M31SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ VG30"
self.nominal_chips = 70
self.expected_chips = 70
self.fan_count = 2
@@ -213,7 +213,7 @@ class M31SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -222,5 +222,5 @@ class M31SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M31S+ V40"
self.nominal_chips = 123
self.expected_chips = 123
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M32V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M32 V10"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2
@@ -33,5 +33,5 @@ class M32V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M32 V20"
self.nominal_chips = 74
self.expected_chips = 74
self.fan_count = 2

View File

@@ -22,5 +22,5 @@ class M32S(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M32S"
self.nominal_chips = 78
self.expected_chips = 78
self.fan_count = 2

View File

@@ -24,7 +24,7 @@ class M33V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33 V10"
self.nominal_chips = 33
self.expected_chips = 33
self.fan_count = 0
@@ -33,7 +33,7 @@ class M33V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33 V20"
self.nominal_chips = 62
self.expected_chips = 62
self.fan_count = 0
@@ -42,5 +42,5 @@ class M33V30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33 V30"
self.nominal_chips = 66
self.expected_chips = 66
self.fan_count = 0

View File

@@ -24,6 +24,6 @@ class M33SVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S VG30"
self.ideal_hashboards = 4
self.nominal_chips = 116
self.expected_hashboards = 4
self.expected_chips = 116
self.fan_count = 0

View File

@@ -24,8 +24,8 @@ class M33SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S+ VG20"
self.ideal_hashboards = 4
self.nominal_chips = 112
self.expected_hashboards = 4
self.expected_chips = 112
self.fan_count = 0
@@ -34,8 +34,8 @@ class M33SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S+ VH20"
self.ideal_hashboards = 4
self.nominal_chips = 100
self.expected_hashboards = 4
self.expected_chips = 100
self.fan_count = 0
@@ -44,8 +44,8 @@ class M33SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S+ VH30"
self.ideal_hashboards = 4
self.nominal_chips = 0 # slot1 116, slot2 106, slot3 116, slot4 106
self.expected_hashboards = 4
self.expected_chips = 0 # slot1 116, slot2 106, slot3 116, slot4 106
warnings.warn(
"Unknown chip count for miner type M30S+ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

View File

@@ -24,8 +24,8 @@ class M33SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S++ VH20"
self.ideal_hashboards = 4
self.nominal_chips = 112
self.expected_hashboards = 4
self.expected_chips = 112
self.fan_count = 0
@@ -34,8 +34,8 @@ class M33SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S++ VH30"
self.ideal_hashboards = 4
self.nominal_chips = 0
self.expected_hashboards = 4
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M30S++ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -47,6 +47,6 @@ class M33SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M33S++ VG40"
self.ideal_hashboards = 4
self.nominal_chips = 174
self.expected_hashboards = 4
self.expected_chips = 174
self.fan_count = 0

View File

@@ -22,6 +22,6 @@ class M34SPlusVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M34S+ VE10"
self.ideal_hashboards = 4
self.nominal_chips = 116
self.expected_hashboards = 4
self.expected_chips = 116
self.fan_count = 0

View File

@@ -24,6 +24,6 @@ class M36SVE10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S VE10"
self.ideal_hashboards = 4
self.nominal_chips = 114
self.expected_hashboards = 4
self.expected_chips = 114
self.fan_count = 0

View File

@@ -24,6 +24,6 @@ class M36SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S+ VG30"
self.ideal_hashboards = 4
self.nominal_chips = 108
self.expected_hashboards = 4
self.expected_chips = 108
self.fan_count = 0

View File

@@ -24,6 +24,6 @@ class M36SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M36S++ VH30"
self.ideal_hashboards = 4
self.nominal_chips = 80
self.expected_hashboards = 4
self.expected_chips = 80
self.fan_count = 0

View File

@@ -24,7 +24,7 @@ class M39V10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M39 V10"
self.nominal_chips = 50
self.expected_chips = 50
self.fan_count = 0
@@ -33,7 +33,7 @@ class M39V20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M39 V20"
self.nominal_chips = 54
self.expected_chips = 54
self.fan_count = 0
@@ -42,5 +42,5 @@ class M39V30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M39 V30"
self.nominal_chips = 68
self.expected_chips = 68
self.fan_count = 0

View File

@@ -24,8 +24,8 @@ class M50VE30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VE30"
self.ideal_hashboards = 4
self.nominal_chips = 255
self.expected_hashboards = 4
self.expected_chips = 255
self.fan_count = 2
@@ -34,7 +34,7 @@ class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VG30"
self.nominal_chips = 156
self.expected_chips = 156
self.fan_count = 2
@@ -43,7 +43,7 @@ class M50VH10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH10"
self.nominal_chips = 86
self.expected_chips = 86
self.fan_count = 2
@@ -52,7 +52,7 @@ class M50VH20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH20"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -61,7 +61,7 @@ class M50VH30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH30"
self.nominal_chips = 117
self.expected_chips = 117
self.fan_count = 2
@@ -70,7 +70,7 @@ class M50VH40(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH40"
self.nominal_chips = 84
self.expected_chips = 84
self.fan_count = 2
@@ -79,7 +79,7 @@ class M50VH50(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH50"
self.nominal_chips = 105
self.expected_chips = 105
self.fan_count = 2
@@ -88,7 +88,7 @@ class M50VH60(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH60"
self.nominal_chips = 84
self.expected_chips = 84
self.fan_count = 2
@@ -97,7 +97,7 @@ class M50VH70(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH70"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VH70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -109,7 +109,7 @@ class M50VH80(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VH80"
self.nominal_chips = 111
self.expected_chips = 111
self.fan_count = 2
@@ -118,7 +118,7 @@ class M50VJ10(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VJ10"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VJ10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -130,7 +130,7 @@ class M50VJ20(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VJ20"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
@@ -142,7 +142,7 @@ class M50VJ30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M50 VJ30"
self.nominal_chips = 0
self.expected_chips = 0
warnings.warn(
"Unknown chip count for miner type M50 VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)

Some files were not shown because too many files have changed in this diff Show More