Compare commits

..

31 Commits

Author SHA1 Message Date
UpstreamData
a08f434e1f bump version number 2022-10-03 13:43:08 -06:00
UpstreamData
9acd6d2fea add MinerData().as_csv() 2022-10-03 13:42:47 -06:00
UpstreamData
0a1cdea2e3 bump version number 2022-09-30 16:01:28 -06:00
UpstreamData
73b1a0493c Add the ability to add MinerData together and sum them, as well as compute division and floor division on them. 2022-09-30 16:00:45 -06:00
UpstreamData
d3a5517fa9 bump version number 2022-09-29 14:10:19 -06:00
UpstreamData
ad42251ee9 add S19 XP to docs 2022-09-29 14:09:26 -06:00
Arceris
0670938ed3 Add in S19 XP spec, and also make an adjustment to BMMiner so it won't fail when hashboards are missing (#20)
* Add in spec for S19 XP

* Adjust BMMiner to account for missing hashboards
2022-09-29 14:03:53 -06:00
UpstreamData
3e96889976 improve poetry setup instructions and improve documentation 2022-09-28 12:54:04 -06:00
UpstreamData
5a7b43ad74 Update README.md 2022-09-28 11:46:56 -06:00
UpstreamData
08c4863a2e improve poetry setup and install instructions 2022-09-28 11:39:25 -06:00
UpstreamData
4dbab75cf4 bump version number 2022-09-28 10:20:17 -06:00
UpstreamData
a90ad3ba6e fix incorrect chip count on M31S+ V30 2022-09-28 10:19:56 -06:00
UpstreamData
98a94ce4a6 bump version number 2022-09-28 10:06:53 -06:00
UpstreamData
f0a8b6e1c7 add support for whatsminer M31S+ V30 2022-09-28 10:05:28 -06:00
UpstreamData
e07bd3bffb bump version number 2022-09-26 13:08:27 -06:00
UpstreamData
dcce944390 Fix a bug where older version of bosminer return excessive hashboard error information 2022-09-26 12:27:37 -06:00
UpstreamData
03ecd118a3 add support for M31S+ V60 and V90 2022-09-26 11:51:47 -06:00
UpstreamData
97c0331762 bump version number 2022-09-26 11:33:42 -06:00
UpstreamData
eda9804dea add support for some new whatsminers types, M31S+ v40 and v80, and improve documentation of supported types. 2022-09-26 11:32:55 -06:00
UpstreamData
e94c81ce44 improve miner network functionality 2022-09-26 09:15:37 -06:00
UpstreamData
c95c58138e bump version number 2022-09-22 10:07:31 -06:00
UpstreamData
03c93b4de1 added pause_mining and resume_mining to all miners, added get_errors to whatsminers, and improved get_errors type hinting 2022-09-22 10:06:27 -06:00
UpstreamData
ff0d15c365 bump version number 2022-09-22 09:06:51 -06:00
UpstreamData
eadcb76d31 add stop_mining and resume_mining for X19 devices 2022-09-22 09:06:22 -06:00
UpstreamData
b7ce9288f8 bump version number 2022-09-13 09:53:03 -06:00
UpstreamData
e077a099d9 add global Innosilicon password option to settings 2022-09-13 09:52:33 -06:00
UpstreamData
8542acfb01 improve documentation 2022-09-13 09:11:15 -06:00
UpstreamData
0d80ce5a0e bump version number 2022-09-12 15:28:22 -06:00
UpstreamData
ddcafe0f2b finish abstracting BaseMiner by implementing get_data() as abstract 2022-09-12 15:27:51 -06:00
UpstreamData
ea195b34db update tests and add code coverage with coverage, although coverage is not required 2022-09-12 15:18:00 -06:00
UpstreamData
7377cb0d26 refactor some classes into their own files and fill base __init__.py with imports 2022-09-12 15:15:13 -06:00
52 changed files with 2129 additions and 380 deletions

17
.coveragerc Normal file
View File

@@ -0,0 +1,17 @@
[report]
exclude_lines =
# Skip @abstractmethod
@abstractmethod
@abc.abstractmethod
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:

View File

