Compare commits

...

94 Commits

Author SHA1 Message Date
UpstreamData
4ed49c2321 version: bump version number. 2024-01-15 14:59:29 -07:00
UpstreamData
c069468803 bug: fix some bugs with epic, update miner repr, and remove get_model from braiinsOS. 2024-01-15 14:58:54 -07:00
UpstreamData
707cf8b848 version: bump version number. 2024-01-15 14:29:04 -07:00
UpstreamData
170843aae7 bug: add handler for failed get_data calls to make errors more verbose. 2024-01-15 14:28:39 -07:00
UpstreamData
f5acf9ec62 Merge branch 'dev_boser'
# Conflicts:
#	pyasic/miners/antminer/hiveon/X9/T9.py
#	pyasic/miners/backends/bosminer_old.py
#	pyasic/miners/backends/braiins_os.py
#	pyasic/miners/backends/btminer.py
#	pyasic/miners/backends/cgminer_avalon.py
#	pyasic/miners/backends/epic.py
#	pyasic/miners/backends/hiveon.py
#	pyasic/miners/backends/innosilicon.py
#	pyasic/miners/base.py
#	tests/miners_tests/__init__.py
2024-01-15 14:25:02 -07:00
UpstreamData
edaf89c73a refactor: fix some formatting issues and bugs. 2024-01-15 14:18:41 -07:00
UpstreamData
ce34dfdde8 bug: fix fault_light check for boser. 2024-01-15 14:00:51 -07:00
UpstreamData
e45e51ce65 refactor: fix merge. 2024-01-15 13:09:23 -07:00
UpstreamData
f1501718a3 feature: finish get_data functions for bosminer 2024-01-15 10:48:03 -07:00
UpstreamData
831d6ee955 feature: add boser fault light functions. 2024-01-15 10:48:02 -07:00
UpstreamData
7be6596fdd refactor: swap except (KeyError, ValueError) to except LookupError. 2024-01-15 10:48:02 -07:00
b-rowan
928e0dd028 feature: start refactoring BOSer and BOSMiner into separate classes. 2024-01-15 10:48:00 -07:00
UpstreamData
672e753afb bug: add test to cross check function arguments, and fix some method implementations and naming. 2024-01-15 10:47:56 -07:00
UpstreamData
269e6aac14 bug: add more tests and finish renaming methods. 2024-01-15 10:47:40 -07:00
UpstreamData
1a4f3f7dc7 bug: make sure all data locations are accurate. 2024-01-15 10:47:40 -07:00
UpstreamData
b0337e8417 refactor: swap (KeyError, IndexError) for LookupError. 2024-01-15 10:47:40 -07:00
UpstreamData
60f3687d02 refactor: optimize imports. 2024-01-15 10:47:39 -07:00
UpstreamData
a8c45cb95d refactor: remove parameters from get_{x} functions and move them to _get_{x}(**params). Add miner.fw_str, and miner.raw_model. Remove model from get_data exclude. Swap fan_count to expected_fans. 2024-01-15 10:47:39 -07:00
UpstreamData
aa9ba66f8e bug: add test to cross check function arguments, and fix some method implementations and naming. 2024-01-15 10:47:39 -07:00
UpstreamData
06cc84f16d refactor: remove parameters from get_{x} functions and move them to _get_{x}(**params). Add miner.fw_str, and miner.raw_model. Remove model from get_data exclude. Swap fan_count to expected_fans. 2024-01-15 10:47:38 -07:00
fdeh
067d5c98f5 Fix VNish get_hashrate and get_fans errors
Update vnish.py. Fix data locations according to the method arguments
2024-01-15 10:47:38 -07:00
UpstreamData
b4b84c773f refactor: remove bad function. 2024-01-15 10:47:38 -07:00
UpstreamData
cd1768aae9 refactor: swap (KeyError, IndexError) for LookupError. 2024-01-15 10:47:37 -07:00
UpstreamData
2ef85d3868 refactor: optimize imports. 2024-01-15 10:47:36 -07:00
UpstreamData
6f64cc5e0d refactor: remove parameters from get_{x} functions and move them to _get_{x}(**params). Add miner.fw_str, and miner.raw_model. Remove model from get_data exclude. Swap fan_count to expected_fans. 2024-01-15 10:47:33 -07:00
b-rowan
d44907435c Merge pull request #91 from UpstreamData/dev_get_params
Move parameters to private methods for `get_{x}` methods
2024-01-15 10:43:08 -07:00
b-rowan
04ca75d00e Merge branch 'master' into dev_get_params 2024-01-15 10:42:37 -07:00
UpstreamData
b56e94ce8c bug: add more tests and finish renaming methods. 2024-01-15 10:35:15 -07:00
UpstreamData
e7d30aad84 bug: make sure all data locations are accurate. 2024-01-15 10:29:39 -07:00
UpstreamData
194fb539a1 refactor: swap (KeyError, IndexError) for LookupError. 2024-01-15 10:23:58 -07:00
UpstreamData
416ea2964b refactor: optimize imports. 2024-01-15 10:23:57 -07:00
UpstreamData
3234f7e06f refactor: remove parameters from get_{x} functions and move them to _get_{x}(**params). Add miner.fw_str, and miner.raw_model. Remove model from get_data exclude. Swap fan_count to expected_fans. 2024-01-15 10:23:57 -07:00
UpstreamData
8fb357544b bug: add test to cross check function arguments, and fix some method implementations and naming. 2024-01-15 10:23:55 -07:00
UpstreamData
34006941ad bug: add test to cross check function arguments, and fix some method implementations and naming. 2024-01-15 10:16:47 -07:00
UpstreamData
3c3c34c54b Merge branch 'master' into dev_get_params 2024-01-15 08:10:46 -07:00
b-rowan
5a61a87766 docs: update docs. 2024-01-14 12:59:13 -07:00
b-rowan
ef9a026ee8 docs: update docs. 2024-01-14 12:58:11 -07:00
b-rowan
71c85e0603 bug: fix a possible failed authentication when using gRPC. 2024-01-14 12:09:29 -07:00
UpstreamData
c5224b808e refactor: remove parameters from get_{x} functions and move them to _get_{x}(**params). Add miner.fw_str, and miner.raw_model. Remove model from get_data exclude. Swap fan_count to expected_fans. 2024-01-14 10:02:50 -07:00
b-rowan
e4c6d751a1 version: bump version number. 2024-01-14 10:02:38 -07:00
fdeh
ff4dfa124b Fix VNish get_hashrate and get_fans errors
Update vnish.py. Fix data locations according to the method arguments
2024-01-14 10:02:38 -07:00
b-rowan
d0eb5119aa version: bump version number. 2024-01-14 10:00:15 -07:00
fdeh
cfa51623c4 Fix VNish get_hashrate and get_fans errors
Update vnish.py. Fix data locations according to the method arguments
2024-01-14 10:00:15 -07:00
b-rowan
96bb56ebd1 version: bump version number. 2024-01-14 09:59:06 -07:00
b-rowan
cdd7beccbe Merge pull request #92 from fdeh75/fix-vnish-data-gathering
Fix VNish get_hashrate and get_fans errors
2024-01-14 09:58:16 -07:00
fdeh
1a544851df Fix VNish get_hashrate and get_fans errors
Update vnish.py. Fix data locations according to the method arguments
2024-01-14 19:53:47 +03:00
UpstreamData
aa2dc5a53d feature: update some gRPC functions, and add as_boser for some of the MinerConfig values. 2024-01-12 15:06:44 -07:00
UpstreamData
361d6e07cc feature: finish get_data functions for bosminer 2024-01-12 13:29:46 -07:00
UpstreamData
53a018f526 feature: add boser fault light functions. 2024-01-12 11:58:26 -07:00
UpstreamData
6c9a378eee feature: add boser config parsing. 2024-01-12 11:54:17 -07:00
UpstreamData
be67ef3471 refactor: remove bad function. 2024-01-11 15:29:29 -07:00
UpstreamData
a094d28a36 refactor: swap (KeyError, IndexError) for LookupError. 2024-01-11 15:20:33 -07:00
UpstreamData
4156f93c0d refactor: optimize imports. 2024-01-11 15:00:48 -07:00
UpstreamData
ed6eb11653 bug: fix being unable to get fw version as part of multicommand. 2024-01-11 13:57:48 -07:00
snyk-bot
39299f2cfa fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-JINJA2-6150717
2024-01-11 11:37:01 -07:00
snyk-bot
c80ca1415a fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-JINJA2-6150717
2024-01-11 11:36:05 -07:00
UpstreamData
a8428a2739 refactor: remove parameters from get_{x} functions and move them to _get_{x}(**params). Add miner.fw_str, and miner.raw_model. Remove model from get_data exclude. Swap fan_count to expected_fans. 2024-01-11 11:33:44 -07:00
UpstreamData
895fb1b43e refactor: swap except (KeyError, ValueError) to except LookupError. 2024-01-11 10:20:18 -07:00
UpstreamData
014896ae1b bug: fix data passed by get_version to BOSminer. 2024-01-11 09:53:06 -07:00
snyk-bot
84ac991685 fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-JINJA2-6150717
2024-01-11 16:00:03 +00:00
b-rowan
bb481553fa bug: fix missing message in grpc command. 2024-01-10 22:46:58 -07:00
b-rowan
7ab3d8b54e feature: improve data gathering slightly on BOSMiner. 2024-01-10 22:26:28 -07:00
b-rowan
36494f2aca bug: remove boser check in miner_factory, and fix bad syntax on comparison. 2024-01-10 22:15:31 -07:00
b-rowan
bea44a72ea feature: start refactoring BOSer and BOSMiner into separate classes. 2024-01-10 22:12:27 -07:00
b-rowan
9da7b44177 feature: add vnish config parsing. 2024-01-06 11:31:12 -07:00
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
156 changed files with 5046 additions and 4527 deletions

289
README.md
View File

@@ -1,142 +1,249 @@
# pyasic # pyasic
*A simplified and standardized interface for Bitcoin ASICs.* *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 - Version](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)
[![pypi](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](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/).
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 [![Code Style - Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
You can install pyasic directly from pip with the command `pip install pyasic`. [![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 Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
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.
<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`. ##### 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.
### Documentation Testing The command `MinerNetwork.scan()` returns a list that contains any miners found.
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
```python ```python
import asyncio import asyncio # asyncio for handling the async part
from pyasic.network import MinerNetwork # miner network handles the scanning
from pyasic.network import MinerNetwork
# define asynchronous function to scan for miners async def scan_miners(): # define async scan function to allow awaiting
async def scan_and_get_data(): # create a miner network
# Define network range to be used for scanning # you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
# This can take a list of IPs, a constructor string, or an IP and subnet mask network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
# 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()
# We can now get data from any of these miners # scan for miners asynchronously
# To do them all we have to create a list of tasks and gather them # this will return the correct type of miners if they are supported with all functionality.
tasks = [miner.get_data() for miner in miners] miners = await network.scan()
# Gather all tasks asynchronously and run them print(miners)
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)
if __name__ == "__main__": 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 ```python
import asyncio 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 from pyasic import get_miner
# define asynchronous function to get miner and data async def set_fault_light():
async def get_miner_data(miner_ip: str): miner = await get_miner("192.168.1.20")
# Use MinerFactory to get miner
# MinerFactory is a singleton, so we can just get the instance in place
miner = await get_miner(miner_ip)
# Get data from the miner # call control function
data = await miner.get_data() await miner.fault_light_on()
print(data)
if __name__ == "__main__": 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 ```python
import asyncio import asyncio
from pyasic import get_miner from pyasic import get_miner
async def get_api_commands(miner_ip: str): async def set_fault_light():
# Get the miner miner = await get_miner("192.168.1.20")
miner = await get_miner(miner_ip)
# List all available commands # get config
# Can also be called explicitly with the function miner.api.get_commands() cfg = await miner.get_config()
print(miner.api.commands)
# send config
await miner.send_config(cfg)
if __name__ == "__main__": 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 ```python
import asyncio from pyasic import settings
from pyasic import get_miner settings.update("default_antminer_password", "my_pwd")
```
async def get_api_commands(miner_ip: str): ##### Default values:
# Get the miner ```
miner = await get_miner(miner_ip) "network_ping_retries": 1,
"network_ping_timeout": 3,
# Run the devdetails command "network_scan_threads": 300,
# This is equivalent to await miner.api.send_command("devdetails") "factory_get_retries": 1,
devdetails: dict = await miner.api.devdetails() "factory_get_timeout": 3,
print(devdetails) "get_data_retries": 1,
"api_function_timeout": 5,
"default_whatsminer_password": "admin",
if __name__ == "__main__": "default_innosilicon_password": "admin",
asyncio.run(get_api_commands("192.168.1.69")) "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,6 +1,6 @@
# pyasic # pyasic
## Miner Data
## Miner Data
::: pyasic.data.MinerData ::: pyasic.data.MinerData
handler: python handler: python
options: options:
@@ -13,3 +13,10 @@
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Fan Data
::: pyasic.data.Fan
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,12 +1,15 @@
# pyasic # 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/) [![PyPI - Version](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/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/pyasic)](https://pypi.org/project/pyasic/)
[![Read the Docs](https://img.shields.io/readthedocs/pyasic)](https://pyasic.readthedocs.io/en/latest/) [![Python - Supported Versions](https://img.shields.io/pypi/pyversions/pyasic.svg)](https://pypi.org/project/pyasic/)
[![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)
[![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 ## Intro

View File

@@ -2,9 +2,8 @@
## A10X Models ## A10X Models
## A10X ## A10X
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.CGMinerA10X ::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4

View File

@@ -2,9 +2,8 @@
## T3X Models ## T3X Models
## T3H+ ## T3H+
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.CGMinerT3HPlus ::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
handler: python handler: python
options: options:
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4

View File

@@ -1,3 +1,3 @@
jinja2<3.1.0 jinja2<3.1.3
mkdocs mkdocs
mkdocstrings[python] mkdocstrings[python]

View File

@@ -263,6 +263,12 @@ If you are sure you want to use this command please use API.send_command("{comma
else: else:
return False, data["STATUS"][0]["Msg"] 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"]: if data["STATUS"] not in ["S", "I"]:
return False, data["Msg"] return False, data["Msg"]
else: else:

View File

@@ -48,10 +48,10 @@ PrePowerOnMessage = Union[
def _crypt(word: str, salt: str) -> str: 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 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. ValueError is raised.
Parameters: Parameters:
@@ -62,7 +62,7 @@ def _crypt(word: str, salt: str) -> str:
An MD5 hash of the word with the salt. An MD5 hash of the word with the salt.
""" """
# compile a standard format for 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 # check if the salt matches
match = standard_salt.match(salt) match = standard_salt.match(salt)
# if the matching fails, the salt is incorrect # 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.errors import APIError, APIWarning
from pyasic.miners import get_miner 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_factory import MinerFactory, miner_factory
from pyasic.miners.miner_listener import MinerListener from pyasic.miners.miner_listener import MinerListener
from pyasic.network import MinerNetwork from pyasic.network import MinerNetwork
@@ -50,6 +50,7 @@ __all__ = [
"APIWarning", "APIWarning",
"get_miner", "get_miner",
"AnyMiner", "AnyMiner",
"DataOptions",
"MinerFactory", "MinerFactory",
"miner_factory", "miner_factory",
"MinerListener", "MinerListener",

View File

@@ -99,13 +99,13 @@ class MinerConfig:
**self.power_scaling.as_bosminer(), **self.power_scaling.as_bosminer(),
} }
def as_bos_grpc(self, user_suffix: str = None) -> dict: def as_boser(self, user_suffix: str = None) -> dict:
return { return {
**self.fan_mode.as_bos_grpc(), **self.fan_mode.as_boser(),
**self.temperature.as_bos_grpc(), **self.temperature.as_boser(),
**self.mining_mode.as_bos_grpc(), **self.mining_mode.as_boser(),
**self.pools.as_bos_grpc(user_suffix=user_suffix), **self.pools.as_boser(user_suffix=user_suffix),
**self.power_scaling.as_bos_grpc(), **self.power_scaling.as_boser(),
} }
def as_epic(self, user_suffix: str = None) -> dict: def as_epic(self, user_suffix: str = None) -> dict:
@@ -161,6 +161,34 @@ class MinerConfig:
power_scaling=PowerScalingConfig.from_bosminer(toml_conf), power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
) )
@classmethod
def from_boser(cls, grpc_miner_conf: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_boser(grpc_miner_conf),
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
temperature=TemperatureConfig.from_boser(grpc_miner_conf),
power_scaling=PowerScalingConfig.from_boser(grpc_miner_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),
)
@classmethod
def from_vnish(cls, web_settings: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_vnish(web_settings),
fan_mode=FanModeConfig.from_vnish(web_settings),
temperature=TemperatureConfig.from_vnish(web_settings),
mining_mode=MiningModeConfig.from_vnish(web_settings),
)
def merge(a: dict, b: dict) -> dict: def merge(a: dict, b: dict) -> dict:
result = deepcopy(a) result = deepcopy(a)

View File

@@ -44,12 +44,15 @@ class MinerConfigOption(Enum):
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return self.value.as_bosminer() return self.value.as_bosminer()
def as_bos_grpc(self) -> dict: def as_boser(self) -> dict:
return self.value.as_bos_grpc() return self.value.as_boser()
def as_epic(self) -> dict: def as_epic(self) -> dict:
return self.value.as_epic() return self.value.as_epic()
def as_vnish(self) -> dict:
return self.value.as_vnish()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs) return self.value(*args, **kwargs)
@@ -88,8 +91,11 @@ class MinerConfigValue:
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return {} return {}
def as_bos_grpc(self) -> dict: def as_boser(self) -> dict:
return {} return {}
def as_epic(self) -> dict: def as_epic(self) -> dict:
return {} return {}
def as_vnish(self) -> dict:
return {}

View File

@@ -22,10 +22,26 @@ from pyasic.config.base import MinerConfigOption, MinerConfigValue
@dataclass @dataclass
class FanModeNormal(MinerConfigValue): class FanModeNormal(MinerConfigValue):
mode: str = field(init=False, default="normal") mode: str = field(init=False, default="normal")
minimum_fans: int = 1
minimum_speed: int = 0
@classmethod @classmethod
def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeNormal": def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeNormal":
return cls() cls_conf = {}
if dict_conf.get("minimum_fans") is not None:
cls_conf["minimum_fans"] = dict_conf["minimum_fans"]
if dict_conf.get("minimum_speed") is not None:
cls_conf["minimum_speed"] = dict_conf["minimum_speed"]
return cls(**cls_conf)
@classmethod
def from_vnish(cls, web_cooling_settings: dict):
cls_conf = {}
if web_cooling_settings.get("fan_min_count") is not None:
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
if web_cooling_settings.get("fan_min_duty") is not None:
cls_conf["minimum_speed"] = web_cooling_settings["fan_min_duty"]
return cls(**cls_conf)
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
@@ -37,16 +53,16 @@ class FanModeNormal(MinerConfigValue):
@dataclass @dataclass
class FanModeManual(MinerConfigValue): class FanModeManual(MinerConfigValue):
mode: str = field(init=False, default="manual") mode: str = field(init=False, default="manual")
minimum_fans: int = 1
speed: int = 100 speed: int = 100
minimum_fans: int = 1
@classmethod @classmethod
def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeManual": def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeManual":
cls_conf = {} 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: if dict_conf.get("speed") is not None:
cls_conf["speed"] = dict_conf["speed"] 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) return cls(**cls_conf)
@classmethod @classmethod
@@ -58,6 +74,15 @@ class FanModeManual(MinerConfigValue):
cls_conf["speed"] = toml_fan_conf["speed"] cls_conf["speed"] = toml_fan_conf["speed"]
return cls(**cls_conf) return cls(**cls_conf)
@classmethod
def from_vnish(cls, web_cooling_settings: dict) -> "FanModeManual":
cls_conf = {}
if web_cooling_settings.get("fan_min_count") is not None:
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
if web_cooling_settings["mode"].get("param") is not None:
cls_conf["speed"] = web_cooling_settings["mode"]["param"]
return cls(**cls_conf)
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)} return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)}
@@ -116,6 +141,17 @@ class FanModeConfig(MinerConfigOption):
else: else:
return cls.default() 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 @classmethod
def from_bosminer(cls, toml_conf: dict): def from_bosminer(cls, toml_conf: dict):
if toml_conf.get("temp_control") is None: if toml_conf.get("temp_control") is None:
@@ -132,3 +168,37 @@ class FanModeConfig(MinerConfigOption):
return cls.manual() return cls.manual()
elif mode == "disabled": elif mode == "disabled":
return cls.immersion() return cls.immersion()
@classmethod
def from_vnish(cls, web_settings: dict):
try:
mode = web_settings["miner"]["cooling"]["mode"]["name"]
except LookupError:
return cls.default()
if mode == "auto":
return cls.normal().from_vnish(web_settings["miner"]["cooling"])
elif mode == "manual":
return cls.manual().from_vnish(web_settings["miner"]["cooling"])
elif mode == "immers":
return cls.immersion()
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
temperature_conf = grpc_miner_conf["temperature"]
except LookupError:
return cls.default()
keys = temperature_conf.keys()
if "auto" in keys:
if "minimumRequiredFans" in keys:
return cls.normal(temperature_conf["minimumRequiredFans"])
return cls.normal()
if "manual" in keys:
conf = {}
if "fanSpeedRatio" in temperature_conf["manual"].keys():
conf["speed"] = int(temperature_conf["manual"]["fanSpeedRatio"])
if "minimumRequiredFans" in keys:
conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"])
return cls.manual(**conf)

View File

@@ -14,9 +14,19 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Union from typing import Dict, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue from pyasic.config.base import MinerConfigOption, MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
HashrateTargetMode,
PerformanceMode,
Power,
PowerTargetMode,
SaveAction,
SetPerformanceModeRequest,
TeraHashrate,
TunerPerformanceMode,
)
@dataclass @dataclass
@@ -99,6 +109,20 @@ class MiningModePowerTune(MinerConfigValue):
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return {"autotuning": {"enabled": True, "psu_power_limit": self.power}} return {"autotuning": {"enabled": True, "psu_power_limit": self.power}}
def as_boser(self) -> dict:
return {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
power_target=PowerTargetMode(
power_target=Power(watt=self.power)
)
)
),
),
}
@dataclass @dataclass
class MiningModeHashrateTune(MinerConfigValue): class MiningModeHashrateTune(MinerConfigValue):
@@ -112,6 +136,22 @@ class MiningModeHashrateTune(MinerConfigValue):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"miner-mode": "0"} return {"miner-mode": "0"}
def as_boser(self) -> dict:
return {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
hashrate_target=HashrateTargetMode(
hashrate_target=TeraHashrate(
terahash_per_second=self.hashrate
)
)
)
),
)
}
@dataclass @dataclass
class ManualBoardSettings(MinerConfigValue): class ManualBoardSettings(MinerConfigValue):
@@ -132,7 +172,7 @@ class MiningModeManual(MinerConfigValue):
global_freq: float global_freq: float
global_volt: float global_volt: float
boards: dict[int, ManualBoardSettings] = field(default_factory=dict) boards: Dict[int, ManualBoardSettings] = field(default_factory=dict)
@classmethod @classmethod
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual": def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual":
@@ -145,6 +185,20 @@ class MiningModeManual(MinerConfigValue):
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"miner-mode": "0"} return {"miner-mode": "0"}
@classmethod
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
# will raise KeyError if it cant find the settings, values cannot be empty
voltage = web_overclock_settings["globals"]["volt"]
freq = web_overclock_settings["globals"]["freq"]
boards = {
idx: ManualBoardSettings(
freq=board["freq"],
volt=voltage if not board["freq"] == 0 else 0,
)
for idx, board in enumerate(web_overclock_settings["chains"])
}
return cls(global_freq=freq, global_volt=voltage, boards=boards)
class MiningModeConfig(MinerConfigOption): class MiningModeConfig(MinerConfigOption):
normal = MiningModeNormal normal = MiningModeNormal
@@ -186,6 +240,29 @@ class MiningModeConfig(MinerConfigOption):
return cls.low() return cls.low()
return cls.default() 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 @classmethod
def from_bosminer(cls, toml_conf: dict): def from_bosminer(cls, toml_conf: dict):
if toml_conf.get("autotuning") is None: if toml_conf.get("autotuning") is None:
@@ -211,3 +288,45 @@ class MiningModeConfig(MinerConfigOption):
if autotuning_conf.get("hashrate_target") is not None: if autotuning_conf.get("hashrate_target") is not None:
return cls.hashrate_tuning(autotuning_conf["hashrate_target"]) return cls.hashrate_tuning(autotuning_conf["hashrate_target"])
return cls.hashrate_tuning() return cls.hashrate_tuning()
@classmethod
def from_vnish(cls, web_settings: dict):
try:
mode_settings = web_settings["miner"]["overclock"]
except KeyError:
return cls.default()
if mode_settings["preset"] == "disabled":
return MiningModeManual.from_vnish(mode_settings)
else:
return cls.power_tuning(int(mode_settings["preset"]))
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
tuner_conf = grpc_miner_conf["tuner"]
if not tuner_conf.get("enabled", False):
return cls.default()
except LookupError:
return cls.default()
if tuner_conf.get("tunerMode") is not None:
if tuner_conf["tunerMode"] == 1:
if tuner_conf.get("powerTarget") is not None:
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
return cls.power_tuning()
if tuner_conf["tunerMode"] == 2:
if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning(
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
)
return cls.hashrate_tuning()
if tuner_conf.get("powerTarget") is not None:
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning(
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
)

View File

@@ -16,7 +16,7 @@
import random import random
import string import string
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Union from typing import Dict, List, Union
from pyasic.config.base import MinerConfigValue from pyasic.config.base import MinerConfigValue
@@ -108,6 +108,12 @@ class Pool(MinerConfigValue):
def from_api(cls, api_pool: dict) -> "Pool": def from_api(cls, api_pool: dict) -> "Pool":
return cls(url=api_pool["URL"], user=api_pool["User"], password="x") 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 @classmethod
def from_am_modern(cls, web_pool: dict) -> "Pool": def from_am_modern(cls, web_pool: dict) -> "Pool":
return cls( return cls(
@@ -135,10 +141,26 @@ class Pool(MinerConfigValue):
password=toml_pool_conf["password"], password=toml_pool_conf["password"],
) )
@classmethod
def from_vnish(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["url"],
user=web_pool["user"],
password=web_pool["pass"],
)
@classmethod
def from_boser(cls, grpc_pool: dict) -> "Pool":
return cls(
url=grpc_pool["url"],
user=grpc_pool["user"],
password=grpc_pool["password"],
)
@dataclass @dataclass
class PoolGroup(MinerConfigValue): class PoolGroup(MinerConfigValue):
pools: list[Pool] = field(default_factory=list) pools: List[Pool] = field(default_factory=list)
quota: int = 1 quota: int = 1
name: str = None name: str = None
@@ -237,6 +259,13 @@ class PoolGroup(MinerConfigValue):
pools.append(Pool.from_api(pool)) pools.append(Pool.from_api(pool))
return cls(pools=pools) 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 @classmethod
def from_am_modern(cls, web_pool_list: list) -> "PoolGroup": def from_am_modern(cls, web_pool_list: list) -> "PoolGroup":
pools = [] pools = []
@@ -262,10 +291,27 @@ class PoolGroup(MinerConfigValue):
) )
return cls() return cls()
@classmethod
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
return cls([Pool.from_vnish(p) for p in web_settings_pools])
@classmethod
def from_boser(cls, grpc_pool_group: dict):
try:
return cls(
pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]],
name=grpc_pool_group["name"],
quota=grpc_pool_group["quota"]["value"]
if grpc_pool_group.get("quota") is not None
else 1,
)
except LookupError:
return cls()
@dataclass @dataclass
class PoolConfig(MinerConfigValue): class PoolConfig(MinerConfigValue):
groups: list[PoolGroup] = field(default_factory=list) groups: List[PoolGroup] = field(default_factory=list)
@classmethod @classmethod
def default(cls) -> "PoolConfig": def default(cls) -> "PoolConfig":
@@ -279,7 +325,7 @@ class PoolConfig(MinerConfigValue):
return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]]) return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]])
@classmethod @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 = [] group_pools = []
for pool in pools: for pool in pools:
if isinstance(pool, dict): if isinstance(pool, dict):
@@ -324,16 +370,24 @@ class PoolConfig(MinerConfigValue):
} }
return {"group": [PoolGroup().as_bosminer()]} return {"group": [PoolGroup().as_bosminer()]}
def as_bos_grpc(self, user_suffix: str = None) -> dict: def as_boser(self, user_suffix: str = None) -> dict:
return {} return {}
@classmethod @classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig": 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"])) pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
return cls([PoolGroup.from_api(pool_data)]) 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 @classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig": def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"] pool_data = web_conf["pools"]
@@ -354,3 +408,22 @@ class PoolConfig(MinerConfigValue):
return cls() return cls()
return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]]) return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
@classmethod
def from_vnish(cls, web_settings: dict) -> "PoolConfig":
try:
return cls([PoolGroup.from_vnish(web_settings["miner"]["pools"])])
except LookupError:
return cls()
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
return cls(
groups=[
PoolGroup.from_boser(group)
for group in grpc_miner_conf["poolGroups"]
]
)
except LookupError:
return cls()

View File

@@ -17,7 +17,12 @@ from dataclasses import dataclass, field
from typing import Union from typing import Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue from pyasic.config.base import MinerConfigOption, MinerConfigValue
from pyasic.web.bosminer.proto.braiins.bos.v1 import DpsPowerTarget, DpsTarget, Hours from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
DpsPowerTarget,
DpsTarget,
Power,
SetDpsRequest,
)
@dataclass @dataclass
@@ -37,13 +42,8 @@ class PowerScalingShutdownEnabled(MinerConfigValue):
return cfg return cfg
def as_bos_grpc(self) -> dict: def as_boser(self) -> dict:
cfg = {"enable_shutdown ": True} return {"enable_shutdown": True, "shutdown_duration": self.duration}
if self.duration is not None:
cfg["shutdown_duration"] = Hours(self.duration)
return cfg
@dataclass @dataclass
@@ -57,7 +57,7 @@ class PowerScalingShutdownDisabled(MinerConfigValue):
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return {"shutdown_enabled": False} return {"shutdown_enabled": False}
def as_bos_grpc(self) -> dict: def as_boser(self) -> dict:
return {"enable_shutdown ": False} return {"enable_shutdown ": False}
@@ -88,6 +88,19 @@ class PowerScalingShutdown(MinerConfigOption):
return cls.disabled() return cls.disabled()
return None return None
@classmethod
def from_boser(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdownEnabled")
if sd_enabled is not None:
if sd_enabled:
try:
return cls.enabled(power_scaling_conf["shutdownDuration"]["hours"])
except KeyError:
return cls.enabled()
else:
return cls.disabled()
return None
@dataclass @dataclass
class PowerScalingEnabled(MinerConfigValue): class PowerScalingEnabled(MinerConfigValue):
@@ -133,20 +146,19 @@ class PowerScalingEnabled(MinerConfigValue):
return {"power_scaling": cfg} return {"power_scaling": cfg}
def as_bos_grpc(self) -> dict: def as_boser(self) -> dict:
cfg = {"enable": True} return {
target_conf = {} "set_dps": SetDpsRequest(
if self.power_step is not None: enable=True,
target_conf["power_step"] = self.power_step **self.shutdown_enabled.as_boser(),
if self.minimum_power is not None: target=DpsTarget(
target_conf["min_power_target"] = self.minimum_power power_target=DpsPowerTarget(
power_step=Power(self.power_step),
cfg["target"] = DpsTarget(power_target=DpsPowerTarget(**target_conf)) min_power_target=Power(self.minimum_power),
)
if self.shutdown_enabled is not None: ),
cfg = {**cfg, **self.shutdown_enabled.as_bos_grpc()} ),
}
return {"dps": cfg}
@dataclass @dataclass
@@ -187,3 +199,20 @@ class PowerScalingConfig(MinerConfigOption):
return cls.disabled() return cls.disabled()
return cls.default() return cls.default()
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
dps_conf = grpc_miner_conf["dps"]
if not dps_conf.get("enabled", False):
return cls.disabled()
except LookupError:
return cls.default()
conf = {"shutdown_enabled": PowerScalingShutdown.from_boser(dps_conf)}
if dps_conf.get("minPowerTarget") is not None:
conf["minimum_power"] = dps_conf["minPowerTarget"]["watt"]
if dps_conf.get("powerStep") is not None:
conf["power_step"] = dps_conf["powerStep"]["watt"]
return cls.enabled(**conf)

View File

@@ -56,3 +56,58 @@ class TemperatureConfig(MinerConfigValue):
hot=temp_control.get("hot_temp"), hot=temp_control.get("hot_temp"),
danger=temp_control.get("dangerous_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)
@classmethod
def from_vnish(cls, web_settings: dict):
try:
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
return cls(target=web_settings["miner"]["cooling"]["mode"]["param"])
except KeyError:
pass
return cls()
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
temperature_conf = grpc_miner_conf["temperature"]
except KeyError:
return cls.default()
root_key = None
for key in ["auto", "manual", "disabled"]:
if key in temperature_conf.keys():
root_key = key
break
if root_key is None:
return cls.default()
conf = {}
keys = temperature_conf[root_key].keys()
if "targetTemperature" in keys:
conf["target"] = int(
temperature_conf[root_key]["targetTemperature"]["degreeC"]
)
if "hotTemperature" in keys:
conf["hot"] = int(temperature_conf[root_key]["hotTemperature"]["degreeC"])
if "dangerousTemperature" in keys:
conf["danger"] = int(
temperature_conf[root_key]["dangerousTemperature"]["degreeC"]
)
return cls(**conf)
return cls.default()

View File

@@ -22,6 +22,9 @@ from dataclasses import asdict, dataclass, field, fields
from datetime import datetime, timezone from datetime import datetime, timezone
from typing import Any, List, Union 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 from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
@@ -35,7 +38,8 @@ class HashBoard:
temp: The temperature of the PCB as an int. temp: The temperature of the PCB as an int.
chip_temp: The temperature of the chips as an int. chip_temp: The temperature of the chips as an int.
chips: The chip count of the board 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.
serial_number: The serial number of the board.
missing: Whether the board is returned from the miners data as a bool. missing: Whether the board is returned from the miners data as a bool.
""" """
@@ -45,6 +49,7 @@ class HashBoard:
chip_temp: int = None chip_temp: int = None
chips: int = None chips: int = None
expected_chips: int = None expected_chips: int = None
serial_number: str = None
missing: bool = True missing: bool = True
def get(self, __key: str, default: Any = None): def get(self, __key: str, default: Any = None):
@@ -105,8 +110,8 @@ class MinerData:
hostname: The network hostname of the miner as a str. hostname: The network hostname of the miner as a str.
hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically. hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically.
_hashrate: Backup for hashrate found via API instead of hashboards. _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. hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics.
temperature_avg: The average temperature across the boards. Calculated automatically. temperature_avg: The average temperature across the boards. Calculated automatically.
env_temp: The environment temps as a float. env_temp: The environment temps as a float.
wattage: Current power draw of the miner as an int. wattage: Current power draw of the miner as an int.
@@ -114,16 +119,12 @@ class MinerData:
fans: A list of fans on the miner with their speeds. 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. 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. 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. expected_chips: The expected 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_expected_chips: The percent of total chips out of the expected count. Calculated automatically.
percent_ideal_hashrate: The percent of total hashrate out of the ideal hashrate. Calculated automatically. percent_expected_hashrate: The percent of total hashrate out of the expected hashrate. Calculated automatically.
percent_ideal_wattage: The percent of total wattage out of the ideal wattage. 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. nominal: Whether the number of chips in the miner is nominal. Calculated automatically.
pool_split: The pool split as a str. config: The parsed config of the miner, using [`MinerConfig`][pyasic.config.MinerConfig].
pool_1_url: The first pool url on the miner as a str.
pool_1_user: The first pool user on the miner as a str.
pool_2_url: The second pool url on the miner as a str.
pool_2_user: The second pool user on the miner as a str.
errors: A list of errors on the miner. errors: A list of errors on the miner.
fault_light: Whether the fault light is on as a boolean. fault_light: Whether the fault light is on as a boolean.
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically. efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
@@ -140,27 +141,24 @@ class MinerData:
fw_ver: str = None fw_ver: str = None
hostname: str = None hostname: str = None
hashrate: float = field(init=False) hashrate: float = field(init=False)
_hashrate: float = None _hashrate: float = field(repr=False, default=None)
nominal_hashrate: float = None expected_hashrate: float = None
hashboards: List[HashBoard] = field(default_factory=list) hashboards: List[HashBoard] = field(default_factory=list)
ideal_hashboards: int = None expected_hashboards: int = None
temperature_avg: int = field(init=False) temperature_avg: int = field(init=False)
env_temp: float = None env_temp: float = None
wattage: int = 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) fans: List[Fan] = field(default_factory=list)
fan_psu: int = None fan_psu: int = None
total_chips: int = field(init=False) total_chips: int = field(init=False)
ideal_chips: int = None expected_chips: int = None
percent_ideal_chips: float = field(init=False) percent_expected_chips: float = field(init=False)
percent_ideal_hashrate: float = field(init=False) percent_expected_hashrate: float = field(init=False)
percent_ideal_wattage: float = field(init=False) percent_expected_wattage: float = field(init=False)
nominal: bool = field(init=False) nominal: bool = field(init=False)
pool_split: str = "0" config: MinerConfig = None
pool_1_url: str = "Unknown"
pool_1_user: str = "Unknown"
pool_2_url: str = ""
pool_2_user: str = ""
errors: List[ errors: List[
Union[WhatsminerError, BraiinsOSError, X19Error, InnosiliconError] Union[WhatsminerError, BraiinsOSError, X19Error, InnosiliconError]
] = field(default_factory=list) ] = field(default_factory=list)
@@ -170,7 +168,11 @@ class MinerData:
@classmethod @classmethod
def fields(cls): 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): def __post_init__(self):
self.datetime = datetime.now(timezone.utc).astimezone() self.datetime = datetime.now(timezone.utc).astimezone()
@@ -248,6 +250,17 @@ class MinerData:
def hashrate(self, val): def hashrate(self, val):
self._hashrate = 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 @property
def total_chips(self): # noqa - Skip PyCharm inspection def total_chips(self): # noqa - Skip PyCharm inspection
if len(self.hashboards) > 0: if len(self.hashboards) > 0:
@@ -265,48 +278,48 @@ class MinerData:
@property @property
def nominal(self): # noqa - Skip PyCharm inspection 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 None
return self.ideal_chips == self.total_chips return self.expected_chips == self.total_chips
@nominal.setter @nominal.setter
def nominal(self, val): def nominal(self, val):
pass pass
@property @property
def percent_ideal_chips(self): # noqa - Skip PyCharm inspection def percent_expected_chips(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 None
if self.total_chips == 0 or self.ideal_chips == 0: if self.total_chips == 0 or self.expected_chips == 0:
return 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 @percent_expected_chips.setter
def percent_ideal_chips(self, val): def percent_expected_chips(self, val):
pass pass
@property @property
def percent_ideal_hashrate(self): # noqa - Skip PyCharm inspection def percent_expected_hashrate(self): # noqa - Skip PyCharm inspection
if self.hashrate is None or self.nominal_hashrate is None: if self.hashrate is None or self.expected_hashrate is None:
return None return None
if self.hashrate == 0 or self.nominal_hashrate == 0: if self.hashrate == 0 or self.expected_hashrate == 0:
return 0 return 0
return round((self.hashrate / self.nominal_hashrate) * 100) return round((self.hashrate / self.expected_hashrate) * 100)
@percent_ideal_hashrate.setter @percent_expected_hashrate.setter
def percent_ideal_hashrate(self, val): def percent_expected_hashrate(self, val):
pass pass
@property @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: if self.wattage_limit is None or self.wattage is None:
return None return None
if self.wattage_limit == 0 or self.wattage == 0: if self.wattage_limit == 0 or self.wattage == 0:
return 0 return 0
return round((self.wattage / self.wattage_limit) * 100) return round((self.wattage / self.wattage_limit) * 100)
@percent_ideal_wattage.setter @percent_expected_wattage.setter
def percent_ideal_wattage(self, val): def percent_expected_wattage(self, val):
pass pass
@property @property
@@ -339,7 +352,7 @@ class MinerData:
def asdict(self) -> dict: def asdict(self) -> dict:
logging.debug(f"MinerData - (To Dict) - Dumping Dict data") 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: def as_dict(self) -> dict:
"""Get this dataclass as a dictionary. """Get this dataclass as a dictionary.

View File

@@ -66,14 +66,14 @@ class _MinerPhaseBalancer:
str(miner.ip): { str(miner.ip): {
"miner": miner, "miner": miner,
"set": 0, "set": 0,
"min": miner.fan_count * FAN_USAGE, "min": miner.expected_fans * FAN_USAGE,
} }
for miner in miners for miner in miners
} }
for miner in miners: for miner in miners:
if ( if (
isinstance(miner, BTMiner) isinstance(miner, BTMiner)
and not (miner.model.startswith("M2") if miner.model else True) and not (miner.raw_model.startswith("M2") if miner.raw_model else True)
) or isinstance(miner, BOSMiner): ) or isinstance(miner, BOSMiner):
if isinstance(miner, S9): if isinstance(miner, S9):
self.miners[str(miner.ip)]["tune"] = True self.miners[str(miner.ip)]["tune"] = True
@@ -98,8 +98,8 @@ class _MinerPhaseBalancer:
self.miners[str(miner.ip)]["tune"] = False self.miners[str(miner.ip)]["tune"] = False
self.miners[str(miner.ip)]["shutdown"] = True self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 3600 self.miners[str(miner.ip)]["max"] = 3600
if miner.model: if miner.raw_model:
if miner.model.startswith("M2"): if miner.raw_model.startswith("M2"):
self.miners[str(miner.ip)]["tune"] = False self.miners[str(miner.ip)]["tune"] = False
self.miners[str(miner.ip)]["shutdown"] = True self.miners[str(miner.ip)]["shutdown"] = True
self.miners[str(miner.ip)]["max"] = 2400 self.miners[str(miner.ip)]["max"] = 2400
@@ -137,10 +137,10 @@ class _MinerPhaseBalancer:
for miner in self.miners 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 pct_ideal = 0
if len(pct_ideal_list) > 0: if len(pct_expected_list) > 0:
pct_ideal = sum(pct_ideal_list) / len(pct_ideal_list) pct_ideal = sum(pct_expected_list) / len(pct_expected_list)
wattage = round(wattage * 1 / (pct_ideal / 100)) wattage = round(wattage * 1 / (pct_ideal / 100))

View File

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

View File

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

View File

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

View File

@@ -14,21 +14,21 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSMiner from pyasic.miners.backends import BOSer
from pyasic.miners.types import S17, S17e, S17Plus, S17Pro from pyasic.miners.types import S17, S17e, S17Plus, S17Pro
class BOSMinerS17(BOSMiner, S17): class BOSMinerS17(BOSer, S17):
pass pass
class BOSMinerS17Plus(BOSMiner, S17Plus): class BOSMinerS17Plus(BOSer, S17Plus):
pass pass
class BOSMinerS17Pro(BOSMiner, S17Pro): class BOSMinerS17Pro(BOSer, S17Pro):
pass pass
class BOSMinerS17e(BOSMiner, S17e): class BOSMinerS17e(BOSer, S17e):
pass pass

View File

@@ -14,17 +14,17 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSMiner from pyasic.miners.backends import BOSer
from pyasic.miners.types import T17, T17e, T17Plus from pyasic.miners.types import T17, T17e, T17Plus
class BOSMinerT17(BOSMiner, T17): class BOSMinerT17(BOSer, T17):
pass pass
class BOSMinerT17Plus(BOSMiner, T17Plus): class BOSMinerT17Plus(BOSer, T17Plus):
pass pass
class BOSMinerT17e(BOSMiner, T17e): class BOSMinerT17e(BOSer, T17e):
pass pass

View File

@@ -14,29 +14,61 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSMiner from pyasic.miners.backends import BOSer
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): class BOSMinerS19(BOSer, S19):
pass pass
class BOSMinerS19Pro(BOSMiner, S19Pro): class BOSMinerS19Plus(BOSer, S19Plus):
pass pass
class BOSMinerS19j(BOSMiner, S19j): class BOSMinerS19Pro(BOSer, S19Pro):
pass pass
class BOSMinerS19jNoPIC(BOSMiner, S19jNoPIC): class BOSMinerS19a(BOSer, S19a):
pass pass
class BOSMinerS19jPro(BOSMiner, S19jPro): class BOSMinerS19j(BOSer, S19j):
pass pass
class BOSMinerS19kProNoPIC(BOSMiner, S19kProNoPIC): class BOSMinerS19jNoPIC(BOSer, S19jNoPIC):
pass
class BOSMinerS19jPro(BOSer, S19jPro):
pass
class BOSMinerS19kProNoPIC(BOSer, S19kProNoPIC):
pass
class BOSMinerS19aPro(BOSer, S19aPro):
pass
class BOSMinerS19jProPlus(BOSer, S19jProPlus):
pass
class BOSMinerS19XP(BOSer, S19XP):
pass pass

View File

@@ -14,9 +14,9 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSMiner from pyasic.miners.backends import BOSer
from pyasic.miners.types import T19 from pyasic.miners.types import T19
class BOSMinerT19(BOSMiner, T19): class BOSMinerT19(BOSer, T19):
pass pass

View File

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

View File

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

View File