@@ -7,47 +7,29 @@
[![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)
## Supported Miners
Supported miners are listed in the docs, [here](https://pyasic.readthedocs.io/en/latest/miners/supported_types/)
## Documentation and Supported Miners
Documentation is located on Read the Docs as [pyasic](https://pyasic.readthedocs.io/en/latest/).
## Documentation
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/).
## Usage
## Installation
You can install pyasic directly from pip with the command `pip install pyasic`.
### Standard Usage
You can install pyasic directly from pip with the command `pip install pyasic`
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).
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)
## Developer Setup
This repo uses poetry for dependencies, which can be installed by following the guide on their website [here](https://python-poetry.org/docs/#installation).
### Developers
To use this repo, first download it, create a virtual environment, enter the virtual environment, and install relevant packages by navigating to this directory and running ```pip install -r requirements-dev.txt``` on Windows or ```pip3 install -r requirements-dev.txt``` on Mac or UNIX if the first command fails.
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.
You can also use poetry by initializing and running ```poetry install```, and you will have to install `pre-commit` (`pip install pre-commit`).
Finally, initialize pre-commit hooks with `poetry run pre-commit install`.
Finally, initialize pre-commit hooks with `pre-commit install`
### Documentation Testing
Testing the documentation can be done by running `poetry run mkdocs serve`, whcih will serve the documentation locally on port 8000.
### Interfacing with miners programmatically
## Interfacing with miners programmatically
##### Note: If you are trying to interface with Whatsminers, there is a bug in the way they are interacted with on Windows, so to fix that you need to change the event loop policy using this code:
```python
# need to import these 2 libraries, you need asyncio anyway so make sure you have sys imported
import sys
import asyncio
# if the computer is windows, set the event loop policy to a WindowsSelector policy
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
```
##### It is likely a good idea to use this code in your program anyway to be preventative.
<br>
To write your own custom programs with this repo, you have many options.
It is recommended that you explore the files in this repo to familiarize yourself with them, try starting with the miners module and going from there.
There are 2 main ways to get a miner and it's functions via scanning or via the MinerFactory.
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
@@ -56,11 +38,6 @@ import sys
from pyasic.network import MinerNetwork
# Fix whatsminer bug
# if the computer is windows, set the event loop policy to a WindowsSelector policy
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# define asynchronous function to scan for miners
async def scan_and_get_data():
@@ -87,7 +64,6 @@ if __name__ == "__main__":
asyncio.run(scan_and_get_data())
```
</br>
#### Getting a miner if you know the IP
```python
@@ -96,11 +72,6 @@ import sys
from pyasic.miners import get_miner
# Fix whatsminer bug
# if the computer is windows, set the event loop policy to a WindowsSelector policy
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
# define asynchronous function to get miner and data
async def get_miner_data(miner_ip: str):
@@ -127,11 +98,6 @@ import sys
from pyasic.miners import get_miner
# Fix whatsminer bug
# if the computer is windows, set the event loop policy to a WindowsSelector policy
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def get_api_commands(miner_ip: str):
# Get the miner
@@ -155,11 +121,6 @@ import sys
from pyasic.miners import get_miner
# Fix whatsminer bug
# if the computer is windows, set the event loop policy to a WindowsSelector policy
if sys.version_info[0] == 3 and sys.version_info[1] >= 8 and sys.platform.startswith('win'):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async def get_api_commands(miner_ip: str):
# Get the miner

View File

@@ -23,3 +23,11 @@
options:
show_root_heading: false
heading_level: 4
<br>
## Innosilicon Error Codes
::: pyasic.data.error_codes.InnosiliconError
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -101,3 +101,165 @@ async def gather_miner_data(): # define async scan function to allow awaiting
if __name__ == "__main__":
asyncio.run(gather_miner_data())
```
<br>
## Controlling miners via pyasic
Every miner class in pyasic must implement all the control functions defined in [`BaseMiner`][pyasic.miners.BaseMiner].
These functions are
[`check_light`](#check-light),
[`fault_light_off`](#fault-light-off),
[`fault_light_on`](#fault-light-on),
[`get_config`](#get-config),
[`get_data`](#get-data),
[`get_errors`](#get-errors),
[`get_hostname`](#get-hostname),
[`get_model`](#get-model),
[`reboot`](#reboot),
[`restart_backend`](#restart-backend), and
[`stop_mining`](#stop-mining), and
[`resume_mining`](#resume-mining), and
[`send_config`](#send-config).
<br>
### Check Light
::: pyasic.miners.BaseMiner.check_light
handler: python
options:
heading_level: 4
<br>
### Fault Light Off
::: pyasic.miners.BaseMiner.fault_light_off
handler: python
options:
heading_level: 4
<br>
### Fault Light On
::: pyasic.miners.BaseMiner.fault_light_on
handler: python
options:
heading_level: 4
<br>
### Get Config
::: pyasic.miners.BaseMiner.get_config
handler: python
options:
heading_level: 4
<br>
### Get Data
::: pyasic.miners.BaseMiner.get_data
handler: python
options:
heading_level: 4
<br>
### Get Errors
::: pyasic.miners.BaseMiner.get_errors
handler: python
options:
heading_level: 4
<br>
### Get Hostname
::: pyasic.miners.BaseMiner.get_hostname
handler: python
options:
heading_level: 4
<br>
### Get Model
::: pyasic.miners.BaseMiner.get_model
handler: python
options:
heading_level: 4
<br>
### Reboot
::: pyasic.miners.BaseMiner.reboot
handler: python
options:
heading_level: 4
<br>
### Restart Backend
::: pyasic.miners.BaseMiner.restart_backend
handler: python
options:
heading_level: 4
<br>
### Stop Mining
::: pyasic.miners.BaseMiner.stop_mining
handler: python
options:
heading_level: 4
<br>
### Resume Mining
::: pyasic.miners.BaseMiner.resume_mining
handler: python
options:
heading_level: 4
<br>
### Send Config
::: pyasic.miners.BaseMiner.send_config
handler: python
options:
heading_level: 4
<br>
## [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
Pyasic implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs.
<br>
### [`MinerData`][pyasic.data.MinerData]
[`MinerData`][pyasic.data.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.asdict()`][pyasic.data.MinerData.asdict] 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`][pyasic.data.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)
```
<br>
### [`MinerConfig`][pyasic.config.MinerConfig]
[`MinerConfig`][pyasic.config.MinerConfig] is pyasic's way to represent a configuration file from a miner.
It is the return from [`get_config()`](#get-config).
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.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`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.

View File

@@ -43,6 +43,14 @@
show_root_heading: false
heading_level: 4
## S19 XP
::: pyasic.miners.antminer.bmminer.X19.S19_XP.BMMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 4
## T19
::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19

View File

@@ -4,93 +4,220 @@
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
##### pyasic currently supports the following miners and subtypes:
* Braiins OS+ Devices:
* X19 Series:
* [S19][pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19]
* [S19 Pro][pyasic.miners.antminer.bosminer.X19.S19_Pro.BOSMinerS19Pro]
* [S19j][pyasic.miners.antminer.bosminer.X19.S19j.BOSMinerS19j]
* [S19j Pro][pyasic.miners.antminer.bosminer.X19.S19j_Pro.BOSMinerS19jPro]
* [T19][pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19]
* X17 Series:
* [S17][pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17]
* [S17+][pyasic.miners.antminer.bosminer.X17.S17_Plus.BOSMinerS17Plus]
* [S17 Pro][pyasic.miners.antminer.bosminer.X17.S17_Pro.BOSMinerS17Pro]
* [S17e][pyasic.miners.antminer.bosminer.X17.S17e.BOSMinerS17e]
* [T17][pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17]
* [T17+][pyasic.miners.antminer.bosminer.X17.T17_Plus.BOSMinerT17Plus]
* [T17e][pyasic.miners.antminer.bosminer.X17.T17e.BOSMinerT17e]
* X9 Series:
* [S9][pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9]
* [S9i][pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9]
* [S9j][pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9]
* Stock Firmware Whatsminers:
* M3X Series:
* [M30S][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30S]:
* [VE10][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE10]
* [VG20][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVG20]
* [VE20][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SVE20]
* [V50][pyasic.miners.whatsminer.btminer.M3X.M30S.BTMinerM30SV50]
* [M30S+][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlus]:
* [VF20][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVF20]
* [VE40][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVE40]
* [VG60][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus.BTMinerM30SPlusVG60]
* [M30S++][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlus]:
* [VG30][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG30]
* [VG40][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVG40]
* [VH60][pyasic.miners.whatsminer.btminer.M3X.M30S_Plus_Plus.BTMinerM30SPlusPlusVH60]
* [M31S][pyasic.miners.whatsminer.btminer.M3X.M31S.BTMinerM31S]
* [M31S+][pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlus]:
* [VE20][pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusVE20]
* [M32][pyasic.miners.whatsminer.btminer.M3X.M32.BTMinerM32]
* [V20][pyasic.miners.whatsminer.btminer.M3X.M32.BTMinerM32V20]
* [M32S][pyasic.miners.whatsminer.btminer.M3X.M32S.BTMinerM32S]
* M2X Series:
* [M20][pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20]:
* [V10][pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10]
* [M20S][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20S]:
* [V10][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10]
* [V20][pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20]
* [M20S+][pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlus]
* [M21][pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21]
* [M21S][pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21S]:
* [V20][pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20]
* [V60][pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60]
* [M21S+][pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlus]
* Stock Firmware Antminers:
* X19 Series:
* [S19][pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19]
* [S19 Pro][pyasic.miners.antminer.bmminer.X19.S19_Pro.BMMinerS19Pro]
* [S19a][pyasic.miners.antminer.bmminer.X19.S19a.BMMinerS19a]
* [S19j][pyasic.miners.antminer.bmminer.X19.S19j.BMMinerS19j]
* [S19j Pro][pyasic.miners.antminer.bmminer.X19.S19j_Pro.BMMinerS19jPro]
* [T19][pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19]
* X17 Series:
* [S17][pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17]
* [S17+][pyasic.miners.antminer.bmminer.X17.S17_Plus.BMMinerS17Plus]
* [S17 Pro][pyasic.miners.antminer.bmminer.X17.S17_Pro.BMMinerS17Pro]
* [S17e][pyasic.miners.antminer.bmminer.X17.S17e.BMMinerS17e]
* [T17][pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17]
* [T17+][pyasic.miners.antminer.bmminer.X17.T17_Plus.BMMinerT17Plus]
* [T17e][pyasic.miners.antminer.bmminer.X17.T17e.BMMinerT17e]
* X9 Series:
* [S9][pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9]
* [S9i][pyasic.miners.antminer.bmminer.X9.S9i.BMMinerS9i]
* [T9][pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9]
* Stock Firmware Avalonminers:
* A7X Series:
* [A721][pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721]
* [A741][pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741]
* [A761][pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761]
* A8X Series:
* [A821][pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821]
* [A841][pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841]
* [A851][pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851]
* A9X Series:
* [A921][pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921]
* A10X Series:
* [A1026][pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026]
* [A1047][pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047]
* [A1066][pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066]
* Stock Firmware Innosilicon Miners:
* T3X Series:
* [T3H+][pyasic.miners.innosilicon.cgminer.T3X.T3H_Plus.CGMinerInnosiliconT3HPlus]
<style>
details {
margin:0px;
padding-top:0px;
padding-bottom:0px;
}
</style>
<details style="margin:0px; padding-top:0px; padding-bottom:0px;">
<summary>Braiins OS+ Devices:</summary>
<ul>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="/miners/antminer/X19#s19-bos">S19</a></li>
<li><a href="/miners/antminer/X19#s19-pro-bos">S19 Pro</a></li>
<li><a href="/miners/antminer/X19#s19j-bos">S19j</a></li>
<li><a href="/miners/antminer/X19#s19j-pro-bos">S19j Pro</a></li>
<li><a href="/miners/antminer/X19#t19-bos">T19</a></li>
</ul>
</details>
<details>
<summary>X17 Series:</summary>
<ul>
<li><a href="/miners/antminer/X17#s17-bos">S17</a></li>
<li><a href="/miners/antminer/X17#s17-plus-bos">S17+</a></li>
<li><a href="/miners/antminer/X17#s17-pro-bos">S17 Pro</a></li>
<li><a href="/miners/antminer/X17#s17e-bos">S17e</a></li>
<li><a href="/miners/antminer/X17#t17-bos">T17</a></li>
<li><a href="/miners/antminer/X17#t17-plus-bos">T17+</a></li>
<li><a href="/miners/antminer/X17#t17e-bos">T17e</a></li>
</ul>
</details>
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="/miners/antminer/X9#s9-bos">S9</a></li>
<li><a href="/miners/antminer/X9#s9-bos">S9i</a></li>
<li><a href="/miners/antminer/X9#s9-bos">S9j</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware Whatsminers:</summary>
<ul>
<details>
<summary>M3X Series:</summary>
<ul>
<details>
<summary><a href="/miners/whatsminer/M3X/#m30s">M30S</a></summary>
<ul>
<li><a href="/miners/whatsminer/M3X/#m30sve10">VE10</a></li>
<li><a href="/miners/whatsminer/M3X/#m30svg20">VG20</a></li>
<li><a href="/miners/whatsminer/M3X/#m30sve20">VE20</a></li>
<li><a href="/miners/whatsminer/M3X/#m30sv50">V50</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M3X/#m30s_1">M30S+</a></summary>
<ul>
<li><a href="/miners/whatsminer/M3X/#m30svf20">VF20</a></li>
<li><a href="/miners/whatsminer/M3X/#m30sve40">VE40</a></li>
<li><a href="/miners/whatsminer/M3X/#m30svg60">VG60</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M3X/#m30s_2">M30S++</a></summary>
<ul>
<li><a href="/miners/whatsminer/M3X/#m30svg30">VG30</a></li>
<li><a href="/miners/whatsminer/M3X/#m30svg40">VG40</a></li>
<li><a href="/miners/whatsminer/M3X/#m30svh60">VH60</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M3X/#m31s">M31S</a></summary>
</details>
<details>
<summary><a href="/miners/whatsminer/M3X/#m31s_1">M31S+</a></summary>
<ul>
<li><a href="/miners/whatsminer/M3X/#m31sve20">VE20</a></li>
<li><a href="/miners/whatsminer/M3X/#m31sv30">V30</a></li>
<li><a href="/miners/whatsminer/M3X/#m31sv40">V40</a></li>
<li><a href="/miners/whatsminer/M3X/#m31sv60">V60</a></li>
<li><a href="/miners/whatsminer/M3X/#m31sv80">V80</a></li>
<li><a href="/miners/whatsminer/M3X/#m31sv90">V90</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M3X/#m32">M32</a></summary>
<ul>
<li><a href="/miners/whatsminer/M3X/#m32v20">V20</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M3X/#m32s">M32S</a></summary>
</details>
</ul>
</details>
<details>
<summary>M2X Series:</summary>
<ul>
<details>
<summary><a href="/miners/whatsminer/M2X/#m20">M20</a></summary>
<ul>
<li><a href="/miners/whatsminer/M2X/#m20v10">V10</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M2X/#m20s">M20S</a></summary>
<ul>
<li><a href="/miners/whatsminer/M2X/#m20sv10">V10</a></li>
<li><a href="/miners/whatsminer/M2X/#m20sv20">V20</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M2X/#m20s_1">M20S+</a></summary>
</details>
<details>
<summary><a href="/miners/whatsminer/M2X/#m21">M21</a></summary>
</details>
<details>
<summary><a href="/miners/whatsminer/M2X/#m21s">M21S</a></summary>
<ul>
<li><a href="/miners/whatsminer/M2X/#m21sv20">V20</a></li>
<li><a href="/miners/whatsminer/M2X/#m21sv60">V60</a></li>
</ul>
</details>
<details>
<summary><a href="/miners/whatsminer/M2X/#m21s_1">M21S+</a></summary>
</details>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware Antminers:</summary>
<ul>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="/miners/antminer/X19/#s19">S19</a></li>
<li><a href="/miners/antminer/X19/#s19-pro">S19 Pro</a></li>
<li><a href="/miners/antminer/X19/#s19a">S19a</a></li>
<li><a href="/miners/antminer/X19/#s19j">S19j</a></li>
<li><a href="/miners/antminer/X19/#s19j-pro">S19j Pro</a></li>
<li><a href="/miners/antminer/X19/#s19-xp">S19 XP</a></li>
<li><a href="/miners/antminer/X19/#t19">T19</a></li>
</ul>
</details>
<details>
<summary>X17 Series:</summary>
<ul>
<li><a href="/miners/antminer/X17/#s17">S17</a></li>
<li><a href="/miners/antminer/X17/#s17_1">S17+</a></li>
<li><a href="/miners/antminer/X17/#s17-pro">S17 Pro</a></li>
<li><a href="/miners/antminer/X17/#s17e">S17e</a></li>
<li><a href="/miners/antminer/X17/#t17">T17</a></li>
<li><a href="/miners/antminer/X17/#t17_1">T17+</a></li>
<li><a href="/miners/antminer/X17/#t17e">T17e</a></li>
</ul>
</details>
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="/miners/antminer/X9/#s9">S9</a></li>
<li><a href="/miners/antminer/X9/#s9i">S9i</a></li>
<li><a href="/miners/antminer/X9/#t9">T9</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware Avalonminers:</summary>
<ul>
<details>
<summary>A7X Series:</summary>
<ul>
<li><a href="/miners/avalonminer/A7X/#a721">A721</a></li>
<li><a href="/miners/avalonminer/A7X/#a741">A741</a></li>
<li><a href="/miners/avalonminer/A7X/#a761">A761</a></li>
</ul>
</details>
<details>
<summary>A8X Series:</summary>
<ul>
<li><a href="/miners/avalonminer/A8X/#a821">A821</a></li>
<li><a href="/miners/avalonminer/A8X/#a841">A841</a></li>
<li><a href="/miners/avalonminer/A8X/#a851">A851</a></li>
</ul>
</details>
<details>
<summary>A9X Series:</summary>
<ul>
<li><a href="/miners/avalonminer/A9X/#a921">A921</a></li>
</ul>
</details>
<details>
<summary>A10X Series:</summary>
<ul>
<li><a href="/miners/avalonminer/A10X/#a1026">A1026</a></li>
<li><a href="/miners/avalonminer/A10X/#a1047">A1047</a></li>
<li><a href="/miners/avalonminer/A10X/#a1066">A1066</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware Innosilicon Miners:</summary>
<ul>
<details>
<summary>T3X Series:</summary>
<ul>
<li><a href="/miners/innosilicon/T3X/#t3h">T3H+</a></li>
</ul>
</details>
</ul>
</details>

View File

@@ -130,6 +130,46 @@
show_root_heading: false
heading_level: 4
## M31S+V30
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV30
handler: python
options:
show_root_heading: false
heading_level: 4
## M31S+V40
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV40
handler: python
options:
show_root_heading: false
heading_level: 4
## M31S+V60
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV60
handler: python
options:
show_root_heading: false
heading_level: 4
## M31S+V80
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV80
handler: python
options:
show_root_heading: false
heading_level: 4
## M31S+V90
::: pyasic.miners.whatsminer.btminer.M3X.M31S_Plus.BTMinerM31SPlusV90
handler: python
options:
show_root_heading: false
heading_level: 4
## M32
::: pyasic.miners.whatsminer.btminer.M3X.M32.BTMinerM32

View File

@@ -11,7 +11,6 @@ nav:
- BTMiner: "miners/backends/btminer.md"
- CGMiner: "miners/backends/cgminer.md"
- Hiveon: "miners/backends/hiveon.md"
- Classes:
- Antminer X9: "miners/antminer/X9.md"
- Antminer X17: "miners/antminer/X17.md"
@@ -23,14 +22,12 @@ nav:
- Whatsminer M2X: "miners/whatsminer/M2X.md"
- Whatsminer M3X: "miners/whatsminer/M3X.md"
- Innosilicon T3X: "miners/innosilicon/T3X.md"
- Network:
- Miner Network: "network/miner_network.md"
- Miner Network Range: "network/miner_network_range.md"
- Data:
- Dataclasses:
- Miner Data: "data/miner_data.md"
- Error Codes: "data/error_codes.md"
- Config:
- Miner Config: "config/miner_config.md"
- Advanced:
- Miner APIs:

647
poetry.lock generated
View File

@@ -11,8 +11,8 @@ idna = ">=2.8"
sniffio = ">=1.1"
[package.extras]
doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
doc = ["packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
test = ["contextlib2", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "uvloop (>=0.15)"]
trio = ["trio (>=0.16)"]
[[package]]
@@ -38,7 +38,7 @@ pywin32 = ["pywin32 (>=227)"]
[[package]]
name = "certifi"
version = "2022.6.15"
version = "2022.9.24"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
@@ -55,9 +55,36 @@ python-versions = "*"
[package.dependencies]
pycparser = "*"
[[package]]
name = "cfgv"
version = "3.3.1"
description = "Validate configuration and produce human readable error messages."
category = "dev"
optional = false
python-versions = ">=3.6.1"
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.5"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "cryptography"
version = "37.0.4"
version = "38.0.1"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
category = "main"
optional = false
@@ -68,11 +95,56 @@ cffi = ">=1.12"
[package.extras]
docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"]
pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
sdist = ["setuptools_rust (>=0.11.4)"]
sdist = ["setuptools-rust (>=0.11.4)"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"]
[[package]]
name = "distlib"
version = "0.3.6"
description = "Distribution utilities"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "filelock"
version = "3.8.0"
description = "A platform independent file lock."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo (>=2022.6.21)", "sphinx (>=5.1.1)", "sphinx-autodoc-typehints (>=1.19.1)"]
testing = ["covdefaults (>=2.2)", "coverage (>=6.4.2)", "pytest (>=7.1.2)", "pytest-cov (>=3)", "pytest-timeout (>=2.1)"]
[[package]]
name = "ghp-import"
version = "2.1.0"
description = "Copy your docs directly to the gh-pages branch."
category = "dev"
optional = false
python-versions = "*"
[package.dependencies]
python-dateutil = ">=2.8.1"
[package.extras]
dev = ["flake8", "markdown", "twine", "wheel"]
[[package]]
name = "griffe"
version = "0.22.2"
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
async = ["aiofiles (>=0.7,<1.0)"]
[[package]]
name = "h11"
@@ -115,19 +187,181 @@ rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
sniffio = "*"
[package.extras]
brotli = ["brotlicffi", "brotli"]
cli = ["click (>=8.0.0,<9.0.0)", "rich (>=10,<13)", "pygments (>=2.0.0,<3.0.0)"]
brotli = ["brotli", "brotlicffi"]
cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<13)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (>=1.0.0,<2.0.0)"]
[[package]]
name = "identify"
version = "2.5.5"
description = "File identification library for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
license = ["ukkonen"]
[[package]]
name = "idna"
version = "3.3"
version = "3.4"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
version = "4.12.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"]
[[package]]
name = "Jinja2"
version = "3.1.2"
description = "A very fast and expressive template engine."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
MarkupSafe = ">=2.0"
[package.extras]
i18n = ["Babel (>=2.7)"]
[[package]]
name = "Markdown"
version = "3.3.7"
description = "Python implementation of Markdown."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
[package.extras]
testing = ["coverage", "pyyaml"]
[[package]]
name = "MarkupSafe"
version = "2.1.1"
description = "Safely add untrusted strings to HTML/XML markup."
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "mergedeep"
version = "1.3.4"
description = "A deep merge function for 🐍."
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "mkdocs"
version = "1.4.0"
description = "Project documentation with Markdown."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
click = ">=7.0"
ghp-import = ">=1.0"
importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""}
Jinja2 = ">=2.11.1"
Markdown = ">=3.2.1,<3.4"
mergedeep = ">=1.3.4"
packaging = ">=20.5"
PyYAML = ">=5.1"
pyyaml-env-tag = ">=0.1"
watchdog = ">=2.0"
[package.extras]
i18n = ["babel (>=2.9.0)"]
[[package]]
name = "mkdocs-autorefs"
version = "0.4.1"
description = "Automatically link across pages in MkDocs."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
Markdown = ">=3.3"
mkdocs = ">=1.1"
[[package]]
name = "mkdocstrings"
version = "0.19.0"
description = "Automatic documentation from sources, for MkDocs."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
Jinja2 = ">=2.11.1"
Markdown = ">=3.3"
MarkupSafe = ">=1.1"
mkdocs = ">=1.2"
mkdocs-autorefs = ">=0.3.1"
mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""}
pymdown-extensions = ">=6.3"
[package.extras]
crystal = ["mkdocstrings-crystal (>=0.3.4)"]
python = ["mkdocstrings-python (>=0.5.2)"]
python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
[[package]]
name = "mkdocstrings-python"
version = "0.7.1"
description = "A Python handler for mkdocstrings."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
griffe = ">=0.11.1"
mkdocstrings = ">=0.19"
[[package]]
name = "nodeenv"
version = "1.7.0"
description = "Node.js virtual environment builder"
category = "dev"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
[package.dependencies]
setuptools = "*"
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "passlib"
version = "1.7.4"
@@ -139,9 +373,37 @@ python-versions = "*"
[package.extras]
argon2 = ["argon2-cffi (>=18.2.0)"]
bcrypt = ["bcrypt (>=3.1.0)"]
build_docs = ["sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)", "cloud-sptheme (>=1.10.1)"]
build_docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"]
totp = ["cryptography"]
[[package]]
name = "platformdirs"
version = "2.5.2"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"]
test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
[[package]]
name = "pre-commit"
version = "2.20.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
cfgv = ">=2.0.0"
identify = ">=1.0.0"
nodeenv = ">=0.11.1"
pyyaml = ">=5.1"
toml = "*"
virtualenv = ">=20.0.8"
[[package]]
name = "pyaml"
version = "21.10.1"
@@ -162,13 +424,57 @@ optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyyaml"
name = "pymdown-extensions"
version = "9.6"
description = "Extension pack for Python Markdown."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
markdown = ">=3.2"
[[package]]
name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "dev"
optional = false
python-versions = ">=3.6.8"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
[package.dependencies]
six = ">=1.5"
[[package]]
name = "PyYAML"
version = "6.0"
description = "YAML parser and emitter for Python"
category = "main"
optional = false
python-versions = ">=3.6"
[[package]]
name = "pyyaml_env_tag"
version = "0.1"
description = "A custom YAML tag for referencing environment variables in YAML files. "
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyyaml = "*"
[[package]]
name = "rfc3986"
version = "1.5.0"
@@ -183,13 +489,34 @@ idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
[package.extras]
idna2008 = ["idna"]
[[package]]
name = "setuptools"
version = "65.4.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
[[package]]
name = "sniffio"
version = "1.2.0"
version = "1.3.0"
description = "Sniff out which async library your code is running under"
category = "main"
optional = false
python-versions = ">=3.5"
python-versions = ">=3.7"
[[package]]
name = "toml"
@@ -207,10 +534,50 @@ category = "main"
optional = false
python-versions = ">=3.7"
[[package]]
name = "virtualenv"
version = "20.16.5"
description = "Virtual Python Environment builder"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
distlib = ">=0.3.5,<1"
filelock = ">=3.4.1,<4"
platformdirs = ">=2.4,<3"
[package.extras]
docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"]
testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"]
[[package]]
name = "watchdog"
version = "2.1.9"
description = "Filesystem events monitoring"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
watchmedo = ["PyYAML (>=3.10)"]
[[package]]
name = "zipp"
version = "3.8.1"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "8d93eafd928d7fed4b0a00d13e46982c2d4310c37acb2faec7e7a477b3f35e9c"
content-hash = "98c3026a6f27c29c0357bbfa07166d6d8b604a869f3a802adc3bb3610f86964c"
[metadata.files]
anyio = [
@@ -222,8 +589,8 @@ asyncssh = [
{file = "asyncssh-2.12.0.tar.gz", hash = "sha256:274101322c4b941823aeed8e1ab6e7be5191686c6db2d2bd35afeba30505e780"},
]
certifi = [
{file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"},
{file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"},
{file = "certifi-2022.9.24-py3-none-any.whl", hash = "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382"},
{file = "certifi-2022.9.24.tar.gz", hash = "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14"},
]
cffi = [
{file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"},
@@ -291,29 +658,61 @@ cffi = [
{file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"},
{file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"},
]
cfgv = [
{file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
{file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
]
colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
cryptography = [
{file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884"},
{file = "cryptography-37.0.4-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6"},
{file = "cryptography-37.0.4-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046"},
{file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5"},
{file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b"},
{file = "cryptography-37.0.4-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8"},
{file = "cryptography-37.0.4-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280"},
{file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3"},
{file = "cryptography-37.0.4-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59"},
{file = "cryptography-37.0.4-cp36-abi3-win32.whl", hash = "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157"},
{file = "cryptography-37.0.4-cp36-abi3-win_amd64.whl", hash = "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327"},
{file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b"},
{file = "cryptography-37.0.4-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"},
{file = "cryptography-37.0.4-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67"},
{file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d"},
{file = "cryptography-37.0.4-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282"},
{file = "cryptography-37.0.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa"},
{file = "cryptography-37.0.4-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441"},
{file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596"},
{file = "cryptography-37.0.4-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a"},
{file = "cryptography-37.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab"},
{file = "cryptography-37.0.4.tar.gz", hash = "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82"},
{file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f"},
{file = "cryptography-38.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad"},
{file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153"},
{file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407"},
{file = "cryptography-38.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e"},
{file = "cryptography-38.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0"},
{file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd"},
{file = "cryptography-38.0.1-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6"},
{file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a"},
{file = "cryptography-38.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294"},
{file = "cryptography-38.0.1-cp36-abi3-win32.whl", hash = "sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0"},
{file = "cryptography-38.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a"},
{file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d"},
{file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9"},
{file = "cryptography-38.0.1-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d"},
{file = "cryptography-38.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"},
{file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6"},
{file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750"},
{file = "cryptography-38.0.1-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013"},
{file = "cryptography-38.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5"},
{file = "cryptography-38.0.1-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61"},
{file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac"},
{file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb"},
{file = "cryptography-38.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a"},
{file = "cryptography-38.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b"},
{file = "cryptography-38.0.1.tar.gz", hash = "sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7"},
]
distlib = [
{file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
{file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
]
filelock = [
{file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
{file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
]
ghp-import = [
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
]
griffe = [
{file = "griffe-0.22.2-py3-none-any.whl", hash = "sha256:cea5415ac6a92f4a22638e3f1f2e661402bac09fb8e8266936d67185a7e0d0fb"},
{file = "griffe-0.22.2.tar.gz", hash = "sha256:1408e336a4155392bbd81eed9f2f44bf144e71b9c664e905630affe83bbc088e"},
]
h11 = [
{file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
@@ -327,14 +726,108 @@ httpx = [
{file = "httpx-0.23.0-py3-none-any.whl", hash = "sha256:42974f577483e1e932c3cdc3cd2303e883cbfba17fe228b0f63589764d7b9c4b"},
{file = "httpx-0.23.0.tar.gz", hash = "sha256:f28eac771ec9eb4866d3fb4ab65abd42d38c424739e80c08d8d20570de60b0ef"},
]
identify = [
{file = "identify-2.5.5-py2.py3-none-any.whl", hash = "sha256:ef78c0d96098a3b5fe7720be4a97e73f439af7cf088ebf47b620aeaa10fadf97"},
{file = "identify-2.5.5.tar.gz", hash = "sha256:322a5699daecf7c6fd60e68852f36f2ecbb6a36ff6e6e973e0d2bb6fca203ee6"},
]
idna = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
importlib-metadata = [
{file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"},
{file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"},
]
Jinja2 = [
{file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
{file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
]
Markdown = [
{file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"},
{file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"},
]
MarkupSafe = [
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
{file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
{file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
{file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
{file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
{file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
{file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
{file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
{file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
]
mergedeep = [
{file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
{file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
]
mkdocs = [
{file = "mkdocs-1.4.0-py3-none-any.whl", hash = "sha256:ce057e9992f017b8e1496b591b6c242cbd34c2d406e2f9af6a19b97dd6248faa"},
{file = "mkdocs-1.4.0.tar.gz", hash = "sha256:e5549a22d59e7cb230d6a791edd2c3d06690908454c0af82edc31b35d57e3069"},
]
mkdocs-autorefs = [
{file = "mkdocs-autorefs-0.4.1.tar.gz", hash = "sha256:70748a7bd025f9ecd6d6feeba8ba63f8e891a1af55f48e366d6d6e78493aba84"},
{file = "mkdocs_autorefs-0.4.1-py3-none-any.whl", hash = "sha256:a2248a9501b29dc0cc8ba4c09f4f47ff121945f6ce33d760f145d6f89d313f5b"},
]
mkdocstrings = [
{file = "mkdocstrings-0.19.0-py3-none-any.whl", hash = "sha256:3217d510d385c961f69385a670b2677e68e07b5fea4a504d86bf54c006c87c7d"},
{file = "mkdocstrings-0.19.0.tar.gz", hash = "sha256:efa34a67bad11229d532d89f6836a8a215937548623b64f3698a1df62e01cc3e"},
]
mkdocstrings-python = [
{file = "mkdocstrings-python-0.7.1.tar.gz", hash = "sha256:c334b382dca202dfa37071c182418a6df5818356a95d54362a2b24822ca3af71"},
{file = "mkdocstrings_python-0.7.1-py3-none-any.whl", hash = "sha256:a22060bfa374697678e9af4e62b020d990dad2711c98f7a9fac5c0345bef93c7"},
]
nodeenv = [
{file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"},
{file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
passlib = [
{file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"},
{file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"},
]
platformdirs = [
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
]
pre-commit = [
{file = "pre_commit-2.20.0-py2.py3-none-any.whl", hash = "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7"},
{file = "pre_commit-2.20.0.tar.gz", hash = "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"},
]
pyaml = [
{file = "pyaml-21.10.1-py2.py3-none-any.whl", hash = "sha256:19985ed303c3a985de4cf8fd329b6d0a5a5b5c9035ea240eccc709ebacbaf4a0"},
{file = "pyaml-21.10.1.tar.gz", hash = "sha256:c6519fee13bf06e3bb3f20cacdea8eba9140385a7c2546df5dbae4887f768383"},
@@ -343,7 +836,19 @@ pycparser = [
{file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]
pyyaml = [
pymdown-extensions = [
{file = "pymdown_extensions-9.6-py3-none-any.whl", hash = "sha256:1e36490adc7bfcef1fdb21bb0306e93af99cff8ec2db199bd17e3bf009768c11"},
{file = "pymdown_extensions-9.6.tar.gz", hash = "sha256:b956b806439bbff10f726103a941266beb03fbe99f897c7d5e774d7170339ad9"},
]
pyparsing = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
]
python-dateutil = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
PyYAML = [
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
@@ -351,6 +856,13 @@ pyyaml = [
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
{file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
{file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
{file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
{file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
@@ -378,13 +890,25 @@ pyyaml = [
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
]
pyyaml_env_tag = [
{file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
{file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
]
rfc3986 = [
{file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
{file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
]
setuptools = [
{file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
{file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
]
six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
sniffio = [
{file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
{file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
{file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
@@ -394,3 +918,38 @@ typing-extensions = [
{file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"},
{file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"},
]
virtualenv = [
{file = "virtualenv-20.16.5-py3-none-any.whl", hash = "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27"},
{file = "virtualenv-20.16.5.tar.gz", hash = "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da"},
]
watchdog = [
{file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"},
{file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"},
{file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"},
{file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"},
{file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"},
{file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"},
{file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"},
{file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"},
{file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"},
{file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"},
{file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"},
{file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"},
{file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"},
{file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"},
{file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"},
{file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"},
{file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"},
{file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"},
{file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"},
]
zipp = [
{file = "zipp-3.8.1-py3-none-any.whl", hash = "sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009"},
{file = "zipp-3.8.1.tar.gz", hash = "sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2"},
]

View File

@@ -19,33 +19,7 @@ import warnings
import logging
from typing import Union
class APIError(Exception):
def __init__(self, *args):
if args:
self.message = args[0]
else:
self.message = None
def __str__(self):
if self.message:
return f"{self.message}"
else:
return "Incorrect API parameters."
class APIWarning(Warning):
def __init__(self, *args):
if args:
self.message = args[0]
else:
self.message = None
def __str__(self):
if self.message:
return f"{self.message}"
else:
return "Incorrect API parameters."
from pyasic.errors import APIError, APIWarning
class BaseMinerAPI:

View File

@@ -24,7 +24,8 @@ from typing import Union
from passlib.handlers.md5_crypt import md5_crypt
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from pyasic.API import BaseMinerAPI, APIError
from pyasic.errors import APIError
from pyasic.API import BaseMinerAPI
from pyasic.settings import PyasicSettings

View File

@@ -11,3 +11,51 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pyasic.API.bmminer import BMMinerAPI
from pyasic.API.bosminer import BOSMinerAPI
from pyasic.API.btminer import BTMinerAPI
from pyasic.API.cgminer import CGMinerAPI
from pyasic.API.unknown import UnknownAPI
from pyasic.config import MinerConfig
from pyasic.data import (
MinerData,
BraiinsOSError,
InnosiliconError,
WhatsminerError,
X19Error,
)
from pyasic.errors import APIError, APIWarning
from pyasic.miners import get_miner
from pyasic.miners.base import AnyMiner
from pyasic.miners.miner_factory import MinerFactory
from pyasic.miners.miner_listener import MinerListener
from pyasic.network import MinerNetwork
from pyasic.settings import PyasicSettings
__all__ = [
"BMMinerAPI",
"BOSMinerAPI",
"BTMinerAPI",
"CGMinerAPI",
"UnknownAPI",
"MinerConfig",
"MinerData",
"BraiinsOSError",
"InnosiliconError",
"WhatsminerError",
"X19Error",
"APIError",
"APIWarning",
"get_miner",
"AnyMiner",
"MinerFactory",
"MinerListener",
"MinerNetwork",
"PyasicSettings",
]

View File

@@ -247,7 +247,7 @@ class MinerConfig:
temp_mode: Literal["auto", "manual", "disabled"] = "auto"
temp_target: float = 70.0
temp_hot: float = 80.0
temp_dangerous: float = 10.0
temp_dangerous: float = 100.0
minimum_fans: int = None
fan_speed: Literal[tuple(range(101))] = None # noqa - Ignore weird Literal usage
@@ -299,6 +299,10 @@ class MinerConfig:
self.temp_mode = "manual"
if data.get("bitmain-fan-pwm"):
self.fan_speed = int(data["bitmain-fan-pwm"])
elif key == "bitmain-work-mode":
if data[key]:
if data[key] == 1:
self.autotuning_wattage = 0
elif key == "fan_control":
for _key in data[key].keys():
if _key == "min_fans":
@@ -409,7 +413,10 @@ class MinerConfig:
"pools": self.pool_groups[0].as_x19(user_suffix=user_suffix),
"bitmain-fan-ctrl": False,
"bitmain-fan-pwn": 100,
"miner-mode": 0, # Normal Mode
}
if self.autotuning_wattage == 0:
cfg["miner-mode"] = 1 # Sleep Mode
if not self.temp_mode == "auto":
cfg["bitmain-fan-ctrl"] = True

View File

@@ -17,6 +17,7 @@ from dataclasses import dataclass, field, asdict
from datetime import datetime, timezone
import time
import json
import copy
from .error_codes import X19Error, WhatsminerError, BraiinsOSError, InnosiliconError
@@ -121,6 +122,43 @@ class MinerData:
def __iter__(self):
return iter([item for item in self.asdict()])
def __truediv__(self, other):
return self // other
def __floordiv__(self, other):
cp = copy.deepcopy(self)
for key in self:
item = getattr(self, key)
if isinstance(item, int):
setattr(cp, key, item // other)
if isinstance(item, float):
setattr(cp, key, round(item / other, 2))
return cp
def __add__(self, other):
if not isinstance(other, MinerData):
raise TypeError("Cannot add MinerData to non MinerData type.")
cp = copy.deepcopy(self)
for key in self:
item = getattr(self, key)
other_item = getattr(other, key)
if item is None:
item = 0
if other_item is None:
other_item = 0
if isinstance(item, int):
setattr(cp, key, item + other_item)
if isinstance(item, float):
setattr(cp, key, item + other_item)
if isinstance(item, str):
setattr(cp, key, "")
if isinstance(item, list):
setattr(cp, key, item + other_item)
if isinstance(item, bool):
setattr(cp, key, item & other_item)
return cp
@property
def total_chips(self): # noqa - Skip PyCharm inspection
return self.right_chips + self.center_chips + self.left_chips
@@ -175,15 +213,39 @@ class MinerData:
def efficiency(self, val):
pass
def asdict(self):
def asdict(self) -> dict:
"""Get this dataclass as a dictionary.
Returns:
A dictionary version of this class.
"""
return asdict(self)
def as_json(self):
def as_json(self) -> str:
"""Get this dataclass as JSON.
Returns:
A JSON version of this class.
"""
data = self.asdict()
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
return json.dumps(data)
def as_influxdb(self, measurement_name: str = "miner_data"):
def as_csv(self) -> str:
data = self.asdict()
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
data_list = [str(data[item]) for item in data]
return ", ".join(data_list)
def as_influxdb(self, measurement_name: str = "miner_data") -> str:
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
Parameters:
measurement_name: The name of the measurement to insert into in influxdb.
Returns:
A influxdb line protocol version of this class.
"""
tag_data = [measurement_name]
field_data = []

View File

@@ -16,3 +16,9 @@ from .whatsminer import WhatsminerError
from .bos import BraiinsOSError
from .X19 import X19Error
from .innosilicon import InnosiliconError
from typing import TypeVar
MinerErrorData = TypeVar(
"MinerErrorData", WhatsminerError, BraiinsOSError, X19Error, InnosiliconError
)

41
pyasic/errors/__init__.py Normal file
View File

@@ -0,0 +1,41 @@
# 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.
class APIError(Exception):
def __init__(self, *args):
if args:
self.message = args[0]
else:
self.message = None
def __str__(self):
if self.message:
return f"{self.message}"
else:
return "Incorrect API parameters."
class APIWarning(Warning):
def __init__(self, *args):
if args:
self.message = args[0]
else:
self.message = None
def __str__(self):
if self.message:
return f"{self.message}"
else:
return "Incorrect API parameters."

View File

@@ -14,7 +14,7 @@
import ipaddress
import logging
from typing import Union
from typing import Union, List
from pyasic.API.bmminer import BMMinerAPI
@@ -22,6 +22,7 @@ from pyasic.miners.base import BaseMiner
from pyasic.data import MinerData
from pyasic.config import MinerConfig
from pyasic.data.error_codes import MinerErrorData
from pyasic.settings import PyasicSettings
@@ -169,7 +170,7 @@ class BMMiner(BaseMiner):
async def fault_light_on(self) -> bool:
return False
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
return []
async def get_mac(self) -> str:
@@ -178,6 +179,12 @@ class BMMiner(BaseMiner):
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 get_data(self) -> MinerData:
"""Get data from the miner.
@@ -249,15 +256,26 @@ class BMMiner(BaseMiner):
data.center_chips = boards[1].get(f"chain_acn{board_offset+1}")
data.right_chips = boards[1].get(f"chain_acn{board_offset+2}")
data.left_board_hashrate = round(
float(boards[1].get(f"chain_rate{board_offset}")) / 1000, 2
)
data.center_board_hashrate = round(
float(boards[1].get(f"chain_rate{board_offset+1}")) / 1000, 2
)
data.right_board_hashrate = round(
float(boards[1].get(f"chain_rate{board_offset+2}")) / 1000, 2
)
try:
data.left_board_hashrate = round(
float(boards[1].get(f"chain_rate{board_offset}")) / 1000, 2
)
except ValueError as e:
data.left_board_hashrate = round(0.00, 2)
try:
data.center_board_hashrate = round(
float(boards[1].get(f"chain_rate{board_offset+1}")) / 1000,
2,
)
except ValueError as e:
data.center_board_hashrate = round(0.00, 2)
try:
data.right_board_hashrate = round(
float(boards[1].get(f"chain_rate{board_offset+2}")) / 1000,
2,
)
except ValueError as e:
data.right_board_hashrate = round(0.00, 2)
if stats:
temp = stats.get("STATS")

View File

@@ -15,16 +15,16 @@
import ipaddress
import logging
import json
from typing import Union
from typing import Union, List
import toml
from pyasic.miners.base import BaseMiner
from pyasic.API.bosminer import BOSMinerAPI
from pyasic.API import APIError
from pyasic.errors import APIError
from pyasic.data.error_codes import BraiinsOSError
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.data import MinerData
from pyasic.config import MinerConfig
@@ -103,6 +103,20 @@ class BOSMiner(BaseMiner):
return True
return False
async def stop_mining(self) -> bool:
data = await self.api.pause()
if data.get("PAUSE"):
if data["PAUSE"][0]:
return True
return False
async def resume_mining(self) -> bool:
data = await self.api.resume()
if data.get("RESUME"):
if data["RESUME"][0]:
return True
return False
async def reboot(self) -> bool:
"""Reboots power to the physical miner."""
logging.debug(f"{self}: Sending reboot command.")
@@ -242,7 +256,7 @@ class BOSMiner(BaseMiner):
self.light = True
return self.light
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
tunerstatus = None
errors = []
@@ -273,7 +287,7 @@ class BOSMiner(BaseMiner):
"Stable",
"Testing performance profile",
]:
_error = board["Status"]
_error = board["Status"].split(" {")[0]
_error = _error[0].lower() + _error[1:]
errors.append(
BraiinsOSError(f"{board_map[_id]} {_error}")
@@ -437,7 +451,7 @@ class BOSMiner(BaseMiner):
"Stable",
"Testing performance profile",
]:
_error = board["Status"]
_error = board["Status"].split(" {")[0]
_error = _error[0].lower() + _error[1:]
data.errors.append(
BraiinsOSError(f"{board_map[_id]} {_error}")

View File

@@ -15,11 +15,13 @@
import logging
import ipaddress
from typing import Union
from typing import Union, List
from pyasic.API.bosminer import BOSMinerAPI
from pyasic.miners.base import BaseMiner
from pyasic.config import MinerConfig
from pyasic.data import MinerData
from pyasic.data.error_codes import MinerErrorData
class BOSMinerOld(BaseMiner):
@@ -76,7 +78,7 @@ class BOSMinerOld(BaseMiner):
async def get_config(self) -> None:
return None
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
return []
async def get_hostname(self) -> str:
@@ -94,5 +96,14 @@ class BOSMinerOld(BaseMiner):
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 get_data(self) -> MinerData:
return MinerData(ip=str(self.ip))

View File

@@ -14,15 +14,15 @@
import ipaddress
import logging
from typing import Union
from typing import Union, List
from pyasic.API.btminer import BTMinerAPI
from pyasic.miners.base import BaseMiner
from pyasic.API import APIError
from pyasic.errors import APIError
from pyasic.data import MinerData
from pyasic.data.error_codes import WhatsminerError
from pyasic.data.error_codes import WhatsminerError, MinerErrorData
from pyasic.config import MinerConfig
from pyasic.settings import PyasicSettings
@@ -156,13 +156,44 @@ class BTMiner(BaseMiner):
return True
return False
async def get_errors(self) -> list:
return []
async def get_errors(self) -> List[MinerErrorData]:
data = []
summary_data = await self.api.summary()
if summary_data[0].get("Error Code Count"):
for i in range(summary_data[0]["Error Code Count"]):
if summary_data[0].get(f"Error Code {i}"):
data.append(
WhatsminerError(error_code=summary_data[0][f"Error Code {i}"])
)
return data
async def reboot(self) -> bool:
data = await self.api.reboot()
if data.get("Msg"):
if data["Msg"] == "API command OK":
return True
return False
async def restart_backend(self) -> bool:
data = await self.api.restart()
if data.get("Msg"):
if data["Msg"] == "API command OK":
return True
return False
async def stop_mining(self) -> bool:
data = await self.api.power_off(respbefore=True)
if data.get("Msg"):
if data["Msg"] == "API command OK":
return True
return False
async def resume_mining(self) -> bool:
data = await self.api.power_on()
if data.get("Msg"):
if data["Msg"] == "API command OK":
return True
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:

View File

@@ -14,15 +14,16 @@
import ipaddress
import logging
from typing import Union
from typing import Union, List
from pyasic.API.cgminer import CGMinerAPI
from pyasic.miners.base import BaseMiner
from pyasic.API import APIError
from pyasic.errors import APIError
from pyasic.config import MinerConfig
from pyasic.data import MinerData
from pyasic.data.error_codes import MinerErrorData
from pyasic.settings import PyasicSettings
@@ -118,8 +119,7 @@ class CGMiner(BaseMiner):
return True
return False
async def start_cgminer(self) -> None:
"""Start cgminer hashing process."""
async def resume_mining(self) -> bool:
commands = [
"mkdir -p /etc/tmp/",
'echo "*/3 * * * * /usr/bin/cgminer-monitor" > /etc/tmp/root',
@@ -128,9 +128,9 @@ class CGMiner(BaseMiner):
]
commands = ";".join(commands)
await self.send_ssh_command(commands)
return True
async def stop_cgminer(self) -> None:
"""Restart cgminer hashing process."""
async def stop_mining(self) -> bool:
commands = [
"mkdir -p /etc/tmp/",
'echo "" > /etc/tmp/root',
@@ -139,6 +139,7 @@ class CGMiner(BaseMiner):
]
commands = ";".join(commands)
await self.send_ssh_command(commands)
return True
async def get_config(self) -> str:
"""Gets the config for the miner and sets it as `self.config`.
@@ -163,7 +164,7 @@ class CGMiner(BaseMiner):
async def fault_light_on(self) -> bool:
return False
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
return []
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:

View File

@@ -0,0 +1,24 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pyasic.miners.base import BaseMiner
class S19XP(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "S19 XP"
self.nominal_chips = 110
self.fan_count = 4

View File

@@ -18,6 +18,8 @@ from .S19_Pro import S19Pro
from .S19j import S19j
from .S19j_Pro import S19jPro
from .S19_XP import S19XP
from .S19a import S19a
from .T19 import T19

View File

@@ -31,3 +31,48 @@ class M31SPlusVE20(BaseMiner):
self.model = "M31S+ VE20"
self.nominal_chips = 78
self.fan_count = 2
class M31SPlusV30(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M31S+ V30"
self.nominal_chips = 117
self.fan_count = 2
class M31SPlusV40(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M31S+ V40"
self.nominal_chips = 123
self.fan_count = 2
class M31SPlusV60(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M31S+ V60"
self.nominal_chips = 156
self.fan_count = 2
class M31SPlusV80(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M31S+ V80"
self.nominal_chips = 129
self.fan_count = 2
class M31SPlusV90(BaseMiner):
def __init__(self, ip: str):
super().__init__()
self.ip = ip
self.model = "M31S+ V90"
self.nominal_chips = 117
self.fan_count = 2

View File

@@ -22,7 +22,15 @@ from .M30S_Plus_Plus import (
)
from .M31S import M31S
from .M31S_Plus import M31SPlus, M31SPlusVE20
from .M31S_Plus import (
M31SPlus,
M31SPlusVE20,
M31SPlusV30,
M31SPlusV40,
M31SPlusV80,
M31SPlusV60,
M31SPlusV90,
)
from .M32 import M32, M32V20
from .M32S import M32S

View File

@@ -0,0 +1,22 @@
# 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 .X19 import BMMinerX19
from pyasic.miners._types import S19XP # noqa - Ignore access to _module
class BMMinerS19XP(BMMinerX19, S19XP):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip

View File

@@ -15,7 +15,7 @@
from pyasic.miners._backends import BMMiner # noqa - Ignore access to _module
from pyasic.config import MinerConfig
from pyasic.data.error_codes import X19Error
from pyasic.data.error_codes import X19Error, MinerErrorData
from pyasic.settings import PyasicSettings
import httpx
@@ -132,7 +132,7 @@ class BMMinerX19(BMMiner):
return True
return False
async def get_errors(self) -> List[X19Error]:
async def get_errors(self) -> List[MinerErrorData]:
errors = []
url = f"http://{self.ip}/cgi-bin/summary.cgi"
auth = httpx.DigestAuth(self.uname, self.pwd)
@@ -146,3 +146,15 @@ class BMMinerX19(BMMiner):
if not item["status"] == "s":
errors.append(X19Error(item["msg"]))
return errors
async def stop_mining(self) -> bool:
cfg = await self.get_config()
cfg.autotuning_wattage = 0
await self.send_config(cfg)
return True
async def resume_mining(self) -> bool:
cfg = await self.get_config()
cfg.autotuning_wattage = 1
await self.send_config(cfg)
return True

View File

@@ -18,6 +18,8 @@ from .S19_Pro import BMMinerS19Pro
from .S19j import BMMinerS19j
from .S19j_Pro import BMMinerS19jPro
from .S19_XP import BMMinerS19XP
from .S19a import BMMinerS19a
from .T19 import BMMinerT19

View File

@@ -13,3 +13,4 @@
# limitations under the License.
from .S9 import CGMinerS9
from .T9 import CGMinerT9

View File

@@ -51,6 +51,12 @@ class CGMinerA10X(CGMiner):
return True
return False
async def stop_mining(self) -> bool:
return False
async def resume_mining(self) -> bool:
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
"""Configures miner with yaml config."""
raise NotImplementedError

View File

@@ -51,6 +51,12 @@ class CGMinerA7X(CGMiner):
return True
return False
async def stop_mining(self) -> bool:
return False
async def resume_mining(self) -> bool:
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
"""Configures miner with yaml config."""
raise NotImplementedError

View File

@@ -51,6 +51,12 @@ class CGMinerA8X(CGMiner):
return True
return False
async def stop_mining(self) -> bool:
return False
async def resume_mining(self) -> bool:
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
"""Configures miner with yaml config."""
raise NotImplementedError

View File

@@ -52,6 +52,12 @@ class CGMinerAvalon921(CGMiner, Avalon921):
return True
return False
async def stop_mining(self) -> bool:
return False
async def resume_mining(self) -> bool:
return False
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
"""Configures miner with yaml config."""
raise NotImplementedError

View File

@@ -16,10 +16,17 @@ import asyncssh
import logging
import ipaddress
from abc import ABC, abstractmethod
from typing import TypeVar
from typing import TypeVar, List, Union
from pyasic.data import MinerData
from pyasic.config import MinerConfig
from pyasic.data.error_codes import (
WhatsminerError,
BraiinsOSError,
InnosiliconError,
X19Error,
MinerErrorData,
)
class BaseMiner(ABC):
@@ -85,58 +92,130 @@ class BaseMiner(ABC):
@abstractmethod
async def fault_light_on(self) -> bool:
"""Turn the fault light of the miner on and return success as a boolean.
Returns:
A boolean value of the success of turning the light on.
"""
pass
@abstractmethod
async def fault_light_off(self) -> bool:
pass
"""Turn the fault light of the miner off and return success as a boolean.
# async def send_file(self, src, dest):
# async with (await self._get_ssh_connection()) as conn:
# await asyncssh.scp(src, (conn, dest))
Returns:
A boolean value of the success of turning the light off.
"""
pass
@abstractmethod
async def check_light(self) -> bool:
pass
"""Check the status and return on or off as a boolean.
# @abstractmethod
async def get_board_info(self):
return None
Returns:
A boolean value where `True` represents on and `False` represents off.
"""
pass
@abstractmethod
async def get_config(self) -> MinerConfig:
"""Get the mining configuration of the miner and return it as a [`MinerConfig`][pyasic.config.MinerConfig].
Returns:
A [`MinerConfig`][pyasic.config.MinerConfig] containing the pool information and mining configuration.
"""
pass
@abstractmethod
async def get_hostname(self) -> str:
"""Get the hostname of the miner and return it as a string.
Returns:
A string representing the hostname of the miner.
"""
pass
@abstractmethod
async def get_model(self) -> str:
"""Get the model of the miner and return it as a string.
Returns:
A string representing the model of the miner.
"""
pass
@abstractmethod
async def reboot(self) -> bool:
"""Reboot the miner and return success as a boolean.
Returns:
A boolean value of the success of rebooting the miner.
"""
pass
@abstractmethod
async def restart_backend(self) -> bool:
"""Restart the mining process of the miner (bosminer, bmminer, cgminer, etc) and return success as a boolean.
Returns:
A boolean value of the success of restarting the mining process.
"""
pass
@abstractmethod
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
"""Set the mining configuration of the miner.
Parameters:
config: A [`MinerConfig`][pyasic.config.MinerConfig] containing the mining config you want to switch the miner to.
user_suffix: A suffix to append to the username when sending to the miner.
"""
return None
@abstractmethod
async def get_mac(self) -> str:
"""Get the MAC address of the miner and return it as a string.
Returns:
A string representing the MAC address of the miner.
"""
pass
@abstractmethod
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
"""Get a list of the errors the miner is experiencing.
Returns:
A list of error classes representing different errors.
"""
pass
@abstractmethod
async def get_data(self) -> MinerData:
"""Get data from the miner in the form of [`MinerData`][pyasic.data.MinerData].
Returns:
A [`MinerData`][pyasic.data.MinerData] instance containing data from the miner.
"""
return MinerData(ip=str(self.ip))
@abstractmethod
async def stop_mining(self) -> bool:
"""Stop the mining process of the miner.
Returns:
A boolean value of the success of stopping the mining process.
"""
pass
@abstractmethod
async def resume_mining(self) -> bool:
"""Stop the mining process of the miner.
Returns:
A boolean value of the success of resuming the mining process.
"""
pass
AnyMiner = TypeVar("AnyMiner", bound=BaseMiner)

View File

@@ -15,14 +15,14 @@
from pyasic.miners._backends import CGMiner # noqa - Ignore access to _module
from pyasic.miners._types import InnosiliconT3HPlus # noqa - Ignore access to _module
from pyasic.data import MinerData
from pyasic.data.error_codes import InnosiliconError
from pyasic.data.error_codes import InnosiliconError, MinerErrorData
from pyasic.settings import PyasicSettings
from pyasic.config import MinerConfig
from pyasic.API import APIError
from pyasic.errors import APIError
import httpx
import warnings
from typing import Union
from typing import Union, List
import logging
@@ -31,7 +31,7 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
super().__init__(ip)
self.ip = ip
self.uname = "admin"
self.pwd = "admin"
self.pwd = PyasicSettings().global_innosilicon_password
self.jwt = None
async def auth(self):
@@ -142,7 +142,7 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
"updatePools", data=config.as_inno(user_suffix=user_suffix)
)
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
errors = []
try:
data = await self.send_web_command("getErrorDetail")
@@ -158,11 +158,6 @@ class CGMinerInnosiliconT3HPlus(CGMiner, InnosiliconT3HPlus):
return errors
async def get_data(self) -> MinerData:
"""Get data from the miner.
Returns:
A [`MinerData`][pyasic.data.MinerData] instance containing the miners data.
"""
data = MinerData(ip=str(self.ip), ideal_chips=self.nominal_chips * 3)
board_offset = -1

View File

@@ -32,7 +32,9 @@ from pyasic.miners._backends.bosminer_old import ( # noqa - Ignore _module impo
from pyasic.miners.unknown import UnknownMiner
from pyasic.API import APIError
from pyasic.errors import APIError
from pyasic.misc import Singleton
import asyncio
import ipaddress
@@ -59,6 +61,7 @@ MINER_CLASSES = {
"Default": BMMinerT9,
"BMMiner": BMMinerT9,
"Hiveon": HiveonT9,
"CGMiner": CGMinerT9,
},
"ANTMINER S17": {
"Default": BMMinerS17,
@@ -126,6 +129,10 @@ MINER_CLASSES = {
"BMMiner": BMMinerS19jPro,
"CGMiner": CGMinerS19jPro,
},
"ANTMINER S19 XP": {
"Default": BMMinerS19XP,
"BMMiner": BMMinerS19XP,
},
"ANTMINER S19A": {
"Default": BMMinerS19a,
"BMMiner": BMMinerS19a,
@@ -194,6 +201,11 @@ MINER_CLASSES = {
"Default": BTMinerM31SPlus,
"BTMiner": BTMinerM31SPlus,
"E20": BTMinerM31SPlusVE20,
"30": BTMinerM31SPlusV30,
"40": BTMinerM31SPlusV40,
"60": BTMinerM31SPlusV60,
"80": BTMinerM31SPlusV80,
"90": BTMinerM31SPlusV90,
},
"M32S": {
"Default": BTMinerM32S,
@@ -252,15 +264,6 @@ MINER_CLASSES = {
}
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class MinerFactory(metaclass=Singleton):
"""A factory to handle identification and selection of the proper class of miner"""
@@ -637,9 +640,7 @@ class MinerFactory(metaclass=Singleton):
else:
# make sure the command succeeded
if data["STATUS"][0]["STATUS"] not in ("S", "I"):
# this is an error
if data["STATUS"][0]["STATUS"] not in ("S", "I"):
return False, data["STATUS"][0]["Msg"]
return False, data["STATUS"][0]["Msg"]
return True, None
@staticmethod

View File

@@ -14,14 +14,7 @@
import asyncio
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
from pyasic.misc import Singleton
class _MinerListener:

View File

@@ -12,9 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import List
from pyasic.API.unknown import UnknownAPI
from pyasic.miners.base import BaseMiner
from pyasic.config import MinerConfig
from pyasic.data import MinerData
from pyasic.data.error_codes import MinerErrorData
class UnknownMiner(BaseMiner):
@@ -47,7 +51,7 @@ class UnknownMiner(BaseMiner):
async def get_config(self) -> None:
return None
async def get_errors(self) -> list:
async def get_errors(self) -> List[MinerErrorData]:
return []
async def get_mac(self) -> str:
@@ -59,5 +63,14 @@ class UnknownMiner(BaseMiner):
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 get_data(self) -> MinerData:
return MinerData(ip=str(self.ip))

View File

@@ -16,6 +16,11 @@ from pyasic.miners._backends import BTMiner # noqa - Ignore access to _module
from pyasic.miners._types import (
M31SPlus,
M31SPlusVE20,
M31SPlusV30,
M31SPlusV40,
M31SPlusV60,
M31SPlusV80,
M31SPlusV90,
) # noqa - Ignore access to _module
@@ -29,3 +34,33 @@ class BTMinerM31SPlusVE20(BTMiner, M31SPlusVE20):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip
class BTMinerM31SPlusV30(BTMiner, M31SPlusV30):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip
class BTMinerM31SPlusV40(BTMiner, M31SPlusV40):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip
class BTMinerM31SPlusV60(BTMiner, M31SPlusV60):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip
class BTMinerM31SPlusV80(BTMiner, M31SPlusV80):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip
class BTMinerM31SPlusV90(BTMiner, M31SPlusV90):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.ip = ip

View File

@@ -33,7 +33,15 @@ from .M30S_Plus_Plus import (
)
from .M31S import BTMinerM31S
from .M31S_Plus import BTMinerM31SPlus, BTMinerM31SPlusVE20
from .M31S_Plus import (
BTMinerM31SPlus,
BTMinerM31SPlusVE20,
BTMinerM31SPlusV30,
BTMinerM31SPlusV40,
BTMinerM31SPlusV60,
BTMinerM31SPlusV80,
BTMinerM31SPlusV90,
)
from .M32 import BTMinerM32, BTMinerM32V20
from .M32S import BTMinerM32S

22
pyasic/misc/__init__.py Normal file
View File

@@ -0,0 +1,22 @@
# 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.
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]

View File

@@ -37,7 +37,9 @@ class MinerNetwork:
"""
def __init__(
self, ip_addr: Union[str, None] = None, mask: Union[str, int, None] = None
self,
ip_addr: Union[str, List[str], None] = None,
mask: Union[str, int, None] = None,
) -> None:
self.network = None
self.ip_addr = ip_addr
@@ -46,6 +48,7 @@ class MinerNetwork:
if mask.startswith("/"):
mask = mask.replace("/", "")
self.mask = mask
self.network = self.get_network()
def __len__(self):
return len([item for item in self.get_network().hosts()])
@@ -53,6 +56,10 @@ class MinerNetwork:
def __repr__(self):
return str(self.network)
def hosts(self):
for x in self.network.hosts():
yield x
def get_network(self) -> ipaddress.ip_network:
"""Get the network using the information passed to the MinerNetwork or from cache.
@@ -63,18 +70,14 @@ class MinerNetwork:
if self.network:
return self.network
# if there is no IP address passed, default to 192.168.1.0
if not self.ip_addr:
self.ip_addr = "192.168.1.0"
if "-" in self.ip_addr:
self.network = MinerNetworkRange(self.ip_addr)
elif isinstance(self.ip_addr, list):
self.network = MinerNetworkRange(self.ip_addr)
else:
# if there is no IP address passed, default to 192.168.1.0
if not self.ip_addr:
default_gateway = "192.168.1.0"
# if we do have an IP address passed, use that
else:
default_gateway = self.ip_addr
# if there is no subnet mask passed, default to /24
if not self.mask:
subnet_mask = "24"
@@ -84,7 +87,7 @@ class MinerNetwork:
# save the network and return it
self.network = ipaddress.ip_network(
f"{default_gateway}/{subnet_mask}", strict=False
f"{self.ip_addr}/{subnet_mask}", strict=False
)
logging.debug(f"Setting MinerNetwork: {self.network}")

View File

@@ -14,14 +14,7 @@
from dataclasses import dataclass
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
from pyasic.misc import Singleton
@dataclass
@@ -35,6 +28,7 @@ class PyasicSettings(metaclass=Singleton):
miner_get_data_retries: int = 1
global_whatsminer_password = "admin"
global_innosilicon_password = "admin"
global_x19_password = "root"
global_x17_password = "root"

View File

@@ -1,54 +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 unittest
from pyasic.miners.miner_factory import MINER_CLASSES
import inspect
import sys
class MinersTest(unittest.TestCase):
def test_miner_model_creation(self):
for miner_model in MINER_CLASSES.keys():
for miner_api in MINER_CLASSES[miner_model].keys():
with self.subTest(miner_model=miner_model, miner_api=miner_api):
miner = MINER_CLASSES[miner_model][miner_api]("0.0.0.0")
self.assertTrue(
isinstance(miner, MINER_CLASSES[miner_model][miner_api])
)
def test_miner_backend_backup_creation(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._backends"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
miner = miner_class("0.0.0.0")
self.assertTrue(isinstance(miner, miner_class))
def test_miner_type_creation_failure(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._types"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
with self.assertRaises(TypeError):
miner_class("0.0.0.0")
if __name__ == "__main__":
unittest.main()

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.17.2"
version = "0.18.2"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"
@@ -15,7 +15,19 @@ passlib = "^1.7.4"
pyaml = "^21.10.1"
toml = "^0.10.2"
[tool.poetry.dev-dependencies]
[tool.poetry.group.dev]
optional = true
[tool.poetry.group.dev.dependencies]
pre-commit = "^2.20.0"
[tool.poetry.group.docs]
optional = true
[tool.poetry.group.docs.dependencies]
mkdocs = "^1.4.0"
mkdocstrings = {extras = ["python"], version = "^0.19.0"}
[build-system]
requires = ["poetry-core>=1.0.0"]

View File

@@ -1,2 +0,0 @@
-r requirements.txt
pre-commit

Binary file not shown.

View File

@@ -13,8 +13,10 @@
# limitations under the License.
import unittest
from pyasic.tests.miners_tests import MinersTest
from pyasic.tests.network_tests import NetworkTest
from tests.miners_tests import MinersTest, MinerFactoryTest
from tests.network_tests import NetworkTest
from tests.config_tests import ConfigTest
if __name__ == "__main__":
# `coverage run --source pyasic -m unittest discover` will give code coverage data
unittest.main()

View File

@@ -0,0 +1,111 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
from pyasic.config import MinerConfig, _PoolGroup, _Pool # noqa
from tests.test_data import (
bosminer_api_pools,
x19_api_pools,
x19_web_pools,
bosminer_config_pools,
)
class ConfigTest(unittest.TestCase):
def setUp(self) -> None:
self.test_config = MinerConfig(
pool_groups=[
_PoolGroup(
quota=1,
group_name="TEST",
pools=[
_Pool(
url="stratum+tcp://pyasic.testpool_1.pool:3333",
username="pyasic.test",
password="123",
),
_Pool(
url="stratum+tcp://pyasic.testpool_2.pool:3333",
username="pyasic.test",
password="123",
),
],
)
],
temp_mode="auto",
temp_target=70.0,
temp_hot=80.0,
temp_dangerous=100.0,
fan_speed=None,
autotuning_enabled=True,
autotuning_wattage=900,
)
def test_config_from_raw(self):
bos_config = MinerConfig().from_raw(bosminer_config_pools)
bos_config.pool_groups[0].group_name = "TEST"
with self.subTest(
msg="Testing BOSMiner config file config.", bos_config=bos_config
):
self.assertEqual(bos_config, self.test_config)
x19_cfg = MinerConfig().from_raw(x19_web_pools)
x19_cfg.pool_groups[0].group_name = "TEST"
with self.subTest(msg="Testing X19 API config.", x19_cfg=x19_cfg):
self.assertEqual(x19_cfg, self.test_config)
def test_config_from_api(self):
bos_cfg = MinerConfig().from_api(bosminer_api_pools["POOLS"])
bos_cfg.pool_groups[0].group_name = "TEST"
with self.subTest(msg="Testing BOSMiner API config.", bos_cfg=bos_cfg):
self.assertEqual(bos_cfg, self.test_config)
x19_cfg = MinerConfig().from_api(x19_api_pools["POOLS"])
x19_cfg.pool_groups[0].group_name = "TEST"
with self.subTest(msg="Testing X19 API config.", x19_cfg=x19_cfg):
self.assertEqual(x19_cfg, self.test_config)
def test_config_as_types(self):
cfg = MinerConfig().from_api(bosminer_api_pools["POOLS"])
cfg.pool_groups[0].group_name = "TEST"
commands = [
func
for func in
# each function in self
dir(cfg)
if callable(getattr(cfg, func)) and
# no __ methods
not func.startswith("__")
]
for command in [cmd for cmd in commands if cmd.startswith("as_")]:
with self.subTest():
output = getattr(cfg, command)()
self.assertEqual(output, getattr(self.test_config, command)())
if f"from_{command.split('as_')[1]}" in commands:
self.assertEqual(
getattr(MinerConfig(), f"from_{command.split('as_')[1]}")(
output
),
self.test_config,
)
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,162 @@
# Copyright 2022 Upstream Data Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
from pyasic.miners.miner_factory import MINER_CLASSES
from pyasic.miners.base import BaseMiner
from pyasic.miners._backends import CGMiner
from pyasic.miners.miner_factory import MinerFactory
from pyasic.miners.miner_listener import MinerListener
import asyncio
import inspect
import sys
class MinersTest(unittest.TestCase):
def test_miner_model_creation(self):
for miner_model in MINER_CLASSES.keys():
for miner_api in MINER_CLASSES[miner_model].keys():
with self.subTest(
msg=f"Creation of miner using model={miner_model}, api={miner_api}",
miner_model=miner_model,
miner_api=miner_api,
):
miner = MINER_CLASSES[miner_model][miner_api]("0.0.0.0")
self.assertTrue(
isinstance(miner, MINER_CLASSES[miner_model][miner_api])
)
def test_miner_backend_backup_creation(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._backends"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
miner = miner_class("0.0.0.0")
self.assertTrue(isinstance(miner, miner_class))
def test_miner_type_creation_failure(self):
backends = inspect.getmembers(
sys.modules["pyasic.miners._types"], inspect.isclass
)
for backend in backends:
miner_class = backend[1]
with self.subTest(miner_class=miner_class):
with self.assertRaises(TypeError):
miner_class("0.0.0.0")
with self.assertRaises(TypeError):
BaseMiner("0.0.0.0")
def test_miner_comparisons(self):
miner_1 = CGMiner("1.1.1.1")
miner_2 = CGMiner("2.2.2.2")
miner_3 = CGMiner("1.1.1.1")
self.assertEqual(miner_1, miner_3)
self.assertGreater(miner_2, miner_1)
self.assertLess(miner_3, miner_2)
class MinerFactoryTest(unittest.TestCase):
def test_miner_factory_creation(self):
self.assertDictEqual(MinerFactory().miners, {})
miner_factory = MinerFactory()
self.assertIs(MinerFactory(), miner_factory)
def test_get_miner_generator(self):
async def _coro():
gen = MinerFactory().get_miner_generator([])
miners = []
async for miner in gen:
miners.append(miner)
return miners
_miners = asyncio.run(_coro())
self.assertListEqual(_miners, [])
def test_miner_selection(self):
for miner_model in MINER_CLASSES.keys():
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", miner_model, None, None
)
self.assertIsInstance(miner, BaseMiner)
for api in ["BOSMiner+", "BOSMiner", "CGMiner", "BTMiner", "BMMiner"]:
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", None, api, None
)
self.assertIsInstance(miner, BaseMiner)
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", "ANTMINER S17+", "Fake API", None
)
self.assertIsInstance(miner, BaseMiner)
with self.subTest():
miner = MinerFactory()._select_miner_from_classes(
"0.0.0.0", "M30S", "BTMiner", "G20"
)
self.assertIsInstance(miner, BaseMiner)
def test_validate_command(self):
bad_test_data_returns = [
{},
{
"cmd": [
{
"STATUS": [
{"STATUS": "E", "Msg": "Command failed for some reason."}
]
}
]
},
{"STATUS": "E", "Msg": "Command failed for some reason."},
{
"STATUS": [{"STATUS": "E", "Msg": "Command failed for some reason."}],
"id": 1,
},
]
for data in bad_test_data_returns:
with self.subTest():
async def _coro(miner_ret):
_data = await MinerFactory()._validate_command(miner_ret)
return _data
ret = asyncio.run(_coro(data))
self.assertFalse(ret[0])
good_test_data_returns = [
{
"STATUS": [{"STATUS": "S", "Msg": "Yay! Command succeeded."}],
"id": 1,
},
]
for data in good_test_data_returns:
with self.subTest():
async def _coro(miner_ret):
_data = await MinerFactory()._validate_command(miner_ret)
return _data
ret = asyncio.run(_coro(data))
self.assertTrue(ret[0])
if __name__ == "__main__":
unittest.main()

View File

@@ -31,8 +31,8 @@ class NetworkTest(unittest.TestCase):
"192.168.1.60",
]
net_1 = list(MinerNetworkRange(net_range_str).hosts())
net_2 = list(MinerNetworkRange(net_range_list).hosts())
net_1 = list(MinerNetwork(net_range_str).get_network().hosts())
net_2 = list(MinerNetwork(net_range_list).get_network().hosts())
correct_net = [
ipaddress.IPv4Address("192.168.1.29"),
@@ -48,7 +48,7 @@ class NetworkTest(unittest.TestCase):
def test_net(self):
net_1_str = "192.168.1.0"
net_1_mask = 29
net_1_mask = "/29"
net_1 = list(MinerNetwork(net_1_str, mask=net_1_mask).get_network().hosts())
@@ -68,6 +68,20 @@ class NetworkTest(unittest.TestCase):
self.assertTrue(net_1 == correct_net)
self.assertTrue(net_2 == correct_net)
def test_net_len(self):
net = MinerNetwork("192.168.1.0", mask=32)
self.assertEqual(len(net), 1)
net2 = MinerNetwork("192.168.1.0", mask=31)
self.assertEqual(len(net2), 2)
def test_net_defaults(self):
net = MinerNetwork()
net_obj = net.get_network()
self.assertEqual(net_obj, MinerNetwork("192.168.1.0", mask=24).get_network())
self.assertEqual(net_obj, net.get_network())
if __name__ == "__main__":
unittest.main()

128
tests/test_data.py Normal file
View File

@@ -0,0 +1,128 @@
# 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.
bosminer_api_pools = {
"STATUS": [
{
"STATUS": "S",
"Msg": "2 Pool(s)",
"Description": "",
}
],
"POOLS": [
{
"POOL": 0,
"URL": "stratum+tcp://pyasic.testpool_1.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_1.pool:3333",
"AsicBoost": True,
},
{
"POOL": 1,
"URL": "stratum+tcp://pyasic.testpool_2.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_2.pool:3333",
"AsicBoost": True,
},
],
"id": 1,
}
x19_api_pools = {
"STATUS": [
{
"STATUS": "S",
"Msg": "2 Pool(s)",
"Description": "",
}
],
"POOLS": [
{
"POOL": 0,
"URL": "stratum+tcp://pyasic.testpool_1.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_1.pool:3333",
},
{
"POOL": 1,
"URL": "stratum+tcp://pyasic.testpool_2.pool:3333",
"Status": "Alive",
"Quota": 1,
"User": "pyasic.test",
"Stratum URL": "pyasic.testpool_2.pool:3333",
},
],
"id": 1,
}
x19_web_pools = {
"pools": [
{
"url": "stratum+tcp://pyasic.testpool_1.pool:3333",
"user": "pyasic.test",
"pass": "123",
},
{
"url": "stratum+tcp://pyasic.testpool_2.pool:3333",
"user": "pyasic.test",
"pass": "123",
},
],
"api-listen": True,
"api-network": True,
"api-groups": "A:stats:pools:devs:summary:version",
"api-allow": "A:0/0,W:*",
"bitmain-fan-ctrl": False,
"bitmain-fan-pwm": "100",
"bitmain-use-vil": True,
"bitmain-freq": "675",
"bitmain-voltage": "1400",
"bitmain-ccdelay": "0",
"bitmain-pwth": "0",
"bitmain-work-mode": "0",
"bitmain-freq-level": "100",
}
bosminer_config_pools = {
"format": {
"version": "1.2+",
"model": "Antminer S9",
"generator": "pyasic",
},
"group": [
{
"name": "TEST",
"quota": 1,
"pool": [
{
"enabled": True,
"url": "stratum+tcp://pyasic.testpool_1.pool:3333",
"user": "pyasic.test",
"password": "123",
},
{
"enabled": True,
"url": "stratum+tcp://pyasic.testpool_2.pool:3333",
"user": "pyasic.test",
"password": "123",
},
],
},
],
}