@@ -25,10 +25,6 @@ from pyasic.miners.types import T9
class HiveonT9(Hiveon, T9): class HiveonT9(Hiveon, T9):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver=api_ver)
self.ip = ip
self.pwd = "admin"
################################################## ##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
@@ -45,39 +41,49 @@ class HiveonT9(Hiveon, T9):
except (TypeError, ValueError, asyncssh.Error, OSError, AttributeError): except (TypeError, ValueError, asyncssh.Error, OSError, AttributeError):
pass pass
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [
HashBoard(slot=board, expected_chips=self.expected_chips)
for board in range(self.expected_hashboards)
]
if api_stats is None:
try:
api_stats = self.api.stats()
except APIError:
return []
board_map = { board_map = {
0: [2, 9, 10], 0: [2, 9, 10],
1: [3, 11, 12], 1: [3, 11, 12],
2: [4, 13, 14], 2: [4, 13, 14],
} }
hashboards = []
for board in board_map: for board in board_map:
hashboard = HashBoard(slot=board, expected_chips=self.nominal_chips)
hashrate = 0 hashrate = 0
chips = 0 chips = 0
for chipset in board_map[board]: for chipset in board_map[board]:
if hashboard.chip_temp == None: if hashboards[board].chip_temp is None:
try: try:
hashboard.board_temp = api_stats["STATS"][1][f"temp{chipset}"] hashboards[board].temp = api_stats["STATS"][1][f"temp{chipset}"]
hashboard.chip_temp = api_stats["STATS"][1][f"temp2_{chipset}"] hashboards[board].chip_temp = api_stats["STATS"][1][
f"temp2_{chipset}"
]
except (KeyError, IndexError): except (KeyError, IndexError):
pass pass
else: else:
hashboard.missing = False hashboards[board].missing = False
try: try:
hashrate += api_stats["STATS"][1][f"chain_rate{chipset}"] hashrate += api_stats["STATS"][1][f"chain_rate{chipset}"]
chips += api_stats["STATS"][1][f"chain_acn{chipset}"] chips += api_stats["STATS"][1][f"chain_acn{chipset}"]
except (KeyError, IndexError): except (KeyError, IndexError):
pass pass
hashboard.hashrate = round(hashrate / 1000, 2) hashboards[board].hashrate = round(hashrate / 1000, 2)
hashboard.chips = chips hashboards[board].chips = chips
hashboards.append(hashboard)
return hashboards return hashboards
async def get_wattage(self, api_stats: dict = None) -> Optional[int]: async def _get_wattage(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -94,7 +100,7 @@ class HiveonT9(Hiveon, T9):
# parse wattage position out of raw data # parse wattage position out of raw data
return round(float(wattage_raw.split(" ")[0])) return round(float(wattage_raw.split(" ")[0]))
async def get_env_temp(self, api_stats: dict = None) -> Optional[float]: async def _get_env_temp(self, api_stats: dict = None) -> Optional[float]:
env_temp_list = [] env_temp_list = []
board_map = { board_map = {
0: [2, 9, 10], 0: [2, 9, 10],

View File

@@ -17,12 +17,12 @@ from .antminer import AntminerModern, AntminerOld
from .bfgminer import BFGMiner from .bfgminer import BFGMiner
from .bfgminer_goldshell import BFGMinerGoldshell from .bfgminer_goldshell import BFGMinerGoldshell
from .bmminer import BMMiner from .bmminer import BMMiner
from .bosminer import BOSMiner from .braiins_os import BOSer, BOSMiner
from .btminer import BTMiner from .btminer import BTMiner
from .cgminer import CGMiner from .cgminer import CGMiner
from .cgminer_avalon import CGMinerAvalon from .cgminer_avalon import CGMinerAvalon
from .epic import ePIC
from .hiveon import Hiveon from .hiveon import Hiveon
from .luxminer import LUXMiner from .luxminer import LUXMiner
from .vnish import VNish from .vnish import VNish
from .epic import ePIC
from .whatsminer import M2X, M3X, M5X, M6X from .whatsminer import M2X, M3X, M5X, M6X

View File

@@ -14,7 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
from typing import List, Optional, Union from typing import List, Optional, Union
from pyasic.API import APIError from pyasic.API import APIError
@@ -23,46 +22,59 @@ from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.backends.cgminer import CGMiner from pyasic.miners.backends.cgminer import CGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
ANTMINER_MODERN_DATA_LOC = { ANTMINER_MODERN_DATA_LOC = DataLocations(
"mac": { **{
"cmd": "get_mac", str(DataOptions.MAC): DataFunction(
"kwargs": {"web_get_system_info": {"web": "get_system_info"}}, "_get_mac", [WebAPICommand("web_get_system_info", "get_system_info")]
}, ),
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.API_VERSION): DataFunction(
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, ),
"hostname": { str(DataOptions.FW_VERSION): DataFunction(
"cmd": "get_hostname", "_get_fw_ver", [RPCAPICommand("api_version", "version")]
"kwargs": {"web_get_system_info": {"web": "get_system_info"}}, ),
}, str(DataOptions.HOSTNAME): DataFunction(
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, "_get_hostname", [WebAPICommand("web_get_system_info", "get_system_info")]
"nominal_hashrate": { ),
"cmd": "get_nominal_hashrate", str(DataOptions.HASHRATE): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
}, ),
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
"wattage": {"cmd": "get_wattage", "kwargs": {}}, ),
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction("_get_hashboards"),
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}}, str(DataOptions.WATTAGE_LIMIT): DataFunction("_get_wattage_limit"),
"fault_light": { str(DataOptions.FANS): DataFunction(
"cmd": "get_fault_light", "_get_fans", [RPCAPICommand("api_stats", "stats")]
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}}, ),
}, str(DataOptions.FAN_PSU): DataFunction("_get_fan_psu"),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.ERRORS): DataFunction(
"is_mining": { "_get_errors", [WebAPICommand("web_summary", "summary")]
"cmd": "is_mining", ),
"kwargs": {"web_get_conf": {"web": "get_miner_conf"}}, str(DataOptions.FAULT_LIGHT): DataFunction(
}, "_get_fault_light",
"uptime": { [WebAPICommand("web_get_blink_status", "get_blink_status")],
"cmd": "get_uptime", ),
"kwargs": {"api_stats": {"api": "stats"}}, 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): class AntminerModern(BMMiner):
@@ -128,7 +140,7 @@ class AntminerModern(BMMiner):
await self.send_config(cfg) await self.send_config(cfg)
return True return True
async def get_hostname(self, web_get_system_info: dict = None) -> Union[str, None]: async def _get_hostname(self, web_get_system_info: dict = None) -> Union[str, None]:
if not web_get_system_info: if not web_get_system_info:
try: try:
web_get_system_info = await self.web.get_system_info() web_get_system_info = await self.web.get_system_info()
@@ -141,7 +153,7 @@ class AntminerModern(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_mac(self, web_get_system_info: dict = None) -> Union[str, None]: async def _get_mac(self, web_get_system_info: dict = None) -> Union[str, None]:
if not web_get_system_info: if not web_get_system_info:
try: try:
web_get_system_info = await self.web.get_system_info() web_get_system_info = await self.web.get_system_info()
@@ -161,7 +173,7 @@ class AntminerModern(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_errors(self, web_summary: dict = None) -> List[MinerErrorData]: async def _get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
if not web_summary: if not web_summary:
try: try:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -177,11 +189,47 @@ class AntminerModern(BMMiner):
errors.append(X19Error(item["msg"])) errors.append(X19Error(item["msg"]))
except KeyError: except KeyError:
continue continue
except (KeyError, IndexError): except LookupError:
pass pass
return errors return errors
async def get_fault_light(self, web_get_blink_status: dict = None) -> bool: 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: if self.light:
return self.light return self.light
@@ -198,7 +246,7 @@ class AntminerModern(BMMiner):
pass pass
return self.light 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: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -207,18 +255,18 @@ class AntminerModern(BMMiner):
if api_stats: if api_stats:
try: try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"] expected_rate = api_stats["STATS"][1]["total_rateideal"]
try: try:
rate_unit = api_stats["STATS"][1]["rate_unit"] rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError: except KeyError:
rate_unit = "GH" rate_unit = "GH"
if rate_unit == "GH": if rate_unit == "GH":
return round(ideal_rate / 1000, 2) return round(expected_rate / 1000, 2)
if rate_unit == "MH": if rate_unit == "MH":
return round(ideal_rate / 1000000, 2) return round(expected_rate / 1000000, 2)
else: else:
return round(ideal_rate, 2) return round(expected_rate, 2)
except (KeyError, IndexError): except LookupError:
pass pass
async def set_static_ip( async def set_static_ip(
@@ -263,7 +311,7 @@ class AntminerModern(BMMiner):
protocol=protocol, protocol=protocol,
) )
async def is_mining(self, web_get_conf: dict = None) -> Optional[bool]: async def _is_mining(self, web_get_conf: dict = None) -> Optional[bool]:
if not web_get_conf: if not web_get_conf:
try: try:
web_get_conf = await self.web.get_miner_conf() web_get_conf = await self.web.get_miner_conf()
@@ -280,7 +328,7 @@ class AntminerModern(BMMiner):
except LookupError: except LookupError:
pass pass
async def get_uptime(self, api_stats: dict = None) -> Optional[int]: async def _get_uptime(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -294,44 +342,48 @@ class AntminerModern(BMMiner):
pass pass
ANTMINER_OLD_DATA_LOC = { ANTMINER_OLD_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {}}, **{
"model": { str(DataOptions.MAC): DataFunction("_get_mac"),
"cmd": "get_model", str(DataOptions.API_VERSION): DataFunction(
"kwargs": {}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
}, ),
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, str(DataOptions.FW_VERSION): DataFunction(
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_fw_ver", [RPCAPICommand("api_version", "version")]
"hostname": { ),
"cmd": "get_hostname", str(DataOptions.HOSTNAME): DataFunction(
"kwargs": {"web_get_system_info": {"web": "get_system_info"}}, "_get_hostname", [WebAPICommand("web_get_system_info", "get_system_info")]
}, ),
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, str(DataOptions.HASHRATE): DataFunction(
"nominal_hashrate": { "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"cmd": "get_nominal_hashrate", ),
"kwargs": {"api_stats": {"api": "stats"}}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, ),
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"wattage": {"cmd": "get_wattage", "kwargs": {}}, "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, ),
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"errors": {"cmd": "get_errors", "kwargs": {}}, str(DataOptions.WATTAGE_LIMIT): DataFunction("_get_wattage_limit"),
"fault_light": { str(DataOptions.FANS): DataFunction(
"cmd": "get_fault_light", "_get_fans", [RPCAPICommand("api_stats", "stats")]
"kwargs": {"web_get_blink_status": {"web": "get_blink_status"}}, ),
}, str(DataOptions.FAN_PSU): DataFunction("_get_fan_psu"),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.ERRORS): DataFunction("_get_errors"),
"is_mining": { str(DataOptions.FAULT_LIGHT): DataFunction(
"cmd": "is_mining", "_get_fault_light",
"kwargs": {"web_get_conf": {"web": "get_miner_conf"}}, [WebAPICommand("web_get_blink_status", "get_blink_status")],
}, ),
"uptime": { str(DataOptions.IS_MINING): DataFunction(
"cmd": "get_uptime", "_is_mining", [WebAPICommand("web_get_conf", "get_miner_conf")]
"kwargs": {"api_stats": {"api": "stats"}}, ),
}, str(DataOptions.UPTIME): DataFunction(
} "_get_uptime", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.CONFIG): DataFunction("get_config"),
}
)
class AntminerOld(CGMiner): class AntminerOld(CGMiner):
@@ -354,7 +406,7 @@ class AntminerOld(CGMiner):
self.config = config self.config = config
await self.web.set_miner_conf(config.as_am_old(user_suffix=user_suffix)) await self.web.set_miner_conf(config.as_am_old(user_suffix=user_suffix))
async def get_mac(self) -> Union[str, None]: async def _get_mac(self) -> Union[str, None]:
try: try:
data = await self.web.get_system_info() data = await self.web.get_system_info()
if data: if data:
@@ -391,7 +443,7 @@ class AntminerOld(CGMiner):
return True return True
return False return False
async def get_fault_light(self, web_get_blink_status: dict = None) -> bool: async def _get_fault_light(self, web_get_blink_status: dict = None) -> bool:
if self.light: if self.light:
return self.light return self.light
@@ -408,7 +460,7 @@ class AntminerOld(CGMiner):
pass pass
return self.light return self.light
async def get_hostname(self, web_get_system_info: dict = None) -> Optional[str]: async def _get_hostname(self, web_get_system_info: dict = None) -> Optional[str]:
if not web_get_system_info: if not web_get_system_info:
try: try:
web_get_system_info = await self.web.get_system_info() web_get_system_info = await self.web.get_system_info()
@@ -421,14 +473,14 @@ class AntminerOld(CGMiner):
except KeyError: except KeyError:
pass pass
async def get_fans(self, api_stats: dict = None) -> List[Fan]: async def _get_fans(self, api_stats: dict = None) -> List[Fan]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass
fans_data = [Fan() for _ in range(self.fan_count)] fans_data = [Fan() for _ in range(self.expected_fans)]
if api_stats: if api_stats:
try: try:
fan_offset = -1 fan_offset = -1
@@ -441,15 +493,15 @@ class AntminerOld(CGMiner):
if fan_offset == -1: if fan_offset == -1:
fan_offset = 3 fan_offset = 3
for fan in range(self.fan_count): for fan in range(self.expected_fans):
fans_data[fan].speed = api_stats["STATS"][1].get( fans_data[fan].speed = api_stats["STATS"][1].get(
f"fan{fan_offset+fan}", 0 f"fan{fan_offset+fan}", 0
) )
except (KeyError, IndexError): except LookupError:
pass pass
return fans_data return fans_data
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [] hashboards = []
if not api_stats: if not api_stats:
@@ -472,9 +524,11 @@ class AntminerOld(CGMiner):
if board_offset == -1: if board_offset == -1:
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( 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}") chip_temp = boards[1].get(f"temp{i}")
@@ -496,12 +550,12 @@ class AntminerOld(CGMiner):
if (not chips) or (not chips > 0): if (not chips) or (not chips > 0):
hashboard.missing = True hashboard.missing = True
hashboards.append(hashboard) hashboards.append(hashboard)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
return hashboards return hashboards
async def is_mining(self, web_get_conf: dict = None) -> Optional[bool]: async def _is_mining(self, web_get_conf: dict = None) -> Optional[bool]:
if not web_get_conf: if not web_get_conf:
try: try:
web_get_conf = await self.web.get_miner_conf() web_get_conf = await self.web.get_miner_conf()
@@ -526,7 +580,7 @@ class AntminerOld(CGMiner):
else: else:
return False return False
async def get_uptime(self, api_stats: dict = None) -> Optional[int]: async def _get_uptime(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()

View File

@@ -14,39 +14,54 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from collections import namedtuple from typing import List, Optional
from typing import List, Optional, Tuple
from pyasic.API.bfgminer import BFGMinerAPI from pyasic.API.bfgminer import BFGMinerAPI
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BFGMINER_DATA_LOC = { BFGMINER_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction("_get_mac"),
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, str(DataOptions.API_VERSION): DataFunction(
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"hostname": {"cmd": "get_hostname", "kwargs": {}}, ),
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, str(DataOptions.FW_VERSION): DataFunction(
"nominal_hashrate": { "_get_fw_ver", [RPCAPICommand("api_version", "version")]
"cmd": "get_nominal_hashrate", ),
"kwargs": {"api_stats": {"api": "stats"}}, str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
}, str(DataOptions.HASHRATE): DataFunction(
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, ),
"wattage": {"cmd": "get_wattage", "kwargs": {}}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, ),
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"errors": {"cmd": "get_errors", "kwargs": {}}, "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"fault_light": {"cmd": "get_fault_light", "kwargs": {}}, ),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"is_mining": {"cmd": "is_mining", "kwargs": {}}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"uptime": {"cmd": "get_uptime", "kwargs": {}}, 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): class BFGMiner(BaseMiner):
@@ -100,14 +115,10 @@ class BFGMiner(BaseMiner):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac(self) -> str: async def _get_mac(self) -> Optional[str]:
return "00:00:00:00:00:00" return None
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
# Check to see if the version info is already cached
if self.api_ver:
return self.api_ver
async def _get_api_ver(self, api_version: dict = None) -> Optional[str]:
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -117,16 +128,12 @@ class BFGMiner(BaseMiner):
if api_version: if api_version:
try: try:
self.api_ver = api_version["VERSION"][0]["API"] self.api_ver = api_version["VERSION"][0]["API"]
except (KeyError, IndexError): except LookupError:
pass pass
return self.api_ver return self.api_ver
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]: async def _get_fw_ver(self, api_version: dict = None) -> Optional[str]:
# Check to see if the version info is already cached
if self.fw_ver:
return self.fw_ver
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -136,31 +143,21 @@ class BFGMiner(BaseMiner):
if api_version: if api_version:
try: try:
self.fw_ver = api_version["VERSION"][0]["CompileTime"] self.fw_ver = api_version["VERSION"][0]["CompileTime"]
except (KeyError, IndexError): except LookupError:
pass pass
return self.fw_ver return self.fw_ver
async def get_version(
self, api_version: dict = None
) -> Tuple[Optional[str], Optional[str]]:
# check if version is cached
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
return miner_version(
api_ver=await self.get_api_ver(api_version),
fw_ver=await self.get_fw_ver(api_version=api_version),
)
async def reboot(self) -> bool: async def reboot(self) -> bool:
return False return False
async def get_fan_psu(self): async def _get_fan_psu(self):
return None return None
async def get_hostname(self) -> Optional[str]: async def _get_hostname(self) -> Optional[str]:
return None return None
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
# get hr from API # get hr from API
if not api_summary: if not api_summary:
try: try:
@@ -171,10 +168,10 @@ class BFGMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return round(float(api_summary["SUMMARY"][0]["MHS 20s"] / 1000000), 2) return round(float(api_summary["SUMMARY"][0]["MHS 20s"] / 1000000), 2)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [] hashboards = []
if not api_stats: if not api_stats:
@@ -197,9 +194,11 @@ class BFGMiner(BaseMiner):
if board_offset == -1: if board_offset == -1:
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( 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}") chip_temp = boards[1].get(f"temp{i}")
@@ -221,21 +220,21 @@ class BFGMiner(BaseMiner):
if (not chips) or (not chips > 0): if (not chips) or (not chips > 0):
hashboard.missing = True hashboard.missing = True
hashboards.append(hashboard) hashboards.append(hashboard)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
return hashboards return hashboards
async def get_env_temp(self) -> Optional[float]: async def _get_env_temp(self) -> Optional[float]:
return None return None
async def get_wattage(self) -> Optional[int]: async def _get_wattage(self) -> Optional[int]:
return None return None
async def get_wattage_limit(self) -> Optional[int]: async def _get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans(self, api_stats: dict = None) -> List[Fan]: async def _get_fans(self, api_stats: dict = None) -> List[Fan]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -255,49 +254,23 @@ class BFGMiner(BaseMiner):
if fan_offset == -1: if fan_offset == -1:
fan_offset = 1 fan_offset = 1
for fan in range(self.fan_count): for fan in range(self.expected_fans):
fans_data[fan] = api_stats["STATS"][1].get( fans_data[fan] = api_stats["STATS"][1].get(
f"fan{fan_offset+fan}", 0 f"fan{fan_offset+fan}", 0
) )
except (KeyError, IndexError): except LookupError:
pass pass
fans = [Fan(speed=d) if d else Fan() for d in fans_data] fans = [Fan(speed=d) if d else Fan() for d in fans_data]
return fans return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def _get_errors(self) -> List[MinerErrorData]:
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 [] return []
async def get_fault_light(self) -> bool: async def _get_fault_light(self) -> bool:
return False 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 # X19 method, not sure compatibility
if not api_stats: if not api_stats:
try: try:
@@ -307,22 +280,22 @@ class BFGMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"] expected_rate = api_stats["STATS"][1]["total_rateideal"]
try: try:
rate_unit = api_stats["STATS"][1]["rate_unit"] rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError: except KeyError:
rate_unit = "GH" rate_unit = "GH"
if rate_unit == "GH": if rate_unit == "GH":
return round(ideal_rate / 1000, 2) return round(expected_rate / 1000, 2)
if rate_unit == "MH": if rate_unit == "MH":
return round(ideal_rate / 1000000, 2) return round(expected_rate / 1000000, 2)
else: else:
return round(ideal_rate, 2) return round(expected_rate, 2)
except (KeyError, IndexError): except LookupError:
pass pass
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None return None
async def get_uptime(self, *args, **kwargs) -> Optional[int]: async def _get_uptime(self, *args, **kwargs) -> Optional[int]:
return None return None

View File

@@ -20,37 +20,54 @@ from pyasic.data import HashBoard
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger from pyasic.logger import logger
from pyasic.miners.backends import BFGMiner from pyasic.miners.backends import BFGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.goldshell import GoldshellWebAPI from pyasic.web.goldshell import GoldshellWebAPI
GOLDSHELL_DATA_LOC = { GOLDSHELL_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {"web_setting": {"web": "setting"}}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction(
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_mac", [WebAPICommand("web_setting", "setting")]
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_status": {"web": "status"}}}, ),
"hostname": {"cmd": "get_hostname", "kwargs": {}}, str(DataOptions.API_VERSION): DataFunction(
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"nominal_hashrate": { ),
"cmd": "get_nominal_hashrate", str(DataOptions.FW_VERSION): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_get_fw_ver", [WebAPICommand("web_status", "status")]
}, ),
"hashboards": { str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
"cmd": "get_hashboards", str(DataOptions.HASHRATE): DataFunction(
"kwargs": { "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"api_devs": {"api": "devs"}, ),
"api_devdetails": {"api": "devdetails"}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
}, ),
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"wattage": {"cmd": "get_wattage", "kwargs": {}}, "_get_hashboards",
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, [
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, RPCAPICommand("api_devs", "devs"),
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, RPCAPICommand("api_devdetails", "devdetails"),
"errors": {"cmd": "get_errors", "kwargs": {}}, ],
"fault_light": {"cmd": "get_fault_light", "kwargs": {}}, ),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"is_mining": {"cmd": "is_mining", "kwargs": {}}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"uptime": {"cmd": "get_uptime", "kwargs": {}}, 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): class BFGMinerGoldshell(BFGMiner):
@@ -92,7 +109,7 @@ class BFGMinerGoldshell(BFGMiner):
url=pool["url"], user=pool["user"], password=pool["pass"] url=pool["url"], user=pool["user"], password=pool["pass"]
) )
async def get_mac(self, web_setting: dict = None) -> str: async def _get_mac(self, web_setting: dict = None) -> str:
if not web_setting: if not web_setting:
try: try:
web_setting = await self.web.setting() web_setting = await self.web.setting()
@@ -105,7 +122,7 @@ class BFGMinerGoldshell(BFGMiner):
except KeyError: except KeyError:
pass pass
async def get_fw_ver(self, web_status: dict = None) -> str: async def _get_fw_ver(self, web_status: dict = None) -> str:
if not web_status: if not web_status:
try: try:
web_status = await self.web.setting() web_status = await self.web.setting()
@@ -118,7 +135,7 @@ class BFGMinerGoldshell(BFGMiner):
except KeyError: except KeyError:
pass pass
async def get_hashboards( async def _get_hashboards(
self, api_devs: dict = None, api_devdetails: dict = None self, api_devs: dict = None, api_devdetails: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if not api_devs: if not api_devs:
@@ -128,8 +145,8 @@ class BFGMinerGoldshell(BFGMiner):
pass pass
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.ideal_hashboards) for i in range(self.expected_hashboards)
] ]
if api_devs: if api_devs:
@@ -168,8 +185,8 @@ class BFGMinerGoldshell(BFGMiner):
return hashboards return hashboards
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None return None
async def get_uptime(self, *args, **kwargs) -> Optional[int]: async def _get_uptime(self, *args, **kwargs) -> Optional[int]:
return None return None

View File

@@ -15,42 +15,56 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging import logging
from collections import namedtuple from typing import List, Optional
from typing import List, Optional, Tuple
from pyasic.API.bmminer import BMMinerAPI from pyasic.API.bmminer import BMMinerAPI
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BMMINER_DATA_LOC = { BMMINER_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction("_get_mac"),
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, str(DataOptions.API_VERSION): DataFunction(
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"hostname": {"cmd": "get_hostname", "kwargs": {}}, ),
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, str(DataOptions.FW_VERSION): DataFunction(
"nominal_hashrate": { "_get_fw_ver", [RPCAPICommand("api_version", "version")]
"cmd": "get_nominal_hashrate", ),
"kwargs": {"api_stats": {"api": "stats"}}, str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
}, str(DataOptions.HASHRATE): DataFunction(
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, ),
"wattage": {"cmd": "get_wattage", "kwargs": {}}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, ),
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"errors": {"cmd": "get_errors", "kwargs": {}}, "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"fault_light": {"cmd": "get_fault_light", "kwargs": {}}, ),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"is_mining": {"cmd": "is_mining", "kwargs": {}}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"uptime": { str(DataOptions.WATTAGE_LIMIT): DataFunction("_get_wattage_limit"),
"cmd": "get_uptime", str(DataOptions.FANS): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_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): class BMMiner(BaseMiner):
@@ -140,14 +154,10 @@ class BMMiner(BaseMiner):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac(self) -> str: async def _get_mac(self) -> Optional[str]:
return "00:00:00:00:00:00" return None
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
# Check to see if the version info is already cached
if self.api_ver:
return self.api_ver
async def _get_api_ver(self, api_version: dict = None) -> Optional[str]:
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -157,16 +167,12 @@ class BMMiner(BaseMiner):
if api_version: if api_version:
try: try:
self.api_ver = api_version["VERSION"][0]["API"] self.api_ver = api_version["VERSION"][0]["API"]
except (KeyError, IndexError): except LookupError:
pass pass
return self.api_ver return self.api_ver
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]: async def _get_fw_ver(self, api_version: dict = None) -> Optional[str]:
# Check to see if the version info is already cached
if self.fw_ver:
return self.fw_ver
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -176,29 +182,19 @@ class BMMiner(BaseMiner):
if api_version: if api_version:
try: try:
self.fw_ver = api_version["VERSION"][0]["CompileTime"] self.fw_ver = api_version["VERSION"][0]["CompileTime"]
except (KeyError, IndexError): except LookupError:
pass pass
return self.fw_ver return self.fw_ver
async def get_version( async def _get_fan_psu(self):
self, api_version: dict = None
) -> Tuple[Optional[str], Optional[str]]:
# check if version is cached
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
return miner_version(
api_ver=await self.get_api_ver(api_version),
fw_ver=await self.get_fw_ver(api_version=api_version),
)
async def get_fan_psu(self):
return None return None
async def get_hostname(self) -> Optional[str]: async def _get_hostname(self) -> Optional[str]:
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname") hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
return hn return hn
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
# get hr from API # get hr from API
if not api_summary: if not api_summary:
try: try:
@@ -209,10 +205,10 @@ class BMMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return round(float(api_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2) return round(float(api_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [] hashboards = []
if not api_stats: if not api_stats:
@@ -247,12 +243,12 @@ class BMMiner(BaseMiner):
if len(real_slots) < 3: if len(real_slots) < 3:
real_slots = list( real_slots = list(
range(board_offset, board_offset + self.ideal_hashboards) range(board_offset, board_offset + self.expected_hashboards)
) )
for i in real_slots: for i in real_slots:
hashboard = HashBoard( 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}") chip_temp = boards[1].get(f"temp{i}")
@@ -279,23 +275,23 @@ class BMMiner(BaseMiner):
return hashboards return hashboards
async def get_env_temp(self) -> Optional[float]: async def _get_env_temp(self) -> Optional[float]:
return None return None
async def get_wattage(self) -> Optional[int]: async def _get_wattage(self) -> Optional[int]:
return None return None
async def get_wattage_limit(self) -> Optional[int]: async def _get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans(self, api_stats: dict = None) -> List[Fan]: async def _get_fans(self, api_stats: dict = None) -> List[Fan]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass
fans = [Fan() for _ in range(self.fan_count)] fans = [Fan() for _ in range(self.expected_fans)]
if api_stats: if api_stats:
try: try:
fan_offset = -1 fan_offset = -1
@@ -308,48 +304,22 @@ class BMMiner(BaseMiner):
if fan_offset == -1: if fan_offset == -1:
fan_offset = 1 fan_offset = 1
for fan in range(self.fan_count): for fan in range(self.expected_fans):
fans[fan].speed = api_stats["STATS"][1].get( fans[fan].speed = api_stats["STATS"][1].get(
f"fan{fan_offset+fan}", 0 f"fan{fan_offset+fan}", 0
) )
except (KeyError, IndexError): except LookupError:
pass pass
return fans return fans
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def _get_errors(self) -> List[MinerErrorData]:
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 [] return []
async def get_fault_light(self) -> bool: async def _get_fault_light(self) -> bool:
return False 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 # X19 method, not sure compatibility
if not api_stats: if not api_stats:
try: try:
@@ -359,27 +329,27 @@ class BMMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"] expected_rate = api_stats["STATS"][1]["total_rateideal"]
try: try:
rate_unit = api_stats["STATS"][1]["rate_unit"] rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError: except KeyError:
rate_unit = "GH" rate_unit = "GH"
if rate_unit == "GH": if rate_unit == "GH":
return round(ideal_rate / 1000, 2) return round(expected_rate / 1000, 2)
if rate_unit == "MH": if rate_unit == "MH":
return round(ideal_rate / 1000000, 2) return round(expected_rate / 1000000, 2)
else: else:
return round(ideal_rate, 2) return round(expected_rate, 2)
except (KeyError, IndexError): except LookupError:
pass pass
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None return None
async def get_uptime(self, api_stats: dict = None) -> Optional[int]: async def _get_uptime(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.web.get_miner_conf() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass

File diff suppressed because it is too large Load Diff

View File

@@ -1,158 +0,0 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import logging
from typing import List, Optional, Tuple
from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard, MinerData
from pyasic.data.error_codes import MinerErrorData
from pyasic.miners.backends import BOSMiner
class BOSMinerOld(BOSMiner):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver)
async def send_ssh_command(self, cmd: str) -> Optional[str]:
result = None
try:
conn = await self._get_ssh_connection()
except ConnectionError:
return None
# open an ssh connection
async with conn:
# 3 retries
for i in range(3):
try:
# run the command and get the result
result = await conn.run(cmd)
result = result.stdout
except Exception as e:
# if the command fails, log it
logging.warning(f"{self} command {cmd} error: {e}")
# on the 3rd retry, return None
if i == 3:
return
continue
# return the result, either command output or None
return result
async def update_to_plus(self):
result = await self.send_ssh_command("opkg update && opkg install bos_plus")
return result
async def check_light(self) -> bool:
return False
async def fault_light_on(self) -> bool:
return False
async def fault_light_off(self) -> bool:
return False
async def get_config(self) -> None:
return None
async def reboot(self) -> bool:
return False
async def restart_backend(self) -> bool:
return False
async def stop_mining(self) -> bool:
return False
async def resume_mining(self) -> bool:
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
return None
async def set_power_limit(self, wattage: int) -> bool:
return False
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
async def get_mac(self, *args, **kwargs) -> Optional[str]:
return None
async def get_model(self, *args, **kwargs) -> str:
return "S9"
async def get_version(self, *args, **kwargs) -> Tuple[Optional[str], Optional[str]]:
return None, None
async def get_hostname(self, *args, **kwargs) -> Optional[str]:
return None
async def get_hashrate(self, *args, **kwargs) -> Optional[float]:
return None
async def get_hashboards(self, *args, **kwargs) -> List[HashBoard]:
return []
async def get_env_temp(self, *args, **kwargs) -> Optional[float]:
return None
async def get_wattage(self, *args, **kwargs) -> Optional[int]:
return None
async def get_wattage_limit(self, *args, **kwargs) -> Optional[int]:
return None
async def get_fans(
self,
*args,
**kwargs,
) -> List[Fan]:
return [Fan(), Fan(), Fan(), Fan()]
async def get_fan_psu(self, *args, **kwargs) -> Optional[int]:
return None
async def get_api_ver(self, *args, **kwargs) -> Optional[str]:
return None
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]:
return None
async def get_data(self, allow_warning: bool = False, **kwargs) -> MinerData:
return MinerData(ip=str(self.ip))
async def is_mining(self, *args, **kwargs) -> Optional[bool]:
return None
async def get_uptime(self, *args, **kwargs) -> Optional[int]:
return None

File diff suppressed because it is too large Load Diff

View File

@@ -15,84 +15,95 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging import logging
from collections import namedtuple from typing import List, Optional
from typing import List, Optional, Tuple
from pyasic.API.btminer import BTMinerAPI from pyasic.API.btminer import BTMinerAPI
from pyasic.config import MinerConfig, MiningModeConfig from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, WhatsminerError from pyasic.data.error_codes import MinerErrorData, WhatsminerError
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
BTMINER_DATA_LOC = { BTMINER_DATA_LOC = DataLocations(
"mac": { **{
"cmd": "get_mac", str(DataOptions.MAC): DataFunction(
"kwargs": { "_get_mac",
"api_summary": {"api": "summary"}, [
"api_get_miner_info": {"api": "get_miner_info"}, RPCAPICommand("api_summary", "summary"),
}, RPCAPICommand("api_get_miner_info", "get_miner_info"),
}, ],
"model": {"cmd": "get_model", "kwargs": {}}, ),
"api_ver": { str(DataOptions.API_VERSION): DataFunction(
"cmd": "get_api_ver", "_get_api_ver", [RPCAPICommand("api_get_version", "get_version")]
"kwargs": {"api_get_version": {"api": "get_version"}}, ),
}, str(DataOptions.FW_VERSION): DataFunction(
"fw_ver": { "_get_fw_ver",
"cmd": "get_fw_ver", [
"kwargs": { RPCAPICommand("api_get_version", "get_version"),
"api_get_version": {"api": "get_version"}, RPCAPICommand("api_summary", "summary"),
"api_summary": {"api": "summary"}, ],
}, ),
}, str(DataOptions.HOSTNAME): DataFunction(
"hostname": { "_get_hostname", [RPCAPICommand("api_get_miner_info", "get_miner_info")]
"cmd": "get_hostname", ),
"kwargs": {"api_get_miner_info": {"api": "get_miner_info"}}, str(DataOptions.HASHRATE): DataFunction(
}, "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, ),
"nominal_hashrate": { str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"cmd": "get_nominal_hashrate", "_get_expected_hashrate", [RPCAPICommand("api_summary", "summary")]
"kwargs": {"api_summary": {"api": "summary"}}, ),
}, str(DataOptions.HASHBOARDS): DataFunction(
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_devs": {"api": "devs"}}}, "_get_hashboards", [RPCAPICommand("api_devs", "devs")]
"env_temp": {"cmd": "get_env_temp", "kwargs": {"api_summary": {"api": "summary"}}}, ),
"wattage": {"cmd": "get_wattage", "kwargs": {"api_summary": {"api": "summary"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"wattage_limit": { "_get_env_temp", [RPCAPICommand("api_summary", "summary")]
"cmd": "get_wattage_limit", ),
"kwargs": {"api_summary": {"api": "summary"}}, str(DataOptions.WATTAGE): DataFunction(
}, "_get_wattage", [RPCAPICommand("api_summary", "summary")]
"fans": { ),
"cmd": "get_fans", str(DataOptions.WATTAGE_LIMIT): DataFunction(
"kwargs": { "_get_wattage_limit", [RPCAPICommand("api_summary", "summary")]
"api_summary": {"api": "summary"}, ),
"api_get_psu": {"api": "get_psu"}, str(DataOptions.FANS): DataFunction(
}, "_get_fans",
}, [
"fan_psu": { RPCAPICommand("api_summary", "summary"),
"cmd": "get_fan_psu", RPCAPICommand("api_get_psu", "get_psu"),
"kwargs": { ],
"api_summary": {"api": "summary"}, ),
"api_get_psu": {"api": "get_psu"}, str(DataOptions.FAN_PSU): DataFunction(
}, "_get_fan_psu",
}, [
"errors": { RPCAPICommand("api_summary", "summary"),
"cmd": "get_errors", RPCAPICommand("api_get_psu", "get_psu"),
"kwargs": { ],
"api_summary": {"api": "summary"}, ),
"api_get_error_code": {"api": "get_error_code"}, str(DataOptions.ERRORS): DataFunction(
}, "_get_errors",
}, [
"fault_light": { RPCAPICommand("api_get_error_code", "get_error_code"),
"cmd": "get_fault_light", RPCAPICommand("api_summary", "summary"),
"kwargs": {"api_get_miner_info": {"api": "get_miner_info"}}, ],
}, ),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.FAULT_LIGHT): DataFunction(
"is_mining": {"cmd": "is_mining", "kwargs": {"api_status": {"api": "status"}}}, "_get_fault_light",
"uptime": { [RPCAPICommand("api_get_miner_info", "get_miner_info")],
"cmd": "get_uptime", ),
"kwargs": {"api_summary": {"api": "summary"}}, 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): class BTMiner(BaseMiner):
@@ -229,7 +240,7 @@ class BTMiner(BaseMiner):
else: else:
cfg = MinerConfig() cfg = MinerConfig()
is_mining = await self.is_mining(status) is_mining = await self._is_mining(status)
if not is_mining: if not is_mining:
cfg.mining_mode = MiningModeConfig.sleep() cfg.mining_mode = MiningModeConfig.sleep()
return cfg return cfg
@@ -273,7 +284,7 @@ class BTMiner(BaseMiner):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac( async def _get_mac(
self, api_summary: dict = None, api_get_miner_info: dict = None self, api_summary: dict = None, api_get_miner_info: dict = None
) -> Optional[str]: ) -> Optional[str]:
if not api_get_miner_info: if not api_get_miner_info:
@@ -299,24 +310,10 @@ class BTMiner(BaseMiner):
try: try:
mac = api_summary["SUMMARY"][0]["MAC"] mac = api_summary["SUMMARY"][0]["MAC"]
return str(mac).upper() return str(mac).upper()
except (KeyError, IndexError): except LookupError:
pass pass
async def get_version( async def _get_api_ver(self, api_get_version: dict = None) -> Optional[str]:
self, api_get_version: dict = None, api_summary: dict = None
) -> Tuple[Optional[str], Optional[str]]:
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
api_ver = await self.get_api_ver(api_get_version=api_get_version)
fw_ver = await self.get_fw_ver(
api_get_version=api_get_version, api_summary=api_summary
)
return miner_version(api_ver, fw_ver)
async def get_api_ver(self, api_get_version: dict = None) -> Optional[str]:
# Check to see if the version info is already cached
if self.api_ver:
return self.api_ver
if not api_get_version: if not api_get_version:
try: try:
api_get_version = await self.api.get_version() api_get_version = await self.api.get_version()
@@ -339,13 +336,9 @@ class BTMiner(BaseMiner):
return self.api_ver return self.api_ver
async def get_fw_ver( async def _get_fw_ver(
self, api_get_version: dict = None, api_summary: dict = None self, api_get_version: dict = None, api_summary: dict = None
) -> Optional[str]: ) -> Optional[str]:
# Check to see if the version info is already cached
if self.fw_ver:
return self.fw_ver
if not api_get_version: if not api_get_version:
try: try:
api_get_version = await self.api.get_version() api_get_version = await self.api.get_version()
@@ -373,12 +366,12 @@ class BTMiner(BaseMiner):
self.fw_ver = api_summary["SUMMARY"][0]["Firmware Version"].replace( self.fw_ver = api_summary["SUMMARY"][0]["Firmware Version"].replace(
"'", "" "'", ""
) )
except (KeyError, IndexError): except LookupError:
pass pass
return self.fw_ver return self.fw_ver
async def get_hostname(self, api_get_miner_info: dict = None) -> Optional[str]: async def _get_hostname(self, api_get_miner_info: dict = None) -> Optional[str]:
hostname = None hostname = None
if not api_get_miner_info: if not api_get_miner_info:
try: try:
@@ -394,7 +387,7 @@ class BTMiner(BaseMiner):
return hostname return hostname
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
# get hr from API # get hr from API
if not api_summary: if not api_summary:
try: try:
@@ -405,13 +398,13 @@ class BTMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2) return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2)
except (KeyError, IndexError): except LookupError:
pass pass
async def get_hashboards(self, api_devs: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_devs: dict = None) -> List[HashBoard]:
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.ideal_hashboards) for i in range(self.expected_hashboards)
] ]
if not api_devs: if not api_devs:
@@ -426,23 +419,24 @@ class BTMiner(BaseMiner):
if len(hashboards) < board["ASC"] + 1: if len(hashboards) < board["ASC"] + 1:
hashboards.append( hashboards.append(
HashBoard( 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"]].chip_temp = round(board["Chip Temp Avg"])
hashboards[board["ASC"]].temp = round(board["Temperature"]) hashboards[board["ASC"]].temp = round(board["Temperature"])
hashboards[board["ASC"]].hashrate = round( hashboards[board["ASC"]].hashrate = round(
float(board["MHS 1m"] / 1000000), 2 float(board["MHS 1m"] / 1000000), 2
) )
hashboards[board["ASC"]].chips = board["Effective Chips"] hashboards[board["ASC"]].chips = board["Effective Chips"]
hashboards[board["ASC"]].serial_number = board["PCB SN"]
hashboards[board["ASC"]].missing = False hashboards[board["ASC"]].missing = False
except (KeyError, IndexError): except LookupError:
pass pass
return hashboards return hashboards
async def get_env_temp(self, api_summary: dict = None) -> Optional[float]: async def _get_env_temp(self, api_summary: dict = None) -> Optional[float]:
if not api_summary: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -452,10 +446,10 @@ class BTMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return api_summary["SUMMARY"][0]["Env Temp"] return api_summary["SUMMARY"][0]["Env Temp"]
except (KeyError, IndexError): except LookupError:
pass pass
async def get_wattage(self, api_summary: dict = None) -> Optional[int]: async def _get_wattage(self, api_summary: dict = None) -> Optional[int]:
if not api_summary: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -466,10 +460,10 @@ class BTMiner(BaseMiner):
try: try:
wattage = api_summary["SUMMARY"][0]["Power"] wattage = api_summary["SUMMARY"][0]["Power"]
return wattage if not wattage == -1 else None return wattage if not wattage == -1 else None
except (KeyError, IndexError): except LookupError:
pass pass
async def get_wattage_limit(self, api_summary: dict = None) -> Optional[int]: async def _get_wattage_limit(self, api_summary: dict = None) -> Optional[int]:
if not api_summary: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -479,10 +473,10 @@ class BTMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return api_summary["SUMMARY"][0]["Power Limit"] return api_summary["SUMMARY"][0]["Power Limit"]
except (KeyError, IndexError): except LookupError:
pass pass
async def get_fans( async def _get_fans(
self, api_summary: dict = None, api_get_psu: dict = None self, api_summary: dict = None, api_get_psu: dict = None
) -> List[Fan]: ) -> List[Fan]:
if not api_summary: if not api_summary:
@@ -491,20 +485,20 @@ class BTMiner(BaseMiner):
except APIError: except APIError:
pass pass
fans = [Fan() for _ in range(self.fan_count)] fans = [Fan() for _ in range(self.expected_fans)]
if api_summary: if api_summary:
try: try:
if self.fan_count > 0: if self.expected_fans > 0:
fans = [ fans = [
Fan(api_summary["SUMMARY"][0].get("Fan Speed In", 0)), Fan(api_summary["SUMMARY"][0].get("Fan Speed In", 0)),
Fan(api_summary["SUMMARY"][0].get("Fan Speed Out", 0)), Fan(api_summary["SUMMARY"][0].get("Fan Speed Out", 0)),
] ]
except (KeyError, IndexError): except LookupError:
pass pass
return fans return fans
async def get_fan_psu( async def _get_fan_psu(
self, api_summary: dict = None, api_get_psu: dict = None self, api_summary: dict = None, api_get_psu: dict = None
) -> Optional[int]: ) -> Optional[int]:
if not api_summary: if not api_summary:
@@ -516,7 +510,7 @@ class BTMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return int(api_summary["SUMMARY"][0]["Power Fanspeed"]) return int(api_summary["SUMMARY"][0]["Power Fanspeed"])
except (KeyError, IndexError): except LookupError:
pass pass
if not api_get_psu: if not api_get_psu:
@@ -531,52 +525,11 @@ class BTMiner(BaseMiner):
except (KeyError, TypeError): except (KeyError, TypeError):
pass pass
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def _get_errors(
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 self, api_summary: dict = None, api_get_error_code: dict = None
) -> List[MinerErrorData]: ) -> List[MinerErrorData]:
errors = [] errors = []
if not api_summary and not api_get_error_code: if not api_get_error_code and not api_summary:
try:
api_summary = await self.api.summary()
except APIError:
pass
if api_summary:
try:
for i in range(api_summary["SUMMARY"][0]["Error Code Count"]):
err = api_summary["SUMMARY"][0].get(f"Error Code {i}")
if err:
errors.append(WhatsminerError(error_code=err))
except (KeyError, IndexError, ValueError, TypeError):
pass
if not api_get_error_code:
try: try:
api_get_error_code = await self.api.get_error_code() api_get_error_code = await self.api.get_error_code()
except APIError: except APIError:
@@ -590,9 +543,6 @@ class BTMiner(BaseMiner):
else: else:
errors.append(WhatsminerError(error_code=int(err))) errors.append(WhatsminerError(error_code=int(err)))
return errors
async def get_nominal_hashrate(self, api_summary: dict = None):
if not api_summary: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -601,13 +551,30 @@ class BTMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
nominal_hashrate = api_summary["SUMMARY"][0]["Factory GHS"] for i in range(api_summary["SUMMARY"][0]["Error Code Count"]):
if nominal_hashrate: err = api_summary["SUMMARY"][0].get(f"Error Code {i}")
return round(nominal_hashrate / 1000, 2) if err:
except (KeyError, IndexError): errors.append(WhatsminerError(error_code=err))
except (LookupError, ValueError, TypeError):
pass
return errors
async def _get_expected_hashrate(self, api_summary: dict = None):
if not api_summary:
try:
api_summary = await self.api.summary()
except APIError:
pass pass
async def get_fault_light(self, api_get_miner_info: dict = None) -> bool: if api_summary:
try:
expected_hashrate = api_summary["SUMMARY"][0]["Factory GHS"]
if expected_hashrate:
return round(expected_hashrate / 1000, 2)
except LookupError:
pass
async def _get_fault_light(self, api_get_miner_info: dict = None) -> bool:
if not api_get_miner_info: if not api_get_miner_info:
try: try:
api_get_miner_info = await self.api.get_miner_info() api_get_miner_info = await self.api.get_miner_info()
@@ -645,7 +612,7 @@ class BTMiner(BaseMiner):
async def set_hostname(self, hostname: str): async def set_hostname(self, hostname: str):
await self.api.set_hostname(hostname) await self.api.set_hostname(hostname)
async def is_mining(self, api_status: dict = None) -> Optional[bool]: async def _is_mining(self, api_status: dict = None) -> Optional[bool]:
if not api_status: if not api_status:
try: try:
api_status = await self.api.status() api_status = await self.api.status()
@@ -664,7 +631,7 @@ class BTMiner(BaseMiner):
except LookupError: except LookupError:
pass pass
async def get_uptime(self, api_summary: dict = None) -> Optional[int]: async def _get_uptime(self, api_summary: dict = None) -> Optional[int]:
if not api_summary: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()

View File

@@ -15,42 +15,56 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging import logging
from collections import namedtuple from typing import List, Optional
from typing import List, Optional, Tuple
from pyasic.API.cgminer import CGMinerAPI from pyasic.API.cgminer import CGMinerAPI
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import (
BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
CGMINER_DATA_LOC = { CGMINER_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction("_get_mac"),
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, str(DataOptions.API_VERSION): DataFunction(
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"hostname": {"cmd": "get_hostname", "kwargs": {}}, ),
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, str(DataOptions.FW_VERSION): DataFunction(
"nominal_hashrate": { "_get_fw_ver", [RPCAPICommand("api_version", "version")]
"cmd": "get_nominal_hashrate", ),
"kwargs": {"api_stats": {"api": "stats"}}, str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
}, str(DataOptions.HASHRATE): DataFunction(
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, ),
"wattage": {"cmd": "get_wattage", "kwargs": {}}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"wattage_limit": {"cmd": "get_wattage_limit", "kwargs": {}}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, ),
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"errors": {"cmd": "get_errors", "kwargs": {}}, "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"fault_light": {"cmd": "get_fault_light", "kwargs": {}}, ),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"is_mining": {"cmd": "is_mining", "kwargs": {}}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"uptime": { str(DataOptions.WATTAGE_LIMIT): DataFunction("_get_wattage_limit"),
"cmd": "get_uptime", str(DataOptions.FANS): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_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): class CGMiner(BaseMiner):
@@ -96,11 +110,9 @@ class CGMiner(BaseMiner):
return result return result
async def restart_backend(self) -> bool: 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() return await self.restart_cgminer()
async def restart_cgminer(self) -> bool: async def restart_cgminer(self) -> bool:
"""Restart cgminer hashing process."""
commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"] commands = ["cgminer-api restart", "/usr/bin/cgminer-monitor >/dev/null 2>&1"]
commands = ";".join(commands) commands = ";".join(commands)
ret = await self.send_ssh_command(commands) ret = await self.send_ssh_command(commands)
@@ -109,7 +121,6 @@ class CGMiner(BaseMiner):
return True return True
async def reboot(self) -> bool: async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.") logging.debug(f"{self}: Sending reboot command.")
ret = await self.send_ssh_command("reboot") ret = await self.send_ssh_command("reboot")
if ret is None: if ret is None:
@@ -168,22 +179,10 @@ class CGMiner(BaseMiner):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac(self) -> Optional[str]: async def _get_mac(self) -> Optional[str]:
return None return None
async def get_version( async def _get_api_ver(self, api_version: dict = None) -> Optional[str]:
self, api_version: dict = None
) -> Tuple[Optional[str], Optional[str]]:
miner_version = namedtuple("MinerVersion", "api_ver fw_ver")
return miner_version(
api_ver=await self.get_api_ver(api_version=api_version),
fw_ver=await self.get_fw_ver(api_version=api_version),
)
async def get_api_ver(self, api_version: dict = None) -> Optional[str]:
if self.api_ver:
return self.api_ver
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -193,15 +192,12 @@ class CGMiner(BaseMiner):
if api_version: if api_version:
try: try:
self.api_ver = api_version["VERSION"][0]["API"] self.api_ver = api_version["VERSION"][0]["API"]
except (KeyError, IndexError): except LookupError:
pass pass
return self.api_ver return self.api_ver
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]: async def _get_fw_ver(self, api_version: dict = None) -> Optional[str]:
if self.fw_ver:
return self.fw_ver
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -211,16 +207,16 @@ class CGMiner(BaseMiner):
if api_version: if api_version:
try: try:
self.fw_ver = api_version["VERSION"][0]["CGMiner"] self.fw_ver = api_version["VERSION"][0]["CGMiner"]
except (KeyError, IndexError): except LookupError:
pass pass
return self.fw_ver return self.fw_ver
async def get_hostname(self) -> Optional[str]: async def _get_hostname(self) -> Optional[str]:
hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname") hn = await self.send_ssh_command("cat /proc/sys/kernel/hostname")
return hn return hn
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
# get hr from API # get hr from API
if not api_summary: if not api_summary:
try: try:
@@ -233,10 +229,10 @@ class CGMiner(BaseMiner):
return round( return round(
float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2 float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2
) )
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [] hashboards = []
if not api_stats: if not api_stats:
@@ -259,9 +255,11 @@ class CGMiner(BaseMiner):
if board_offset == -1: if board_offset == -1:
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( 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}") chip_temp = boards[1].get(f"temp{i}")
@@ -283,28 +281,28 @@ class CGMiner(BaseMiner):
if (not chips) or (not chips > 0): if (not chips) or (not chips > 0):
hashboard.missing = True hashboard.missing = True
hashboards.append(hashboard) hashboards.append(hashboard)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
return hashboards return hashboards
async def get_env_temp(self) -> Optional[float]: async def _get_env_temp(self) -> Optional[float]:
return None return None
async def get_wattage(self) -> Optional[int]: async def _get_wattage(self) -> Optional[int]:
return None return None
async def get_wattage_limit(self) -> Optional[int]: async def _get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans(self, api_stats: dict = None) -> List[Fan]: async def _get_fans(self, api_stats: dict = None) -> List[Fan]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass
fans = [Fan() for _ in range(self.fan_count)] fans = [Fan() for _ in range(self.expected_fans)]
if api_stats: if api_stats:
try: try:
fan_offset = -1 fan_offset = -1
@@ -317,50 +315,24 @@ class CGMiner(BaseMiner):
if fan_offset == -1: if fan_offset == -1:
fan_offset = 1 fan_offset = 1
for fan in range(self.fan_count): for fan in range(self.expected_fans):
fans[fan].speed = api_stats["STATS"][1].get( fans[fan].speed = api_stats["STATS"][1].get(
f"fan{fan_offset+fan}", 0 f"fan{fan_offset+fan}", 0
) )
except (KeyError, IndexError): except LookupError:
pass pass
return fans return fans
async def get_fan_psu(self) -> Optional[int]: async def _get_fan_psu(self) -> Optional[int]:
return None return None
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def _get_errors(self) -> List[MinerErrorData]:
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 [] return []
async def get_fault_light(self) -> bool: async def _get_fault_light(self) -> bool:
return False 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 # X19 method, not sure compatibility
if not api_stats: if not api_stats:
try: try:
@@ -370,24 +342,24 @@ class CGMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"] expected_rate = api_stats["STATS"][1]["total_rateideal"]
try: try:
rate_unit = api_stats["STATS"][1]["rate_unit"] rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError: except KeyError:
rate_unit = "GH" rate_unit = "GH"
if rate_unit == "GH": if rate_unit == "GH":
return round(ideal_rate / 1000, 2) return round(expected_rate / 1000, 2)
if rate_unit == "MH": if rate_unit == "MH":
return round(ideal_rate / 1000000, 2) return round(expected_rate / 1000000, 2)
else: else:
return round(ideal_rate, 2) return round(expected_rate, 2)
except (KeyError, IndexError): except LookupError:
pass pass
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None return None
async def get_uptime(self, api_stats: dict = None) -> Optional[int]: async def _get_uptime(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()

View File

@@ -14,7 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging
import re import re
from typing import List, Optional from typing import List, Optional
@@ -23,36 +22,49 @@ from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner from pyasic.miners.backends import CGMiner
from pyasic.miners.base import DataFunction, DataLocations, DataOptions, RPCAPICommand
AVALON_DATA_LOC = { AVALON_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {"api_version": {"api": "version"}}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction(
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_mac", [RPCAPICommand("api_version", "version")]
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"api_version": {"api": "version"}}}, ),
"hostname": {"cmd": "get_hostname", "kwargs": {"mac": {"api": "version"}}}, str(DataOptions.API_VERSION): DataFunction(
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_devs": {"api": "devs"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"nominal_hashrate": { ),
"cmd": "get_nominal_hashrate", str(DataOptions.FW_VERSION): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_get_fw_ver", [RPCAPICommand("api_version", "version")]
}, ),
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
"env_temp": {"cmd": "get_env_temp", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.HASHRATE): DataFunction(
"wattage": {"cmd": "get_wattage", "kwargs": {}}, "_get_hashrate", [RPCAPICommand("api_devs", "devs")]
"wattage_limit": { ),
"cmd": "get_wattage_limit", str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
}, ),
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.HASHBOARDS): DataFunction(
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"errors": {"cmd": "get_errors", "kwargs": {}}, ),
"fault_light": { str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"cmd": "get_fault_light", "_get_env_temp", [RPCAPICommand("api_stats", "stats")]
"kwargs": {"api_stats": {"api": "stats"}}, ),
}, str(DataOptions.WATTAGE): DataFunction("_get_wattage"),
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, str(DataOptions.WATTAGE_LIMIT): DataFunction(
"is_mining": {"cmd": "is_mining", "kwargs": {}}, "_get_wattage_limit", [RPCAPICommand("api_stats", "stats")]
"uptime": {"cmd": "get_uptime", "kwargs": {}}, ),
} 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): class CGMinerAvalon(CGMiner):
@@ -115,7 +127,7 @@ class CGMinerAvalon(CGMiner):
@staticmethod @staticmethod
def parse_stats(stats): def parse_stats(stats):
_stats_items = re.findall(".+?\[*?]", stats) _stats_items = re.findall(".+?\\[*?]", stats)
stats_items = [] stats_items = []
stats_dict = {} stats_dict = {}
for item in _stats_items: for item in _stats_items:
@@ -160,7 +172,7 @@ class CGMinerAvalon(CGMiner):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac(self, api_version: dict = None) -> Optional[str]: async def _get_mac(self, api_version: dict = None) -> Optional[str]:
if not api_version: if not api_version:
try: try:
api_version = await self.api.version() api_version = await self.api.version()
@@ -178,7 +190,7 @@ class CGMinerAvalon(CGMiner):
except (KeyError, ValueError): except (KeyError, ValueError):
pass pass
async def get_hostname(self, mac: str = None) -> Optional[str]: async def _get_hostname(self) -> Optional[str]:
return None return None
# if not mac: # if not mac:
# mac = await self.get_mac() # mac = await self.get_mac()
@@ -186,7 +198,7 @@ class CGMinerAvalon(CGMiner):
# if mac: # if mac:
# return f"Avalon{mac.replace(':', '')[-6:]}" # return f"Avalon{mac.replace(':', '')[-6:]}"
async def get_hashrate(self, api_devs: dict = None) -> Optional[float]: async def _get_hashrate(self, api_devs: dict = None) -> Optional[float]:
if not api_devs: if not api_devs:
try: try:
api_devs = await self.api.devs() api_devs = await self.api.devs()
@@ -199,10 +211,10 @@ class CGMinerAvalon(CGMiner):
except (KeyError, IndexError, ValueError, TypeError): except (KeyError, IndexError, ValueError, TypeError):
pass pass
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [ hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.ideal_hashboards) for i in range(self.expected_hashboards)
] ]
if not api_stats: if not api_stats:
@@ -218,7 +230,7 @@ class CGMinerAvalon(CGMiner):
except (IndexError, KeyError, ValueError, TypeError): except (IndexError, KeyError, ValueError, TypeError):
return hashboards return hashboards
for board in range(self.ideal_hashboards): for board in range(self.expected_hashboards):
try: try:
hashboards[board].chip_temp = int(parsed_stats["MTmax"][board]) hashboards[board].chip_temp = int(parsed_stats["MTmax"][board])
except LookupError: except LookupError:
@@ -247,7 +259,7 @@ class CGMinerAvalon(CGMiner):
return hashboards 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: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -262,7 +274,7 @@ class CGMinerAvalon(CGMiner):
except (IndexError, KeyError, ValueError, TypeError): except (IndexError, KeyError, ValueError, TypeError):
pass pass
async def get_env_temp(self, api_stats: dict = None) -> Optional[float]: async def _get_env_temp(self, api_stats: dict = None) -> Optional[float]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -277,10 +289,10 @@ class CGMinerAvalon(CGMiner):
except (IndexError, KeyError, ValueError, TypeError): except (IndexError, KeyError, ValueError, TypeError):
pass pass
async def get_wattage(self) -> Optional[int]: async def _get_wattage(self) -> Optional[int]:
return None return None
async def get_wattage_limit(self, api_stats: dict = None) -> Optional[int]: async def _get_wattage_limit(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -295,14 +307,14 @@ class CGMinerAvalon(CGMiner):
except (IndexError, KeyError, ValueError, TypeError): except (IndexError, KeyError, ValueError, TypeError):
pass pass
async def get_fans(self, api_stats: dict = None) -> List[Fan]: async def _get_fans(self, api_stats: dict = None) -> List[Fan]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
except APIError: except APIError:
pass pass
fans_data = [Fan() for _ in range(self.fan_count)] fans_data = [Fan() for _ in range(self.expected_fans)]
if api_stats: if api_stats:
try: try:
unparsed_stats = api_stats["STATS"][0]["MM ID0"] unparsed_stats = api_stats["STATS"][0]["MM ID0"]
@@ -310,43 +322,17 @@ class CGMinerAvalon(CGMiner):
except LookupError: except LookupError:
return fans_data return fans_data
for fan in range(self.fan_count): for fan in range(self.expected_fans):
try: try:
fans_data[fan].speed = int(parsed_stats[f"Fan{fan + 1}"]) fans_data[fan].speed = int(parsed_stats[f"Fan{fan + 1}"])
except (IndexError, KeyError, ValueError, TypeError): except (IndexError, KeyError, ValueError, TypeError):
pass pass
return fans_data return fans_data
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def _get_errors(self) -> List[MinerErrorData]:
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 [] return []
async def get_fault_light(self, api_stats: dict = None) -> bool: # noqa async def _get_fault_light(self, api_stats: dict = None) -> bool: # noqa
if self.light: if self.light:
return self.light return self.light
if not api_stats: if not api_stats:
@@ -375,5 +361,8 @@ class CGMinerAvalon(CGMiner):
pass pass
return False return False
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None
async def _get_uptime(self) -> Optional[int]:
return None return None

View File

@@ -14,49 +14,72 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from typing import List, Optional, Tuple, Union from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger 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 from pyasic.web.epic import ePICWebAPI
EPIC_DATA_LOC = { EPIC_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "network"}}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction(
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_mac", [WebAPICommand("web_network", "network")]
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}}, ),
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}}, str(DataOptions.API_VERSION): DataFunction("_get_api_ver"),
"hashrate": {"cmd": "get_hashrate", "kwargs": {"web_summary": {"web": "summary"}}}, str(DataOptions.FW_VERSION): DataFunction(
"nominal_hashrate": { "_get_fw_ver", [WebAPICommand("web_summary", "summary")]
"cmd": "get_nominal_hashrate", ),
"kwargs": {"web_summary": {"web": "summary"}}, str(DataOptions.HOSTNAME): DataFunction(
}, "_get_hostname", [WebAPICommand("web_summary", "summary")]
"hashboards": { ),
"cmd": "get_hashboards", str(DataOptions.HASHRATE): DataFunction(
"kwargs": { "_get_hashrate", [WebAPICommand("web_summary", "summary")]
"web_summary": {"web": "summary"}, ),
"web_hashrate": {"web": "hashrate"}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
}, "_get_expected_hashrate", [WebAPICommand("web_summary", "summary")]
}, ),
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}}, "_get_hashboards",
"fans": {"cmd": "get_fans", "kwargs": {"web_summary": {"web": "summary"}}}, [
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, WebAPICommand("web_summary", "summary"),
"fault_light": { WebAPICommand("web_hashrate", "hashrate"),
"cmd": "get_fault_light", ],
"kwargs": {"web_summary": {"web": "summary"}}, ),
}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"pools": {"cmd": "get_pools", "kwargs": {"web_summary": {"web": "summary"}}}, str(DataOptions.WATTAGE): DataFunction(
"is_mining": {"cmd": "is_mining", "kwargs": {}}, "_get_wattage", [WebAPICommand("web_summary", "summary")]
"uptime": {"cmd": "get_uptime", "kwargs": {"web_summary": {"web": "summary"}}}, ),
"errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "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: def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
# interfaces # interfaces
@@ -64,13 +87,26 @@ class ePIC(BMMiner):
# static data # static data
self.api_type = "ePIC" self.api_type = "ePIC"
self.fw_str = "ePIC"
# data gathering locations # data gathering locations
self.data_locations = EPIC_DATA_LOC self.data_locations = EPIC_DATA_LOC
async def get_model(self) -> Optional[str]: async def get_config(self) -> MinerConfig:
if self.model is not None: summary = None
return self.model + " (ePIC)" try:
return "? (ePIC)" 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: async def restart_backend(self) -> bool:
data = await self.web.restart_epic() data = await self.web.restart_epic()
@@ -108,18 +144,18 @@ class ePIC(BMMiner):
pass pass
return False return False
async def get_mac(self, web_summary: dict = None) -> str: async def _get_mac(self, web_network: dict = None) -> str:
if not web_summary: if not web_network:
web_summary = await self.web.network() web_network = await self.web.network()
if web_summary: if web_network:
try: try:
for network in web_summary: for network in web_network:
mac = web_summary[network]["mac_address"] mac = web_network[network]["mac_address"]
return mac return mac
except KeyError: except KeyError:
pass pass
async def get_hostname(self, web_summary: dict = None) -> str: async def _get_hostname(self, web_summary: dict = None) -> str:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
if web_summary: if web_summary:
@@ -129,7 +165,7 @@ class ePIC(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_wattage(self, web_summary: dict = None) -> Optional[int]: async def _get_wattage(self, web_summary: dict = None) -> Optional[int]:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -141,7 +177,7 @@ class ePIC(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_hashrate(self, web_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, web_summary: dict = None) -> Optional[float]:
# get hr from API # get hr from API
if not web_summary: if not web_summary:
try: try:
@@ -152,15 +188,14 @@ class ePIC(BMMiner):
if web_summary: if web_summary:
try: try:
hashrate = 0 hashrate = 0
if web_summary["HBs"] != None: if web_summary["HBs"] is not None:
for hb in web_summary["HBs"]: for hb in web_summary["HBs"]:
hashrate += hb["Hashrate"][0] hashrate += hb["Hashrate"][0]
return round(float(float(hashrate / 1000000)), 2) return round(float(float(hashrate / 1000000)), 2)
except (LookupError, ValueError, TypeError) as e: except (LookupError, ValueError, TypeError):
logger.error(e)
pass 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 # get hr from API
if not web_summary: if not web_summary:
try: try:
@@ -171,7 +206,7 @@ class ePIC(BMMiner):
if web_summary: if web_summary:
try: try:
hashrate = 0 hashrate = 0
if web_summary["HBs"] != None: if web_summary.get("HBs") is not None:
for hb in web_summary["HBs"]: for hb in web_summary["HBs"]:
if hb["Hashrate"][1] == 0: if hb["Hashrate"][1] == 0:
ideal = 1.0 ideal = 1.0
@@ -180,11 +215,10 @@ class ePIC(BMMiner):
hashrate += hb["Hashrate"][0] / ideal hashrate += hb["Hashrate"][0] / ideal
return round(float(float(hashrate / 1000000)), 2) return round(float(float(hashrate / 1000000)), 2)
except (IndexError, KeyError, ValueError, TypeError) as e: except (LookupError, ValueError, TypeError):
logger.error(e)
pass pass
async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]: async def _get_fw_ver(self, web_summary: dict = None) -> Optional[str]:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -196,7 +230,7 @@ class ePIC(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_fans(self, web_summary: dict = None) -> List[Fan]: async def _get_fans(self, web_summary: dict = None) -> List[Fan]:
if not web_summary: if not web_summary:
try: try:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -213,7 +247,7 @@ class ePIC(BMMiner):
fans.append(Fan()) fans.append(Fan())
return fans return fans
async def get_hashboards( async def _get_hashboards(
self, web_summary: dict = None, web_hashrate: dict = None self, web_summary: dict = None, web_hashrate: dict = None
) -> List[HashBoard]: ) -> List[HashBoard]:
if not web_summary: if not web_summary:
@@ -227,10 +261,11 @@ class ePIC(BMMiner):
except APIError: except APIError:
pass pass
hb_list = [ hb_list = [
HashBoard(slot=i, expected_chips=self.nominal_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.ideal_hashboards) for i in range(self.expected_hashboards)
] ]
if web_summary["HBs"] != None:
if web_summary.get("HBs") is not None:
for hb in web_summary["HBs"]: for hb in web_summary["HBs"]:
for hr in web_hashrate: for hr in web_hashrate:
if hr["Index"] == hb["Index"]: if hr["Index"] == hb["Index"]:
@@ -244,36 +279,10 @@ class ePIC(BMMiner):
hb_list[hr["Index"]].temp = hb["Temperature"] hb_list[hr["Index"]].temp = hb["Temperature"]
return hb_list return hb_list
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None return None
async def get_pools(self, web_summary: dict = None) -> List[dict]: async def _get_uptime(self, web_summary: dict = None) -> Optional[int]:
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: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
if web_summary: if web_summary:
@@ -284,7 +293,7 @@ class ePIC(BMMiner):
pass pass
return None return None
async def get_fault_light(self, web_summary: dict = None) -> bool: async def _get_fault_light(self, web_summary: dict = None) -> bool:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
if web_summary: if web_summary:
@@ -295,16 +304,40 @@ class ePIC(BMMiner):
pass pass
return False return False
async def get_errors(self, web_summary: dict = None) -> List[MinerErrorData]: async def _get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
errors = [] errors = []
if web_summary: if web_summary:
try: try:
error = web_summary["Status"]["Last Error"] error = web_summary["Status"]["Last Error"]
if error != None: if error is not None:
errors.append(X19Error(str(error))) errors.append(X19Error(str(error)))
return errors return errors
except KeyError: except KeyError:
pass pass
return errors return errors
async def fault_light_off(self) -> bool:
return False
async def fault_light_on(self) -> bool:
return False
async def _get_api_ver(self, *args, **kwargs) -> Optional[str]:
pass
async def _get_env_temp(self, *args, **kwargs) -> Optional[float]:
pass
async def _get_fan_psu(self, *args, **kwargs) -> Optional[int]:
pass
async def _get_wattage_limit(self, *args, **kwargs) -> Optional[int]:
pass
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
pass
async def set_power_limit(self, wattage: int) -> bool:
return False

View File

@@ -14,18 +14,67 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from typing import Optional from typing import List, Optional
from pyasic.data import HashBoard
from pyasic.miners.backends import BMMiner from pyasic.miners.backends import BMMiner
from pyasic.miners.base import DataFunction, DataLocations, DataOptions, RPCAPICommand
HIVEON_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction("get_mac"),
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", [RPCAPICommand("api_stats", "stats")]
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage", [RPCAPICommand("api_stats", "stats")]
),
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 Hiveon(BMMiner): class Hiveon(BMMiner):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None: def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.pwd = "admin"
# static data # static data
self.api_type = "Hiveon" self.api_type = "Hiveon"
# data gathering locations
self.data_locations = HIVEON_DATA_LOC
async def get_model(self) -> Optional[str]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
if self.model is not None: pass
return self.model + " (Hiveon)"
return "? (Hiveon)" async def _get_wattage(self, api_stats: dict = None) -> Optional[int]:
pass
async def _get_env_temp(self, api_stats: dict = None) -> Optional[float]:
pass

View File

@@ -0,0 +1,403 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.error_codes.innosilicon import InnosiliconError
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.innosilicon import InnosiliconWebAPI
INNOSILICON_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"_get_mac",
[
WebAPICommand("web_get_all", "getAll"),
WebAPICommand("web_overview", "overview"),
],
),
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"),
WebAPICommand("web_get_all", "getAll"),
],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[
RPCAPICommand("api_stats", "stats"),
WebAPICommand("web_get_all", "getAll"),
],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[
WebAPICommand("web_get_all", "getAll"),
RPCAPICommand("api_stats", "stats"),
],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[
WebAPICommand("web_get_all", "getAll"),
],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[
WebAPICommand("web_get_all", "getAll"),
],
),
str(DataOptions.FAN_PSU): DataFunction("_get_fan_psu"),
str(DataOptions.ERRORS): DataFunction(
"_get_errors",
[
WebAPICommand("web_get_error_detail", "getErrorDetail"),
],
),
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 Innosilicon(CGMiner):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver=api_ver)
# interfaces
self.web = InnosiliconWebAPI(ip)
# static data
# data gathering locations
self.data_locations = INNOSILICON_DATA_LOC
# autotuning/shutdown support
self.supports_shutdown = True
# data storage
self.api_ver = api_ver
async def fault_light_on(self) -> bool:
return False
async def fault_light_off(self) -> bool:
return False
async def get_config(self) -> MinerConfig:
# get pool data
try:
pools = await self.web.pools()
except APIError:
return self.config
self.config = MinerConfig.from_inno(pools)
return self.config
async def reboot(self) -> bool:
try:
data = await self.web.reboot()
except APIError:
pass
else:
return data["success"]
async def restart_cgminer(self) -> bool:
try:
data = await self.web.restart_cgminer()
except APIError:
pass
else:
return data["success"]
async def restart_backend(self) -> bool:
return await self.restart_cgminer()
async def stop_mining(self) -> bool:
return False
# data = await self.web.poweroff()
# try:
# return data["success"]
# except KeyError:
# return False
async def resume_mining(self) -> bool:
return False
# data = await self.web.restart_cgminer()
# print(data)
# try:
# return data["success"]
# except KeyError:
# return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config
await self.web.update_pools(config.as_inno(user_suffix=user_suffix))
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
async def _get_mac(
self, web_get_all: dict = None, web_overview: dict = None
) -> Optional[str]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all and not web_overview:
try:
web_overview = await self.web.overview()
except APIError:
pass
if web_get_all:
try:
mac = web_get_all["mac"]
return mac.upper()
except KeyError:
pass
if web_overview:
try:
mac = web_overview["version"]["ethaddr"]
return mac.upper()
except KeyError:
pass
async def _get_hashrate(
self, api_summary: dict = None, web_get_all: dict = None
) -> Optional[float]:
if web_get_all:
web_get_all = web_get_all["all"]
if not api_summary and not web_get_all:
try:
api_summary = await self.api.summary()
except APIError:
pass
if web_get_all:
try:
if "Hash Rate H" in web_get_all["total_hash"].keys():
return round(
float(web_get_all["total_hash"]["Hash Rate H"] / 1000000000000),
2,
)
elif "Hash Rate" in web_get_all["total_hash"].keys():
return round(
float(web_get_all["total_hash"]["Hash Rate"] / 1000000), 5
)
except KeyError:
pass
if api_summary:
try:
return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2)
except (KeyError, IndexError):
pass
async def _get_hashboards(
self, api_stats: dict = None, web_get_all: dict = None
) -> List[HashBoard]:
if web_get_all:
web_get_all = web_get_all["all"]
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
idx = board["Chain ID"]
chips = board["Num active chips"]
except KeyError:
pass
else:
hashboards[idx].chips = chips
hashboards[idx].missing = False
if web_get_all:
if web_get_all.get("chain"):
for board in web_get_all["chain"]:
idx = board.get("ASC")
if idx is not None:
temp = board.get("Temp min")
if temp:
hashboards[idx].temp = round(temp)
hashrate = board.get("Hash Rate H")
if hashrate:
hashboards[idx].hashrate = round(
hashrate / 1000000000000, 2
)
chip_temp = board.get("Temp max")
if chip_temp:
hashboards[idx].chip_temp = round(chip_temp)
return hashboards
async def _get_wattage(
self, web_get_all: dict = None, api_stats: dict = None
) -> Optional[int]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if web_get_all:
try:
return web_get_all["power"]
except KeyError:
pass
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
wattage = board["power"]
except KeyError:
pass
else:
wattage = int(wattage)
return wattage
async def _get_fans(self, web_get_all: dict = None) -> List[Fan]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
fans = [Fan() for _ in range(self.expected_fans)]
if web_get_all:
try:
spd = web_get_all["fansSpeed"]
except KeyError:
pass
else:
round((int(spd) * 6000) / 100)
for i in range(self.expected_fans):
fans[i].speed = spd
return fans
async def _get_errors(
self, web_get_error_detail: dict = None
) -> List[MinerErrorData]:
errors = []
if not web_get_error_detail:
try:
web_get_error_detail = await self.web.get_error_detail()
except APIError:
pass
if web_get_error_detail:
try:
# only 1 error?
# TODO: check if this should be a loop, can't remember.
err = web_get_error_detail["code"]
except KeyError:
pass
else:
err = int(err)
if not err == 0:
errors.append(InnosiliconError(error_code=err))
return errors
async def _get_wattage_limit(self, web_get_all: dict = None) -> Optional[int]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if web_get_all:
try:
level = web_get_all["running_mode"]["level"]
except KeyError:
pass
else:
# this is very possibly not correct.
level = int(level)
limit = 1250 + (250 * level)
return limit
async def _get_expected_hashrate(self) -> Optional[float]:
pass

View File

@@ -13,87 +13,56 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import asyncio
import logging
from collections import namedtuple
from typing import List, Optional, Tuple, Union from typing import List, Optional, Tuple, Union
import toml
from pyasic.API.bosminer import BOSMinerAPI
from pyasic.API.luxminer import LUXMinerAPI from pyasic.API.luxminer import LUXMinerAPI
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.miners.base import BaseMiner from pyasic.miners.base import (
from pyasic.web.bosminer import BOSMinerWebAPI BaseMiner,
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
)
LUXMINER_DATA_LOC = { LUXMINER_DATA_LOC = DataLocations(
"mac": { **{
"cmd": "get_mac", str(DataOptions.MAC): DataFunction(
"kwargs": {"api_config": {"api": "config"}}, "_get_mac", [RPCAPICommand("api_config", "config")]
}, ),
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.API_VERSION): DataFunction("_get_api_ver"),
"api_ver": { str(DataOptions.FW_VERSION): DataFunction("_get_fw_ver"),
"cmd": "get_api_ver", str(DataOptions.HOSTNAME): DataFunction("_get_hostname"),
"kwargs": {}, str(DataOptions.HASHRATE): DataFunction(
}, "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"fw_ver": { ),
"cmd": "get_fw_ver", str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"kwargs": {}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
}, ),
"hostname": { str(DataOptions.HASHBOARDS): DataFunction(
"cmd": "get_hostname", "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"kwargs": {}, ),
}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"hashrate": { str(DataOptions.WATTAGE): DataFunction(
"cmd": "get_hashrate", "_get_wattage", [RPCAPICommand("api_power", "power")]
"kwargs": {}, ),
}, str(DataOptions.WATTAGE_LIMIT): DataFunction("_get_wattage_limit"),
"nominal_hashrate": { str(DataOptions.FANS): DataFunction(
"cmd": "get_nominal_hashrate", "_get_fans", [RPCAPICommand("api_fans", "fans")]
"kwargs": {}, ),
}, str(DataOptions.FAN_PSU): DataFunction("_get_fan_psu"),
"hashboards": { str(DataOptions.ERRORS): DataFunction("_get_errors"),
"cmd": "get_hashboards", str(DataOptions.FAULT_LIGHT): DataFunction("_get_fault_light"),
"kwargs": {}, str(DataOptions.IS_MINING): DataFunction("_is_mining"),
}, str(DataOptions.UPTIME): DataFunction(
"wattage": { "_get_uptime", [RPCAPICommand("api_stats", "stats")]
"cmd": "get_wattage", ),
"kwargs": {}, str(DataOptions.CONFIG): DataFunction("get_config"),
}, }
"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"}},
},
}
class LUXMiner(BaseMiner): class LUXMiner(BaseMiner):
@@ -105,6 +74,7 @@ class LUXMiner(BaseMiner):
# static data # static data
self.api_type = "LUXMiner" self.api_type = "LUXMiner"
self.fw_str = "LuxOS"
# data gathering locations # data gathering locations
self.data_locations = LUXMINER_DATA_LOC self.data_locations = LUXMINER_DATA_LOC
# autotuning/shutdown support # autotuning/shutdown support
@@ -129,7 +99,6 @@ class LUXMiner(BaseMiner):
return return
async def fault_light_on(self) -> bool: async def fault_light_on(self) -> bool:
"""Sends command to turn on fault light on the miner."""
try: try:
session_id = await self._get_session() session_id = await self._get_session()
if session_id: if session_id:
@@ -140,7 +109,6 @@ class LUXMiner(BaseMiner):
return False return False
async def fault_light_off(self) -> bool: async def fault_light_off(self) -> bool:
"""Sends command to turn off fault light on the miner."""
try: try:
session_id = await self._get_session() session_id = await self._get_session()
if session_id: if session_id:
@@ -151,11 +119,9 @@ class LUXMiner(BaseMiner):
return False return False
async def restart_backend(self) -> bool: 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() return await self.restart_luxminer()
async def restart_luxminer(self) -> bool: async def restart_luxminer(self) -> bool:
"""Restart luxminer hashing process."""
try: try:
session_id = await self._get_session() session_id = await self._get_session()
if session_id: if session_id:
@@ -185,7 +151,6 @@ class LUXMiner(BaseMiner):
pass pass
async def reboot(self) -> bool: async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
try: try:
session_id = await self._get_session() session_id = await self._get_session()
if session_id: if session_id:
@@ -208,7 +173,7 @@ class LUXMiner(BaseMiner):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac(self, api_config: dict = None) -> Optional[str]: async def _get_mac(self, api_config: dict = None) -> Optional[str]:
mac = None mac = None
if not api_config: if not api_config:
try: try:
@@ -224,24 +189,19 @@ class LUXMiner(BaseMiner):
return mac return mac
async def get_model(self) -> Optional[str]:
if self.model is not None:
return self.model + " (LuxOS)"
return "? (LuxOS)"
async def get_version(self) -> Tuple[Optional[str], Optional[str]]: async def get_version(self) -> Tuple[Optional[str], Optional[str]]:
pass pass
async def get_api_ver(self) -> Optional[str]: async def _get_api_ver(self) -> Optional[str]:
pass pass
async def get_fw_ver(self) -> Optional[str]: async def _get_fw_ver(self) -> Optional[str]:
pass pass
async def get_hostname(self) -> Union[str, None]: async def _get_hostname(self) -> Union[str, None]:
pass pass
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
if not api_summary: if not api_summary:
try: try:
api_summary = await self.api.summary() api_summary = await self.api.summary()
@@ -251,10 +211,10 @@ class LUXMiner(BaseMiner):
if api_summary: if api_summary:
try: try:
return round(float(api_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2) return round(float(api_summary["SUMMARY"][0]["GHS 5s"] / 1000), 2)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
async def get_hashboards(self, api_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, api_stats: dict = None) -> List[HashBoard]:
hashboards = [] hashboards = []
if not api_stats: if not api_stats:
@@ -277,9 +237,11 @@ class LUXMiner(BaseMiner):
if board_offset == -1: if board_offset == -1:
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( 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}") chip_temp = boards[1].get(f"temp{i}")
@@ -301,15 +263,15 @@ class LUXMiner(BaseMiner):
if (not chips) or (not chips > 0): if (not chips) or (not chips > 0):
hashboard.missing = True hashboard.missing = True
hashboards.append(hashboard) hashboards.append(hashboard)
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
return hashboards return hashboards
async def get_env_temp(self) -> Optional[float]: async def _get_env_temp(self) -> Optional[float]:
return None return None
async def get_wattage(self, api_power: dict) -> Optional[int]: async def _get_wattage(self, api_power: dict) -> Optional[int]:
if not api_power: if not api_power:
try: try:
api_power = await self.api.power() api_power = await self.api.power()
@@ -319,13 +281,13 @@ class LUXMiner(BaseMiner):
if api_power: if api_power:
try: try:
return api_power["POWER"][0]["Watts"] return api_power["POWER"][0]["Watts"]
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
pass pass
async def get_wattage_limit(self) -> Optional[int]: async def _get_wattage_limit(self) -> Optional[int]:
return None return None
async def get_fans(self, api_fans: dict = None) -> List[Fan]: async def _get_fans(self, api_fans: dict = None) -> List[Fan]:
if not api_fans: if not api_fans:
try: try:
api_fans = await self.api.fans() api_fans = await self.api.fans()
@@ -335,73 +297,23 @@ class LUXMiner(BaseMiner):
fans = [] fans = []
if api_fans: if api_fans:
for fan in range(self.fan_count): for fan in range(self.expected_fans):
try: try:
fans.append(Fan(api_fans["FANS"][0]["RPM"])) fans.append(Fan(api_fans["FANS"][fan]["RPM"]))
except (IndexError, KeyError, ValueError, TypeError): except (LookupError, ValueError, TypeError):
fans.append(Fan()) fans.append(Fan())
return fans return fans
async def get_fan_psu(self) -> Optional[int]: async def _get_fan_psu(self) -> Optional[int]:
return None return None
async def get_pools(self, api_pools: dict = None) -> List[dict]: async def _get_errors(self) -> List[MinerErrorData]:
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 pass
async def get_fault_light(self) -> bool: async def _get_fault_light(self) -> bool:
pass 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: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()
@@ -410,24 +322,24 @@ class LUXMiner(BaseMiner):
if api_stats: if api_stats:
try: try:
ideal_rate = api_stats["STATS"][1]["total_rateideal"] expected_rate = api_stats["STATS"][1]["total_rateideal"]
try: try:
rate_unit = api_stats["STATS"][1]["rate_unit"] rate_unit = api_stats["STATS"][1]["rate_unit"]
except KeyError: except KeyError:
rate_unit = "GH" rate_unit = "GH"
if rate_unit == "GH": if rate_unit == "GH":
return round(ideal_rate / 1000, 2) return round(expected_rate / 1000, 2)
if rate_unit == "MH": if rate_unit == "MH":
return round(ideal_rate / 1000000, 2) return round(expected_rate / 1000000, 2)
else: else:
return round(ideal_rate, 2) return round(expected_rate, 2)
except (KeyError, IndexError): except LookupError:
pass pass
async def is_mining(self) -> Optional[bool]: async def _is_mining(self) -> Optional[bool]:
pass pass
async def get_uptime(self, api_stats: dict = None) -> Optional[int]: async def _get_uptime(self, api_stats: dict = None) -> Optional[int]:
if not api_stats: if not api_stats:
try: try:
api_stats = await self.api.stats() api_stats = await self.api.stats()

View File

@@ -16,37 +16,60 @@
from typing import Optional from typing import Optional
from pyasic import MinerConfig
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger from pyasic.logger import logger
from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.base import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.vnish import VNishWebAPI from pyasic.web.vnish import VNishWebAPI
VNISH_DATA_LOC = { VNISH_DATA_LOC = DataLocations(
"mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "summary"}}}, **{
"model": {"cmd": "get_model", "kwargs": {}}, str(DataOptions.MAC): DataFunction(
"api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}}, "_get_mac", [WebAPICommand("web_summary", "summary")]
"fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}}, ),
"hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}}, str(DataOptions.API_VERSION): DataFunction(
"hashrate": {"cmd": "get_hashrate", "kwargs": {"api_summary": {"api": "summary"}}}, "_get_api_ver", [RPCAPICommand("api_version", "version")]
"nominal_hashrate": { ),
"cmd": "get_nominal_hashrate", str(DataOptions.FW_VERSION): DataFunction(
"kwargs": {"api_stats": {"api": "stats"}}, "_get_fw_ver", [WebAPICommand("web_summary", "summary")]
}, ),
"hashboards": {"cmd": "get_hashboards", "kwargs": {"api_stats": {"api": "stats"}}}, str(DataOptions.HOSTNAME): DataFunction(
"env_temp": {"cmd": "get_env_temp", "kwargs": {}}, "_get_hostname", [WebAPICommand("web_summary", "summary")]
"wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}}, ),
"wattage_limit": { str(DataOptions.HASHRATE): DataFunction(
"cmd": "get_wattage_limit", "_get_hashrate", [RPCAPICommand("api_summary", "summary")]
"kwargs": {"web_settings": {"web": "settings"}}, ),
}, str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"fans": {"cmd": "get_fans", "kwargs": {"api_stats": {"api": "stats"}}}, "_get_expected_hashrate", [RPCAPICommand("api_stats", "stats")]
"fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, ),
"errors": {"cmd": "get_errors", "kwargs": {}}, str(DataOptions.HASHBOARDS): DataFunction(
"fault_light": {"cmd": "get_fault_light", "kwargs": {}}, "_get_hashboards", [RPCAPICommand("api_stats", "stats")]
"pools": {"cmd": "get_pools", "kwargs": {"api_pools": {"api": "pools"}}}, ),
"is_mining": {"cmd": "is_mining", "kwargs": {}}, str(DataOptions.ENVIRONMENT_TEMP): DataFunction("_get_env_temp"),
"uptime": {"cmd": "get_uptime", "kwargs": {}}, 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", [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 VNish(BMMiner): class VNish(BMMiner):
@@ -57,14 +80,10 @@ class VNish(BMMiner):
# static data # static data
self.api_type = "VNish" self.api_type = "VNish"
self.fw_str = "VNish"
# data gathering locations # data gathering locations
self.data_locations = VNISH_DATA_LOC self.data_locations = VNISH_DATA_LOC
async def get_model(self) -> Optional[str]:
if self.model is not None:
return self.model + " (VNish)"
return "? (VNish)"
async def restart_backend(self) -> bool: async def restart_backend(self) -> bool:
data = await self.web.restart_vnish() data = await self.web.restart_vnish()
if data: if data:
@@ -101,7 +120,7 @@ class VNish(BMMiner):
pass pass
return False return False
async def get_mac(self, web_summary: dict = None) -> str: async def _get_mac(self, web_summary: dict = None) -> str:
if not web_summary: if not web_summary:
web_info = await self.web.info() web_info = await self.web.info()
@@ -119,7 +138,7 @@ class VNish(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_hostname(self, web_summary: dict = None) -> str: async def _get_hostname(self, web_summary: dict = None) -> str:
if not web_summary: if not web_summary:
web_info = await self.web.info() web_info = await self.web.info()
@@ -137,7 +156,7 @@ class VNish(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_wattage(self, web_summary: dict = None) -> Optional[int]: async def _get_wattage(self, web_summary: dict = None) -> Optional[int]:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -149,7 +168,7 @@ class VNish(BMMiner):
except KeyError: except KeyError:
pass pass
async def get_hashrate(self, api_summary: dict = None) -> Optional[float]: async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]:
# get hr from API # get hr from API
if not api_summary: if not api_summary:
try: try:
@@ -162,11 +181,11 @@ class VNish(BMMiner):
return round( return round(
float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2 float(float(api_summary["SUMMARY"][0]["GHS 5s"]) / 1000), 2
) )
except (IndexError, KeyError, ValueError, TypeError) as e: except (LookupError, ValueError, TypeError) as e:
logger.error(e) logger.error(e)
pass pass
async def get_wattage_limit(self, web_settings: dict = None) -> Optional[int]: async def _get_wattage_limit(self, web_settings: dict = None) -> Optional[int]:
if not web_settings: if not web_settings:
web_settings = await self.web.summary() web_settings = await self.web.summary()
@@ -179,7 +198,7 @@ class VNish(BMMiner):
except (KeyError, TypeError): except (KeyError, TypeError):
pass pass
async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]: async def _get_fw_ver(self, web_summary: dict = None) -> Optional[str]:
if not web_summary: if not web_summary:
web_summary = await self.web.summary() web_summary = await self.web.summary()
@@ -191,8 +210,16 @@ class VNish(BMMiner):
except KeyError: except KeyError:
pass pass
async def is_mining(self, *args, **kwargs) -> Optional[bool]: async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
return None return None
async def get_uptime(self, *args, **kwargs) -> Optional[int]: async def _get_uptime(self, *args, **kwargs) -> Optional[int]:
return None return None
async def get_config(self) -> MinerConfig:
try:
web_settings = await self.web.settings()
except APIError:
return self.config
self.config = MinerConfig.from_vnish(web_settings)
return self.config

View File

@@ -17,16 +17,82 @@ import asyncio
import ipaddress import ipaddress
import logging import logging
from abc import ABC, abstractmethod 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 import asyncssh
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard, MinerData from pyasic.data import Fan, HashBoard, MinerData
from pyasic.data.error_codes import MinerErrorData from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError
from pyasic.logger import logger from pyasic.logger import logger
class DataOptions(Enum):
MAC = "mac"
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): class BaseMiner(ABC):
def __init__(self, ip: str, *args, **kwargs) -> None: def __init__(self, ip: str, *args, **kwargs) -> None:
# interfaces # interfaces
@@ -40,13 +106,14 @@ class BaseMiner(ABC):
self.api_type = None self.api_type = None
# type # type
self.make = None self.make = None
self.model = None self.raw_model = None
self.fw_str = None
# physical attributes # physical attributes
self.ideal_hashboards = 3 self.expected_hashboards = 3
self.nominal_chips = 0 self.expected_chips = 0
self.fan_count = 2 self.expected_fans = 2
# data gathering locations # data gathering locations
self.data_locations = None self.data_locations: DataLocations = None
# autotuning/shutdown support # autotuning/shutdown support
self.supports_autotuning = False self.supports_autotuning = False
self.supports_shutdown = False self.supports_shutdown = False
@@ -63,7 +130,7 @@ class BaseMiner(ABC):
return object.__new__(cls) return object.__new__(cls)
def __repr__(self): def __repr__(self):
return f"{'' if not self.api_type else self.api_type}{'' if not self.model else ' ' + self.model}: {str(self.ip)}" return f"{self.model}: {str(self.ip)}"
def __lt__(self, other): def __lt__(self, other):
return ipaddress.ip_address(self.ip) < ipaddress.ip_address(other.ip) return ipaddress.ip_address(self.ip) < ipaddress.ip_address(other.ip)
@@ -74,6 +141,13 @@ class BaseMiner(ABC):
def __eq__(self, other): def __eq__(self, other):
return ipaddress.ip_address(self.ip) == ipaddress.ip_address(other.ip) return ipaddress.ip_address(self.ip) == ipaddress.ip_address(other.ip)
@property
def model(self):
model_data = [self.raw_model if self.raw_model is not None else "Unknown"]
if self.fw_str is not None:
model_data.append(f"({self.fw_str})")
return " ".join(model_data)
@property @property
def pwd(self): # noqa - Skip PyCharm inspection def pwd(self): # noqa - Skip PyCharm inspection
data = [] data = []
@@ -173,7 +247,7 @@ class BaseMiner(ABC):
@abstractmethod @abstractmethod
async def get_config(self) -> MinerConfig: 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]. """Get the mining configuration of the miner and return it as a [`MinerConfig`][pyasic.config.MinerConfig].
Returns: Returns:
@@ -243,14 +317,13 @@ class BaseMiner(ABC):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
@abstractmethod async def get_mac(self) -> Optional[str]:
async def get_mac(self, *args, **kwargs) -> Optional[str]:
"""Get the MAC address of the miner and return it as a string. """Get the MAC address of the miner and return it as a string.
Returns: Returns:
A string representing the MAC address of the miner. A string representing the MAC address of the miner.
""" """
pass return await self._get_mac()
async def get_model(self) -> Optional[str]: async def get_model(self) -> Optional[str]:
"""Get the model of the miner and return it as a string. """Get the model of the miner and return it as a string.
@@ -260,182 +333,228 @@ class BaseMiner(ABC):
""" """
return self.model return self.model
@abstractmethod async def get_api_ver(self) -> Optional[str]:
async def get_api_ver(self, *args, **kwargs) -> Optional[str]:
"""Get the API version of the miner and is as a string. """Get the API version of the miner and is as a string.
Returns: Returns:
API version as a string. API version as a string.
""" """
pass return await self._get_api_ver()
@abstractmethod async def get_fw_ver(self) -> Optional[str]:
async def get_fw_ver(self, *args, **kwargs) -> Optional[str]:
"""Get the firmware version of the miner and is as a string. """Get the firmware version of the miner and is as a string.
Returns: Returns:
Firmware version as a string. Firmware version as a string.
""" """
pass return await self._get_fw_ver()
@abstractmethod async def get_version(self) -> Tuple[Optional[str], Optional[str]]:
async def get_version(self, *args, **kwargs) -> Tuple[Optional[str], Optional[str]]:
"""Get the API version and firmware version of the miner and return them as strings. """Get the API version and firmware version of the miner and return them as strings.
Returns: Returns:
A tuple of (API version, firmware version) as strings. A tuple of (API version, firmware version) as strings.
""" """
pass api_ver = await self.get_api_ver()
fw_ver = await self.get_fw_ver()
return api_ver, fw_ver
@abstractmethod async def get_hostname(self) -> Optional[str]:
async def get_hostname(self, *args, **kwargs) -> Optional[str]:
"""Get the hostname of the miner and return it as a string. """Get the hostname of the miner and return it as a string.
Returns: Returns:
A string representing the hostname of the miner. A string representing the hostname of the miner.
""" """
pass return await self._get_hostname()
@abstractmethod async def get_hashrate(self) -> Optional[float]:
async def get_hashrate(self, *args, **kwargs) -> Optional[float]:
"""Get the hashrate of the miner and return it as a float in TH/s. """Get the hashrate of the miner and return it as a float in TH/s.
Returns: Returns:
Hashrate of the miner in TH/s as a float. Hashrate of the miner in TH/s as a float.
""" """
pass return await self._get_hashrate()
@abstractmethod async def get_hashboards(self) -> List[HashBoard]:
async def get_hashboards(self, *args, **kwargs) -> List[HashBoard]:
"""Get hashboard data from the miner in the form of [`HashBoard`][pyasic.data.HashBoard]. """Get hashboard data from the miner in the form of [`HashBoard`][pyasic.data.HashBoard].
Returns: Returns:
A [`HashBoard`][pyasic.data.HashBoard] instance containing hashboard data from the miner. A [`HashBoard`][pyasic.data.HashBoard] instance containing hashboard data from the miner.
""" """
pass return await self._get_hashboards()
@abstractmethod async def get_env_temp(self) -> Optional[float]:
async def get_env_temp(self, *args, **kwargs) -> Optional[float]:
"""Get environment temp from the miner as a float. """Get environment temp from the miner as a float.
Returns: Returns:
Environment temp of the miner as a float. Environment temp of the miner as a float.
""" """
pass return await self._get_env_temp()
@abstractmethod async def get_wattage(self) -> Optional[int]:
async def get_wattage(self, *args, **kwargs) -> Optional[int]:
"""Get wattage from the miner as an int. """Get wattage from the miner as an int.
Returns: Returns:
Wattage of the miner as an int. Wattage of the miner as an int.
""" """
pass return await self._get_wattage()
@abstractmethod async def get_wattage_limit(self) -> Optional[int]:
async def get_wattage_limit(self, *args, **kwargs) -> Optional[int]:
"""Get wattage limit from the miner as an int. """Get wattage limit from the miner as an int.
Returns: Returns:
Wattage limit of the miner as an int. Wattage limit of the miner as an int.
""" """
pass return await self._get_wattage_limit()
@abstractmethod async def get_fans(self) -> List[Fan]:
async def get_fans(self, *args, **kwargs) -> List[Fan]:
"""Get fan data from the miner in the form [fan_1, fan_2, fan_3, fan_4]. """Get fan data from the miner in the form [fan_1, fan_2, fan_3, fan_4].
Returns: Returns:
A list of fan data. A list of fan data.
""" """
pass return await self._get_fans()
@abstractmethod async def get_fan_psu(self) -> Optional[int]:
async def get_fan_psu(self, *args, **kwargs) -> Optional[int]:
"""Get PSU fan speed from the miner. """Get PSU fan speed from the miner.
Returns: Returns:
PSU fan speed. PSU fan speed.
""" """
pass return await self._get_fan_psu()
@abstractmethod async def get_errors(self) -> List[MinerErrorData]:
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. """Get a list of the errors the miner is experiencing.
Returns: Returns:
A list of error classes representing different errors. A list of error classes representing different errors.
""" """
pass return await self._get_errors()
@abstractmethod async def get_fault_light(self) -> bool:
async def get_fault_light(self, *args, **kwargs) -> bool:
"""Check the status of the fault light and return on or off as a boolean. """Check the status of the fault light and return on or off as a boolean.
Returns: Returns:
A boolean value where `True` represents on and `False` represents off. A boolean value where `True` represents on and `False` represents off.
""" """
pass return await self._get_fault_light()
@abstractmethod async def get_expected_hashrate(self) -> Optional[float]:
async def get_nominal_hashrate(self, *args, **kwargs) -> Optional[float]:
"""Get the nominal hashrate from factory if available. """Get the nominal hashrate from factory if available.
Returns: Returns:
A float value of nominal hashrate in TH/s. A float value of nominal hashrate in TH/s.
""" """
pass return await self._get_expected_hashrate()
@abstractmethod async def is_mining(self) -> Optional[bool]:
async def is_mining(self, *args, **kwargs) -> Optional[bool]:
"""Check whether the miner is mining. """Check whether the miner is mining.
Returns: Returns:
A boolean value representing if the miner is mining. A boolean value representing if the miner is mining.
""" """
pass return await self._is_mining()
@abstractmethod async def get_uptime(self) -> Optional[int]:
async def get_uptime(self, *args, **kwargs) -> Optional[int]:
"""Get the uptime of the miner in seconds. """Get the uptime of the miner in seconds.
Returns: Returns:
The uptime of the miner in seconds. The uptime of the miner in seconds.
""" """
return await self._get_uptime()
@abstractmethod
async def _get_mac(self, *args, **kwargs) -> Optional[str]:
pass
@abstractmethod
async def _get_api_ver(self, *args, **kwargs) -> Optional[str]:
pass
@abstractmethod
async def _get_fw_ver(self, *args, **kwargs) -> Optional[str]:
pass
@abstractmethod
async def _get_hostname(self, *args, **kwargs) -> Optional[str]:
pass
@abstractmethod
async def _get_hashrate(self, *args, **kwargs) -> Optional[float]:
pass
@abstractmethod
async def _get_hashboards(self, *args, **kwargs) -> List[HashBoard]:
pass
@abstractmethod
async def _get_env_temp(self, *args, **kwargs) -> Optional[float]:
pass
@abstractmethod
async def _get_wattage(self, *args, **kwargs) -> Optional[int]:
pass
@abstractmethod
async def _get_wattage_limit(self, *args, **kwargs) -> Optional[int]:
pass
@abstractmethod
async def _get_fans(self, *args, **kwargs) -> List[Fan]:
pass
@abstractmethod
async def _get_fan_psu(self, *args, **kwargs) -> Optional[int]:
pass
@abstractmethod
async def _get_errors(self, *args, **kwargs) -> List[MinerErrorData]:
pass
@abstractmethod
async def _get_fault_light(self, *args, **kwargs) -> bool:
pass
@abstractmethod
async def _get_expected_hashrate(self, *args, **kwargs) -> Optional[float]:
pass
@abstractmethod
async def _is_mining(self, *args, **kwargs) -> Optional[bool]:
pass
@abstractmethod
async def _get_uptime(self, *args, **kwargs) -> Optional[int]:
pass pass
async def _get_data( 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: ) -> dict:
if include is None: if include is not None:
include = [str(i) for i in include]
else:
# everything # everything
include = list(self.data_locations.keys()) include = [str(enum_value.value) for enum_value in DataOptions]
if exclude is not None: if exclude is not None:
for item in exclude: for item in exclude:
if item in include: if str(item) in include:
include.remove(item) include.remove(str(item))
api_multicommand = set() api_multicommand = set()
web_multicommand = [] web_multicommand = []
for data_name in include: for data_name in include:
try: try:
fn_args = self.data_locations[data_name]["kwargs"] fn_args = getattr(self.data_locations, data_name).kwargs
for arg_name in fn_args: for arg in fn_args:
if fn_args[arg_name].get("api"): if isinstance(arg, RPCAPICommand):
api_multicommand.add(fn_args[arg_name]["api"]) api_multicommand.add(arg.cmd)
if fn_args[arg_name].get("web"): if isinstance(arg, WebAPICommand):
if not fn_args[arg_name]["web"] in web_multicommand: if arg.cmd not in web_multicommand:
web_multicommand.append(fn_args[arg_name]["web"]) web_multicommand.append(arg.cmd)
except KeyError as e: except KeyError as e:
logger.error(e, data_name) logger.error(e, data_name)
continue continue
@@ -465,59 +584,40 @@ class BaseMiner(ABC):
for data_name in include: for data_name in include:
try: try:
fn_args = self.data_locations[data_name]["kwargs"] fn_args = getattr(self.data_locations, data_name).kwargs
args_to_send = {k: None for k in fn_args} args_to_send = {k.name: None for k in fn_args}
for arg_name in fn_args: for arg in fn_args:
try: try:
if fn_args[arg_name].get("api"): if isinstance(arg, RPCAPICommand):
if api_command_data.get("multicommand"): if api_command_data.get("multicommand"):
args_to_send[arg_name] = api_command_data[ args_to_send[arg.name] = api_command_data[arg.cmd][0]
fn_args[arg_name]["api"]
][0]
else: else:
args_to_send[arg_name] = api_command_data args_to_send[arg.name] = api_command_data
if fn_args[arg_name].get("web"): if isinstance(arg, WebAPICommand):
if web_command_data is not None: if web_command_data is not None:
if web_command_data.get("multicommand"): if web_command_data.get("multicommand"):
args_to_send[arg_name] = web_command_data[ args_to_send[arg.name] = web_command_data[arg.cmd]
fn_args[arg_name]["web"]
]
else: else:
if not web_command_data == {"multicommand": False}: 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: except LookupError:
args_to_send[arg_name] = None args_to_send[arg.name] = None
except LookupError: except LookupError:
continue continue
try:
function = getattr(self, self.data_locations[data_name]["cmd"]) function = getattr(self, getattr(self.data_locations, data_name).cmd)
if not data_name == "pools":
miner_data[data_name] = await function(**args_to_send) miner_data[data_name] = await function(**args_to_send)
else: except Exception as e:
pools_data = await function(**args_to_send) raise APIError(
if pools_data: f"Failed to call {data_name} on {self} while getting data."
try: ) from e
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
return miner_data return miner_data
async def get_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: ) -> MinerData:
"""Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData]. """Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].
@@ -532,11 +632,12 @@ class BaseMiner(ABC):
data = MinerData( data = MinerData(
ip=str(self.ip), ip=str(self.ip),
make=self.make, make=self.make,
ideal_chips=self.nominal_chips * self.ideal_hashboards, model=self.model,
ideal_hashboards=self.ideal_hashboards, expected_chips=self.expected_chips * self.expected_hashboards,
expected_hashboards=self.expected_hashboards,
hashboards=[ hashboards=[
HashBoard(slot=i, expected_chips=self.nominal_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.ideal_hashboards) for i in range(self.expected_hashboards)
], ],
) )

View File

@@ -13,346 +13,10 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging
from typing import List, Optional
from pyasic.config import MinerConfig from pyasic.miners.backends.innosilicon import Innosilicon
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import InnosiliconError, MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.types import A10X from pyasic.miners.types import A10X
from pyasic.web.inno import InnosiliconWebAPI
class CGMinerA10X(CGMiner, A10X): class InnosiliconA10X(Innosilicon, A10X):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None: pass
super().__init__(ip, api_ver=api_ver)
self.ip = ip
self.web = InnosiliconWebAPI(ip)
async def fault_light_on(self) -> bool:
return False
async def fault_light_off(self) -> bool:
return False
async def get_config(self, web_pools: dict = None) -> MinerConfig:
if not web_pools:
try:
web_pools = await self.web.pools()
except APIError as e:
logging.warning(e)
if web_pools:
if "pools" in web_pools.keys():
cfg = MinerConfig().from_raw(web_pools)
self.config = cfg
return self.config
async def reboot(self) -> bool:
try:
data = await self.web.reboot()
except APIError:
pass
else:
return data["success"]
async def restart_cgminer(self) -> bool:
try:
data = await self.web.restart_cgminer()
except APIError:
pass
else:
return data["success"]
async def restart_backend(self) -> bool:
return await self.restart_cgminer()
async def stop_mining(self) -> bool:
return False
# data = await self.web.poweroff()
# try:
# return data["success"]
# except KeyError:
# return False
async def resume_mining(self) -> bool:
return False
# data = await self.web.restart_cgminer()
# print(data)
# try:
# return data["success"]
# except KeyError:
# return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
pass
# doesnt work for some reason
# self.config = config
# await self.web.update_pools(config.as_inno(user_suffix=user_suffix))
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
async def get_mac(
self, web_get_all: dict = None, web_overview: dict = None
) -> Optional[str]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all and not web_overview:
try:
web_overview = await self.web.overview()
except APIError:
pass
if web_get_all:
try:
mac = web_get_all["mac"]
return mac.upper()
except KeyError:
pass
if web_overview:
try:
mac = web_overview["version"]["ethaddr"]
return mac.upper()
except KeyError:
pass
async def get_hashrate(
self, api_summary: dict = None, web_get_all: dict = None
) -> Optional[float]:
if web_get_all:
web_get_all = web_get_all["all"]
if not api_summary and not web_get_all:
try:
api_summary = await self.api.summary()
except APIError:
pass
if web_get_all:
try:
return round(float(web_get_all["total_hash"]["Hash Rate"] / 1000000), 5)
except KeyError:
pass
if api_summary:
try:
return round(
float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000000000), 5
)
except (KeyError, IndexError):
pass
async def get_hashboards(
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)
]
if web_get_all:
web_get_all = web_get_all["all"]
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
idx = board["Chain ID"]
chips = board["Num active chips"]
except KeyError:
pass
else:
hashboards[idx].chips = chips
hashboards[idx].missing = False
if web_get_all:
if web_get_all.get("chain"):
for board in web_get_all["chain"]:
idx = board.get("ASC")
if idx is not None:
temp = board.get("Temp min")
if temp:
hashboards[idx].temp = round(temp)
hashrate = board.get("Hash Rate H")
if hashrate:
hashboards[idx].hashrate = round(
hashrate / 1000000000000, 5
)
chip_temp = board.get("Temp max")
if chip_temp:
hashboards[idx].chip_temp = round(chip_temp)
return hashboards
async def get_wattage(
self, web_get_all: dict = None, api_stats: dict = None
) -> Optional[int]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if web_get_all:
try:
return web_get_all["power"]
except KeyError:
pass
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
wattage = board["power"]
except KeyError:
pass
else:
wattage = int(wattage)
return wattage
async def get_fans(self, web_get_all: dict = None) -> List[Fan]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
fans = [Fan() for _ in range(self.fan_count)]
if web_get_all:
try:
spd = web_get_all["fansSpeed"]
except KeyError:
pass
else:
round((int(spd) * 6000) / 100)
for i in range(self.fan_count):
fans[i].speed = spd
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
errors = []
if not web_get_error_detail:
try:
web_get_error_detail = await self.web.get_error_detail()
except APIError:
pass
if web_get_error_detail:
try:
# only 1 error?
# TODO: check if this should be a loop, can't remember.
err = web_get_error_detail["code"]
except KeyError:
pass
else:
err = int(err)
if not err == 0:
errors.append(InnosiliconError(error_code=err))
return errors
async def get_fw_ver(self, api_version: dict = None) -> Optional[str]:
if self.fw_ver:
return self.fw_ver
if not api_version:
try:
api_version = await self.api.version()
except APIError:
pass
if api_version:
try:
self.fw_ver = api_version["VERSION"][0]["CGMiner"].split("-")[-1:][0]
except (KeyError, IndexError):
pass
return self.fw_ver
async def get_wattage_limit(self, web_get_all: dict = None) -> Optional[int]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if web_get_all:
try:
level = web_get_all["running_mode"]["level"]
except KeyError:
pass
else:
# this is very possibly not correct.
level = int(level)
limit = 1250 + (250 * level)
return limit

View File

@@ -14,4 +14,4 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .A10X import CGMinerA10X from .A10X import InnosiliconA10X

View File

@@ -13,307 +13,10 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import logging
from typing import List, Optional
from pyasic.config import MinerConfig from pyasic.miners.backends.innosilicon import Innosilicon
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import InnosiliconError, MinerErrorData
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.types import T3HPlus from pyasic.miners.types import T3HPlus
from pyasic.web.inno import InnosiliconWebAPI
class CGMinerT3HPlus(CGMiner, T3HPlus): class InnosiliconT3HPlus(Innosilicon, T3HPlus):
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None: pass
super().__init__(ip, api_ver=api_ver)
self.ip = ip
self.web = InnosiliconWebAPI(ip)
async def fault_light_on(self) -> bool:
return False
async def fault_light_off(self) -> bool:
return False
async def get_config(self, api_pools: dict = None) -> MinerConfig:
# get pool data
try:
pools = await self.api.pools()
except APIError:
return self.config
self.config = MinerConfig.from_api(pools)
return self.config
async def reboot(self) -> bool:
try:
data = await self.web.reboot()
except APIError:
pass
else:
return data["success"]
async def restart_cgminer(self) -> bool:
try:
data = await self.web.restart_cgminer()
except APIError:
pass
else:
return data["success"]
async def restart_backend(self) -> bool:
return await self.restart_cgminer()
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config
await self.web.update_pools(config.as_inno(user_suffix=user_suffix))
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
async def get_mac(
self, web_get_all: dict = None, web_overview: dict = None
) -> Optional[str]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all and not web_overview:
try:
web_overview = await self.web.overview()
except APIError:
pass
if web_get_all:
try:
mac = web_get_all["mac"]
return mac.upper()
except KeyError:
pass
if web_overview:
try:
mac = web_overview["version"]["ethaddr"]
return mac.upper()
except KeyError:
pass
async def get_hashrate(
self, api_summary: dict = None, web_get_all: dict = None
) -> Optional[float]:
if web_get_all:
web_get_all = web_get_all["all"]
if not api_summary and not web_get_all:
try:
api_summary = await self.api.summary()
except APIError:
pass
if web_get_all:
try:
return round(
float(web_get_all["total_hash"]["Hash Rate H"] / 1000000000000), 2
)
except KeyError:
pass
if api_summary:
try:
return round(float(api_summary["SUMMARY"][0]["MHS 1m"] / 1000000), 2)
except (KeyError, IndexError):
pass
async def get_hashboards(
self, api_stats: dict = None, web_get_all: dict = None
) -> List[HashBoard]:
if web_get_all:
web_get_all = web_get_all["all"]
hashboards = [
HashBoard(slot=i, expected_chips=self.nominal_chips)
for i in range(self.ideal_hashboards)
]
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
idx = board["Chain ID"]
chips = board["Num active chips"]
except KeyError:
pass
else:
hashboards[idx].chips = chips
hashboards[idx].missing = False
if web_get_all:
if web_get_all.get("chain"):
for board in web_get_all["chain"]:
idx = board.get("ASC")
if idx is not None:
temp = board.get("Temp min")
if temp:
hashboards[idx].temp = round(temp)
hashrate = board.get("Hash Rate H")
if hashrate:
hashboards[idx].hashrate = round(
hashrate / 1000000000000, 2
)
chip_temp = board.get("Temp max")
if chip_temp:
hashboards[idx].chip_temp = round(chip_temp)
return hashboards
async def get_wattage(
self, web_get_all: dict = None, api_stats: dict = None
) -> Optional[int]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if web_get_all:
try:
return web_get_all["power"]
except KeyError:
pass
if not api_stats:
try:
api_stats = await self.api.stats()
except APIError:
pass
if api_stats:
if api_stats.get("STATS"):
for board in api_stats["STATS"]:
try:
wattage = board["power"]
except KeyError:
pass
else:
wattage = int(wattage)
return wattage
async def get_fans(self, web_get_all: dict = None) -> List[Fan]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
fans = [Fan() for _ in range(self.fan_count)]
if web_get_all:
try:
spd = web_get_all["fansSpeed"]
except KeyError:
pass
else:
round((int(spd) * 6000) / 100)
for i in range(self.fan_count):
fans[i].speed = spd
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
errors = []
if not web_get_error_detail:
try:
web_get_error_detail = await self.web.get_error_detail()
except APIError:
pass
if web_get_error_detail:
try:
# only 1 error?
# TODO: check if this should be a loop, can't remember.
err = web_get_error_detail["code"]
except KeyError:
pass
else:
err = int(err)
if not err == 0:
errors.append(InnosiliconError(error_code=err))
return errors
async def get_wattage_limit(self, web_get_all: dict = None) -> Optional[int]:
if web_get_all:
web_get_all = web_get_all["all"]
if not web_get_all:
try:
web_get_all = await self.web.get_all()
except APIError:
pass
else:
web_get_all = web_get_all["all"]
if web_get_all:
try:
level = web_get_all["running_mode"]["level"]
except KeyError:
pass
else:
# this is very possibly not correct.
level = int(level)
limit = 1250 + (250 * level)
return limit

View File

@@ -14,4 +14,4 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .T3H import CGMinerT3HPlus from .T3H import InnosiliconT3HPlus

View File

@@ -92,6 +92,7 @@ MINER_CLASSES = {
"ANTMINER S19 XP": BMMinerS19XP, "ANTMINER S19 XP": BMMinerS19XP,
"ANTMINER S19A": BMMinerS19a, "ANTMINER S19A": BMMinerS19a,
"ANTMINER S19A PRO": BMMinerS19aPro, "ANTMINER S19A PRO": BMMinerS19aPro,
"ANTMINER S19 PRO HYD.": BMMinerS19ProHydro,
"ANTMINER T19": BMMinerT19, "ANTMINER T19": BMMinerT19,
}, },
MinerTypes.WHATSMINER: { MinerTypes.WHATSMINER: {
@@ -325,8 +326,8 @@ MINER_CLASSES = {
}, },
MinerTypes.INNOSILICON: { MinerTypes.INNOSILICON: {
None: CGMiner, None: CGMiner,
"T3H+": CGMinerT3HPlus, "T3H+": InnosiliconT3HPlus,
"A10X": CGMinerA10X, "A10X": InnosiliconA10X,
}, },
MinerTypes.GOLDSHELL: { MinerTypes.GOLDSHELL: {
None: BFGMiner, None: BFGMiner,
@@ -346,13 +347,18 @@ MINER_CLASSES = {
"ANTMINER T17+": BOSMinerT17Plus, "ANTMINER T17+": BOSMinerT17Plus,
"ANTMINER T17E": BOSMinerT17e, "ANTMINER T17E": BOSMinerT17e,
"ANTMINER S19": BOSMinerS19, "ANTMINER S19": BOSMinerS19,
"ANTMINER S19+": BOSMinerS19Plus,
"ANTMINER S19 PRO": BOSMinerS19Pro, "ANTMINER S19 PRO": BOSMinerS19Pro,
"ANTMINER S19A": BOSMinerS19a,
"ANTMINER S19A Pro": BOSMinerS19aPro,
"ANTMINER S19J": BOSMinerS19j, "ANTMINER S19J": BOSMinerS19j,
"ANTMINER S19J88NOPIC": BOSMinerS19jNoPIC, "ANTMINER S19J88NOPIC": BOSMinerS19jNoPIC,
"ANTMINER S19J PRO": BOSMinerS19jPro, "ANTMINER S19J PRO": BOSMinerS19jPro,
"ANTMINER S19J PRO NOPIC": BOSMinerS19jPro, "ANTMINER S19J PRO NOPIC": BOSMinerS19jPro,
"ANTMINER T19": BOSMinerT19, "ANTMINER S19J PRO+": BOSMinerS19jProPlus,
"ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC, "ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC,
"ANTMINER S19XP": BOSMinerS19XP,
"ANTMINER T19": BOSMinerT19,
}, },
MinerTypes.VNISH: { MinerTypes.VNISH: {
None: VNish, None: VNish,
@@ -470,6 +476,7 @@ class MinerFactory:
fn = miner_model_fns.get(miner_type) fn = miner_model_fns.get(miner_type)
if fn is not None: if fn is not None:
# noinspection PyArgumentList
task = asyncio.create_task(fn(ip)) task = asyncio.create_task(fn(ip))
try: try:
miner_model = await asyncio.wait_for( miner_model = await asyncio.wait_for(
@@ -478,15 +485,10 @@ class MinerFactory:
except asyncio.TimeoutError: except asyncio.TimeoutError:
pass pass
boser_enabled = None
if miner_type == MinerTypes.BRAIINS_OS:
boser_enabled = await self.get_boser_braiins_os(ip)
miner = self._select_miner_from_classes( miner = self._select_miner_from_classes(
ip, ip,
miner_type=miner_type, miner_type=miner_type,
miner_model=miner_model, miner_model=miner_model,
boser_enabled=boser_enabled,
) )
if miner is not None and not isinstance(miner, UnknownMiner): if miner is not None and not isinstance(miner, UnknownMiner):
@@ -769,13 +771,9 @@ class MinerFactory:
ip: ipaddress.ip_address, ip: ipaddress.ip_address,
miner_model: Union[str, None], miner_model: Union[str, None],
miner_type: Union[MinerTypes, None], miner_type: Union[MinerTypes, None],
boser_enabled: bool = None,
) -> AnyMiner: ) -> AnyMiner:
kwargs = {}
if boser_enabled is not None:
kwargs["boser"] = boser_enabled
try: try:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip, **kwargs) return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
except LookupError: except LookupError:
if miner_type in MINER_CLASSES: if miner_type in MINER_CLASSES:
return MINER_CLASSES[miner_type][None](ip) return MINER_CLASSES[miner_type][None](ip)
@@ -903,15 +901,6 @@ class MinerFactory:
except (httpx.HTTPError, LookupError): except (httpx.HTTPError, LookupError):
pass pass
async def get_boser_braiins_os(self, ip: str):
# TODO: refine this check
try:
sock_json_data = await self.send_api_command(ip, "version")
return sock_json_data["STATUS"][0]["Msg"].split(" ")[0].upper() == "BOSER"
except LookupError:
# let the bosminer class decide
return None
async def get_miner_model_vnish(self, ip: str) -> Optional[str]: async def get_miner_model_vnish(self, ip: str) -> Optional[str]:
sock_json_data = await self.send_api_command(ip, "stats") sock_json_data = await self.send_api_command(ip, "stats")
try: try:

View File

@@ -21,6 +21,6 @@ class Z15(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "Z15" self.raw_model = "Z15"
self.nominal_chips = 3 self.expected_chips = 3
self.fan_count = 2 self.fan_count = 2

View File

@@ -21,16 +21,16 @@ class S17(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S17" self.raw_model = "S17"
self.nominal_chips = 48 self.expected_chips = 48
self.fan_count = 4 self.fan_count = 4
class S17Plus(AntMiner): # noqa - ignore ABC method implementation class S17Plus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.model = "S17+" self.raw_model = "S17+"
self.nominal_chips = 65 self.expected_chips = 65
self.fan_count = 4 self.fan_count = 4
@@ -38,8 +38,8 @@ class S17Pro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S17 Pro" self.raw_model = "S17 Pro"
self.nominal_chips = 48 self.expected_chips = 48
self.fan_count = 4 self.fan_count = 4
@@ -47,6 +47,6 @@ class S17e(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S17e" self.raw_model = "S17e"
self.nominal_chips = 135 self.expected_chips = 135
self.fan_count = 4 self.fan_count = 4

View File

@@ -21,8 +21,8 @@ class T17(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "T17" self.raw_model = "T17"
self.nominal_chips = 30 self.expected_chips = 30
self.fan_count = 4 self.fan_count = 4
@@ -30,8 +30,8 @@ class T17Plus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "T17+" self.raw_model = "T17+"
self.nominal_chips = 44 self.expected_chips = 44
self.fan_count = 4 self.fan_count = 4
@@ -39,6 +39,6 @@ class T17e(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "T17e" self.raw_model = "T17e"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 4 self.fan_count = 4

View File

@@ -21,8 +21,8 @@ class S19(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19" self.raw_model = "S19"
self.nominal_chips = 76 self.expected_chips = 76
self.fan_count = 4 self.fan_count = 4
@@ -30,8 +30,8 @@ class S19NoPIC(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19 No PIC" self.raw_model = "S19 No PIC"
self.nominal_chips = 88 self.expected_chips = 88
self.fan_count = 4 self.fan_count = 4
@@ -39,8 +39,8 @@ class S19Pro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19 Pro" self.raw_model = "S19 Pro"
self.nominal_chips = 114 self.expected_chips = 114
self.fan_count = 4 self.fan_count = 4
@@ -48,8 +48,8 @@ class S19i(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19i" self.raw_model = "S19i"
self.nominal_chips = 80 self.expected_chips = 80
self.fan_count = 4 self.fan_count = 4
@@ -57,8 +57,8 @@ class S19Plus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19+" self.raw_model = "S19+"
self.nominal_chips = 80 self.expected_chips = 80
self.fan_count = 4 self.fan_count = 4
@@ -66,8 +66,8 @@ class S19ProPlus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19 Pro+" self.raw_model = "S19 Pro+"
self.nominal_chips = 120 self.expected_chips = 120
self.fan_count = 4 self.fan_count = 4
@@ -75,8 +75,8 @@ class S19XP(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19 XP" self.raw_model = "S19 XP"
self.nominal_chips = 110 self.expected_chips = 110
self.fan_count = 4 self.fan_count = 4
@@ -84,8 +84,8 @@ class S19a(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19a" self.raw_model = "S19a"
self.nominal_chips = 72 self.expected_chips = 72
self.fan_count = 4 self.fan_count = 4
@@ -93,8 +93,8 @@ class S19aPro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19a Pro" self.raw_model = "S19a Pro"
self.nominal_chips = 100 self.expected_chips = 100
self.fan_count = 4 self.fan_count = 4
@@ -102,8 +102,8 @@ class S19j(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19j" self.raw_model = "S19j"
self.nominal_chips = 114 self.expected_chips = 114
self.fan_count = 4 self.fan_count = 4
@@ -111,8 +111,8 @@ class S19jNoPIC(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19j No PIC" self.raw_model = "S19j No PIC"
self.nominal_chips = 88 self.expected_chips = 88
self.fan_count = 4 self.fan_count = 4
@@ -120,8 +120,8 @@ class S19jPro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19j Pro" self.raw_model = "S19j Pro"
self.nominal_chips = 126 self.expected_chips = 126
self.fan_count = 4 self.fan_count = 4
@@ -129,8 +129,8 @@ class S19jProPlus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19j Pro+" self.raw_model = "S19j Pro+"
self.nominal_chips = 120 self.expected_chips = 120
self.fan_count = 4 self.fan_count = 4
@@ -138,8 +138,8 @@ class S19kPro(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19k Pro" self.raw_model = "S19k Pro"
self.nominal_chips = 77 self.expected_chips = 77
self.fan_count = 4 self.fan_count = 4
@@ -147,8 +147,8 @@ class S19L(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19L" self.raw_model = "S19L"
self.nominal_chips = 76 self.expected_chips = 76
self.fan_count = 4 self.fan_count = 4
@@ -156,6 +156,16 @@ class S19kProNoPIC(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S19k Pro No PIC" self.raw_model = "S19k Pro No PIC"
self.nominal_chips = 77 self.expected_chips = 77
self.fan_count = 4 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.raw_model = "S19 Pro Hydro"
self.expected_chips = 180
self.expected_hashboards = 4
self.fan_count = 0

View File

@@ -21,6 +21,6 @@ class T19(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "T19" self.raw_model = "T19"
self.nominal_chips = 76 self.expected_chips = 76
self.fan_count = 4 self.fan_count = 4

View File

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

View File

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

View File

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

View File

@@ -20,6 +20,6 @@ class L3Plus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "L3+" self.raw_model = "L3+"
self.nominal_chips = 72 self.expected_chips = 72
self.fan_count = 2 self.fan_count = 2

View File

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

View File

@@ -20,6 +20,6 @@ class L7(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "L7" self.raw_model = "L7"
self.nominal_chips = 120 self.expected_chips = 120
self.fan_count = 4 self.fan_count = 4

View File

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

View File

@@ -21,8 +21,8 @@ class S9(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S9" self.raw_model = "S9"
self.nominal_chips = 63 self.expected_chips = 63
self.fan_count = 2 self.fan_count = 2
@@ -30,8 +30,8 @@ class S9i(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S9i" self.raw_model = "S9i"
self.nominal_chips = 63 self.expected_chips = 63
self.fan_count = 2 self.fan_count = 2
@@ -39,6 +39,6 @@ class S9j(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "S9j" self.raw_model = "S9j"
self.nominal_chips = 63 self.expected_chips = 63
self.fan_count = 2 self.fan_count = 2

View File

@@ -21,6 +21,6 @@ class T9(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "T9" self.raw_model = "T9"
self.nominal_chips = 54 self.expected_chips = 54
self.fan_count = 2 self.fan_count = 2

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import AvalonMiner from pyasic.miners.makes import AvalonMiner
@@ -22,6 +21,6 @@ class Avalon1166Pro(AvalonMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "Avalon 1166 Pro" self.raw_model = "Avalon 1166 Pro"
self.nominal_chips = 120 self.expected_chips = 120
self.fan_count = 4 self.fan_count = 4

View File

@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import AvalonMiner from pyasic.miners.makes import AvalonMiner
@@ -22,6 +21,6 @@ class Avalon1246(AvalonMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "Avalon 1246" self.raw_model = "Avalon 1246"
self.nominal_chips = 120 self.expected_chips = 120
self.fan_count = 4 self.fan_count = 4

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,4 +20,4 @@ class A10X(InnosiliconMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None: def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "A10X" self.raw_model = "A10X"

View File

@@ -21,6 +21,6 @@ class T3HPlus(InnosiliconMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0") -> None: def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "T3H+" self.raw_model = "T3H+"
self.nominal_chips = 114 self.expected_chips = 114
self.fan_count = 4 self.fan_count = 4

View File

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

View File

@@ -21,8 +21,8 @@ class M20PV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M20P V10" self.raw_model = "M20P V10"
self.nominal_chips = 156 self.expected_chips = 156
self.fan_count = 2 self.fan_count = 2
@@ -30,6 +30,6 @@ class M20PV30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M20P V30" self.raw_model = "M20P V30"
self.nominal_chips = 148 self.expected_chips = 148
self.fan_count = 2 self.fan_count = 2

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,8 +21,8 @@ class M20SV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M20S V10" self.raw_model = "M20S V10"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -32,8 +30,8 @@ class M20SV20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M20S V20" self.raw_model = "M20S V20"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -41,6 +39,6 @@ class M20SV30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M20S V30" self.raw_model = "M20S V30"
self.nominal_chips = 140 self.expected_chips = 140
self.fan_count = 2 self.fan_count = 2

View File

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

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,6 +21,6 @@ class M21V10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M21 V10" self.raw_model = "M21 V10"
self.nominal_chips = 33 self.expected_chips = 33
self.fan_count = 2 self.fan_count = 2

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,8 +21,8 @@ class M21SV20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M21S V20" self.raw_model = "M21S V20"
self.nominal_chips = 66 self.expected_chips = 66
self.fan_count = 2 self.fan_count = 2
@@ -32,8 +30,8 @@ class M21SV60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M21S V60" self.raw_model = "M21S V60"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -41,6 +39,6 @@ class M21SV70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M21S V70" self.raw_model = "M21S V70"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2

View File

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

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,6 +21,6 @@ class M29V10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M29 V10" self.raw_model = "M29 V10"
self.nominal_chips = 50 self.expected_chips = 50
self.fan_count = 2 self.fan_count = 2

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,8 +21,8 @@ class M30V10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30 V10" self.raw_model = "M30 V10"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -32,6 +30,6 @@ class M30V20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30 V20" self.raw_model = "M30 V20"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,7 +21,7 @@ class M30KV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30K V10" self.raw_model = "M30K V10"
self.ideal_hashboards = 4 self.expected_hashboards = 4
self.nominal_chips = 240 self.expected_chips = 240
self.fan_count = 2 self.fan_count = 2

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,7 +21,7 @@ class M30LV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30L V10" self.raw_model = "M30L V10"
self.board_num = 4 self.board_num = 4
self.nominal_chips = 144 self.expected_chips = 144
self.fan_count = 2 self.fan_count = 2

View File

@@ -23,8 +23,8 @@ class M30SV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V10" self.raw_model = "M30S V10"
self.nominal_chips = 148 self.expected_chips = 148
self.fan_count = 2 self.fan_count = 2
@@ -32,8 +32,8 @@ class M30SV20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V20" self.raw_model = "M30S V20"
self.nominal_chips = 156 self.expected_chips = 156
self.fan_count = 2 self.fan_count = 2
@@ -41,8 +41,8 @@ class M30SV30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V30" self.raw_model = "M30S V30"
self.nominal_chips = 164 self.expected_chips = 164
self.fan_count = 2 self.fan_count = 2
@@ -50,8 +50,8 @@ class M30SV40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V40" self.raw_model = "M30S V40"
self.nominal_chips = 172 self.expected_chips = 172
self.fan_count = 2 self.fan_count = 2
@@ -59,8 +59,8 @@ class M30SV50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V50" self.raw_model = "M30S V50"
self.nominal_chips = 156 self.expected_chips = 156
self.fan_count = 2 self.fan_count = 2
@@ -68,8 +68,8 @@ class M30SV60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V60" self.raw_model = "M30S V60"
self.nominal_chips = 164 self.expected_chips = 164
self.fan_count = 2 self.fan_count = 2
@@ -77,8 +77,8 @@ class M30SV70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V70" self.raw_model = "M30S V70"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30SV70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -89,8 +89,8 @@ class M30SV80(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S V80" self.raw_model = "M30S V80"
self.nominal_chips = 129 self.expected_chips = 129
self.fan_count = 2 self.fan_count = 2
@@ -98,8 +98,8 @@ class M30SVE10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE10" self.raw_model = "M30S VE10"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -107,8 +107,8 @@ class M30SVE20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE20" self.raw_model = "M30S VE20"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -116,8 +116,8 @@ class M30SVE30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE30" self.raw_model = "M30S VE30"
self.nominal_chips = 117 self.expected_chips = 117
self.fan_count = 2 self.fan_count = 2
@@ -125,8 +125,8 @@ class M30SVE40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE40" self.raw_model = "M30S VE40"
self.nominal_chips = 123 self.expected_chips = 123
self.fan_count = 2 self.fan_count = 2
@@ -134,8 +134,8 @@ class M30SVE50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE50" self.raw_model = "M30S VE50"
self.nominal_chips = 129 self.expected_chips = 129
self.fan_count = 2 self.fan_count = 2
@@ -143,8 +143,8 @@ class M30SVE60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE60" self.raw_model = "M30S VE60"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30SVE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30SVE60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -155,8 +155,8 @@ class M30SVE70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VE70" self.raw_model = "M30S VE70"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30SVE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30SVE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -167,8 +167,8 @@ class M30SVF10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VF10" self.raw_model = "M30S VF10"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -176,8 +176,8 @@ class M30SVF20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VF20" self.raw_model = "M30S VF20"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2
@@ -185,8 +185,8 @@ class M30SVF30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VF30" self.raw_model = "M30S VF30"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 2 self.fan_count = 2
@@ -194,8 +194,8 @@ class M30SVG10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VG10" self.raw_model = "M30S VG10"
self.nominal_chips = 66 self.expected_chips = 66
self.fan_count = 2 self.fan_count = 2
@@ -203,8 +203,8 @@ class M30SVG20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VG20" self.raw_model = "M30S VG20"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -212,8 +212,8 @@ class M30SVG30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VG30" self.raw_model = "M30S VG30"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2
@@ -221,8 +221,8 @@ class M30SVG40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VG40" self.raw_model = "M30S VG40"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 2 self.fan_count = 2
@@ -230,8 +230,8 @@ class M30SVH10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VH10" self.raw_model = "M30S VH10"
self.nominal_chips = 64 self.expected_chips = 64
self.fan_count = 2 self.fan_count = 2
@@ -239,8 +239,8 @@ class M30SVH20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VH20" self.raw_model = "M30S VH20"
self.nominal_chips = 66 self.expected_chips = 66
self.fan_count = 2 self.fan_count = 2
@@ -248,8 +248,8 @@ class M30SVH30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VH30" self.raw_model = "M30S VH30"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30SVH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30SVH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -260,8 +260,8 @@ class M30SVH40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VH40" self.raw_model = "M30S VH40"
self.nominal_chips = 64 self.expected_chips = 64
self.fan_count = 2 self.fan_count = 2
@@ -269,8 +269,8 @@ class M30SVH50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VH50" self.raw_model = "M30S VH50"
self.nominal_chips = 66 self.expected_chips = 66
self.fan_count = 2 self.fan_count = 2
@@ -278,8 +278,8 @@ class M30SVH60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VH60" self.raw_model = "M30S VH60"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30SVH60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30SVH60, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -290,6 +290,6 @@ class M30SVI20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S VI20" self.raw_model = "M30S VI20"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2

View File

@@ -23,8 +23,8 @@ class M30SPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V10" self.raw_model = "M30S+ V10"
self.nominal_chips = 215 self.expected_chips = 215
self.fan_count = 2 self.fan_count = 2
@@ -32,8 +32,8 @@ class M30SPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V20" self.raw_model = "M30S+ V20"
self.nominal_chips = 255 self.expected_chips = 255
self.fan_count = 2 self.fan_count = 2
@@ -41,8 +41,8 @@ class M30SPlusV30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V30" self.raw_model = "M30S+ V30"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S+ V30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -53,8 +53,8 @@ class M30SPlusV40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V40" self.raw_model = "M30S+ V40"
self.nominal_chips = 235 self.expected_chips = 235
self.fan_count = 2 self.fan_count = 2
@@ -62,8 +62,8 @@ class M30SPlusV50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V50" self.raw_model = "M30S+ V50"
self.nominal_chips = 225 self.expected_chips = 225
self.fan_count = 2 self.fan_count = 2
@@ -71,8 +71,8 @@ class M30SPlusV60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V60" self.raw_model = "M30S+ V60"
self.nominal_chips = 245 self.expected_chips = 245
self.fan_count = 2 self.fan_count = 2
@@ -80,8 +80,8 @@ class M30SPlusV70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V70" self.raw_model = "M30S+ V70"
self.nominal_chips = 235 self.expected_chips = 235
self.fan_count = 2 self.fan_count = 2
@@ -89,8 +89,8 @@ class M30SPlusV80(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V80" self.raw_model = "M30S+ V80"
self.nominal_chips = 245 self.expected_chips = 245
self.fan_count = 2 self.fan_count = 2
@@ -98,8 +98,8 @@ class M30SPlusV90(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V90" self.raw_model = "M30S+ V90"
self.nominal_chips = 225 self.expected_chips = 225
self.fan_count = 2 self.fan_count = 2
@@ -107,8 +107,8 @@ class M30SPlusV100(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ V100" self.raw_model = "M30S+ V100"
self.nominal_chips = 215 self.expected_chips = 215
self.fan_count = 2 self.fan_count = 2
@@ -116,8 +116,8 @@ class M30SPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE30" self.raw_model = "M30S+ VE30"
self.nominal_chips = 148 self.expected_chips = 148
self.fan_count = 2 self.fan_count = 2
@@ -125,8 +125,8 @@ class M30SPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE40" self.raw_model = "M30S+ VE40"
self.nominal_chips = 156 self.expected_chips = 156
self.fan_count = 2 self.fan_count = 2
@@ -134,8 +134,8 @@ class M30SPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE50" self.raw_model = "M30S+ VE50"
self.nominal_chips = 164 self.expected_chips = 164
self.fan_count = 2 self.fan_count = 2
@@ -143,8 +143,8 @@ class M30SPlusVE60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE60" self.raw_model = "M30S+ VE60"
self.nominal_chips = 172 self.expected_chips = 172
self.fan_count = 2 self.fan_count = 2
@@ -152,8 +152,8 @@ class M30SPlusVE70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE70" self.raw_model = "M30S+ VE70"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S+ VE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S+ VE70, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -164,8 +164,8 @@ class M30SPlusVE80(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE80" self.raw_model = "M30S+ VE80"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S+ VE80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -176,8 +176,8 @@ class M30SPlusVE90(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE90" self.raw_model = "M30S+ VE90"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S+ VE90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S+ VE90, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -188,8 +188,8 @@ class M30SPlusVE100(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VE100" self.raw_model = "M30S+ VE100"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S+ VE100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S+ VE100, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -200,8 +200,8 @@ class M30SPlusVF20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VF20" self.raw_model = "M30S+ VF20"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -209,8 +209,8 @@ class M30SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VF30" self.raw_model = "M30S+ VF30"
self.nominal_chips = 117 self.expected_chips = 117
self.fan_count = 2 self.fan_count = 2
@@ -218,8 +218,8 @@ class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VG20" self.raw_model = "M30S+ VG20"
self.nominal_chips = 82 self.expected_chips = 82
self.fan_count = 2 self.fan_count = 2
@@ -227,8 +227,8 @@ class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VG30" self.raw_model = "M30S+ VG30"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 2 self.fan_count = 2
@@ -236,8 +236,8 @@ class M30SPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VG40" self.raw_model = "M30S+ VG40"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -245,8 +245,8 @@ class M30SPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VG50" self.raw_model = "M30S+ VG50"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -254,8 +254,8 @@ class M30SPlusVG60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VG60" self.raw_model = "M30S+ VG60"
self.nominal_chips = 86 self.expected_chips = 86
self.fan_count = 2 self.fan_count = 2
@@ -263,8 +263,8 @@ class M30SPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VH10" self.raw_model = "M30S+ VH10"
self.nominal_chips = 64 self.expected_chips = 64
self.fan_count = 2 self.fan_count = 2
@@ -272,8 +272,8 @@ class M30SPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VH20" self.raw_model = "M30S+ VH20"
self.nominal_chips = 66 self.expected_chips = 66
self.fan_count = 2 self.fan_count = 2
@@ -281,8 +281,8 @@ class M30SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VH30" self.raw_model = "M30S+ VH30"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -290,8 +290,8 @@ class M30SPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VH40" self.raw_model = "M30S+ VH40"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2
@@ -299,8 +299,8 @@ class M30SPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VH50" self.raw_model = "M30S+ VH50"
self.nominal_chips = 64 self.expected_chips = 64
self.fan_count = 2 self.fan_count = 2
@@ -308,6 +308,6 @@ class M30SPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S+ VH60" self.raw_model = "M30S+ VH60"
self.nominal_chips = 66 self.expected_chips = 66
self.fan_count = 2 self.fan_count = 2

View File

@@ -23,9 +23,9 @@ class M30SPlusPlusV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ V10" self.raw_model = "M30S++ V10"
self.ideal_hashboards = 4 self.expected_hashboards = 4
self.nominal_chips = 255 self.expected_chips = 255
self.fan_count = 2 self.fan_count = 2
@@ -33,9 +33,9 @@ class M30SPlusPlusV20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ V20" self.raw_model = "M30S++ V20"
self.ideal_hashboards = 4 self.expected_hashboards = 4
self.nominal_chips = 255 self.expected_chips = 255
self.fan_count = 2 self.fan_count = 2
@@ -43,8 +43,8 @@ class M30SPlusPlusVE30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VE30" self.raw_model = "M30S++ VE30"
self.nominal_chips = 215 self.expected_chips = 215
self.fan_count = 2 self.fan_count = 2
@@ -52,8 +52,8 @@ class M30SPlusPlusVE40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VE40" self.raw_model = "M30S++ VE40"
self.nominal_chips = 225 self.expected_chips = 225
self.fan_count = 2 self.fan_count = 2
@@ -61,8 +61,8 @@ class M30SPlusPlusVE50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VE50" self.raw_model = "M30S++ VE50"
self.nominal_chips = 235 self.expected_chips = 235
self.fan_count = 2 self.fan_count = 2
@@ -70,8 +70,8 @@ class M30SPlusPlusVF40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VF40" self.raw_model = "M30S++ VF40"
self.nominal_chips = 156 self.expected_chips = 156
self.fan_count = 2 self.fan_count = 2
@@ -79,8 +79,8 @@ class M30SPlusPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VG30" self.raw_model = "M30S++ VG30"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -88,8 +88,8 @@ class M30SPlusPlusVG40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VG40" self.raw_model = "M30S++ VG40"
self.nominal_chips = 117 self.expected_chips = 117
self.fan_count = 2 self.fan_count = 2
@@ -97,8 +97,8 @@ class M30SPlusPlusVG50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VG50" self.raw_model = "M30S++ VG50"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S++ VG50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S++ VG50, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -109,8 +109,8 @@ class M30SPlusPlusVH10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH10" self.raw_model = "M30S++ VH10"
self.nominal_chips = 82 self.expected_chips = 82
self.fan_count = 2 self.fan_count = 2
@@ -118,8 +118,8 @@ class M30SPlusPlusVH20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH20" self.raw_model = "M30S++ VH20"
self.nominal_chips = 86 self.expected_chips = 86
self.fan_count = 2 self.fan_count = 2
@@ -127,8 +127,8 @@ class M30SPlusPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH30" self.raw_model = "M30S++ VH30"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -136,8 +136,8 @@ class M30SPlusPlusVH40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH40" self.raw_model = "M30S++ VH40"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -145,8 +145,8 @@ class M30SPlusPlusVH50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH50" self.raw_model = "M30S++ VH50"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2
@@ -154,8 +154,8 @@ class M30SPlusPlusVH60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH60" self.raw_model = "M30S++ VH60"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 2 self.fan_count = 2
@@ -163,8 +163,8 @@ class M30SPlusPlusVH70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH70" self.raw_model = "M30S++ VH70"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -172,8 +172,8 @@ class M30SPlusPlusVH80(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH80" self.raw_model = "M30S++ VH80"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2
@@ -181,8 +181,8 @@ class M30SPlusPlusVH90(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH90" self.raw_model = "M30S++ VH90"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 2 self.fan_count = 2
@@ -190,8 +190,8 @@ class M30SPlusPlusVH100(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VH100" self.raw_model = "M30S++ VH100"
self.nominal_chips = 82 self.expected_chips = 82
self.fan_count = 2 self.fan_count = 2
@@ -199,8 +199,8 @@ class M30SPlusPlusVJ20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VJ20" self.raw_model = "M30S++ VJ20"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S++ VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S++ VJ20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -211,8 +211,8 @@ class M30SPlusPlusVJ30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M30S++ VJ30" self.raw_model = "M30S++ VJ30"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M30S++ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M30S++ VJ30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,8 +21,8 @@ class M31V10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31 V10" self.raw_model = "M31 V10"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -32,6 +30,6 @@ class M31V20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31 V20" self.raw_model = "M31 V20"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,8 +21,8 @@ class M31HV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31H V10" self.raw_model = "M31H V10"
self.nominal_chips = 114 self.expected_chips = 114
self.fan_count = 0 self.fan_count = 0
@@ -32,7 +30,7 @@ class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31H V40" self.raw_model = "M31H V40"
self.ideal_hashboards = 4 self.expected_hashboards = 4
self.nominal_chips = 136 self.expected_chips = 136
self.fan_count = 0 self.fan_count = 0

View File

@@ -14,8 +14,6 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import warnings
from pyasic.miners.makes import WhatsMiner from pyasic.miners.makes import WhatsMiner
@@ -23,6 +21,6 @@ class M31LV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31L V10" self.raw_model = "M31L V10"
self.nominal_chips = 114 self.expected_chips = 114
self.fan_count = 2 self.fan_count = 2

View File

@@ -23,8 +23,8 @@ class M31SV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V10" self.raw_model = "M31S V10"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -32,8 +32,8 @@ class M31SV20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V20" self.raw_model = "M31S V20"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -41,8 +41,8 @@ class M31SV30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V30" self.raw_model = "M31S V30"
self.nominal_chips = 117 self.expected_chips = 117
self.fan_count = 2 self.fan_count = 2
@@ -50,8 +50,8 @@ class M31SV40(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V40" self.raw_model = "M31S V40"
self.nominal_chips = 123 self.expected_chips = 123
self.fan_count = 2 self.fan_count = 2
@@ -59,8 +59,8 @@ class M31SV50(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V50" self.raw_model = "M31S V50"
self.nominal_chips = 78 self.expected_chips = 78
self.fan_count = 2 self.fan_count = 2
@@ -68,8 +68,8 @@ class M31SV60(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V60" self.raw_model = "M31S V60"
self.nominal_chips = 105 self.expected_chips = 105
self.fan_count = 2 self.fan_count = 2
@@ -77,8 +77,8 @@ class M31SV70(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V70" self.raw_model = "M31S V70"
self.nominal_chips = 111 self.expected_chips = 111
self.fan_count = 2 self.fan_count = 2
@@ -86,8 +86,8 @@ class M31SV80(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V80" self.raw_model = "M31S V80"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M31SV80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M31SV80, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
) )
@@ -98,8 +98,8 @@ class M31SV90(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S V90" self.raw_model = "M31S V90"
self.nominal_chips = 117 self.expected_chips = 117
self.fan_count = 2 self.fan_count = 2
@@ -107,8 +107,8 @@ class M31SVE10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S VE10" self.raw_model = "M31S VE10"
self.nominal_chips = 70 self.expected_chips = 70
self.fan_count = 2 self.fan_count = 2
@@ -116,8 +116,8 @@ class M31SVE20(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S VE20" self.raw_model = "M31S VE20"
self.nominal_chips = 74 self.expected_chips = 74
self.fan_count = 2 self.fan_count = 2
@@ -125,8 +125,8 @@ class M31SVE30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"): def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver) super().__init__(ip, api_ver)
self.ip = ip self.ip = ip
self.model = "M31S VE30" self.raw_model = "M31S VE30"
self.nominal_chips = 0 self.expected_chips = 0
warnings.warn( warnings.warn(
"Unknown chip count for miner type M31SVE30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." "Unknown chip count for miner type M31SVE30, 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