Compare commits

...

115 Commits

Author SHA1 Message Date
Upstream Data
d0aeb5a6ce version: bump version number. 2024-08-07 11:40:46 -06:00
Brett Rowan
030f8c6079 Merge pull request #184 from jameshilliard/update-deps
Update dependencies and make versions less strict
2024-08-07 11:21:58 -06:00
James Hilliard
7195e204ce Update dependencies and make versions less strict 2024-08-07 11:18:45 -06:00
Upstream Data
1cec2ca7f3 version: bump version number. 2024-07-30 15:32:20 -06:00
Brett Rowan
a3c4187411 Merge pull request #162 from Ytemiloluwa/BOser
feat: Add _get_pools method to BOSer backend class.
2024-07-30 15:32:02 -06:00
ytemiloluwa
18a2df5d9b replaced getattr with getitem 2024-07-30 22:20:13 +01:00
Upstream Data
6d66c793cb version: bump version number. 2024-07-30 14:14:36 -06:00
Upstream Data
b434c8df1a feature: add support for antminer S19 Pro Plus Hydro. 2024-07-30 13:27:06 -06:00
Upstream Data
2b8fa2fc2b bug: Handle default case with boser power conf. 2024-07-30 13:22:47 -06:00
Upstream Data
1497d2abea bug: add user suffix to boser send_config. 2024-07-30 13:10:12 -06:00
Upstream Data
a2ca79843d feature: implement send_config method for boser. 2024-07-30 13:09:16 -06:00
Upstream Data
f6500e7d66 version: bump version number. 2024-07-29 09:53:52 -06:00
Upstream Data
ea2fd0fc9a feature: add braiinsOS+ pool configuration. 2024-07-29 09:53:02 -06:00
Upstream Data
e2cbd30a99 feature: update braiins proto files. 2024-07-29 09:38:57 -06:00
Upstream Data
151ea44b10 docs: fix settings documentation. 2024-07-29 09:11:15 -06:00
Brett Rowan
6487a0b08e Merge pull request #179 from tydal-borge/patch-1
documentation: Changed settings key in readme to reflect default values
2024-07-29 09:08:39 -06:00
Brett Rowan
552fdf9ec0 Merge pull request #178 from tydal-borge/dev_antminer_customauth
bug: Add custom auth to Digest of AntMiner model check
2024-07-29 09:08:17 -06:00
Børge Holm-Wennberg
00cf1449f9 docs for settings mismatches 2024-07-29 10:45:22 +02:00
Børge Holm-Wennberg
8ec88e385a Remove leftover print of config 2024-07-29 10:41:20 +02:00
tydal-borge
cc29b2960a Changed settings key to reflect default values
Also see issue #106  for details
2024-07-29 10:28:39 +02:00
Børge Holm-Wennberg
568ffd67c4 Add custom auth to Digest of AntMiner model check 2024-07-29 09:47:19 +02:00
Brett Rowan
1d2dc3fddf Merge pull request #175 from Ytemiloluwa/Innosilicon 2024-07-26 07:18:32 -06:00
ytemiloluwa
c44150fd15 APICommand: web to rpc 2024-07-26 10:22:28 +01:00
ytemiloluwa
34eec3ff2e backends: add _get_pools to Innosilicon 2024-07-25 13:11:28 +01:00
Brett Rowan
e1416b5a4b Merge pull request #173
feature: add support for Vnish S19 Pro Hydro.
2024-07-12 09:59:57 -06:00
Brett Rowan
3ca75729b9 feature: add support for Vnish S19 Pro Hydro. 2024-07-12 09:59:03 -06:00
Brett Rowan
73031eea65 Merge pull request #163 from 1e9abhi1e10/update_firmware_4 2024-07-11 09:59:15 -06:00
Brett Rowan
1643c5b7ee Merge pull request #172 from UpstreamData/snyk-fix-645b55b0f4651537ebb3c1b9e8d9b811 2024-07-10 09:08:19 -06:00
snyk-bot
ca5db726bd fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-ZIPP-7430899
2024-07-10 07:56:03 +00:00
Brett Rowan
4bb4d32b48 Merge pull request #171 from Ytemiloluwa/LuxMiner 2024-07-08 09:00:17 -06:00
ytemiloluwa
fec7a89807 backends: add _get_pools method to luxminer 2024-07-08 15:00:57 +01:00
Upstream Data
db2615a4eb version: bump version number. 2024-07-05 08:57:00 -06:00
Upstream Data
eea5d5ba2a bug: fix dataclass ordering issues and bitaxe issues. 2024-07-05 08:56:36 -06:00
Brett Rowan
f405bbff4d Merge pull request #168 from Ytemiloluwa/BOSMinerPoolUrl 2024-07-03 08:34:42 -06:00
ytemiloluwa
dd8d895b50 Backends: updated BOSMiner _get_pools to include PoolUrl dataclass. 2024-07-03 07:30:30 +01:00
Brett Rowan
dff4e98523 Merge pull request #167 from Ytemiloluwa/Antminer 2024-07-02 22:14:10 -06:00
ytemiloluwa
846bbb9033 Backends: updated BMMiner _get_pools to include PoolUrl dataclass. 2024-07-03 04:55:26 +01:00
1e9abhi1e10
e6f9a33b3c Removed trailing whitespace 2024-07-03 08:50:50 +05:30
1e9abhi1e10
092126bded Added checksum validation and command handling 2024-07-03 08:43:22 +05:30
upstreamdata
e5d5cb4dab version: bump version number. 2024-07-02 15:06:03 -06:00
upstreamdata
bd76966d3a docs: update docs nav. 2024-07-02 15:05:45 -06:00
upstreamdata
d46908a298 docs: update docs. 2024-07-02 15:03:26 -06:00
Brett Rowan
e4cec021b0 Merge pull request #164 from Ytemiloluwa/poolURL 2024-07-02 08:46:48 -06:00
upstreamdata
42d2d975db feature: add firmware type to bitaxe. 2024-07-01 23:11:26 -06:00
upstreamdata
427f91d677 bug: use default chip count for bitaxe when asicCount is not set. 2024-07-01 23:08:31 -06:00
upstreamdata
7a9c9237a3 bug: fix bitaxe not identifying on some versions, switch to using ASICModel key. 2024-06-30 22:03:50 -06:00
ytemiloluwa
9b431b020f method: from_str 2024-06-30 18:20:41 +01:00
upstreamdata
ee1eece181 feature: add config sending for bitaxe. 2024-06-29 15:12:33 -06:00
upstreamdata
3627194f34 feature: add config parsing from bitaxe. 2024-06-29 14:59:27 -06:00
ytemiloluwa
65cfb57605 PEP8: case fix 2024-06-29 17:40:15 +01:00
ytemiloluwa
8e4a547c77 dataclass: PoolUrl 2024-06-29 17:40:14 +01:00
ytemiloluwa
a751efe7d5 dataclass: PoolUrl 2024-06-29 17:40:14 +01:00
Brett Rowan
e859895261 Merge pull request #166 from UpstreamData/dev_bitaxe
feature: Add basic bitaxe support.
2024-06-29 08:42:40 -06:00
upstreamdata
fca72eb747 bug: fix some of the issues with bitaxe. 2024-06-28 22:43:13 -06:00
Brett Rowan
923e963369 feature: basic bitaxe support. 2024-06-28 09:09:42 -06:00
Brett Rowan
7a3c9a3460 refactor: fix hashrate return typing. 2024-06-28 08:35:27 -06:00
1e9abhi1e10
bd9592c19c Use web attribute 2024-06-23 08:38:35 +05:30
ytemiloluwa
1bb597999d backends: updated _get_pools arg in BOSer 2024-06-22 19:06:14 +01:00
1e9abhi1e10
7803fa60f2 removed trailing whitespace 2024-06-22 08:12:03 +05:30
1e9abhi1e10
4adb7dc92c feat: Add update firmware for ePIC miner 2024-06-22 08:06:44 +05:30
ytemiloluwa
64265206c2 backends: updated _get_pools in BOSer 2024-06-19 09:25:35 +01:00
ytemiloluwa
eec8f66b81 backends: _get_pools in BOSer 2024-06-18 14:22:30 +01:00
Brett Rowan
30f385c2d9 Merge pull request #158 from 1e9abhi1e10/update_firmware_3
feat: Add update firmware for Whatsminer
2024-06-13 13:10:24 -06:00
1e9abhi1e10
87377fbe4c Use the rpc update_firmware method 2024-06-12 22:23:43 +05:30
1e9abhi1e10
2a66602c2c Implement upgrade_firmware and make necessary changes according to the reviews. 2024-06-12 08:45:46 +05:30
Brett Rowan
8cc18ca272 Merge pull request #161 from jpcomps/add_board_info 2024-06-11 12:15:33 -06:00
John-Paul Compagnone
677db8fd0d add active field 2024-06-11 14:14:22 -04:00
John-Paul Compagnone
a458adc45f change default for tuned 2024-06-11 11:28:28 -04:00
John-Paul Compagnone
5a09ddcb04 remove latency 2024-06-11 11:13:50 -04:00
John-Paul Compagnone
4d9fde572e feat: add voltage,tuned status to HB. Add pool_data to ePIC 2024-06-11 11:10:07 -04:00
Brett Rowan
3e4b347506 Merge pull request #160 from UpstreamData/dev
Improved config handling.
2024-06-11 08:46:45 -06:00
Upstream Data
927bbae0c1 Merge remote-tracking branch 'origin/dev' into dev 2024-06-11 08:41:38 -06:00
Upstream Data
7e3e1f19aa bug: fix bad naming for a scaling parameter. 2024-06-11 08:41:27 -06:00
Brett Rowan
5a4b1b6ee1 Merge pull request #159 from Ytemiloluwa/BOSMiner 2024-06-10 21:04:27 -06:00
ytemiloluwa
25767aab8e pools: async/await 2024-06-11 03:44:52 +01:00
ytemiloluwa
b3a0949395 backends: _get_pools in BOSMiner 2024-06-07 14:57:19 +01:00
1e9abhi1e10
18e6fc2a3c Move upgrade_firmware to pyasic.rpc.btminer.py 2024-06-07 06:08:52 +05:30
1e9abhi1e10
4d45b6e50f feat: Add update firmware for Whatsminer 2024-06-07 03:27:34 +05:30
John-Paul Compagnone
4031a42350 add BoardTune and ManualMode to ePIC 2024-06-06 11:23:46 -04:00
Upstream Data
4698a806f0 feature: refactor scaling into mining mode. 2024-06-05 17:11:52 -06:00
Upstream Data
aec53aa5f0 bug: fix some loggin issues. 2024-06-05 10:27:07 -06:00
Upstream Data
e15ddd020c bug: fix missing import. 2024-06-05 10:26:25 -06:00
Upstream Data
6f4aead0d4 bug: fix dependencies. 2024-06-05 10:25:44 -06:00
Upstream Data
6b3bf31597 docs: fix some documentation. 2024-06-05 08:12:58 -06:00
Brett Rowan
2ac118a008 Merge pull request #147 from 1e9abhi1e10/update_firmware_1 2024-06-04 19:08:08 -06:00
1e9abhi1e10
c87880529c Removed updater directory 2024-06-05 06:35:08 +05:30
Brett Rowan
b12766321d Merge pull request #156 from UpstreamData/dev_dps 2024-06-04 17:31:07 -06:00
Upstream Data
3d6bee2d85 feature: rename config.power_scaling to scaling, and add hashrate scaling for boser (missing from parser). 2024-06-04 15:56:44 -06:00
Upstream Data
c8a8315ad0 version: bump version number. 2024-06-04 09:28:15 -06:00
Upstream Data
dac9bcc3de refactor: improve _get_data implementation slightly. 2024-06-04 08:53:25 -06:00
1e9abhi1e10
7688288d05 Improved file structure and add requested changes 2024-06-03 02:46:34 +05:30
Brett Rowan
46621d6b93 Merge pull request #151 from Ytemiloluwa/master 2024-06-02 13:50:51 -06:00
ytemiloluwa
35700f7e57 pools: MinerData 2024-06-02 20:47:58 +01:00
ytemiloluwa
08e6744595 index: unused variable 2024-06-02 07:26:43 +01:00
ytemiloluwa
2de3e5e328 update: _get_pools method 2024-06-02 07:13:39 +01:00
Temi
51f2eb1b1d Merge branch 'UpstreamData:master' into master 2024-06-02 06:51:28 +01:00
1e9abhi1e10
b4faf7c49e Fix code style issues 2024-05-29 18:02:43 +05:30
1e9abhi1e10
26d9562c18 Add structured directories for firmware update 2024-05-29 17:52:01 +05:30
Brett Rowan
d40d92c1ca version: bump version number. 2024-05-28 21:46:41 -06:00
Brett Rowan
7ea63643a9 bug: fix a bunch of spelling mistakes. 2024-05-28 21:46:13 -06:00
1e9abhi1e10
0bd5c22681 Implement a class to handle firmware management tasks 2024-05-29 08:54:13 +05:30
1e9abhi1e10
8f0cf5b3a3 Made some changes in the code based on reviews 2024-05-29 02:05:58 +05:30
1e9abhi1e10
6458a71b5d Add upgrade_firmware for BOS miner. 2024-05-29 01:20:37 +05:30
1e9abhi1e10
dbdd23e37d Moved update_firmware from base.py to braiins_os.py 2024-05-28 16:12:02 +05:30
upstreamdata
313c324771 bug: fix into naming on vnish with expected hashrate. 2024-05-27 15:57:06 -06:00
upstreamdata
a9fd9343d8 feature: add vnish fault light. 2024-05-26 21:59:59 -06:00
upstreamdata
8f41d4d0bc bug: fix MRO with vnish. 2024-05-26 21:59:57 -06:00
ytemiloluwa
521853863b base: _get_pools and get_pools 2024-05-26 13:21:53 +01:00
ytemiloluwa
b7a5a647b3 pools: DATA LOC 2024-05-26 00:36:07 +01:00
ytemiloluwa
4434f9ccad get_data: kwarg key 2024-05-26 00:22:57 +01:00
ytemiloluwa
82a1cc3cfe backends: _get_pools in antminer 2024-05-25 23:08:33 +01:00
1e9abhi1e10
2d92c2c0e2 Replace aiohttp with httpx and add the next steps for update_firmware 2024-05-26 02:22:21 +05:30
Brett Rowan
6f10c91482 Merge pull request #150 from UpstreamData/snyk-fix-c1d73ce3e71f8a7a86f4d2be00564ee4 2024-05-25 09:00:39 -06:00
snyk-bot
f2d6bce165 fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-JINJA2-6809379
2024-05-25 02:29:08 +00:00
1e9abhi1e10
1ab05c7a5e Add firmare update funtionality 2024-05-20 23:43:39 +05:30
98 changed files with 2905 additions and 1410 deletions

View File

@@ -42,7 +42,7 @@ It is recommended to install `pyasic` in a [virtual environment](https://realpyt
##### Installing `pyasic`
`python -m pip install .` or `poetry install`
`python -m pip install pyasic` or `poetry install`
##### Additional Developer Setup
```
@@ -255,24 +255,30 @@ if __name__ == "__main__":
```python
from pyasic import settings
settings.update("default_antminer_password", "my_pwd")
settings.update("default_antminer_web_password", "my_pwd")
```
##### Default values:
```
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"network_scan_semaphore": None,
"factory_get_retries": 1,
"factory_get_timeout": 3,
"get_data_retries": 1,
"api_function_timeout": 5,
"default_whatsminer_password": "admin",
"default_innosilicon_password": "admin",
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
"default_goldshell_password": "123456789",
"antminer_mining_mode_as_str": False,
"default_whatsminer_rpc_password": "admin",
"default_innosilicon_web_password": "admin",
"default_antminer_web_password": "root",
"default_bosminer_web_password": "root",
"default_vnish_web_password": "admin",
"default_goldshell_web_password": "123456789",
"default_auradine_web_password": "admin",
"default_epic_web_password": "letmein",
"default_hive_web_password": "admin",
"default_antminer_ssh_password": "miner",
"default_bosminer_ssh_password": "root",
# ADVANCED
# Only use this if you know what you are doing

View File

@@ -49,6 +49,8 @@ def backend_str(backend: MinerTypes) -> str:
return "LuxOS Firmware Miners"
case MinerTypes.MARATHON:
return "Mara Firmware Miners"
case MinerTypes.BITAXE:
return "Stock Firmware BitAxe Miners"
def create_url_str(mtype: str):

View File

@@ -41,7 +41,7 @@ It is recommended to install `pyasic` in a [virtual environment](https://realpyt
##### Installing `pyasic`
`python -m pip install .` or `poetry install`
`python -m pip install pyasic` or `poetry install`
---
## Getting started
@@ -249,25 +249,30 @@ if __name__ == "__main__":
```python
from pyasic import settings
settings.update("default_antminer_password", "my_pwd")
settings.update("default_antminer_web_password", "my_pwd")
```
##### Default values:
```
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"network_scan_semaphore": None,
"factory_get_retries": 1,
"factory_get_timeout": 3,
"get_data_retries": 1,
"api_function_timeout": 5,
"antminer_mining_mode_as_str": False,
"default_whatsminer_password": "admin",
"default_innosilicon_password": "admin",
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
"default_goldshell_password": "123456789",
"default_whatsminer_rpc_password": "admin",
"default_innosilicon_web_password": "admin",
"default_antminer_web_password": "root",
"default_bosminer_web_password": "root",
"default_vnish_web_password": "admin",
"default_goldshell_web_password": "123456789",
"default_auradine_web_password": "admin",
"default_epic_web_password": "letmein",
"default_hive_web_password": "admin",
"default_antminer_ssh_password": "miner",
"default_bosminer_ssh_password": "root",
# ADVANCED
# Only use this if you know what you are doing

View File

@@ -1,7 +1,7 @@
# pyasic
## X15 Models
## Z15
## Z15 (Stock)
::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15
handler: python
options:

View File

@@ -1,49 +1,49 @@
# pyasic
## X17 Models
## S17
## S17 (Stock)
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
handler: python
options:
show_root_heading: false
heading_level: 4
## S17+
## S17+ (Stock)
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 4
## S17 Pro
## S17 Pro (Stock)
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro
handler: python
options:
show_root_heading: false
heading_level: 4
## S17e
## S17e (Stock)
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e
handler: python
options:
show_root_heading: false
heading_level: 4
## T17
## T17 (Stock)
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
handler: python
options:
show_root_heading: false
heading_level: 4
## T17+
## T17+ (Stock)
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus
handler: python
options:
show_root_heading: false
heading_level: 4
## T17e
## T17e (Stock)
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e
handler: python
options:

View File

@@ -1,224 +1,231 @@
# pyasic
## X19 Models
## S19
## S19 (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19
handler: python
options:
show_root_heading: false
heading_level: 4
## S19L
## S19L (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro
## S19 Pro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Pro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j
## S19j (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j
handler: python
options:
show_root_heading: false
heading_level: 4
## S19i
## S19i (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python
options:
show_root_heading: false
heading_level: 4
## S19+
## S19+ (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j No PIC
## S19j No PIC (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro+
## S19 Pro+ (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j Pro
## S19j Pro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 XP
## S19 XP (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 4
## S19a
## S19a (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 4
## S19a Pro
## S19a Pro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Hydro
## S19 Hydro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro Hydro
## S19 Pro Hydro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProHydro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro+ Hydro
## S19 Pro+ Hydro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlusHydro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19K Pro
## S19K Pro (Stock)
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro
handler: python
options:
show_root_heading: false
heading_level: 4
## T19
## T19 (Stock)
::: pyasic.miners.antminer.bmminer.X19.T19.BMMinerT19
handler: python
options:
show_root_heading: false
heading_level: 4
## S19
## S19 (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19
handler: python
options:
show_root_heading: false
heading_level: 4
## S19+
## S19+ (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 Pro
## S19 Pro (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Pro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19a
## S19a (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 4
## S19a Pro
## S19a Pro (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j
## S19j (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19j
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j No PIC
## S19j No PIC (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j Pro
## S19j Pro (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j Pro No PIC
## S19j Pro No PIC (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j Pro+
## S19j Pro+ (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j Pro+
## S19j Pro+ (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 4
## S19j Pro+ No PIC
## S19j Pro+ No PIC (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlusNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19k Pro No PIC
## S19k Pro No PIC (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 XP
## S19k Pro No PIC (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 XP (BOS+)
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 4
## T19
## T19 (BOS+)
::: pyasic.miners.antminer.bosminer.X19.T19.BOSMinerT19
handler: python
options:

View File

@@ -1,27 +1,34 @@
# pyasic
## X21 Models
## S21
## S21 (Stock)
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21
handler: python
options:
show_root_heading: false
heading_level: 4
## T21
## T21 (Stock)
::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21
## S21 (BOS+)
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21 (VNish)
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21 (ePIC)
::: pyasic.miners.antminer.epic.X21.S21.ePICS21
handler: python

View File

@@ -1,21 +1,21 @@
# pyasic
## X3 Models
## D3
## D3 (Stock)
::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3
handler: python
options:
show_root_heading: false
heading_level: 4
## HS3
## HS3 (Stock)
::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3
handler: python
options:
show_root_heading: false
heading_level: 4
## L3+
## L3+ (Stock)
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## X5 Models
## DR5
## DR5 (Stock)
::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## X7 Models
## L7
## L7 (Stock)
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
handler: python
options:

View File

@@ -1,35 +1,35 @@
# pyasic
## X9 Models
## E9Pro
## E9Pro (Stock)
::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro
handler: python
options:
show_root_heading: false
heading_level: 4
## S9
## S9 (Stock)
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
handler: python
options:
show_root_heading: false
heading_level: 4
## S9i
## S9i (Stock)
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i
handler: python
options:
show_root_heading: false
heading_level: 4
## S9j
## S9j (Stock)
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j
handler: python
options:
show_root_heading: false
heading_level: 4
## T9
## T9 (Stock)
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
handler: python
options:
@@ -43,7 +43,7 @@
show_root_heading: false
heading_level: 4
## T9 (Hive)
## T9 (Stock)
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
handler: python
options:

View File

@@ -1,21 +1,21 @@
# pyasic
## AD Models
## AT1500
## AT1500 (Stock)
::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500
handler: python
options:
show_root_heading: false
heading_level: 4
## AT2860
## AT2860 (Stock)
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860
handler: python
options:
show_root_heading: false
heading_level: 4
## AT2880
## AT2880 (Stock)
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880
handler: python
options:

View File

@@ -1,14 +1,14 @@
# pyasic
## AI Models
## AI2500
## AI2500 (Stock)
::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500
handler: python
options:
show_root_heading: false
heading_level: 4
## AI3680
## AI3680 (Stock)
::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680
handler: python
options:

View File

@@ -1,14 +1,14 @@
# pyasic
## AT Models
## AD2500
## AD2500 (Stock)
::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500
handler: python
options:
show_root_heading: false
heading_level: 4
## AD3500
## AD3500 (Stock)
::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500
handler: python
options:

View File

@@ -1,21 +1,21 @@
# pyasic
## A10X Models
## Avalon 1026
## Avalon 1026 (Stock)
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
handler: python
options:
show_root_heading: false
heading_level: 4
## Avalon 1047
## Avalon 1047 (Stock)
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
handler: python
options:
show_root_heading: false
heading_level: 4
## Avalon 1066
## Avalon 1066 (Stock)
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## A11X Models
## Avalon 1166 Pro
## Avalon 1166 Pro (Stock)
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## A12X Models
## Avalon 1246
## Avalon 1246 (Stock)
::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246
handler: python
options:

View File

@@ -1,21 +1,21 @@
# pyasic
## A7X Models
## Avalon 721
## Avalon 721 (Stock)
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
handler: python
options:
show_root_heading: false
heading_level: 4
## Avalon 741
## Avalon 741 (Stock)
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
handler: python
options:
show_root_heading: false
heading_level: 4
## Avalon 761
## Avalon 761 (Stock)
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
handler: python
options:

View File

@@ -1,21 +1,21 @@
# pyasic
## A8X Models
## Avalon 821
## Avalon 821 (Stock)
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
handler: python
options:
show_root_heading: false
heading_level: 4
## Avalon 841
## Avalon 841 (Stock)
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
handler: python
options:
show_root_heading: false
heading_level: 4
## Avalon 851
## Avalon 851 (Stock)
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## A9X Models
## Avalon 921
## Avalon 921 (Stock)
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
handler: python
options:

24
docs/miners/bitaxe/BM.md Normal file
View File

@@ -0,0 +1,24 @@
# pyasic
## BM Models
## Supra (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
handler: python
options:
show_root_heading: false
heading_level: 4
## Ultra (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1366.BitAxeUltra
handler: python
options:
show_root_heading: false
heading_level: 4
## Max (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -0,0 +1,17 @@
# pyasic
## blockminer Models
## BlockMiner 520i (ePIC)
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner520i
handler: python
options:
show_root_heading: false
heading_level: 4
## BlockMiner 720i (ePIC)
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner720i
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,21 +1,21 @@
# pyasic
## X5 Models
## CK5
## CK5 (Stock)
::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5
handler: python
options:
show_root_heading: false
heading_level: 4
## HS5
## HS5 (Stock)
::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5
handler: python
options:
show_root_heading: false
heading_level: 4
## KD5
## KD5 (Stock)
::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5
handler: python
options:

View File

@@ -1,14 +1,14 @@
# pyasic
## XBox Models
## KD Box II
## KD Box II (Stock)
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII
handler: python
options:
show_root_heading: false
heading_level: 4
## KD Box Pro
## KD Box Pro (Stock)
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## XMax Models
## KD Max
## KD Max (Stock)
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## A10X Models
## A10X
## A10X (Stock)
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
handler: python
options:

View File

@@ -1,7 +1,7 @@
# pyasic
## T3X Models
## T3H+
## T3H+ (Stock)
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
handler: python
options:

View File

@@ -18,78 +18,78 @@ details {
<details>
<summary>X3 Series:</summary>
<ul>
<li><a href="../antminer/X3#d3">D3</a></li>
<li><a href="../antminer/X3#hs3">HS3</a></li>
<li><a href="../antminer/X3#l3_1">L3+</a></li>
<li><a href="../antminer/X3#d3-stock">D3 (Stock)</a></li>
<li><a href="../antminer/X3#hs3-stock">HS3 (Stock)</a></li>
<li><a href="../antminer/X3#l3_1-stock">L3+ (Stock)</a></li>
</ul>
</details>
<details>
<summary>X5 Series:</summary>
<ul>
<li><a href="../antminer/X5#dr5">DR5</a></li>
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X7 Series:</summary>
<ul>
<li><a href="../antminer/X7#l7">L7</a></li>
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="../antminer/X9#e9pro">E9Pro</a></li>
<li><a href="../antminer/X9#s9">S9</a></li>
<li><a href="../antminer/X9#s9i">S9i</a></li>
<li><a href="../antminer/X9#s9j">S9j</a></li>
<li><a href="../antminer/X9#t9">T9</a></li>
<li><a href="../antminer/X9#e9pro-stock">E9Pro (Stock)</a></li>
<li><a href="../antminer/X9#s9-stock">S9 (Stock)</a></li>
<li><a href="../antminer/X9#s9i-stock">S9i (Stock)</a></li>
<li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li>
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X15 Series:</summary>
<ul>
<li><a href="../antminer/X15#z15">Z15</a></li>
<li><a href="../antminer/X15#z15-stock">Z15 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X17 Series:</summary>
<ul>
<li><a href="../antminer/X17#s17">S17</a></li>
<li><a href="../antminer/X17#s17_1">S17+</a></li>
<li><a href="../antminer/X17#s17-pro">S17 Pro</a></li>
<li><a href="../antminer/X17#s17e">S17e</a></li>
<li><a href="../antminer/X17#t17">T17</a></li>
<li><a href="../antminer/X17#t17_1">T17+</a></li>
<li><a href="../antminer/X17#t17e">T17e</a></li>
<li><a href="../antminer/X17#s17-stock">S17 (Stock)</a></li>
<li><a href="../antminer/X17#s17_1-stock">S17+ (Stock)</a></li>
<li><a href="../antminer/X17#s17-pro-stock">S17 Pro (Stock)</a></li>
<li><a href="../antminer/X17#s17e-stock">S17e (Stock)</a></li>
<li><a href="../antminer/X17#t17-stock">T17 (Stock)</a></li>
<li><a href="../antminer/X17#t17_1-stock">T17+ (Stock)</a></li>
<li><a href="../antminer/X17#t17e-stock">T17e (Stock)</a></li>
</ul>
</details>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="../antminer/X19#s19">S19</a></li>
<li><a href="../antminer/X19#s19l">S19L</a></li>
<li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
<li><a href="../antminer/X19#s19j">S19j</a></li>
<li><a href="../antminer/X19#s19i">S19i</a></li>
<li><a href="../antminer/X19#s19_1">S19+</a></li>
<li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
<li><a href="../antminer/X19#s19-pro_1">S19 Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
<li><a href="../antminer/X19#s19-xp">S19 XP</a></li>
<li><a href="../antminer/X19#s19a">S19a</a></li>
<li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li>
<li><a href="../antminer/X19#s19-hydro">S19 Hydro</a></li>
<li><a href="../antminer/X19#s19-pro-hydro">S19 Pro Hydro</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro">S19 Pro+ Hydro</a></li>
<li><a href="../antminer/X19#s19k-pro">S19K Pro</a></li>
<li><a href="../antminer/X19#t19">T19</a></li>
<li><a href="../antminer/X19#s19-stock">S19 (Stock)</a></li>
<li><a href="../antminer/X19#s19l-stock">S19L (Stock)</a></li>
<li><a href="../antminer/X19#s19-pro-stock">S19 Pro (Stock)</a></li>
<li><a href="../antminer/X19#s19j-stock">S19j (Stock)</a></li>
<li><a href="../antminer/X19#s19i-stock">S19i (Stock)</a></li>
<li><a href="../antminer/X19#s19_1-stock">S19+ (Stock)</a></li>
<li><a href="../antminer/X19#s19j-no-pic-stock">S19j No PIC (Stock)</a></li>
<li><a href="../antminer/X19#s19-pro_1-stock">S19 Pro+ (Stock)</a></li>
<li><a href="../antminer/X19#s19j-pro-stock">S19j Pro (Stock)</a></li>
<li><a href="../antminer/X19#s19-xp-stock">S19 XP (Stock)</a></li>
<li><a href="../antminer/X19#s19a-stock">S19a (Stock)</a></li>
<li><a href="../antminer/X19#s19a-pro-stock">S19a Pro (Stock)</a></li>
<li><a href="../antminer/X19#s19-hydro-stock">S19 Hydro (Stock)</a></li>
<li><a href="../antminer/X19#s19-pro-hydro-stock">S19 Pro Hydro (Stock)</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro-stock">S19 Pro+ Hydro (Stock)</a></li>
<li><a href="../antminer/X19#s19k-pro-stock">S19K Pro (Stock)</a></li>
<li><a href="../antminer/X19#t19-stock">T19 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21">S21</a></li>
<li><a href="../antminer/X21#t21">T21</a></li>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#t21-stock">T21 (Stock)</a></li>
</ul>
</details>
</ul>
@@ -100,234 +100,234 @@ details {
<details>
<summary>M2X Series:</summary>
<ul>
<li><a href="../whatsminer/M2X#m20-v10">M20 V10</a></li>
<li><a href="../whatsminer/M2X#m20s-v10">M20S V10</a></li>
<li><a href="../whatsminer/M2X#m20s-v20">M20S V20</a></li>
<li><a href="../whatsminer/M2X#m20s-v30">M20S V30</a></li>
<li><a href="../whatsminer/M2X#m20p-v10">M20P V10</a></li>
<li><a href="../whatsminer/M2X#m20p-v30">M20P V30</a></li>
<li><a href="../whatsminer/M2X#m20s_1-v30">M20S+ V30</a></li>
<li><a href="../whatsminer/M2X#m21-v10">M21 V10</a></li>
<li><a href="../whatsminer/M2X#m21s-v20">M21S V20</a></li>
<li><a href="../whatsminer/M2X#m21s-v60">M21S V60</a></li>
<li><a href="../whatsminer/M2X#m21s-v70">M21S V70</a></li>
<li><a href="../whatsminer/M2X#m21s_1-v20">M21S+ V20</a></li>
<li><a href="../whatsminer/M2X#m29-v10">M29 V10</a></li>
<li><a href="../whatsminer/M2X#m20-v10-stock">M20 V10 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m20s-v10-stock">M20S V10 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m20s-v20-stock">M20S V20 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m20s-v30-stock">M20S V30 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m20p-v10-stock">M20P V10 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m20p-v30-stock">M20P V30 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m20s_1-v30-stock">M20S+ V30 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m21-v10-stock">M21 V10 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m21s-v20-stock">M21S V20 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m21s-v60-stock">M21S V60 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m21s-v70-stock">M21S V70 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m21s_1-v20-stock">M21S+ V20 (Stock)</a></li>
<li><a href="../whatsminer/M2X#m29-v10-stock">M29 V10 (Stock)</a></li>
</ul>
</details>
<details>
<summary>M3X Series:</summary>
<ul>
<li><a href="../whatsminer/M3X#m30-v10">M30 V10</a></li>
<li><a href="../whatsminer/M3X#m30-v20">M30 V20</a></li>
<li><a href="../whatsminer/M3X#m30k-v10">M30K V10</a></li>
<li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
<li><a href="../whatsminer/M3X#m30s-v10">M30S V10</a></li>
<li><a href="../whatsminer/M3X#m30s-v20">M30S V20</a></li>
<li><a href="../whatsminer/M3X#m30s-v30">M30S V30</a></li>
<li><a href="../whatsminer/M3X#m30s-v40">M30S V40</a></li>
<li><a href="../whatsminer/M3X#m30s-v50">M30S V50</a></li>
<li><a href="../whatsminer/M3X#m30s-v60">M30S V60</a></li>
<li><a href="../whatsminer/M3X#m30s-v70">M30S V70</a></li>
<li><a href="../whatsminer/M3X#m30s-v80">M30S V80</a></li>
<li><a href="../whatsminer/M3X#m30s-ve10">M30S VE10</a></li>
<li><a href="../whatsminer/M3X#m30s-ve20">M30S VE20</a></li>
<li><a href="../whatsminer/M3X#m30s-ve30">M30S VE30</a></li>
<li><a href="../whatsminer/M3X#m30s-ve40">M30S VE40</a></li>
<li><a href="../whatsminer/M3X#m30s-ve50">M30S VE50</a></li>
<li><a href="../whatsminer/M3X#m30s-ve60">M30S VE60</a></li>
<li><a href="../whatsminer/M3X#m30s-ve70">M30S VE70</a></li>
<li><a href="../whatsminer/M3X#m30s-vf10">M30S VF10</a></li>
<li><a href="../whatsminer/M3X#m30s-vf20">M30S VF20</a></li>
<li><a href="../whatsminer/M3X#m30s-vf30">M30S VF30</a></li>
<li><a href="../whatsminer/M3X#m30s-vg10">M30S VG10</a></li>
<li><a href="../whatsminer/M3X#m30s-vg20">M30S VG20</a></li>
<li><a href="../whatsminer/M3X#m30s-vg30">M30S VG30</a></li>
<li><a href="../whatsminer/M3X#m30s-vg40">M30S VG40</a></li>
<li><a href="../whatsminer/M3X#m30s-vh10">M30S VH10</a></li>
<li><a href="../whatsminer/M3X#m30s-vh20">M30S VH20</a></li>
<li><a href="../whatsminer/M3X#m30s-vh30">M30S VH30</a></li>
<li><a href="../whatsminer/M3X#m30s-vh40">M30S VH40</a></li>
<li><a href="../whatsminer/M3X#m30s-vh50">M30S VH50</a></li>
<li><a href="../whatsminer/M3X#m30s-vh60">M30S VH60</a></li>
<li><a href="../whatsminer/M3X#m30s-vi20">M30S VI20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v10">M30S+ V10</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v20">M30S+ V20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v30">M30S+ V30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v40">M30S+ V40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v50">M30S+ V50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v60">M30S+ V60</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v70">M30S+ V70</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v80">M30S+ V80</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v90">M30S+ V90</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v100">M30S+ V100</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve30">M30S+ VE30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve40">M30S+ VE40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve50">M30S+ VE50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve60">M30S+ VE60</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve70">M30S+ VE70</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve80">M30S+ VE80</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve90">M30S+ VE90</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve100">M30S+ VE100</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf20">M30S+ VF20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf30">M30S+ VF30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg20">M30S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg30">M30S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg40">M30S+ VG40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg50">M30S+ VG50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg60">M30S+ VG60</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh10">M30S+ VH10</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh20">M30S+ VH20</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh30">M30S+ VH30</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh40">M30S+ VH40</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh50">M30S+ VH50</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh60">M30S+ VH60</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-v10">M30S++ V10</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-v20">M30S++ V20</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve30">M30S++ VE30</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve40">M30S++ VE40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve50">M30S++ VE50</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vf40">M30S++ VF40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg30">M30S++ VG30</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg40">M30S++ VG40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg50">M30S++ VG50</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh10">M30S++ VH10</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh20">M30S++ VH20</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh30">M30S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh40">M30S++ VH40</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh50">M30S++ VH50</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh60">M30S++ VH60</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh70">M30S++ VH70</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh80">M30S++ VH80</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh90">M30S++ VH90</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh100">M30S++ VH100</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vj20">M30S++ VJ20</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vj30">M30S++ VJ30</a></li>
<li><a href="../whatsminer/M3X#m31-v10">M31 V10</a></li>
<li><a href="../whatsminer/M3X#m31-v20">M31 V20</a></li>
<li><a href="../whatsminer/M3X#m31h-v10">M31H V10</a></li>
<li><a href="../whatsminer/M3X#m31h-v40">M31H V40</a></li>
<li><a href="../whatsminer/M3X#m30l-v10">M30L V10</a></li>
<li><a href="../whatsminer/M3X#m31s-v10">M31S V10</a></li>
<li><a href="../whatsminer/M3X#m31s-v20">M31S V20</a></li>
<li><a href="../whatsminer/M3X#m31s-v30">M31S V30</a></li>
<li><a href="../whatsminer/M3X#m31s-v40">M31S V40</a></li>
<li><a href="../whatsminer/M3X#m31s-v50">M31S V50</a></li>
<li><a href="../whatsminer/M3X#m31s-v60">M31S V60</a></li>
<li><a href="../whatsminer/M3X#m31s-v70">M31S V70</a></li>
<li><a href="../whatsminer/M3X#m31s-v80">M31S V80</a></li>
<li><a href="../whatsminer/M3X#m31s-v90">M31S V90</a></li>
<li><a href="../whatsminer/M3X#m31s-ve10">M31S VE10</a></li>
<li><a href="../whatsminer/M3X#m31s-ve20">M31S VE20</a></li>
<li><a href="../whatsminer/M3X#m31s-ve30">M31S VE30</a></li>
<li><a href="../whatsminer/M3X#m31se-v10">M31SE V10</a></li>
<li><a href="../whatsminer/M3X#m31se-v20">M31SE V20</a></li>
<li><a href="../whatsminer/M3X#m31se-v30">M31SE V30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v10">M31S+ V10</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v20">M31S+ V20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v30">M31S+ V30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v40">M31S+ V40</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v50">M31S+ V50</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v60">M31S+ V60</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v80">M31S+ V80</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v90">M31S+ V90</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v100">M31S+ V100</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve10">M31S+ VE10</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve20">M31S+ VE20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve30">M31S+ VE30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve40">M31S+ VE40</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve50">M31S+ VE50</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve60">M31S+ VE60</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve80">M31S+ VE80</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vf20">M31S+ VF20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vf30">M31S+ VF30</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vg20">M31S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vg30">M31S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m32-v10">M32 V10</a></li>
<li><a href="../whatsminer/M3X#m32-v20">M32 V20</a></li>
<li><a href="../whatsminer/M3X#m33-v10">M33 V10</a></li>
<li><a href="../whatsminer/M3X#m33-v20">M33 V20</a></li>
<li><a href="../whatsminer/M3X#m33-v30">M33 V30</a></li>
<li><a href="../whatsminer/M3X#m33s-vg30">M33S VG30</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vg20">M33S+ VG20</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh20">M33S+ VH20</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh30">M33S+ VH30</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh20">M33S++ VH20</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh30">M33S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vg40">M33S++ VG40</a></li>
<li><a href="../whatsminer/M3X#m34s_1-ve10">M34S+ VE10</a></li>
<li><a href="../whatsminer/M3X#m36s-ve10">M36S VE10</a></li>
<li><a href="../whatsminer/M3X#m36s_1-vg30">M36S+ VG30</a></li>
<li><a href="../whatsminer/M3X#m36s_1_1-vh30">M36S++ VH30</a></li>
<li><a href="../whatsminer/M3X#m39-v10">M39 V10</a></li>
<li><a href="../whatsminer/M3X#m39-v20">M39 V20</a></li>
<li><a href="../whatsminer/M3X#m39-v30">M39 V30</a></li>
<li><a href="../whatsminer/M3X#m30-v10-stock">M30 V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30-v20-stock">M30 V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30k-v10-stock">M30K V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30l-v10-stock">M30L V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v10-stock">M30S V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v20-stock">M30S V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v30-stock">M30S V30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v40-stock">M30S V40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v50-stock">M30S V50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v60-stock">M30S V60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v70-stock">M30S V70 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-v80-stock">M30S V80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve10-stock">M30S VE10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve20-stock">M30S VE20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve30-stock">M30S VE30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve40-stock">M30S VE40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve50-stock">M30S VE50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve60-stock">M30S VE60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-ve70-stock">M30S VE70 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vf10-stock">M30S VF10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vf20-stock">M30S VF20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vf30-stock">M30S VF30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vg10-stock">M30S VG10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vg20-stock">M30S VG20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vg30-stock">M30S VG30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vg40-stock">M30S VG40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vh10-stock">M30S VH10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vh20-stock">M30S VH20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vh30-stock">M30S VH30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vh40-stock">M30S VH40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vh50-stock">M30S VH50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vh60-stock">M30S VH60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s-vi20-stock">M30S VI20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v10-stock">M30S+ V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v20-stock">M30S+ V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v30-stock">M30S+ V30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v40-stock">M30S+ V40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v50-stock">M30S+ V50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v60-stock">M30S+ V60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v70-stock">M30S+ V70 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v80-stock">M30S+ V80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v90-stock">M30S+ V90 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-v100-stock">M30S+ V100 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve30-stock">M30S+ VE30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve40-stock">M30S+ VE40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve50-stock">M30S+ VE50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve60-stock">M30S+ VE60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve70-stock">M30S+ VE70 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve80-stock">M30S+ VE80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve90-stock">M30S+ VE90 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-ve100-stock">M30S+ VE100 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf20-stock">M30S+ VF20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vf30-stock">M30S+ VF30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg20-stock">M30S+ VG20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg30-stock">M30S+ VG30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg40-stock">M30S+ VG40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg50-stock">M30S+ VG50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vg60-stock">M30S+ VG60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh10-stock">M30S+ VH10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh20-stock">M30S+ VH20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh30-stock">M30S+ VH30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh40-stock">M30S+ VH40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh50-stock">M30S+ VH50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1-vh60-stock">M30S+ VH60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-v10-stock">M30S++ V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-v20-stock">M30S++ V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve30-stock">M30S++ VE30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve40-stock">M30S++ VE40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-ve50-stock">M30S++ VE50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vf40-stock">M30S++ VF40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg30-stock">M30S++ VG30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg40-stock">M30S++ VG40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vg50-stock">M30S++ VG50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh10-stock">M30S++ VH10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh20-stock">M30S++ VH20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh30-stock">M30S++ VH30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh40-stock">M30S++ VH40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh50-stock">M30S++ VH50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh60-stock">M30S++ VH60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh70-stock">M30S++ VH70 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh80-stock">M30S++ VH80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh90-stock">M30S++ VH90 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vh100-stock">M30S++ VH100 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vj20-stock">M30S++ VJ20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30s_1_1-vj30-stock">M30S++ VJ30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31-v10-stock">M31 V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31-v20-stock">M31 V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31h-v10-stock">M31H V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31h-v40-stock">M31H V40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m30l-v10-stock">M30L V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v10-stock">M31S V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v20-stock">M31S V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v30-stock">M31S V30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v40-stock">M31S V40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v50-stock">M31S V50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v60-stock">M31S V60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v70-stock">M31S V70 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v80-stock">M31S V80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-v90-stock">M31S V90 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-ve10-stock">M31S VE10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-ve20-stock">M31S VE20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s-ve30-stock">M31S VE30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31se-v10-stock">M31SE V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31se-v20-stock">M31SE V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31se-v30-stock">M31SE V30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v10-stock">M31S+ V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v20-stock">M31S+ V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v30-stock">M31S+ V30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v40-stock">M31S+ V40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v50-stock">M31S+ V50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v60-stock">M31S+ V60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v80-stock">M31S+ V80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v90-stock">M31S+ V90 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-v100-stock">M31S+ V100 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve10-stock">M31S+ VE10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve20-stock">M31S+ VE20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve30-stock">M31S+ VE30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve40-stock">M31S+ VE40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve50-stock">M31S+ VE50 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve60-stock">M31S+ VE60 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-ve80-stock">M31S+ VE80 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vf20-stock">M31S+ VF20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vf30-stock">M31S+ VF30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vg20-stock">M31S+ VG20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m31s_1-vg30-stock">M31S+ VG30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m32-v10-stock">M32 V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m32-v20-stock">M32 V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33-v10-stock">M33 V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33-v20-stock">M33 V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33-v30-stock">M33 V30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s-vg30-stock">M33S VG30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vg20-stock">M33S+ VG20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh20-stock">M33S+ VH20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s_1-vh30-stock">M33S+ VH30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh20-stock">M33S++ VH20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vh30-stock">M33S++ VH30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m33s_1_1-vg40-stock">M33S++ VG40 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m34s_1-ve10-stock">M34S+ VE10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m36s-ve10-stock">M36S VE10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m36s_1-vg30-stock">M36S+ VG30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m36s_1_1-vh30-stock">M36S++ VH30 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m39-v10-stock">M39 V10 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m39-v20-stock">M39 V20 (Stock)</a></li>
<li><a href="../whatsminer/M3X#m39-v30-stock">M39 V30 (Stock)</a></li>
</ul>
</details>
<details>
<summary>M5X Series:</summary>
<ul>
<li><a href="../whatsminer/M5X#m50-ve30">M50 VE30</a></li>
<li><a href="../whatsminer/M5X#m50-vg30">M50 VG30</a></li>
<li><a href="../whatsminer/M5X#m50-vh10">M50 VH10</a></li>
<li><a href="../whatsminer/M5X#m50-vh20">M50 VH20</a></li>
<li><a href="../whatsminer/M5X#m50-vh30">M50 VH30</a></li>
<li><a href="../whatsminer/M5X#m50-vh40">M50 VH40</a></li>
<li><a href="../whatsminer/M5X#m50-vh50">M50 VH50</a></li>
<li><a href="../whatsminer/M5X#m50-vh60">M50 VH60</a></li>
<li><a href="../whatsminer/M5X#m50-vh70">M50 VH70</a></li>
<li><a href="../whatsminer/M5X#m50-vh80">M50 VH80</a></li>
<li><a href="../whatsminer/M5X#m50-vj10">M50 VJ10</a></li>
<li><a href="../whatsminer/M5X#m50-vj20">M50 VJ20</a></li>
<li><a href="../whatsminer/M5X#m50-vj30">M50 VJ30</a></li>
<li><a href="../whatsminer/M5X#m50s-vj10">M50S VJ10</a></li>
<li><a href="../whatsminer/M5X#m50s-vj20">M50S VJ20</a></li>
<li><a href="../whatsminer/M5X#m50s-vj30">M50S VJ30</a></li>
<li><a href="../whatsminer/M5X#m50s-vh10">M50S VH10</a></li>
<li><a href="../whatsminer/M5X#m50s-vh20">M50S VH20</a></li>
<li><a href="../whatsminer/M5X#m50s-vh30">M50S VH30</a></li>
<li><a href="../whatsminer/M5X#m50s-vh40">M50S VH40</a></li>
<li><a href="../whatsminer/M5X#m50s-vh50">M50S VH50</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vh30">M50S+ VH30</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vh40">M50S+ VH40</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vj30">M50S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vk20">M50S+ VK20</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk10">M50S++ VK10</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk20">M50S++ VK20</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk30">M50S++ VK30</a></li>
<li><a href="../whatsminer/M5X#m53-vh30">M53 VH30</a></li>
<li><a href="../whatsminer/M5X#m53s-vh30">M53S VH30</a></li>
<li><a href="../whatsminer/M5X#m53s-vj40">M53S VJ40</a></li>
<li><a href="../whatsminer/M5X#m53s_1-vj30">M53S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m53s_1_1-vk10">M53S++ VK10</a></li>
<li><a href="../whatsminer/M5X#m56-vh30">M56 VH30</a></li>
<li><a href="../whatsminer/M5X#m56s-vh30">M56S VH30</a></li>
<li><a href="../whatsminer/M5X#m56s_1-vj30">M56S+ VJ30</a></li>
<li><a href="../whatsminer/M5X#m59-vh30">M59 VH30</a></li>
<li><a href="../whatsminer/M5X#m50-ve30-stock">M50 VE30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vg30-stock">M50 VG30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh10-stock">M50 VH10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh20-stock">M50 VH20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh30-stock">M50 VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh40-stock">M50 VH40 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh50-stock">M50 VH50 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh60-stock">M50 VH60 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh70-stock">M50 VH70 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vh80-stock">M50 VH80 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vj10-stock">M50 VJ10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vj20-stock">M50 VJ20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50-vj30-stock">M50 VJ30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vj10-stock">M50S VJ10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vj20-stock">M50S VJ20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vj30-stock">M50S VJ30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vh10-stock">M50S VH10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vh20-stock">M50S VH20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vh30-stock">M50S VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vh40-stock">M50S VH40 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s-vh50-stock">M50S VH50 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vh30-stock">M50S+ VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vh40-stock">M50S+ VH40 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vj30-stock">M50S+ VJ30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1-vk20-stock">M50S+ VK20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk10-stock">M50S++ VK10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk20-stock">M50S++ VK20 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m50s_1_1-vk30-stock">M50S++ VK30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m53-vh30-stock">M53 VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m53s-vh30-stock">M53S VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m53s-vj40-stock">M53S VJ40 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m53s_1-vj30-stock">M53S+ VJ30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m53s_1_1-vk10-stock">M53S++ VK10 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m56-vh30-stock">M56 VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m56s-vh30-stock">M56S VH30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m56s_1-vj30-stock">M56S+ VJ30 (Stock)</a></li>
<li><a href="../whatsminer/M5X#m59-vh30-stock">M59 VH30 (Stock)</a></li>
</ul>
</details>
<details>
<summary>M6X Series:</summary>
<ul>
<li><a href="../whatsminer/M6X#m60-vk10">M60 VK10</a></li>
<li><a href="../whatsminer/M6X#m60-vk20">M60 VK20</a></li>
<li><a href="../whatsminer/M6X#m60-vk30">M60 VK30</a></li>
<li><a href="../whatsminer/M6X#m60-vk40">M60 VK40</a></li>
<li><a href="../whatsminer/M6X#m60s-vk10">M60S VK10</a></li>
<li><a href="../whatsminer/M6X#m60s-vk20">M60S VK20</a></li>
<li><a href="../whatsminer/M6X#m60s-vk30">M60S VK30</a></li>
<li><a href="../whatsminer/M6X#m60s-vk40">M60S VK40</a></li>
<li><a href="../whatsminer/M6X#m63-vk10">M63 VK10</a></li>
<li><a href="../whatsminer/M6X#m63-vk20">M63 VK20</a></li>
<li><a href="../whatsminer/M6X#m63-vk30">M63 VK30</a></li>
<li><a href="../whatsminer/M6X#m63s-vk10">M63S VK10</a></li>
<li><a href="../whatsminer/M6X#m63s-vk20">M63S VK20</a></li>
<li><a href="../whatsminer/M6X#m63s-vk30">M63S VK30</a></li>
<li><a href="../whatsminer/M6X#m66-vk20">M66 VK20</a></li>
<li><a href="../whatsminer/M6X#m66-vk30">M66 VK30</a></li>
<li><a href="../whatsminer/M6X#m66s-vk20">M66S VK20</a></li>
<li><a href="../whatsminer/M6X#m66s-vk30">M66S VK30</a></li>
<li><a href="../whatsminer/M6X#m66s-vk40">M66S VK40</a></li>
<li><a href="../whatsminer/M6X#m60-vk10-stock">M60 VK10 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60-vk20-stock">M60 VK20 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60-vk30-stock">M60 VK30 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60-vk40-stock">M60 VK40 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60s-vk10-stock">M60S VK10 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60s-vk20-stock">M60S VK20 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60s-vk30-stock">M60S VK30 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m60s-vk40-stock">M60S VK40 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m63-vk10-stock">M63 VK10 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m63-vk20-stock">M63 VK20 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m63-vk30-stock">M63 VK30 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m63s-vk10-stock">M63S VK10 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m63s-vk20-stock">M63S VK20 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m63s-vk30-stock">M63S VK30 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m66-vk20-stock">M66 VK20 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m66-vk30-stock">M66 VK30 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m66s-vk20-stock">M66S VK20 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m66s-vk30-stock">M66S VK30 (Stock)</a></li>
<li><a href="../whatsminer/M6X#m66s-vk40-stock">M66S VK40 (Stock)</a></li>
</ul>
</details>
</ul>
@@ -338,43 +338,43 @@ details {
<details>
<summary>A7X Series:</summary>
<ul>
<li><a href="../avalonminer/A7X#avalon-721">Avalon 721</a></li>
<li><a href="../avalonminer/A7X#avalon-741">Avalon 741</a></li>
<li><a href="../avalonminer/A7X#avalon-761">Avalon 761</a></li>
<li><a href="../avalonminer/A7X#avalon-721-stock">Avalon 721 (Stock)</a></li>
<li><a href="../avalonminer/A7X#avalon-741-stock">Avalon 741 (Stock)</a></li>
<li><a href="../avalonminer/A7X#avalon-761-stock">Avalon 761 (Stock)</a></li>
</ul>
</details>
<details>
<summary>A8X Series:</summary>
<ul>
<li><a href="../avalonminer/A8X#avalon-821">Avalon 821</a></li>
<li><a href="../avalonminer/A8X#avalon-841">Avalon 841</a></li>
<li><a href="../avalonminer/A8X#avalon-851">Avalon 851</a></li>
<li><a href="../avalonminer/A8X#avalon-821-stock">Avalon 821 (Stock)</a></li>
<li><a href="../avalonminer/A8X#avalon-841-stock">Avalon 841 (Stock)</a></li>
<li><a href="../avalonminer/A8X#avalon-851-stock">Avalon 851 (Stock)</a></li>
</ul>
</details>
<details>
<summary>A9X Series:</summary>
<ul>
<li><a href="../avalonminer/A9X#avalon-921">Avalon 921</a></li>
<li><a href="../avalonminer/A9X#avalon-921-stock">Avalon 921 (Stock)</a></li>
</ul>
</details>
<details>
<summary>A10X Series:</summary>
<ul>
<li><a href="../avalonminer/A10X#avalon-1026">Avalon 1026</a></li>
<li><a href="../avalonminer/A10X#avalon-1047">Avalon 1047</a></li>
<li><a href="../avalonminer/A10X#avalon-1066">Avalon 1066</a></li>
<li><a href="../avalonminer/A10X#avalon-1026-stock">Avalon 1026 (Stock)</a></li>
<li><a href="../avalonminer/A10X#avalon-1047-stock">Avalon 1047 (Stock)</a></li>
<li><a href="../avalonminer/A10X#avalon-1066-stock">Avalon 1066 (Stock)</a></li>
</ul>
</details>
<details>
<summary>A11X Series:</summary>
<ul>
<li><a href="../avalonminer/A11X#avalon-1166-pro">Avalon 1166 Pro</a></li>
<li><a href="../avalonminer/A11X#avalon-1166-pro-stock">Avalon 1166 Pro (Stock)</a></li>
</ul>
</details>
<details>
<summary>A12X Series:</summary>
<ul>
<li><a href="../avalonminer/A12X#avalon-1246">Avalon 1246</a></li>
<li><a href="../avalonminer/A12X#avalon-1246-stock">Avalon 1246 (Stock)</a></li>
</ul>
</details>
</ul>
@@ -385,13 +385,13 @@ details {
<details>
<summary>T3X Series:</summary>
<ul>
<li><a href="../innosilicon/T3X#t3h_1">T3H+</a></li>
<li><a href="../innosilicon/T3X#t3h_1-stock">T3H+ (Stock)</a></li>
</ul>
</details>
<details>
<summary>A10X Series:</summary>
<ul>
<li><a href="../innosilicon/A10X#a10x">A10X</a></li>
<li><a href="../innosilicon/A10X#a10x-stock">A10X (Stock)</a></li>
</ul>
</details>
</ul>
@@ -402,22 +402,22 @@ details {
<details>
<summary>X5 Series:</summary>
<ul>
<li><a href="../goldshell/X5#ck5">CK5</a></li>
<li><a href="../goldshell/X5#hs5">HS5</a></li>
<li><a href="../goldshell/X5#kd5">KD5</a></li>
<li><a href="../goldshell/X5#ck5-stock">CK5 (Stock)</a></li>
<li><a href="../goldshell/X5#hs5-stock">HS5 (Stock)</a></li>
<li><a href="../goldshell/X5#kd5-stock">KD5 (Stock)</a></li>
</ul>
</details>
<details>
<summary>XMax Series:</summary>
<ul>
<li><a href="../goldshell/XMax#kd-max">KD Max</a></li>
<li><a href="../goldshell/XMax#kd-max-stock">KD Max (Stock)</a></li>
</ul>
</details>
<details>
<summary>XBox Series:</summary>
<ul>
<li><a href="../goldshell/XBox#kd-box-ii">KD Box II</a></li>
<li><a href="../goldshell/XBox#kd-box-pro">KD Box Pro</a></li>
<li><a href="../goldshell/XBox#kd-box-ii-stock">KD Box II (Stock)</a></li>
<li><a href="../goldshell/XBox#kd-box-pro-stock">KD Box Pro (Stock)</a></li>
</ul>
</details>
</ul>
@@ -446,27 +446,28 @@ details {
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="../antminer/X19#s19">S19</a></li>
<li><a href="../antminer/X19#s19_1">S19+</a></li>
<li><a href="../antminer/X19#s19-pro">S19 Pro</a></li>
<li><a href="../antminer/X19#s19a">S19a</a></li>
<li><a href="../antminer/X19#s19a-pro">S19a Pro</a></li>
<li><a href="../antminer/X19#s19j">S19j</a></li>
<li><a href="../antminer/X19#s19j-no-pic">S19j No PIC</a></li>
<li><a href="../antminer/X19#s19j-pro">S19j Pro</a></li>
<li><a href="../antminer/X19#s19j-pro-no-pic">S19j Pro No PIC</a></li>
<li><a href="../antminer/X19#s19j-pro_1">S19j Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro_1">S19j Pro+</a></li>
<li><a href="../antminer/X19#s19j-pro_1-no-pic">S19j Pro+ No PIC</a></li>
<li><a href="../antminer/X19#s19k-pro-no-pic">S19k Pro No PIC</a></li>
<li><a href="../antminer/X19#s19-xp">S19 XP</a></li>
<li><a href="../antminer/X19#t19">T19</a></li>
<li><a href="../antminer/X19#s19-bos_1">S19 (BOS+)</a></li>
<li><a href="../antminer/X19#s19_1-bos_1">S19+ (BOS+)</a></li>
<li><a href="../antminer/X19#s19-pro-bos_1">S19 Pro (BOS+)</a></li>
<li><a href="../antminer/X19#s19a-bos_1">S19a (BOS+)</a></li>
<li><a href="../antminer/X19#s19a-pro-bos_1">S19a Pro (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-bos_1">S19j (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-no-pic-bos_1">S19j No PIC (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-pro-bos_1">S19j Pro (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-pro-no-pic-bos_1">S19j Pro No PIC (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-pro_1-bos_1">S19j Pro+ (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-pro_1-bos_1">S19j Pro+ (BOS+)</a></li>
<li><a href="../antminer/X19#s19j-pro_1-no-pic-bos_1">S19j Pro+ No PIC (BOS+)</a></li>
<li><a href="../antminer/X19#s19k-pro-no-pic-bos_1">S19k Pro No PIC (BOS+)</a></li>
<li><a href="../antminer/X19#s19k-pro-no-pic-bos_1">S19k Pro No PIC (BOS+)</a></li>
<li><a href="../antminer/X19#s19-xp-bos_1">S19 XP (BOS+)</a></li>
<li><a href="../antminer/X19#t19-bos_1">T19 (BOS+)</a></li>
</ul>
</details>
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21">S21</a></li>
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
</ul>
</details>
</ul>
@@ -507,6 +508,12 @@ details {
<li><a href="../antminer/X19#t19-vnish">T19 (VNish)</a></li>
</ul>
</details>
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-vnish">S21 (VNish)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -546,7 +553,7 @@ details {
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="../antminer/X9#t9-hive">T9 (Hive)</a></li>
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
</ul>
</details>
</ul>
@@ -586,23 +593,23 @@ details {
<details>
<summary>AD Series:</summary>
<ul>
<li><a href="../auradine/AD#at1500">AT1500</a></li>
<li><a href="../auradine/AD#at2860">AT2860</a></li>
<li><a href="../auradine/AD#at2880">AT2880</a></li>
<li><a href="../auradine/AD#at1500-stock">AT1500 (Stock)</a></li>
<li><a href="../auradine/AD#at2860-stock">AT2860 (Stock)</a></li>
<li><a href="../auradine/AD#at2880-stock">AT2880 (Stock)</a></li>
</ul>
</details>
<details>
<summary>AI Series:</summary>
<ul>
<li><a href="../auradine/AI#ai2500">AI2500</a></li>
<li><a href="../auradine/AI#ai3680">AI3680</a></li>
<li><a href="../auradine/AI#ai2500-stock">AI2500 (Stock)</a></li>
<li><a href="../auradine/AI#ai3680-stock">AI3680 (Stock)</a></li>
</ul>
</details>
<details>
<summary>AT Series:</summary>
<ul>
<li><a href="../auradine/AT#ad2500">AD2500</a></li>
<li><a href="../auradine/AT#ad3500">AD3500</a></li>
<li><a href="../auradine/AT#ad2500-stock">AD2500 (Stock)</a></li>
<li><a href="../auradine/AT#ad3500-stock">AD3500 (Stock)</a></li>
</ul>
</details>
</ul>
@@ -630,4 +637,17 @@ details {
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware BitAxe Miners:</summary>
<ul>
<details>
<summary>BM Series:</summary>
<ul>
<li><a href="../bitaxe/BM#supra-stock">Supra (Stock)</a></li>
<li><a href="../bitaxe/BM#ultra-stock">Ultra (Stock)</a></li>
<li><a href="../bitaxe/BM#max-stock">Max (Stock)</a></li>
</ul>
</details>
</ul>
</details>

View File

@@ -1,91 +1,91 @@
# pyasic
## M2X Models
## M20 V10
## M20 V10 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
handler: python
options:
show_root_heading: false
heading_level: 4
## M20S V10
## M20S V10 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M20S V20
## M20S V20 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
handler: python
options:
show_root_heading: false
heading_level: 4
## M20S V30
## M20S V30 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
handler: python
options:
show_root_heading: false
heading_level: 4
## M20P V10
## M20P V10 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
handler: python
options:
show_root_heading: false
heading_level: 4
## M20P V30
## M20P V30 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
handler: python
options:
show_root_heading: false
heading_level: 4
## M20S+ V30
## M20S+ V30 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
handler: python
options:
show_root_heading: false
heading_level: 4
## M21 V10
## M21 V10 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
handler: python
options:
show_root_heading: false
heading_level: 4
## M21S V20
## M21S V20 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
handler: python
options:
show_root_heading: false
heading_level: 4
## M21S V60
## M21S V60 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
handler: python
options:
show_root_heading: false
heading_level: 4
## M21S V70
## M21S V70 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
handler: python
options:
show_root_heading: false
heading_level: 4
## M21S+ V20
## M21S+ V20 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
handler: python
options:
show_root_heading: false
heading_level: 4
## M29 V10
## M29 V10 (Stock)
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
handler: python
options:

File diff suppressed because it is too large Load Diff

View File

@@ -1,259 +1,259 @@
# pyasic
## M5X Models
## M50 VE30
## M50 VE30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VE30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VG30
## M50 VG30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VG30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH10
## M50 VH10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH10
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH20
## M50 VH20 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH20
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH30
## M50 VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH40
## M50 VH40 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH40
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH50
## M50 VH50 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH50
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH60
## M50 VH60 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH60
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH70
## M50 VH70 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH70
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VH80
## M50 VH80 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VH80
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VJ10
## M50 VJ10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ10
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VJ20
## M50 VJ20 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ20
handler: python
options:
show_root_heading: false
heading_level: 4
## M50 VJ30
## M50 VJ30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50.BTMinerM50VJ30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VJ10
## M50S VJ10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ10
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VJ20
## M50S VJ20 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ20
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VJ30
## M50S VJ30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVJ30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VH10
## M50S VH10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH10
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VH20
## M50S VH20 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH20
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VH30
## M50S VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VH40
## M50S VH40 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH40
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S VH50
## M50S VH50 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S.BTMinerM50SVH50
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S+ VH30
## M50S+ VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S+ VH40
## M50S+ VH40 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVH40
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S+ VJ30
## M50S+ VJ30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVJ30
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S+ VK20
## M50S+ VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus.BTMinerM50SPlusVK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S++ VK10
## M50S++ VK10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S++ VK20
## M50S++ VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M50S++ VK30
## M50S++ VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M50S_Plus_Plus.BTMinerM50SPlusPlusVK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M53 VH30
## M53 VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M53.BTMinerM53VH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M53S VH30
## M53S VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M53S VJ40
## M53S VJ40 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M53S.BTMinerM53SVJ40
handler: python
options:
show_root_heading: false
heading_level: 4
## M53S+ VJ30
## M53S+ VJ30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus.BTMinerM53SPlusVJ30
handler: python
options:
show_root_heading: false
heading_level: 4
## M53S++ VK10
## M53S++ VK10 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M53S_Plus_Plus.BTMinerM53SPlusPlusVK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M56 VH30
## M56 VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M56.BTMinerM56VH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M56S VH30
## M56S VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M56S.BTMinerM56SVH30
handler: python
options:
show_root_heading: false
heading_level: 4
## M56S+ VJ30
## M56S+ VJ30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M56S_Plus.BTMinerM56SPlusVJ30
handler: python
options:
show_root_heading: false
heading_level: 4
## M59 VH30
## M59 VH30 (Stock)
::: pyasic.miners.whatsminer.btminer.M5X.M59.BTMinerM59VH30
handler: python
options:

View File

@@ -1,133 +1,133 @@
# pyasic
## M6X Models
## M60 VK10
## M60 VK10 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M60 VK20
## M60 VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M60 VK30
## M60 VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M60 VK40
## M60 VK40 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60.BTMinerM60VK40
handler: python
options:
show_root_heading: false
heading_level: 4
## M60S VK10
## M60S VK10 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M60S VK20
## M60S VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M60S VK30
## M60S VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M60S VK40
## M60S VK40 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M60S.BTMinerM60SVK40
handler: python
options:
show_root_heading: false
heading_level: 4
## M63 VK10
## M63 VK10 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M63 VK20
## M63 VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M63 VK30
## M63 VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M63.BTMinerM63VK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M63S VK10
## M63S VK10 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK10
handler: python
options:
show_root_heading: false
heading_level: 4
## M63S VK20
## M63S VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M63S VK30
## M63S VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M63S.BTMinerM63SVK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M66 VK20
## M66 VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M66 VK30
## M66 VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M66.BTMinerM66VK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M66S VK20
## M66S VK20 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK20
handler: python
options:
show_root_heading: false
heading_level: 4
## M66S VK30
## M66S VK30 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK30
handler: python
options:
show_root_heading: false
heading_level: 4
## M66S VK40
## M66S VK40 (Stock)
::: pyasic.miners.whatsminer.btminer.M6X.M66S.BTMinerM66SVK40
handler: python
options:

View File

@@ -1,3 +1,4 @@
jinja2<3.1.4
mkdocs
mkdocstrings[python]
zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability

View File

@@ -13,12 +13,17 @@ Settings options:
- `get_data_retries`
- `api_function_timeout`
- `antminer_mining_mode_as_str`
- `default_whatsminer_password`
- `default_innosilicon_password`
- `default_antminer_password`
- `default_bosminer_password`
- `default_vnish_password`
- `default_goldshell_password`
- `default_whatsminer_rpc_password`
- `default_innosilicon_web_password`
- `default_antminer_web_password`
- `default_bosminer_web_password`
- `default_vnish_web_password`
- `default_goldshell_web_password`
- `default_auradine_web_password`
- `default_epic_web_password`
- `default_hive_web_password`
- `default_antminer_ssh_password`
- `default_bosminer_ssh_password`
- `socket_linger_time`

View File

@@ -68,6 +68,8 @@ nav:
- Auradine AD: "miners/auradine/AD.md"
- Auradine AI: "miners/auradine/AI.md"
- Auradine AT: "miners/auradine/AT.md"
- Blockminer: "miners/blockminer/blockminer.md"
- BitAxe BM: "miners/bitaxe/BM.md"
- Base Miner: "miners/base_miner.md"
- Settings:
- Settings: "settings/settings.md"

503
poetry.lock generated
View File

@@ -1,14 +1,25 @@
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "aiofiles"
version = "24.1.0"
description = "File support for asyncio."
optional = false
python-versions = ">=3.8"
files = [
{file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
{file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
]
[[package]]
name = "anyio"
version = "4.3.0"
version = "4.4.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.8"
files = [
{file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"},
{file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"},
{file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"},
{file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"},
]
[package.dependencies]
@@ -39,18 +50,18 @@ wheel = ">=0.23.0,<1.0"
[[package]]
name = "asyncssh"
version = "2.14.2"
version = "2.15.0"
description = "AsyncSSH: Asynchronous SSHv2 client and server library"
optional = false
python-versions = ">= 3.6"
python-versions = ">=3.6"
files = [
{file = "asyncssh-2.14.2-py3-none-any.whl", hash = "sha256:6ff9923389a16bda4f681c1fc253386cc4e1f19fb74fd0684dd0d31943ebe5e4"},
{file = "asyncssh-2.14.2.tar.gz", hash = "sha256:e956bf8988d07a06ba3305f6604e261f4ca014c4a232f0873f1c7692fbe3cfc2"},
{file = "asyncssh-2.15.0-py3-none-any.whl", hash = "sha256:72d140a4a4f6d246d04eabfe0052a9b94475d6d814ef1cc7bb8b3860d0ce39e5"},
{file = "asyncssh-2.15.0.tar.gz", hash = "sha256:0a13a43816f46cf084657fb05596823b93d5b48e75ef7763c8a756ec1217837a"},
]
[package.dependencies]
cryptography = ">=39.0"
typing-extensions = ">=3.6"
typing-extensions = ">=4.0.0"
[package.extras]
bcrypt = ["bcrypt (>=3.1.3)"]
@@ -81,74 +92,89 @@ compiler = ["black (>=19.3b0)", "isort (>=5.11.5,<6.0.0)", "jinja2 (>=3.0.3)"]
[[package]]
name = "certifi"
version = "2024.2.2"
version = "2024.7.4"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
{file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
{file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
{file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
{file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
]
[[package]]
name = "cffi"
version = "1.16.0"
version = "1.17.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
files = [
{file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
{file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
{file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
{file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
{file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
{file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
{file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
{file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
{file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
{file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
{file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
{file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
{file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
{file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
{file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
{file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
{file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
{file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
{file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
{file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
{file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
{file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
{file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
{file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
{file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
{file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
{file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
{file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
{file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
{file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
{file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
{file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"},
{file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"},
{file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"},
{file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"},
{file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"},
{file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"},
{file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"},
{file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"},
{file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"},
{file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"},
{file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"},
{file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"},
{file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"},
{file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"},
{file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"},
{file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"},
{file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"},
{file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"},
{file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"},
{file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"},
{file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"},
{file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"},
{file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"},
{file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"},
{file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"},
{file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"},
{file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"},
{file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"},
{file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"},
{file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"},
{file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"},
{file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"},
{file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"},
{file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"},
{file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"},
{file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"},
{file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"},
]
[package.dependencies]
@@ -192,43 +218,38 @@ files = [
[[package]]
name = "cryptography"
version = "42.0.7"
version = "43.0.0"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
files = [
{file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477"},
{file = "cryptography-42.0.7-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a"},
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604"},
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8"},
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55"},
{file = "cryptography-42.0.7-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc"},
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2"},
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13"},
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da"},
{file = "cryptography-42.0.7-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7"},
{file = "cryptography-42.0.7-cp37-abi3-win32.whl", hash = "sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b"},
{file = "cryptography-42.0.7-cp37-abi3-win_amd64.whl", hash = "sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678"},
{file = "cryptography-42.0.7-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4"},
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858"},
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785"},
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda"},
{file = "cryptography-42.0.7-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9"},
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e"},
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f"},
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1"},
{file = "cryptography-42.0.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886"},
{file = "cryptography-42.0.7-cp39-abi3-win32.whl", hash = "sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda"},
{file = "cryptography-42.0.7-cp39-abi3-win_amd64.whl", hash = "sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b"},
{file = "cryptography-42.0.7-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82"},
{file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60"},
{file = "cryptography-42.0.7-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd"},
{file = "cryptography-42.0.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582"},
{file = "cryptography-42.0.7-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562"},
{file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14"},
{file = "cryptography-42.0.7-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9"},
{file = "cryptography-42.0.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68"},
{file = "cryptography-42.0.7.tar.gz", hash = "sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2"},
{file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"},
{file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"},
{file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"},
{file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"},
{file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"},
{file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"},
{file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"},
{file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"},
{file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"},
{file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"},
{file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"},
]
[package.dependencies]
@@ -241,7 +262,7 @@ nox = ["nox"]
pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
[[package]]
@@ -257,13 +278,13 @@ files = [
[[package]]
name = "exceptiongroup"
version = "1.2.1"
version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
{file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
]
[package.extras]
@@ -271,18 +292,18 @@ test = ["pytest (>=6)"]
[[package]]
name = "filelock"
version = "3.14.0"
version = "3.15.4"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.8"
files = [
{file = "filelock-3.14.0-py3-none-any.whl", hash = "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f"},
{file = "filelock-3.14.0.tar.gz", hash = "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a"},
{file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"},
{file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"},
]
[package.extras]
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"]
typing = ["typing-extensions (>=4.8)"]
[[package]]
@@ -304,13 +325,13 @@ dev = ["flake8", "markdown", "twine", "wheel"]
[[package]]
name = "griffe"
version = "0.45.0"
version = "0.47.0"
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."
optional = false
python-versions = ">=3.8"
files = [
{file = "griffe-0.45.0-py3-none-any.whl", hash = "sha256:90fe5c90e1b0ca7dd6fee78f9009f4e01b37dbc9ab484a9b2c1578915db1e571"},
{file = "griffe-0.45.0.tar.gz", hash = "sha256:85cb2868d026ea51c89bdd589ad3ccc94abc5bd8d5d948e3d4450778a2a05b4a"},
{file = "griffe-0.47.0-py3-none-any.whl", hash = "sha256:07a2fd6a8c3d21d0bbb0decf701d62042ccc8a576645c7f8799fe1f10de2b2de"},
{file = "griffe-0.47.0.tar.gz", hash = "sha256:95119a440a3c932b13293538bdbc405bee4c36428547553dc6b327e7e7d35e5a"},
]
[package.dependencies]
@@ -429,13 +450,13 @@ files = [
[[package]]
name = "identify"
version = "2.5.36"
version = "2.6.0"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"},
{file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"},
{file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"},
{file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"},
]
[package.extras]
@@ -454,22 +475,22 @@ files = [
[[package]]
name = "importlib-metadata"
version = "7.1.0"
version = "8.2.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "importlib_metadata-7.1.0-py3-none-any.whl", hash = "sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570"},
{file = "importlib_metadata-7.1.0.tar.gz", hash = "sha256:b78938b926ee8d5f020fc4772d487045805a55ddbad2ecf21c6d60938dc7fcd2"},
{file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"},
{file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
testing = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "isort"
@@ -805,27 +826,24 @@ files = [
[[package]]
name = "nodeenv"
version = "1.8.0"
version = "1.9.1"
description = "Node.js virtual environment builder"
optional = false
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
{file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
{file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
]
[package.dependencies]
setuptools = "*"
[[package]]
name = "packaging"
version = "24.0"
version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.7"
python-versions = ">=3.8"
files = [
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
@@ -892,13 +910,13 @@ virtualenv = ">=20.10.0"
[[package]]
name = "pyaml"
version = "23.12.0"
version = "24.7.0"
description = "PyYAML-based module to produce a bit more pretty and readable YAML-serialized data"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyaml-23.12.0-py3-none-any.whl", hash = "sha256:90407d74c95a55d9b41d3860fcc1759640444d2795df748a328d077bc4f58393"},
{file = "pyaml-23.12.0.tar.gz", hash = "sha256:ce6f648efdfb1b3a5579f8cedb04facf0fa1e8f64846b639309b585bb322b4e5"},
{file = "pyaml-24.7.0-py3-none-any.whl", hash = "sha256:6b06596cb5ac438a3fad1e1bf5775088c4d3afb927e2b03a29305d334835deb2"},
{file = "pyaml-24.7.0.tar.gz", hash = "sha256:5d0fdf9e681036fb263a783d0298fc3af580a6e2a6cf1a3314ffc48dc3d91ccb"},
]
[package.dependencies]
@@ -920,13 +938,13 @@ files = [
[[package]]
name = "pymdown-extensions"
version = "10.8.1"
version = "10.9"
description = "Extension pack for Python Markdown."
optional = false
python-versions = ">=3.8"
files = [
{file = "pymdown_extensions-10.8.1-py3-none-any.whl", hash = "sha256:f938326115884f48c6059c67377c46cf631c733ef3629b6eed1349989d1b30cb"},
{file = "pymdown_extensions-10.8.1.tar.gz", hash = "sha256:3ab1db5c9e21728dabf75192d71471f8e50f216627e9a1fa9535ecb0231b9940"},
{file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"},
{file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"},
]
[package.dependencies]
@@ -952,62 +970,64 @@ six = ">=1.5"
[[package]]
name = "pyyaml"
version = "6.0.1"
version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.6"
python-versions = ">=3.8"
files = [
{file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
{file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
{file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
{file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
{file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
{file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
{file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
{file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
{file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
{file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
{file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
{file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
{file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
{file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
{file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
{file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
{file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
{file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
{file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
{file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
{file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
{file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
{file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
{file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
{file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
{file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
{file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
{file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
{file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
{file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
{file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
{file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
{file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
{file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"},
{file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
{file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
{file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
{file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
{file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
{file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
{file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
{file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
{file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
{file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
{file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
{file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
{file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
{file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
{file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
{file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
{file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
{file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
{file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
{file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
{file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
{file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
{file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
{file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
{file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
{file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
{file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
{file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
{file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
{file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
{file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
]
[[package]]
@@ -1024,22 +1044,6 @@ files = [
[package.dependencies]
pyyaml = "*"
[[package]]
name = "setuptools"
version = "69.5.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"},
{file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "six"
version = "1.16.0"
@@ -1075,24 +1079,24 @@ files = [
[[package]]
name = "typing-extensions"
version = "4.11.0"
version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
name = "virtualenv"
version = "20.26.2"
version = "20.26.3"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.7"
files = [
{file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"},
{file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"},
{file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"},
{file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"},
]
[package.dependencies]
@@ -1106,40 +1110,43 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess
[[package]]
name = "watchdog"
version = "4.0.0"
version = "4.0.1"
description = "Filesystem events monitoring"
optional = false
python-versions = ">=3.8"
files = [
{file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"},
{file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"},
{file = "watchdog-4.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c"},
{file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b"},
{file = "watchdog-4.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935"},
{file = "watchdog-4.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b"},
{file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257"},
{file = "watchdog-4.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19"},
{file = "watchdog-4.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b"},
{file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85"},
{file = "watchdog-4.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4"},
{file = "watchdog-4.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605"},
{file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101"},
{file = "watchdog-4.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca"},
{file = "watchdog-4.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8"},
{file = "watchdog-4.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b"},
{file = "watchdog-4.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92"},
{file = "watchdog-4.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87"},
{file = "watchdog-4.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269"},
{file = "watchdog-4.0.0-py3-none-win32.whl", hash = "sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c"},
{file = "watchdog-4.0.0-py3-none-win_amd64.whl", hash = "sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245"},
{file = "watchdog-4.0.0-py3-none-win_ia64.whl", hash = "sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7"},
{file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"},
{file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645"},
{file = "watchdog-4.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b"},
{file = "watchdog-4.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b"},
{file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5"},
{file = "watchdog-4.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767"},
{file = "watchdog-4.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459"},
{file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175"},
{file = "watchdog-4.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7"},
{file = "watchdog-4.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28"},
{file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35"},
{file = "watchdog-4.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db"},
{file = "watchdog-4.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709"},
{file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba"},
{file = "watchdog-4.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235"},
{file = "watchdog-4.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682"},
{file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7"},
{file = "watchdog-4.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5"},
{file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193"},
{file = "watchdog-4.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625"},
{file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd"},
{file = "watchdog-4.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_armv7l.whl", hash = "sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_i686.whl", hash = "sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64.whl", hash = "sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5"},
{file = "watchdog-4.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84"},
{file = "watchdog-4.0.1-py3-none-win32.whl", hash = "sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429"},
{file = "watchdog-4.0.1-py3-none-win_amd64.whl", hash = "sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a"},
{file = "watchdog-4.0.1-py3-none-win_ia64.whl", hash = "sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d"},
{file = "watchdog-4.0.1.tar.gz", hash = "sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44"},
]
[package.extras]
@@ -1147,13 +1154,13 @@ watchmedo = ["PyYAML (>=3.10)"]
[[package]]
name = "wheel"
version = "0.43.0"
version = "0.44.0"
description = "A built-package format for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "wheel-0.43.0-py3-none-any.whl", hash = "sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81"},
{file = "wheel-0.43.0.tar.gz", hash = "sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85"},
{file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"},
{file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"},
]
[package.extras]
@@ -1161,20 +1168,20 @@ test = ["pytest (>=6.0.0)", "setuptools (>=65)"]
[[package]]
name = "zipp"
version = "3.18.2"
version = "3.19.2"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
{file = "zipp-3.18.2-py3-none-any.whl", hash = "sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e"},
{file = "zipp-3.18.2.tar.gz", hash = "sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059"},
{file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
{file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
testing = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.8"
content-hash = "b2b3bd7e96e830fbad7f57ea91d517400a500ec3686ceda841d07f8d34efd52b"
content-hash = "be96d28fb2d257294061aa04fa3046c5e2a7c672275f819cb253b366b52554b3"

View File

@@ -17,8 +17,8 @@ from dataclasses import asdict, dataclass, field
from pyasic.config.fans import FanModeConfig
from pyasic.config.mining import MiningModeConfig
from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig
from pyasic.config.power_scaling import PowerScalingConfig
from pyasic.config.temperature import TemperatureConfig
from pyasic.misc import merge_dicts
@@ -32,9 +32,6 @@ class MinerConfig:
fan_mode: FanModeConfig = field(default_factory=FanModeConfig.default)
temperature: TemperatureConfig = field(default_factory=TemperatureConfig.default)
mining_mode: MiningModeConfig = field(default_factory=MiningModeConfig.default)
power_scaling: PowerScalingConfig = field(
default_factory=PowerScalingConfig.default
)
def __getitem__(self, item):
try:
@@ -54,7 +51,6 @@ class MinerConfig:
**self.mining_mode.as_am_modern(),
**self.pools.as_am_modern(user_suffix=user_suffix),
**self.temperature.as_am_modern(),
**self.power_scaling.as_am_modern(),
}
def as_wm(self, user_suffix: str = None) -> dict:
@@ -64,7 +60,6 @@ class MinerConfig:
**self.mining_mode.as_wm(),
**self.pools.as_wm(user_suffix=user_suffix),
**self.temperature.as_wm(),
**self.power_scaling.as_wm(),
}
def as_am_old(self, user_suffix: str = None) -> dict:
@@ -74,7 +69,6 @@ class MinerConfig:
**self.mining_mode.as_am_old(),
**self.pools.as_am_old(user_suffix=user_suffix),
**self.temperature.as_am_old(),
**self.power_scaling.as_am_old(),
}
def as_goldshell(self, user_suffix: str = None) -> dict:
@@ -84,7 +78,6 @@ class MinerConfig:
**self.mining_mode.as_goldshell(),
**self.pools.as_goldshell(user_suffix=user_suffix),
**self.temperature.as_goldshell(),
**self.power_scaling.as_goldshell(),
}
def as_avalon(self, user_suffix: str = None) -> dict:
@@ -94,7 +87,6 @@ class MinerConfig:
**self.mining_mode.as_avalon(),
**self.pools.as_avalon(user_suffix=user_suffix),
**self.temperature.as_avalon(),
**self.power_scaling.as_avalon(),
}
def as_inno(self, user_suffix: str = None) -> dict:
@@ -104,7 +96,6 @@ class MinerConfig:
**self.mining_mode.as_inno(),
**self.pools.as_inno(user_suffix=user_suffix),
**self.temperature.as_inno(),
**self.power_scaling.as_inno(),
}
def as_bosminer(self, user_suffix: str = None) -> dict:
@@ -113,7 +104,6 @@ class MinerConfig:
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
**self.mining_mode.as_bosminer(),
**self.pools.as_bosminer(user_suffix=user_suffix),
**self.power_scaling.as_bosminer(),
}
def as_boser(self, user_suffix: str = None) -> dict:
@@ -123,7 +113,6 @@ class MinerConfig:
**self.temperature.as_boser(),
**self.mining_mode.as_boser(),
**self.pools.as_boser(user_suffix=user_suffix),
**self.power_scaling.as_boser(),
}
def as_epic(self, user_suffix: str = None) -> dict:
@@ -132,7 +121,6 @@ class MinerConfig:
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
**self.mining_mode.as_epic(),
**self.pools.as_epic(user_suffix=user_suffix),
**self.power_scaling.as_epic(),
}
def as_auradine(self, user_suffix: str = None) -> dict:
@@ -142,7 +130,6 @@ class MinerConfig:
**self.temperature.as_auradine(),
**self.mining_mode.as_auradine(),
**self.pools.as_auradine(user_suffix=user_suffix),
**self.power_scaling.as_auradine(),
}
def as_mara(self, user_suffix: str = None) -> dict:
@@ -151,7 +138,14 @@ class MinerConfig:
**self.temperature.as_mara(),
**self.mining_mode.as_mara(),
**self.pools.as_mara(user_suffix=user_suffix),
**self.power_scaling.as_mara(),
}
def as_bitaxe(self, user_suffix: str = None) -> dict:
return {
**self.fan_mode.as_bitaxe(),
**self.temperature.as_bitaxe(),
**self.mining_mode.as_bitaxe(),
**self.pools.as_bitaxe(user_suffix=user_suffix),
}
@classmethod
@@ -162,7 +156,6 @@ class MinerConfig:
mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
power_scaling=PowerScalingConfig.from_dict(dict_conf.get("power_scaling")),
)
@classmethod
@@ -202,7 +195,6 @@ class MinerConfig:
mining_mode=MiningModeConfig.from_bosminer(toml_conf),
fan_mode=FanModeConfig.from_bosminer(toml_conf),
temperature=TemperatureConfig.from_bosminer(toml_conf),
power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
)
@classmethod
@@ -213,7 +205,6 @@ class MinerConfig:
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
temperature=TemperatureConfig.from_boser(grpc_miner_conf),
power_scaling=PowerScalingConfig.from_boser(grpc_miner_conf),
)
@classmethod
@@ -252,3 +243,10 @@ class MinerConfig:
fan_mode=FanModeConfig.from_mara(web_miner_config),
mining_mode=MiningModeConfig.from_mara(web_miner_config),
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_bitaxe(web_system_info),
fan_mode=FanModeConfig.from_bitaxe(web_system_info),
)

View File

@@ -46,7 +46,7 @@ class MinerConfigOption(Enum):
return self.value.as_bosminer()
def as_boser(self) -> dict:
return self.value.as_boser()
return self.value.as_boser
def as_epic(self) -> dict:
return self.value.as_epic()
@@ -60,6 +60,9 @@ class MinerConfigOption(Enum):
def as_mara(self) -> dict:
return self.value.as_mara()
def as_bitaxe(self) -> dict:
return self.value.as_bitaxe()
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
@@ -74,7 +77,6 @@ class MinerConfigOption(Enum):
raise KeyError
@dataclass
class MinerConfigValue:
@classmethod
@@ -120,6 +122,9 @@ class MinerConfigValue:
def as_mara(self) -> dict:
return {}
def as_bitaxe(self) -> dict:
return {}
def __getitem__(self, item):
try:
return getattr(self, item)

View File

@@ -80,6 +80,9 @@ class FanModeNormal(MinerConfigValue):
},
}
def as_bitaxe(self) -> dict:
return {"autoFanspeed": 1}
@dataclass
class FanModeManual(MinerConfigValue):
@@ -138,6 +141,9 @@ class FanModeManual(MinerConfigValue):
},
}
def as_bitaxe(self) -> dict:
return {"autoFanspeed": 0, "fanspeed": self.speed}
@dataclass
class FanModeImmersion(MinerConfigValue):
@@ -291,3 +297,10 @@ class FanModeConfig(MinerConfigOption):
except LookupError:
pass
return cls.default()
@classmethod
def from_bitaxe(cls, web_system_info: dict):
if web_system_info["autofanspeed"] == 1:
return cls.normal()
else:
return cls.manual(speed=web_system_info["fanspeed"])

View File

@@ -20,16 +20,23 @@ from dataclasses import dataclass, field
from pyasic import settings
from pyasic.config.base import MinerConfigOption, MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
DpsHashrateTarget,
DpsPowerTarget,
DpsTarget,
HashrateTargetMode,
PerformanceMode,
Power,
PowerTargetMode,
SaveAction,
SetDpsRequest,
SetPerformanceModeRequest,
TeraHashrate,
TunerPerformanceMode,
)
from .algo import TunerAlgo
from .scaling import ScalingConfig
@dataclass
class MiningModeNormal(MinerConfigValue):
@@ -140,56 +147,12 @@ class MiningModeHPM(MinerConfigValue):
return {"mode": {"mode": "turbo"}}
@dataclass
class StandardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
def as_epic(self) -> str:
return VOptAlgo().as_epic()
@dataclass
class VOptAlgo(MinerConfigValue):
mode: str = field(init=False, default="voltage_optimizer")
def as_epic(self) -> str:
return "VoltageOptimizer"
@dataclass
class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="chip_tune")
def as_epic(self) -> str:
return "ChipTune"
@dataclass
class TunerAlgo(MinerConfigOption):
standard = StandardTuneAlgo
voltage_optimizer = VOptAlgo
chip_tune = ChipTuneAlgo
@classmethod
def default(cls):
return cls.standard()
@classmethod
def from_dict(cls, dict_conf: dict | None):
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
cls_attr = getattr(cls, mode)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)
@dataclass
class MiningModePowerTune(MinerConfigValue):
mode: str = field(init=False, default="power_tuning")
power: int = None
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
@@ -198,6 +161,8 @@ class MiningModePowerTune(MinerConfigValue):
cls_conf["power"] = dict_conf["power"]
if dict_conf.get("algo"):
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
if dict_conf.get("scaling"):
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
return cls(**cls_conf)
@@ -212,13 +177,26 @@ class MiningModePowerTune(MinerConfigValue):
return {}
def as_bosminer(self) -> dict:
conf = {"enabled": True, "mode": "power_target"}
tuning_cfg = {"enabled": True, "mode": "power_target"}
if self.power is not None:
conf["power_target"] = self.power
return {"autotuning": conf}
tuning_cfg["power_target"] = self.power
cfg = {"autotuning": tuning_cfg}
if self.scaling is not None:
scaling_cfg = {"enabled": True}
if self.scaling.step is not None:
scaling_cfg["power_step"] = self.scaling.step
if self.scaling.minimum is not None:
scaling_cfg["min_power_target"] = self.scaling.minimum
if self.scaling.shutdown is not None:
scaling_cfg = {**scaling_cfg, **self.scaling.shutdown.as_bosminer()}
cfg["performance_scaling"] = scaling_cfg
return cfg
def as_boser(self) -> dict:
return {
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
mode=PerformanceMode(
@@ -230,6 +208,23 @@ class MiningModePowerTune(MinerConfigValue):
),
),
}
if self.scaling is not None:
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
enable=True,
**sd_cfg,
target=DpsTarget(
power_target=DpsPowerTarget(
power_step=Power(self.scaling.step),
min_power_target=Power(self.scaling.minimum),
)
),
),
)
return cfg
def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
@@ -250,21 +245,18 @@ class MiningModePowerTune(MinerConfigValue):
class MiningModeHashrateTune(MinerConfigValue):
mode: str = field(init=False, default="hashrate_tuning")
hashrate: int = None
throttle_limit: int = None
throttle_step: int = None
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModeHashrateTune":
cls_conf = {}
if dict_conf.get("hashrate"):
cls_conf["hashrate"] = dict_conf["hashrate"]
if dict_conf.get("throttle_limit"):
cls_conf["throttle_limit"] = dict_conf["throttle_limit"]
if dict_conf.get("throttle_step"):
cls_conf["throttle_step"] = dict_conf["throttle_step"]
if dict_conf.get("algo"):
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
if dict_conf.get("scaling"):
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
return cls(**cls_conf)
@@ -279,8 +271,9 @@ class MiningModeHashrateTune(MinerConfigValue):
conf["hashrate_target"] = self.hashrate
return {"autotuning": conf}
@property
def as_boser(self) -> dict:
return {
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
mode=PerformanceMode(
@@ -294,6 +287,23 @@ class MiningModeHashrateTune(MinerConfigValue):
),
)
}
if self.scaling is not None:
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
enable=True,
**sd_cfg,
target=DpsTarget(
hashrate_target=DpsHashrateTarget(
hashrate_step=TeraHashrate(self.scaling.step),
min_hashrate_target=TeraHashrate(self.scaling.minimum),
)
),
),
)
return cfg
def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
@@ -305,10 +315,11 @@ class MiningModeHashrateTune(MinerConfigValue):
"target": self.hashrate,
}
}
if self.throttle_limit is not None:
mode["ptune"]["min_throttle"] = self.throttle_limit
if self.throttle_step is not None:
mode["ptune"]["throttle_step"] = self.throttle_step
if self.scaling is not None:
if self.scaling.minimum is not None:
mode["ptune"]["min_throttle"] = self.scaling.minimum
if self.scaling.step is not None:
mode["ptune"]["throttle_step"] = self.scaling.step
return mode
def as_mara(self) -> dict:
@@ -373,6 +384,24 @@ class MiningModeManual(MinerConfigValue):
}
return cls(global_freq=freq, global_volt=voltage, boards=boards)
@classmethod
def from_epic(cls, epic_conf: dict) -> "MiningModeManual":
voltage = 0
freq = 0
if epic_conf.get("HwConfig") is not None:
freq = epic_conf["HwConfig"]["Boards Target Clock"][0]["Data"]
if epic_conf.get("Power Supply Stats") is not None:
voltage = epic_conf["Power Supply Stats"]["Target Voltage"]
boards = {}
if epic_conf.get("HBs") is not None:
boards = {
board["Index"]: ManualBoardSettings(
freq=board["Core Clock Avg"], volt=board["Input Voltage"]
)
for board in epic_conf["HBs"]
}
return cls(global_freq=freq, global_volt=voltage, boards=boards)
def as_mara(self) -> dict:
return {
"mode": {
@@ -432,15 +461,32 @@ class MiningModeConfig(MinerConfigOption):
if tuner_running:
algo_info = web_conf["PerpetualTune"]["Algorithm"]
if algo_info.get("VoltageOptimizer") is not None:
scaling_cfg = None
if "Throttle Step" in algo_info["VoltageOptimizer"]:
scaling_cfg = ScalingConfig(
minimum=algo_info["VoltageOptimizer"].get(
"Min Throttle Target"
),
step=algo_info["VoltageOptimizer"].get("Throttle Step"),
)
return cls.hashrate_tuning(
hashrate=algo_info["VoltageOptimizer"].get("Target"),
throttle_limit=algo_info["VoltageOptimizer"].get(
"Min Throttle Target"
),
throttle_step=algo_info["VoltageOptimizer"].get(
"Throttle Step"
),
algo=TunerAlgo.voltage_optimizer(),
scaling=scaling_cfg,
)
elif algo_info.get("BoardTune") is not None:
scaling_cfg = None
if "Throttle Step" in algo_info["BoardTune"]:
scaling_cfg = ScalingConfig(
minimum=algo_info["BoardTune"].get("Min Throttle Target"),
step=algo_info["BoardTune"].get("Throttle Step"),
)
return cls.hashrate_tuning(
hashrate=algo_info["BoardTune"].get("Target"),
algo=TunerAlgo.board_tune(),
scaling=scaling_cfg,
)
else:
return cls.hashrate_tuning(
@@ -448,7 +494,7 @@ class MiningModeConfig(MinerConfigOption):
algo=TunerAlgo.chip_tune(),
)
else:
return cls.normal()
return MiningModeManual.from_epic(web_conf)
except KeyError:
return cls.default()
@@ -465,18 +511,31 @@ class MiningModeConfig(MinerConfigOption):
if autotuning_conf.get("psu_power_limit") is not None:
# old autotuning conf
return cls.power_tuning(autotuning_conf["psu_power_limit"])
return cls.power_tuning(
autotuning_conf["psu_power_limit"],
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
if autotuning_conf.get("mode") is not None:
# new autotuning conf
mode = autotuning_conf["mode"]
if mode == "power_target":
if autotuning_conf.get("power_target") is not None:
return cls.power_tuning(autotuning_conf["power_target"])
return cls.power_tuning()
return cls.power_tuning(
autotuning_conf["power_target"],
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
return cls.power_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
if mode == "hashrate_target":
if autotuning_conf.get("hashrate_target") is not None:
return cls.hashrate_tuning(autotuning_conf["hashrate_target"])
return cls.hashrate_tuning()
return cls.hashrate_tuning(
autotuning_conf["hashrate_target"],
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
return cls.hashrate_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
@classmethod
def from_vnish(cls, web_settings: dict):
@@ -502,24 +561,40 @@ class MiningModeConfig(MinerConfigOption):
if tuner_conf.get("tunerMode") is not None:
if tuner_conf["tunerMode"] == 1:
if tuner_conf.get("powerTarget") is not None:
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
return cls.power_tuning()
return cls.power_tuning(
tuner_conf["powerTarget"]["watt"],
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
)
return cls.power_tuning(
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power")
)
if tuner_conf["tunerMode"] == 2:
if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning(
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
scaling=ScalingConfig.from_boser(
grpc_miner_conf, mode="hashrate"
),
)
return cls.hashrate_tuning()
return cls.hashrate_tuning(
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
)
if tuner_conf.get("powerTarget") is not None:
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
return cls.power_tuning(
tuner_conf["powerTarget"]["watt"],
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
)
if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning(
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
)
return cls.default()
@classmethod
def from_auradine(cls, web_mode: dict):
try:

View File

@@ -0,0 +1,59 @@
from __future__ import annotations
from dataclasses import dataclass, field
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@dataclass
class StandardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
def as_epic(self) -> str:
return VOptAlgo().as_epic()
@dataclass
class VOptAlgo(MinerConfigValue):
mode: str = field(init=False, default="voltage_optimizer")
def as_epic(self) -> str:
return "VoltageOptimizer"
@dataclass
class BoardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="board_tune")
def as_epic(self) -> str:
return "BoardTune"
@dataclass
class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="chip_tune")
def as_epic(self) -> str:
return "ChipTune"
@dataclass
class TunerAlgo(MinerConfigOption):
standard = StandardTuneAlgo
voltage_optimizer = VOptAlgo
board_tune = BoardTuneAlgo
chip_tune = ChipTuneAlgo
@classmethod
def default(cls):
return cls.standard()
@classmethod
def from_dict(cls, dict_conf: dict | None):
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
cls_attr = getattr(cls, mode)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)

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. -
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
@dataclass
class ScalingShutdown(MinerConfigValue):
enabled: bool = False
duration: int = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown":
return cls(
enabled=dict_conf.get("enabled", False), duration=dict_conf.get("duration")
)
@classmethod
def from_bosminer(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdown_enabled")
if sd_enabled is not None:
return cls(sd_enabled, power_scaling_conf.get("shutdown_duration"))
return None
@classmethod
def from_boser(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdownEnabled")
if sd_enabled is not None:
try:
return cls(sd_enabled, power_scaling_conf["shutdownDuration"]["hours"])
except KeyError:
return cls(sd_enabled)
return None
def as_bosminer(self) -> dict:
cfg = {"shutdown_enabled": self.enabled}
if self.duration is not None:
cfg["shutdown_duration"] = self.duration
return cfg
def as_boser(self) -> dict:
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
@dataclass
class ScalingConfig(MinerConfigValue):
step: int = None
minimum: int = None
shutdown: ScalingShutdown = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig":
cls_conf = {
"step": dict_conf.get("step"),
"minimum": dict_conf.get("minimum"),
}
shutdown = dict_conf.get("shutdown")
if shutdown is not None:
cls_conf["shutdown"] = ScalingShutdown.from_dict(shutdown)
return cls(**cls_conf)
@classmethod
def from_bosminer(cls, toml_conf: dict, mode: str = None):
if mode == "power":
return cls._from_bosminer_power(toml_conf)
if mode == "hashrate":
# not implemented yet
pass
@classmethod
def _from_bosminer_power(cls, toml_conf: dict):
power_scaling = toml_conf.get("power_scaling")
if power_scaling is None:
power_scaling = toml_conf.get("performance_scaling")
if power_scaling is not None:
enabled = power_scaling.get("enabled")
if not enabled:
return None
power_step = power_scaling.get("power_step")
min_power = power_scaling.get("min_psu_power_limit")
if min_power is None:
min_power = power_scaling.get("min_power_target")
sd_mode = ScalingShutdown.from_bosminer(power_scaling)
return cls(step=power_step, minimum=min_power, shutdown=sd_mode)
@classmethod
def from_boser(cls, grpc_miner_conf: dict, mode: str = None):
if mode == "power":
return cls._from_boser_power(grpc_miner_conf)
if mode == "hashrate":
# not implemented yet
pass
@classmethod
def _from_boser_power(cls, grpc_miner_conf: dict):
try:
dps_conf = grpc_miner_conf["dps"]
if not dps_conf.get("enabled", False):
return None
except LookupError:
return None
conf = {"shutdown": ScalingShutdown.from_boser(dps_conf)}
if dps_conf.get("minPowerTarget") is not None:
conf["minimum"] = dps_conf["minPowerTarget"]["watt"]
if dps_conf.get("powerStep") is not None:
conf["step"] = dps_conf["powerStep"]["watt"]
return cls(**conf)

View File

@@ -21,6 +21,13 @@ from dataclasses import dataclass, field
from typing import List
from pyasic.config.base import MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
PoolConfiguration,
PoolGroupConfiguration,
Quota,
SaveAction,
SetPoolGroupsRequest,
)
@dataclass
@@ -127,6 +134,18 @@ class Pool(MinerConfigValue):
}
return {"url": self.url, "user": self.user, "pass": self.password}
def as_bitaxe(self, user_suffix: str = None) -> dict:
return {
"stratumURL": self.url,
"stratumUser": f"{self.user}{user_suffix}",
"stratumPassword": self.password,
}
def as_boser(self) -> PoolConfiguration:
return PoolConfiguration(
url=self.url, user=self.user, password=self.password, enabled=True
)
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "Pool":
return cls(
@@ -194,6 +213,15 @@ class Pool(MinerConfigValue):
password=web_pool["pass"],
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "Pool":
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
return cls(
url=url,
user=web_system_info["stratumUser"],
password=web_system_info.get("stratumPassword", ""),
)
@dataclass
class PoolGroup(MinerConfigValue):
@@ -287,6 +315,16 @@ class PoolGroup(MinerConfigValue):
def as_mara(self, user_suffix: str = None) -> list:
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
def as_bitaxe(self, user_suffix: str = None) -> dict:
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
def as_boser(self, user_suffix: str = None) -> PoolGroupConfiguration:
return PoolGroupConfiguration(
name=self.name,
quota=Quota(value=self.quota),
pools=[p.as_boser() for p in self.pools],
)
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
cls_conf = {}
@@ -360,6 +398,10 @@ class PoolGroup(MinerConfigValue):
def from_mara(cls, web_config_pools: dict) -> "PoolGroup":
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
return cls(pools=[Pool.from_bitaxe(web_system_info)])
@dataclass
class PoolConfig(MinerConfigValue):
@@ -423,7 +465,12 @@ class PoolConfig(MinerConfigValue):
return {"group": [PoolGroup().as_bosminer()]}
def as_boser(self, user_suffix: str = None) -> dict:
return {}
return {
"set_pool_groups": SetPoolGroupsRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
)
}
def as_auradine(self, user_suffix: str = None) -> dict:
if len(self.groups) > 0:
@@ -456,6 +503,9 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
return {"pools": []}
def as_bitaxe(self, user_suffix: str = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
@classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig":
try:
@@ -514,3 +564,7 @@ class PoolConfig(MinerConfigValue):
@classmethod
def from_mara(cls, web_config: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_bitaxe(web_system_info)])

View File

@@ -1,221 +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. -
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass, field
from pyasic.config.base import MinerConfigOption, MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
DpsPowerTarget,
DpsTarget,
Power,
SetDpsRequest,
)
@dataclass
class PowerScalingShutdownEnabled(MinerConfigValue):
mode: str = field(init=False, default="enabled")
duration: int = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingShutdownEnabled":
return cls(duration=dict_conf.get("duration"))
def as_bosminer(self) -> dict:
cfg = {"shutdown_enabled": True}
if self.duration is not None:
cfg["shutdown_duration"] = self.duration
return cfg
def as_boser(self) -> dict:
return {"enable_shutdown": True, "shutdown_duration": self.duration}
@dataclass
class PowerScalingShutdownDisabled(MinerConfigValue):
mode: str = field(init=False, default="disabled")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingShutdownDisabled":
return cls()
def as_bosminer(self) -> dict:
return {"shutdown_enabled": False}
def as_boser(self) -> dict:
return {"enable_shutdown ": False}
class PowerScalingShutdown(MinerConfigOption):
enabled = PowerScalingShutdownEnabled
disabled = PowerScalingShutdownDisabled
@classmethod
def from_dict(cls, dict_conf: dict | None):
if dict_conf is None:
return cls.default()
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
clsattr = getattr(cls, mode)
if clsattr is not None:
return clsattr().from_dict(dict_conf)
@classmethod
def from_bosminer(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdown_enabled")
if sd_enabled is not None:
if sd_enabled:
return cls.enabled(power_scaling_conf.get("shutdown_duration"))
else:
return cls.disabled()
return None
@classmethod
def from_boser(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdownEnabled")
if sd_enabled is not None:
if sd_enabled:
try:
return cls.enabled(power_scaling_conf["shutdownDuration"]["hours"])
except KeyError:
return cls.enabled()
else:
return cls.disabled()
return None
@dataclass
class PowerScalingEnabled(MinerConfigValue):
mode: str = field(init=False, default="enabled")
power_step: int = None
minimum_power: int = None
shutdown_enabled: PowerScalingShutdownEnabled | PowerScalingShutdownDisabled = None
@classmethod
def from_bosminer(cls, power_scaling_conf: dict) -> "PowerScalingEnabled":
power_step = power_scaling_conf.get("power_step")
min_power = power_scaling_conf.get("min_psu_power_limit")
if min_power is None:
min_power = power_scaling_conf.get("min_power_target")
sd_mode = PowerScalingShutdown.from_bosminer(power_scaling_conf)
return cls(
power_step=power_step, minimum_power=min_power, shutdown_enabled=sd_mode
)
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "PowerScalingEnabled":
cls_conf = {
"power_step": dict_conf.get("power_step"),
"minimum_power": dict_conf.get("minimum_power"),
}
shutdown_enabled = dict_conf.get("shutdown_enabled")
if shutdown_enabled is not None:
cls_conf["shutdown_enabled"] = PowerScalingShutdown.from_dict(
shutdown_enabled
)
return cls(**cls_conf)
def as_bosminer(self) -> dict:
cfg = {"enabled": True}
if self.power_step is not None:
cfg["power_step"] = self.power_step
if self.minimum_power is not None:
cfg["min_power_target"] = self.minimum_power
if self.shutdown_enabled is not None:
cfg = {**cfg, **self.shutdown_enabled.as_bosminer()}
return {"performance_scaling": cfg}
def as_boser(self) -> dict:
return {
"set_dps": SetDpsRequest(
enable=True,
**self.shutdown_enabled.as_boser(),
target=DpsTarget(
power_target=DpsPowerTarget(
power_step=Power(self.power_step),
min_power_target=Power(self.minimum_power),
)
),
),
}
@dataclass
class PowerScalingDisabled(MinerConfigValue):
mode: str = field(init=False, default="disabled")
class PowerScalingConfig(MinerConfigOption):
enabled = PowerScalingEnabled
disabled = PowerScalingDisabled
@classmethod
def default(cls):
return cls.disabled()
@classmethod
def from_dict(cls, dict_conf: dict | None):
if dict_conf is None:
return cls.default()
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
clsattr = getattr(cls, mode)
if clsattr is not None:
return clsattr().from_dict(dict_conf)
@classmethod
def from_bosminer(cls, toml_conf: dict):
power_scaling = toml_conf.get("power_scaling")
if power_scaling is None:
power_scaling = toml_conf.get("performance_scaling")
if power_scaling is not None:
enabled = power_scaling.get("enabled")
if enabled is not None:
if enabled:
return cls.enabled().from_bosminer(power_scaling)
else:
return cls.disabled()
return cls.default()
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
try:
dps_conf = grpc_miner_conf["dps"]
if not dps_conf.get("enabled", False):
return cls.disabled()
except LookupError:
return cls.default()
conf = {"shutdown_enabled": PowerScalingShutdown.from_boser(dps_conf)}
if dps_conf.get("minPowerTarget") is not None:
conf["minimum_power"] = dps_conf["minPowerTarget"]["watt"]
if dps_conf.get("powerStep") is not None:
conf["power_step"] = dps_conf["powerStep"]["watt"]
return cls.enabled(**conf)

View File

@@ -29,6 +29,7 @@ from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .fans import Fan
from .hashrate import AlgoHashRate, HashUnit
from pyasic.data.pools import PoolMetrics
@dataclass
@@ -71,6 +72,7 @@ class MinerData:
fault_light: Whether the fault light is on as a boolean.
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
is_mining: Whether the miner is mining.
pools: A list of PoolMetrics instances, each representing metrics for a pool.
"""
# general
@@ -143,6 +145,9 @@ class MinerData:
uptime: int = None
efficiency: int = field(init=False)
# pools
pools: list[PoolMetrics] = field(default_factory=list)
@classmethod
def fields(cls):
return [f.name for f in fields(cls) if not f.name.startswith("_")]

View File

@@ -33,6 +33,9 @@ class HashBoard:
expected_chips: The expected chip count of the board as an int.
serial_number: The serial number of the board.
missing: Whether the board is returned from the miners data as a bool.
tuned: Whether the board is tuned as a bool.
active: Whether the board is currently tuning as a bool.
voltage: Current input voltage of the board as a float.
"""
slot: int = 0
@@ -43,6 +46,9 @@ class HashBoard:
expected_chips: int = None
serial_number: str = None
missing: bool = True
tuned: bool = None
active: bool = None
voltage: float = None
def get(self, __key: str, default: Any = None):
try:

View File

@@ -1,4 +1,35 @@
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
from urllib.parse import urlparse
class Scheme(Enum):
STRATUM_V1 = "stratum+tcp"
STRATUM_V2 = "stratum2+tcp"
@dataclass
class PoolUrl:
scheme: Scheme
host: str
port: int
pubkey: Optional[str] = None
def __str__(self) -> str:
if self.scheme == Scheme.STRATUM_V2 and self.pubkey:
return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}"
else:
return f"{self.scheme.value}://{self.host}:{self.port}"
@classmethod
def from_str(cls, url: str) -> "PoolUrl":
parsed_url = urlparse(url)
scheme = Scheme(parsed_url.scheme)
host = parsed_url.hostname
port = parsed_url.port
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
@dataclass
@@ -19,13 +50,13 @@ class PoolMetrics:
pool_stale_percent: Percentage of stale shares by the pool.
"""
url: PoolUrl
accepted: int = None
rejected: int = None
get_failures: int = None
remote_failures: int = None
active: bool = None
alive: bool = None
url: str = None
index: int = None
user: str = None
pool_rejected_percent: float = field(init=False)

View File

@@ -25,6 +25,7 @@ class MinerMake(str, Enum):
GOLDSHELL = "Goldshell"
AURADINE = "Auradine"
EPIC = "ePIC"
BITAXE = "BitAxe"
def __str__(self):
return self.value

View File

@@ -329,6 +329,15 @@ class AuradineModels(str, Enum):
return self.value
class BitAxeModels(str, Enum):
BM1366 = "Ultra"
BM1368 = "Supra"
BM1397 = "Max"
def __str__(self):
return self.value
class MinerModel:
ANTMINER = AntminerModels
WHATSMINER = WhatsminerModels
@@ -337,3 +346,4 @@ class MinerModel:
GOLDSHELL = GoldshellModels
AURADINE = AuradineModels
EPIC = ePICModels
BITAXE = BitAxeModels

View File

@@ -29,6 +29,7 @@ from pyasic.miners.device.models import (
S19kProNoPIC,
S19Plus,
S19Pro,
S19ProPlusHydro,
)
@@ -82,3 +83,7 @@ class BOSMinerS19jProPlusNoPIC(BOSer, S19jProPlusNoPIC):
class BOSMinerS19XP(BOSer, S19XP):
pass
class BOSMinerS19ProPlusHydro(BOSer, S19ProPlusHydro):
pass

View File

@@ -27,6 +27,7 @@ from .S19 import (
BOSMinerS19kProNoPIC,
BOSMinerS19Plus,
BOSMinerS19Pro,
BOSMinerS19ProPlusHydro,
BOSMinerS19XP,
)
from .T19 import BOSMinerT19

View File

@@ -24,6 +24,7 @@ from pyasic.miners.device.models import (
S19jPro,
S19NoPIC,
S19Pro,
S19ProHydro,
)
@@ -57,3 +58,7 @@ class VNishS19j(VNish, S19j):
class VNishS19jPro(VNish, S19jPro):
pass
class VNishS19ProHydro(VNish, S19ProHydro):
pass

View File

@@ -22,6 +22,7 @@ from .S19 import (
VNishS19jPro,
VNishS19NoPIC,
VNishS19Pro,
VNishS19ProHydro,
VNishS19XP,
)
from .T19 import VNishT19

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 pyasic.miners.backends import VNish
from pyasic.miners.device.models import S21
class VNishS21(VNish, S21):
pass

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# 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 .S21 import VNishS21

View File

@@ -18,3 +18,4 @@ from .X3 import *
from .X7 import *
from .X17 import *
from .X19 import *
from .X21 import *

View File

@@ -19,6 +19,7 @@ from typing import List, Optional, Union
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.backends.cgminer import CGMiner
@@ -79,6 +80,10 @@ ANTMINER_MODERN_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -90,7 +95,7 @@ class AntminerModern(BMMiner):
web: AntminerModernWebAPI
_rpc_cls = AntminerRPCAPI
web: AntminerRPCAPI
rpc: AntminerRPCAPI
_ssh_cls = AntminerModernSSH
ssh: AntminerModernSSH
@@ -151,7 +156,7 @@ class AntminerModern(BMMiner):
await self.send_config(cfg)
return True
async def _get_hostname(self, web_get_system_info: dict = None) -> Union[str, None]:
async def _get_hostname(self, web_get_system_info: dict = None) -> Optional[str]:
if web_get_system_info is None:
try:
web_get_system_info = await self.web.get_system_info()
@@ -164,7 +169,7 @@ class AntminerModern(BMMiner):
except KeyError:
pass
async def _get_mac(self, web_get_system_info: dict = None) -> Union[str, None]:
async def _get_mac(self, web_get_system_info: dict = None) -> Optional[str]:
if web_get_system_info is None:
try:
web_get_system_info = await self.web.get_system_info()
@@ -259,7 +264,9 @@ class AntminerModern(BMMiner):
pass
return self.light
async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
@@ -275,7 +282,7 @@ class AntminerModern(BMMiner):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass
@@ -351,6 +358,36 @@ class AntminerModern(BMMiner):
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data
ANTMINER_OLD_DATA_LOC = DataLocations(
**{
@@ -412,7 +449,7 @@ class AntminerOld(CGMiner):
self.config = config
await self.web.set_miner_conf(config.as_am_old(user_suffix=user_suffix))
async def _get_mac(self) -> Union[str, None]:
async def _get_mac(self) -> Optional[str]:
try:
data = await self.web.get_system_info()
if data:

View File

@@ -236,7 +236,7 @@ class Auradine(StockFirmware):
except LookupError:
pass
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()

View File

@@ -173,7 +173,7 @@ class AvalonMiner(CGMiner):
except (KeyError, ValueError):
pass
async def _get_hashrate(self, rpc_devs: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_devs: dict = None) -> Optional[AlgoHashRate]:
if rpc_devs is None:
try:
rpc_devs = await self.rpc.devs()
@@ -238,7 +238,9 @@ class AvalonMiner(CGMiner):
return hashboards
async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
@@ -251,7 +253,7 @@ class AvalonMiner(CGMiner):
parsed_stats = self.parse_stats(unparsed_stats)
return AlgoHashRate.SHA256(
parsed_stats["GHSmm"], HashUnit.SHA256.GH
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass

View File

@@ -105,7 +105,7 @@ class BFGMiner(StockFirmware):
return self.fw_ver
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
# get hr from API
if rpc_summary is None:
try:
@@ -207,7 +207,9 @@ class BFGMiner(StockFirmware):
return fans
async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
# X19 method, not sure compatibility
if rpc_stats is None:
try:
@@ -224,6 +226,6 @@ class BFGMiner(StockFirmware):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -0,0 +1,189 @@
from typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.device import MinerFirmware
from pyasic.miners.base import BaseMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.web.bitaxe import BitAxeWebAPI
BITAXE_DATA_LOC = DataLocations(
**{
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.HOSTNAME): DataFunction(
"_get_hostname",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[WebAPICommand("web_system_info", "system/info")],
),
}
)
class BitAxe(BaseMiner):
"""Handler for BitAxe"""
web: BitAxeWebAPI
_web_cls = BitAxeWebAPI
firmware = MinerFirmware.STOCK
data_locations = BITAXE_DATA_LOC
async def reboot(self) -> bool:
await self.web.restart()
return True
async def get_config(self) -> MinerConfig:
web_system_info = await self.web.system_info()
return MinerConfig.from_bitaxe(web_system_info)
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
await self.web.update_settings(**config.as_bitaxe())
async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return round(web_system_info["power"])
except KeyError:
pass
async def _get_hashrate(
self, web_system_info: dict = None
) -> Optional[AlgoHashRate]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return AlgoHashRate.SHA256(
web_system_info["hashRate"], HashUnit.SHA256.GH
).into(self.algo.unit.default)
except KeyError:
pass
async def _get_uptime(self, web_system_info: dict = None) -> Optional[int]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["uptimeSeconds"]
except KeyError:
pass
async def _get_hashboards(self, web_system_info: dict = None) -> List[HashBoard]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return [
HashBoard(
hashrate=AlgoHashRate.SHA256(
web_system_info["hashRate"], HashUnit.SHA256.GH
).into(self.algo.unit.default),
chip_temp=web_system_info.get("temp"),
temp=web_system_info.get("vrTemp"),
chips=web_system_info.get("asicCount", 1),
expected_chips=self.expected_chips,
missing=False,
active=True,
voltage=web_system_info.get("voltage"),
)
]
except KeyError:
pass
return []
async def _get_fans(self, web_system_info: dict = None) -> List[Fan]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return [Fan(speed=web_system_info["fanrpm"])]
except KeyError:
pass
return []
async def _get_hostname(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["hostname"]
except KeyError:
pass
async def _get_api_ver(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["version"]
except KeyError:
pass
async def _get_fw_ver(self, web_system_info: dict = None) -> Optional[str]:
if web_system_info is None:
try:
web_system_info = await self.web.system_info()
except APIError:
pass
if web_system_info is not None:
try:
return web_system_info["version"]
except KeyError:
pass

View File

@@ -109,7 +109,7 @@ class BMMiner(StockFirmware):
return self.fw_ver
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
# get hr from API
if rpc_summary is None:
try:
@@ -223,7 +223,9 @@ class BMMiner(StockFirmware):
return fans
async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
# X19 method, not sure compatibility
if rpc_stats is None:
try:
@@ -240,7 +242,7 @@ class BMMiner(StockFirmware):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -13,16 +13,20 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import base64
import logging
import time
from pathlib import Path
from typing import List, Optional, Union
import aiofiles
import toml
from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.data import (
DataFunction,
@@ -91,6 +95,10 @@ BOSMINER_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -181,7 +189,6 @@ class BOSMiner(BraiinsOSFirmware):
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config
print(config)
parsed_cfg = config.as_bosminer(user_suffix=user_suffix)
toml_conf = toml.dumps(
@@ -341,7 +348,7 @@ class BOSMiner(BraiinsOSFirmware):
return None
return hostname
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()
@@ -517,7 +524,9 @@ class BOSMiner(BraiinsOSFirmware):
except (TypeError, AttributeError):
return self.light
async def _get_expected_hashrate(self, rpc_devs: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_devs: dict = None
) -> Optional[AlgoHashRate]:
if rpc_devs is None:
try:
rpc_devs = await self.rpc.devs()
@@ -570,6 +579,85 @@ class BOSMiner(BraiinsOSFirmware):
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data
async def upgrade_firmware(self, file: Path):
"""
Upgrade the firmware of the BOSMiner device.
Args:
file (Path): The local file path of the firmware to be uploaded.
Returns:
str: Confirmation message after upgrading the firmware.
"""
try:
logging.info("Starting firmware upgrade process.")
if not file:
raise ValueError("File location must be provided for firmware upgrade.")
# Read the firmware file contents
async with aiofiles.open(file, "rb") as f:
upgrade_contents = await f.read()
# Encode the firmware contents in base64
encoded_contents = base64.b64encode(upgrade_contents).decode("utf-8")
# Upload the firmware file to the BOSMiner device
logging.info(f"Uploading firmware file from {file} to the device.")
await self.ssh.send_command(
f"echo {encoded_contents} | base64 -d > /tmp/firmware.tar && sysupgrade /tmp/firmware.tar"
)
logging.info("Firmware upgrade process completed successfully.")
return "Firmware upgrade completed successfully."
except FileNotFoundError as e:
logging.error(f"File not found during the firmware upgrade process: {e}")
raise
except ValueError as e:
logging.error(
f"Validation error occurred during the firmware upgrade process: {e}"
)
raise
except OSError as e:
logging.error(f"OS error occurred during the firmware upgrade process: {e}")
raise
except Exception as e:
logging.error(
f"An unexpected error occurred during the firmware upgrade process: {e}",
exc_info=True,
)
raise
BOSER_DATA_LOC = DataLocations(
**{
@@ -633,6 +721,10 @@ BOSER_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("grpc_pool_groups", "get_pool_groups")]
)
}
)
@@ -694,6 +786,11 @@ class BOSer(BraiinsOSFirmware):
return MinerConfig.from_boser(grpc_conf)
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
boser_cfg = config.as_boser(user_suffix=user_suffix)
for key in boser_cfg:
await self.web.send_command(key, message=boser_cfg[key])
async def set_power_limit(self, wattage: int) -> bool:
try:
result = await self.web.set_power_target(
@@ -779,7 +876,7 @@ class BOSer(BraiinsOSFirmware):
except LookupError:
pass
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()
@@ -796,7 +893,7 @@ class BOSer(BraiinsOSFirmware):
async def _get_expected_hashrate(
self, grpc_miner_details: dict = None
) -> Optional[float]:
) -> Optional[AlgoHashRate]:
if grpc_miner_details is None:
try:
grpc_miner_details = await self.web.get_miner_details()
@@ -971,3 +1068,27 @@ class BOSer(BraiinsOSFirmware):
return int(rpc_summary["SUMMARY"][0]["Elapsed"])
except LookupError:
pass
async def _get_pools(self, grpc_pool_groups: dict = None) -> List[PoolMetrics]:
if grpc_pool_groups is None:
try:
grpc_pool_groups = await self.web.get_pool_groups()
except APIError:
return []
pools_data = []
for group in grpc_pool_groups["poolGroups"]:
for idx, pool_info in enumerate(group["pools"]):
pool_data = PoolMetrics(
url=pool_info["url"],
user=pool_info["user"],
index=idx,
accepted=pool_info["stats"]["acceptedShares"],
rejected=pool_info["stats"]["rejectedShares"],
get_failures=pool_info["stats"]["stale_shares"],
remote_failures=0,
active=pool_info["active"],
alive=pool_info["alive"]
)
pools_data.append(pool_data)
return pools_data

View File

@@ -15,8 +15,11 @@
# ------------------------------------------------------------------------------
import logging
from pathlib import Path
from typing import List, Optional
import aiofiles
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
@@ -386,7 +389,7 @@ class BTMiner(StockFirmware):
return hostname
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()
@@ -562,7 +565,9 @@ class BTMiner(StockFirmware):
pass
return errors
async def _get_expected_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_summary: dict = None
) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()
@@ -575,7 +580,7 @@ class BTMiner(StockFirmware):
if expected_hashrate:
return AlgoHashRate.SHA256(
expected_hashrate, HashUnit.SHA256.GH
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass
@@ -649,3 +654,48 @@ class BTMiner(StockFirmware):
return int(rpc_summary["SUMMARY"][0]["Elapsed"])
except LookupError:
pass
async def upgrade_firmware(self, file: Path, token: str):
"""
Upgrade the firmware of the Whatsminer device.
Args:
file (Path): The local file path of the firmware to be uploaded.
token (str): The authentication token for the firmware upgrade.
Returns:
str: Confirmation message after upgrading the firmware.
"""
try:
logging.info("Starting firmware upgrade process for Whatsminer.")
if not file:
raise ValueError("File location must be provided for firmware upgrade.")
# Read the firmware file contents
async with aiofiles.open(file, "rb") as f:
upgrade_contents = await f.read()
result = await self.rpc.update_firmware(upgrade_contents)
logging.info(
"Firmware upgrade process completed successfully for Whatsminer."
)
return result
except FileNotFoundError as e:
logging.error(f"File not found during the firmware upgrade process: {e}")
raise
except ValueError as e:
logging.error(
f"Validation error occurred during the firmware upgrade process: {e}"
)
raise
except OSError as e:
logging.error(f"OS error occurred during the firmware upgrade process: {e}")
raise
except Exception as e:
logging.error(
f"An unexpected error occurred during the firmware upgrade process: {e}",
exc_info=True,
)
raise

View File

@@ -109,7 +109,7 @@ class CGMiner(StockFirmware):
return self.fw_ver
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()

View File

@@ -14,11 +14,13 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from pathlib import Path
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.data.pools import PoolMetrics
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
@@ -58,10 +60,6 @@ EPIC_DATA_LOC = DataLocations(
"_get_wattage",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.VOLTAGE): DataFunction(
"_get_voltage",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_summary", "summary")],
@@ -82,6 +80,10 @@ EPIC_DATA_LOC = DataLocations(
"_is_mining",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("web_summary", "summary")],
),
}
)
@@ -219,21 +221,7 @@ class ePIC(ePICFirmware):
except KeyError:
pass
async def _get_voltage(self, web_summary: dict = None) -> Optional[float]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
voltage = web_summary["Power Supply Stats"]["Output Voltage"]
return voltage
except KeyError:
pass
async def _get_hashrate(self, web_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, web_summary: dict = None) -> Optional[AlgoHashRate]:
if web_summary is None:
try:
web_summary = await self.web.summary()
@@ -252,7 +240,9 @@ class ePIC(ePICFirmware):
except (LookupError, ValueError, TypeError):
pass
async def _get_expected_hashrate(self, web_summary: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, web_summary: dict = None
) -> Optional[AlgoHashRate]:
if web_summary is None:
try:
web_summary = await self.web.summary()
@@ -323,6 +313,24 @@ class ePIC(ePICFirmware):
except APIError:
pass
tuned = True
active = False
if web_summary is not None:
tuner_running = web_summary["PerpetualTune"]["Running"]
if tuner_running:
active = True
algo_info = web_summary["PerpetualTune"]["Algorithm"]
if algo_info.get("VoltageOptimizer") is not None:
tuned = algo_info["VoltageOptimizer"].get("Optimized")
elif algo_info.get("BoardTune") is not None:
tuned = algo_info["BoardTune"].get("Optimized")
else:
tuned = algo_info["ChipTune"].get("Optimized")
# To be extra detailed, also ensure the miner is in "Mining" state
tuned = tuned and web_summary["Status"]["Operating State"] == "Mining"
active = active and web_summary["Status"]["Operating State"] == "Mining"
hb_list = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -349,6 +357,9 @@ class ePIC(ePICFirmware):
).into(self.algo.unit.default)
hb_list[hb["Index"]].chips = num_of_chips
hb_list[hb["Index"]].temp = hb["Temperature"]
hb_list[hb["Index"]].tuned = tuned
hb_list[hb["Index"]].active = active
hb_list[hb["Index"]].voltage = hb["Input Voltage"]
return hb_list
async def _is_mining(self, web_summary, *args, **kwargs) -> Optional[bool]:
@@ -411,3 +422,48 @@ class ePIC(ePICFirmware):
except KeyError:
pass
return errors
async def _get_pools(self, web_summary: dict = None) -> List[PoolMetrics]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
pool_data = []
try:
if web_summary is not None:
if (
web_summary.get("Session") is not None
and web_summary.get("Stratum") is not None
):
pool_data.append(
PoolMetrics(
accepted=web_summary["Session"].get("Accepted"),
rejected=web_summary["Session"].get("Rejected"),
get_failures=0,
remote_failures=0,
active=web_summary["Stratum"].get("IsPoolConnected"),
alive=web_summary["Stratum"].get("IsPoolConnected"),
url=web_summary["Stratum"].get("Current Pool"),
user=web_summary["Stratum"].get("Current User"),
index=web_summary["Stratum"].get("Config Id"),
)
)
return pool_data
except LookupError:
pass
async def upgrade_firmware(self, file: Path | str, keep_settings: bool = True) -> bool:
"""
Upgrade the firmware of the ePIC miner device.
Args:
file (Path | str): The local file path of the firmware to be uploaded.
keep_settings (bool): Whether to keep the current settings after the update.
Returns:
bool: Whether the firmware update succeeded.
"""
return await self.web.system_update(file=file, keep_settings=keep_settings)

View File

@@ -29,6 +29,7 @@ from pyasic.miners.data import (
WebAPICommand,
)
from pyasic.web.innosilicon import InnosiliconWebAPI
from pyasic.data.pools import PoolMetrics, PoolUrl
INNOSILICON_DATA_LOC = DataLocations(
**{
@@ -90,6 +91,10 @@ INNOSILICON_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")]
)
}
)
@@ -111,7 +116,7 @@ class Innosilicon(CGMiner):
except APIError:
return self.config
self.config = MinerConfig.from_inno(pools)
self.config = MinerConfig.from_inno([pools])
return self.config
async def reboot(self) -> bool:
@@ -169,7 +174,7 @@ class Innosilicon(CGMiner):
async def _get_hashrate(
self, rpc_summary: dict = None, web_get_all: dict = None
) -> Optional[float]:
) -> Optional[AlgoHashRate]:
if web_get_all:
web_get_all = web_get_all["all"]
@@ -365,3 +370,33 @@ class Innosilicon(CGMiner):
level = int(level)
limit = 1250 + (250 * level)
return limit
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -17,6 +17,7 @@ from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import LuxOSFirmware
@@ -51,6 +52,9 @@ LUXMINER_DATA_LOC = DataLocations(
str(DataOptions.UPTIME): DataFunction(
"_get_uptime", [RPCAPICommand("rpc_stats", "stats")]
),
str(DataOptions.POOLS): DataFunction(
"_get_pools", [RPCAPICommand("rpc_pools", "pools")]
),
}
)
@@ -162,7 +166,7 @@ class LUXMiner(LuxOSFirmware):
return mac
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()
@@ -263,7 +267,9 @@ class LUXMiner(LuxOSFirmware):
fans.append(Fan())
return fans
async def _get_expected_hashrate(self, rpc_stats: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
@@ -279,7 +285,7 @@ class LUXMiner(LuxOSFirmware):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass
@@ -295,3 +301,33 @@ class LUXMiner(LuxOSFirmware):
return int(rpc_stats["STATS"][1]["Elapsed"])
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -225,7 +225,7 @@ class MaraMiner(MaraFirmware):
except LookupError:
pass
async def _get_hashrate(self, web_brief: dict = None) -> Optional[float]:
async def _get_hashrate(self, web_brief: dict = None) -> Optional[AlgoHashRate]:
if web_brief is None:
try:
web_brief = await self.web.brief()
@@ -271,7 +271,9 @@ class MaraMiner(MaraFirmware):
pass
return False
async def _get_expected_hashrate(self, web_brief: dict = None) -> Optional[float]:
async def _get_expected_hashrate(
self, web_brief: dict = None
) -> Optional[AlgoHashRate]:
if web_brief is None:
try:
web_brief = await self.web.brief()
@@ -282,13 +284,13 @@ class MaraMiner(MaraFirmware):
try:
return AlgoHashRate.SHA256(
web_brief["hashrate_ideal"], HashUnit.SHA256.GH
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass
async def _get_wattage_limit(
self, web_miner_config: dict = None
) -> Optional[float]:
) -> Optional[AlgoHashRate]:
if web_miner_config is None:
try:
web_miner_config = await self.web.get_miner_config()

View File

@@ -15,7 +15,7 @@
from typing import List, Optional, Tuple
from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data import AlgoHashRate, Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.miners.base import BaseMiner
from pyasic.rpc.unknown import UnknownRPCAPI
@@ -80,7 +80,7 @@ class UnknownMiner(BaseMiner):
async def _get_hostname(self) -> Optional[str]:
return None
async def _get_hashrate(self) -> Optional[float]:
async def _get_hashrate(self) -> Optional[AlgoHashRate]:
return None
async def _get_hashboards(self) -> List[HashBoard]:
@@ -113,7 +113,7 @@ class UnknownMiner(BaseMiner):
async def _get_fault_light(self) -> bool:
return False
async def _get_expected_hashrate(self) -> Optional[float]:
async def _get_expected_hashrate(self) -> Optional[AlgoHashRate]:
return None
async def _is_mining(self, *args, **kwargs) -> Optional[bool]:

View File

@@ -84,7 +84,7 @@ VNISH_DATA_LOC = DataLocations(
)
class VNish(BMMiner, VNishFirmware):
class VNish(VNishFirmware, BMMiner):
"""Handler for VNish miners"""
_web_cls = VNishWebAPI
@@ -147,6 +147,22 @@ class VNish(BMMiner, VNishFirmware):
except KeyError:
pass
async def fault_light_off(self) -> bool:
result = await self.web.find_miner()
if result is not None:
if result.get("on") is False:
return True
else:
await self.web.find_miner()
async def fault_light_on(self) -> bool:
result = await self.web.find_miner()
if result is not None:
if result.get("on") is True:
return True
else:
await self.web.find_miner()
async def _get_hostname(self, web_summary: dict = None) -> str:
if web_summary is None:
web_info = await self.web.info()
@@ -177,7 +193,7 @@ class VNish(BMMiner, VNishFirmware):
except KeyError:
pass
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[float]:
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
# get hr from API
if rpc_summary is None:
try:

View File

@@ -19,9 +19,10 @@ import warnings
from typing import List, Optional, Protocol, Tuple, Type, TypeVar, Union
from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard, MinerData
from pyasic.data import AlgoHashRate, Fan, HashBoard, MinerData
from pyasic.data.device import DeviceInfo
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.pools import PoolMetrics
from pyasic.device import MinerModel
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.firmware import MinerFirmware
@@ -237,7 +238,7 @@ class MinerProtocol(Protocol):
"""
return await self._get_hostname()
async def get_hashrate(self) -> Optional[float]:
async def get_hashrate(self) -> Optional[AlgoHashRate]:
"""Get the hashrate of the miner and return it as a float in TH/s.
Returns:
@@ -317,7 +318,7 @@ class MinerProtocol(Protocol):
"""
return await self._get_fault_light()
async def get_expected_hashrate(self) -> Optional[float]:
async def get_expected_hashrate(self) -> Optional[AlgoHashRate]:
"""Get the nominal hashrate from factory if available.
Returns:
@@ -341,6 +342,14 @@ class MinerProtocol(Protocol):
"""
return await self._get_uptime()
async def get_pools(self) -> List[PoolMetrics]:
"""Get the pools information from Miner.
Return:
The pool information of the miner.
"""
return await self._get_pools()
async def _get_mac(self) -> Optional[str]:
pass
@@ -353,7 +362,7 @@ class MinerProtocol(Protocol):
async def _get_hostname(self) -> Optional[str]:
pass
async def _get_hashrate(self) -> Optional[float]:
async def _get_hashrate(self) -> Optional[AlgoHashRate]:
pass
async def _get_hashboards(self) -> List[HashBoard]:
@@ -383,7 +392,7 @@ class MinerProtocol(Protocol):
async def _get_fault_light(self) -> Optional[bool]:
pass
async def _get_expected_hashrate(self) -> Optional[float]:
async def _get_expected_hashrate(self) -> Optional[AlgoHashRate]:
pass
async def _is_mining(self) -> Optional[bool]:
@@ -392,56 +401,69 @@ class MinerProtocol(Protocol):
async def _get_uptime(self) -> Optional[int]:
pass
async def _get_pools(self) -> List[PoolMetrics]:
pass
async def _get_data(
self,
allow_warning: bool,
include: List[Union[str, DataOptions]] = None,
exclude: List[Union[str, DataOptions]] = None,
) -> dict:
# handle include
if include is not None:
include = [str(i) for i in include]
else:
# everything
include = [str(enum_value.value) for enum_value in DataOptions]
# handle exclude
# prioritized over include, including x and excluding x will exclude x
if exclude is not None:
for item in exclude:
if str(item) in include:
include.remove(str(item))
api_multicommand = set()
web_multicommand = []
rpc_multicommand = set()
web_multicommand = set()
# create multicommand
for data_name in include:
try:
# get kwargs needed for the _get_xyz function
fn_args = getattr(self.data_locations, data_name).kwargs
# keep track of which RPC/Web commands need to be sent
for arg in fn_args:
if isinstance(arg, RPCAPICommand):
api_multicommand.add(arg.cmd)
rpc_multicommand.add(arg.cmd)
if isinstance(arg, WebAPICommand):
if arg.cmd not in web_multicommand:
web_multicommand.append(arg.cmd)
web_multicommand.add(arg.cmd)
except KeyError as e:
logger.error(e, data_name)
logger.error(type(e), e, data_name)
continue
if len(api_multicommand) > 0:
api_command_task = asyncio.create_task(
self.api.multicommand(*api_multicommand, allow_warning=allow_warning)
# create tasks for all commands that need to be sent, or no-op with sleep(0) -> None
if len(rpc_multicommand) > 0:
rpc_command_task = asyncio.create_task(
self.rpc.multicommand(*rpc_multicommand, allow_warning=allow_warning)
)
else:
api_command_task = asyncio.sleep(0)
rpc_command_task = asyncio.create_task(asyncio.sleep(0))
if len(web_multicommand) > 0:
web_command_task = asyncio.create_task(
self.web.multicommand(*web_multicommand, allow_warning=allow_warning)
)
else:
web_command_task = asyncio.sleep(0)
web_command_task = asyncio.create_task(asyncio.sleep(0))
web_command_data = await web_command_task
# make sure the tasks complete
await asyncio.gather(rpc_command_task, web_command_task)
# grab data out of the tasks
web_command_data = web_command_task.result()
if web_command_data is None:
web_command_data = {}
api_command_data = await api_command_task
api_command_data = rpc_command_task.result()
if api_command_data is None:
api_command_data = {}

View File

@@ -0,0 +1 @@
from .espminer import *

View File

@@ -0,0 +1,6 @@
from pyasic.miners.backends.bitaxe import BitAxe
from pyasic.miners.device.models.bitaxe import Ultra
class BitAxeUltra(BitAxe, Ultra):
pass

View File

@@ -0,0 +1,6 @@
from pyasic.miners.backends.bitaxe import BitAxe
from pyasic.miners.device.models.bitaxe import Supra
class BitAxeSupra(BitAxe, Supra):
pass

View File

@@ -0,0 +1,6 @@
from pyasic.miners.backends.bitaxe import BitAxe
from pyasic.miners.device.models.bitaxe import Max
class BitAxeMax(BitAxe, Max):
pass

View File

@@ -0,0 +1,3 @@
from .BM1366 import BitAxeUltra
from .BM1368 import BitAxeSupra
from .BM1397 import BitAxeMax

View File

@@ -0,0 +1 @@
from .BM import *

View File

@@ -37,7 +37,7 @@ class DataOptions(Enum):
IS_MINING = "is_mining"
UPTIME = "uptime"
CONFIG = "config"
VOLTAGE = "voltage"
POOLS = "pools"
def __str__(self):
return self.value

View File

@@ -44,3 +44,7 @@ class AuradineMake(BaseMiner):
class ePICMake(BaseMiner):
make = MinerMake.EPIC
class BitAxeMake(BaseMiner):
make = MinerMake.BITAXE

View File

@@ -18,6 +18,6 @@ from pyasic.miners.device.makes import AntMinerMake
class L3Plus(AntMinerMake):
raw_model = MinerModel.ANTMINER
raw_model = MinerModel.ANTMINER.L3Plus
expected_chips = 72

View File

@@ -0,0 +1,10 @@
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import BitAxeMake
class Ultra(BitAxeMake):
raw_model = MinerModel.BITAXE.BM1366
expected_hashboards = 1
expected_chips = 1
expected_fans = 1

View File

@@ -0,0 +1,10 @@
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import BitAxeMake
class Supra(BitAxeMake):
raw_model = MinerModel.BITAXE.BM1368
expected_hashboards = 1
expected_chips = 1
expected_fans = 1

View File

@@ -0,0 +1,10 @@
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import BitAxeMake
class Max(BitAxeMake):
raw_model = MinerModel.BITAXE.BM1397
expected_hashboards = 1
expected_chips = 1
expected_fans = 1

View File

@@ -0,0 +1,3 @@
from .BM1366 import Ultra
from .BM1368 import Supra
from .BM1397 import Max

View File

@@ -0,0 +1 @@
from .BM import *

View File

@@ -31,8 +31,10 @@ from pyasic.miners.antminer import *
from pyasic.miners.auradine import *
from pyasic.miners.avalonminer import *
from pyasic.miners.backends import *
from pyasic.miners.backends.bitaxe import BitAxe
from pyasic.miners.backends.unknown import UnknownMiner
from pyasic.miners.base import AnyMiner
from pyasic.miners.bitaxe import *
from pyasic.miners.blockminer import *
from pyasic.miners.device.makes import *
from pyasic.miners.goldshell import *
@@ -53,6 +55,7 @@ class MinerTypes(enum.Enum):
EPIC = 9
AURADINE = 10
MARATHON = 11
BITAXE = 12
MINER_CLASSES = {
@@ -366,6 +369,7 @@ MINER_CLASSES = {
"ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC,
"ANTMINER S19K PRO": BOSMinerS19kProNoPIC,
"ANTMINER S19 XP": BOSMinerS19XP,
"ANTMINER S19 PRO+ HYD.": BOSMinerS19ProPlusHydro,
"ANTMINER T19": BOSMinerT19,
"ANTMINER S21": BOSMinerS21,
},
@@ -383,7 +387,9 @@ MINER_CLASSES = {
"ANTMINER S19J PRO": VNishS19jPro,
"ANTMINER S19A": VNishS19a,
"ANTMINER S19A PRO": VNishS19aPro,
"ANTMINER S19 PRO HYD.": VNishS19ProHydro,
"ANTMINER T19": VNishT19,
"ANTMINER S21": VNishS21,
},
MinerTypes.EPIC: {
None: ePIC,
@@ -437,6 +443,12 @@ MINER_CLASSES = {
"ANTMINER S21": MaraS21,
"ANTMINER T21": MaraT21,
},
MinerTypes.BITAXE: {
None: BitAxe,
"BM1368": BitAxeSupra,
"BM1366": BitAxeUltra,
"BM1397": BitAxeMax,
},
}
@@ -513,6 +525,7 @@ class MinerFactory:
MinerTypes.LUX_OS: self.get_miner_model_luxos,
MinerTypes.AURADINE: self.get_miner_model_auradine,
MinerTypes.MARATHON: self.get_miner_model_marathon,
MinerTypes.BITAXE: self.get_miner_model_bitaxe,
}
fn = miner_model_fns.get(miner_type)
@@ -530,7 +543,6 @@ class MinerFactory:
miner_type=miner_type,
miner_model=miner_model,
)
return miner
async def _get_miner_type(self, ip: str) -> MinerTypes | None:
@@ -595,6 +607,8 @@ class MinerFactory:
return MinerTypes.WHATSMINER
if "Braiins OS" in web_text:
return MinerTypes.BRAIINS_OS
if "AxeOS" in web_text:
return MinerTypes.BITAXE
if "cloud-box" in web_text:
return MinerTypes.GOLDSHELL
if "AnthillOS" in web_text:
@@ -673,7 +687,7 @@ class MinerFactory:
return MinerTypes.BRAIINS_OS
if "BTMINER" in upper_data or "BITMICRO" in upper_data:
return MinerTypes.WHATSMINER
if "VNISH" in upper_data:
if "VNISH" in upper_data or "DEVICE PATH" in upper_data:
return MinerTypes.VNISH
if "HIVEON" in upper_data:
return MinerTypes.HIVEON
@@ -820,7 +834,9 @@ class MinerFactory:
async def _get_model_antminer_web(self, ip: str) -> str | None:
# last resort, this is slow
auth = httpx.DigestAuth("root", "root")
auth = httpx.DigestAuth(
"root", settings.get("default_antminer_web_password", "root")
)
web_json_data = await self.send_web_command(
ip, "/cgi-bin/get_system_info.cgi", auth=auth
)
@@ -1008,6 +1024,18 @@ class MinerFactory:
except (TypeError, LookupError):
pass
async def get_miner_model_bitaxe(self, ip: str) -> str | None:
web_json_data = await self.send_web_command(ip, "/api/system/info")
try:
miner_model = web_json_data["ASICModel"]
if miner_model == "":
return None
return miner_model
except (TypeError, LookupError):
pass
miner_factory = MinerFactory()

View File

@@ -23,6 +23,7 @@ import json
import logging
import re
from typing import Literal, Union
import struct
import httpx
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
@@ -559,11 +560,24 @@ class BTMinerRPCAPI(BaseMinerRPCAPI):
"""
return await self.send_privileged_command("set_normal_power")
async def update_firmware(self): # noqa - static
"""Not implemented."""
# to be determined if this will be added later
# requires a file stream in bytes
return NotImplementedError
async def update_firmware(self, firmware: bytes):
"""Upgrade the firmware running on the miner and using the firmware passed in bytes.
Parameters:
firmware (bytes): The firmware binary data to be uploaded.
Returns:
bool: A boolean indicating the success of the firmware upgrade.
Raises:
APIError: If the miner is not ready for firmware update.
"""
ready = await self.send_privileged_command("upgrade_firmware")
if not ready.get("Msg") == "ready":
raise APIError(f"Not ready for firmware update: {self}")
file_size = struct.pack("<I", len(firmware))
await self._send_bytes(file_size + firmware)
return True
async def reboot(self, timeout: int = 10) -> dict:
"""Reboot the miner using the API.

96
pyasic/web/bitaxe.py Normal file
View File

@@ -0,0 +1,96 @@
from __future__ import annotations
import asyncio
import json
from typing import Any
import httpx
from pyasic import APIError, settings
from pyasic.web.base import BaseWebAPI
class BitAxeWebAPI(BaseWebAPI):
async def send_command(
self,
command: str | bytes,
ignore_errors: bool = False,
allow_warning: bool = True,
privileged: bool = False,
**parameters: Any,
) -> dict:
url = f"http://{self.ip}:{self.port}/api/{command}"
try:
async with httpx.AsyncClient(
transport=settings.transport(),
) as client:
if parameters.get("post", False):
parameters.pop("post")
data = await client.post(
url,
timeout=settings.get("api_function_timeout", 3),
json=parameters,
)
elif parameters.get("patch", False):
parameters.pop("patch")
data = await client.patch(
url,
timeout=settings.get("api_function_timeout", 3),
json=parameters,
)
else:
data = await client.get(url)
except httpx.HTTPError:
pass
else:
if data.status_code == 200:
try:
return data.json()
except json.decoder.JSONDecodeError:
pass
async def multicommand(
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
) -> dict:
"""Execute multiple commands simultaneously on the BitAxe miner.
Args:
*commands (str): Commands to execute.
ignore_errors (bool): Whether to ignore errors during command execution.
allow_warning (bool): Whether to proceed despite warnings.
Returns:
dict: A dictionary containing responses for all commands executed.
"""
tasks = {}
# send all commands individually
for cmd in commands:
tasks[cmd] = asyncio.create_task(
self.send_command(cmd, allow_warning=allow_warning)
)
await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True)
data = {"multicommand": True}
for cmd in tasks:
try:
result = tasks[cmd].result()
if result is None or result == {}:
result = {}
data[cmd] = result
except APIError:
pass
return data
async def system_info(self):
return await self.send_command("system/info")
async def swarm_info(self):
return await self.send_command("swarm/info")
async def restart(self):
return await self.send_command("system/restart", post=True)
async def update_settings(self, **config):
return await self.send_command("system", patch=True, **config)

View File

@@ -414,15 +414,6 @@ class BOSerWebAPI(BaseWebAPI):
"get_pool_groups", message=GetPoolGroupsRequest(), privileged=True
)
async def create_pool_group(self) -> dict:
raise NotImplementedError
async def update_pool_group(self) -> dict:
raise NotImplementedError
async def remove_pool_group(self) -> dict:
raise NotImplementedError
async def get_miner_configuration(self) -> dict:
return await self.send_command(
"get_miner_configuration",
@@ -490,3 +481,15 @@ class BOSerWebAPI(BaseWebAPI):
),
privileged=True,
)
async def set_pool_groups(
self,
pool_groups: List[PoolGroupConfiguration],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_pool_groups",
message=SetPoolGroupsRequest(
save_action=save_action, pool_groups=pool_groups
),
)

View File

@@ -18,7 +18,7 @@ if TYPE_CHECKING:
@dataclass(eq=False, repr=False)
class ApiVersion(betterproto.Message):
"""LATEST_API_VERSION=1.0.0-beta.4"""
"""LATEST_API_VERSION=1.2.0"""
major: int = betterproto.uint64_field(1)
minor: int = betterproto.uint64_field(2)
@@ -52,6 +52,7 @@ class ApiVersionServiceStub(betterproto.ServiceStub):
class ApiVersionServiceBase(ServiceBase):
async def get_api_version(
self, api_version_request: "ApiVersionRequest"
) -> "ApiVersion":

View File

@@ -1,5 +1,5 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# sources: bos/v1/actions.proto, bos/v1/authentication.proto, bos/v1/common.proto, bos/v1/configuration.proto, bos/v1/constraints.proto, bos/v1/cooling.proto, bos/v1/license.proto, bos/v1/miner.proto, bos/v1/performance.proto, bos/v1/pool.proto, bos/v1/units.proto, bos/v1/work.proto
# sources: bos/v1/actions.proto, bos/v1/authentication.proto, bos/v1/common.proto, bos/v1/configuration.proto, bos/v1/constraints.proto, bos/v1/cooling.proto, bos/v1/license.proto, bos/v1/miner.proto, bos/v1/network.proto, bos/v1/performance.proto, bos/v1/pool.proto, bos/v1/units.proto, bos/v1/work.proto
# plugin: python-betterproto
# This file has been @generated
import warnings
@@ -69,6 +69,7 @@ class Platform(betterproto.Enum):
PLATFORM_AM3_AML = 4
PLATFORM_STM32MP157C_II1_AM2 = 5
PLATFORM_CVITEK_BM1_AM2 = 6
PLATFORM_ZYNQ_BM3_AM2 = 7
class BosMode(betterproto.Enum):
@@ -134,6 +135,12 @@ class SupportArchiveFormat(betterproto.Enum):
"""BOS custom format"""
class NetworkProtocol(betterproto.Enum):
NETWORK_PROTOCOL_UNSPECIFIED = 0
NETWORK_PROTOCOL_DHCP = 1
NETWORK_PROTOCOL_STATIC = 2
@dataclass(eq=False, repr=False)
class StartRequest(betterproto.Message):
"""Request for start bosminer action."""
@@ -256,7 +263,14 @@ class LoginRequest(betterproto.Message):
class LoginResponse(betterproto.Message):
"""Response for login action."""
pass
token: str = betterproto.string_field(1)
"""Token to be used for authentication"""
timeout_s: int = betterproto.uint32_field(2)
"""
Authentication token validity/timeout in seconds. Token validity refreshed
to this value with each request.
"""
@dataclass(eq=False, repr=False)
@@ -555,7 +569,16 @@ class TunerConfiguration(betterproto.Message):
@dataclass(eq=False, repr=False)
class TunerConstraints(betterproto.Message):
power_target: "PowerConstraints" = betterproto.message_field(1)
"""Tuner power target mode constraints"""
hashrate_target: "HashrateConstraints" = betterproto.message_field(2)
"""Tuner hashrate target mode constraints"""
enabled: "BooleanConstraint" = betterproto.message_field(3)
"""Tuner enabled enabled default value"""
default_mode: "TunerMode" = betterproto.enum_field(4)
"""Default tuner mode"""
@dataclass(eq=False, repr=False)
@@ -618,6 +641,9 @@ class DpsConstraints(betterproto.Message):
shutdown_duration: "DurationConstraints" = betterproto.message_field(6)
"""Dynamic Performance Scaling shutdown duration constraints"""
enabled: "BooleanConstraint" = betterproto.message_field(7)
"""Dynamic Performance Scaling enabled default value"""
@dataclass(eq=False, repr=False)
class HashboardConstraints(betterproto.Message):
@@ -1104,6 +1130,11 @@ class PoolStats(betterproto.Message):
generated_work: int = betterproto.uint64_field(6)
"""Generated work"""
last_share_time: Optional[datetime] = betterproto.message_field(
7, optional=True, group="_last_share_time"
)
"""Last share time"""
@dataclass(eq=False, repr=False)
class GetPoolGroupsRequest(betterproto.Message):
@@ -1184,6 +1215,33 @@ class RemovePoolGroupResponse(betterproto.Message):
pass
@dataclass(eq=False, repr=False)
class SetPoolGroupsRequest(betterproto.Message):
"""Request for setting pool groups"""
save_action: "SaveAction" = betterproto.enum_field(1)
"""
Save action SAVE just update config but changes will not be applied
SAVE_AND_APPLY and SAVE_AND_FORCE_APPLY are equal for this method. Pools
config will be updated and changes will be applied that will trigger
restart.
"""
pool_groups: List["PoolGroupConfiguration"] = betterproto.message_field(2)
"""
Pool groups configuration `uid` must not be specified (it will be
generated)
"""
@dataclass(eq=False, repr=False)
class SetPoolGroupsResponse(betterproto.Message):
"""Response on setting pool groups"""
pool_groups: List["PoolGroupConfiguration"] = betterproto.message_field(1)
"""Configured pool groups"""
@dataclass(eq=False, repr=False)
class GetMinerConfigurationRequest(betterproto.Message):
pass
@@ -1234,8 +1292,9 @@ class NoneLicense(betterproto.Message):
time_to_restricted: int = betterproto.uint32_field(1)
"""
BOS Initialization timeout - number of seconds elapsed since bosminer start
i.e., number of seconds BOS will start mining in restricted mode burning
15% of hashrate
i.e., number of seconds BOS will start mining in restricted mode burning 5%
of hashrate For more, see Section 3.10 of
https://braiins.com/os/plus/license
"""
@@ -1350,6 +1409,27 @@ class BosVersion(betterproto.Message):
bos_plus: bool = betterproto.bool_field(3)
@dataclass(eq=False, repr=False)
class ErrorCode(betterproto.Message):
code: str = betterproto.string_field(1)
reason: str = betterproto.string_field(2)
hint: str = betterproto.string_field(3)
@dataclass(eq=False, repr=False)
class Component(betterproto.Message):
name: str = betterproto.string_field(1)
index: int = betterproto.uint32_field(2)
@dataclass(eq=False, repr=False)
class MinerError(betterproto.Message):
timestamp: str = betterproto.string_field(1)
message: str = betterproto.string_field(2)
error_codes: List["ErrorCode"] = betterproto.message_field(3)
components: List["Component"] = betterproto.message_field(4)
@dataclass(eq=False, repr=False)
class GetMinerStatusRequest(betterproto.Message):
pass
@@ -1402,6 +1482,12 @@ class GetMinerDetailsResponse(betterproto.Message):
system_uptime_s: int = betterproto.uint64_field(11)
"""System uptime"""
status: "MinerStatus" = betterproto.enum_field(12)
"""Miner status"""
kernel_version: str = betterproto.string_field(13)
"""Kernel version"""
def __post_init__(self) -> None:
super().__post_init__()
if self.is_set("system_uptime"):
@@ -1437,6 +1523,17 @@ class GetMinerStatsResponse(betterproto.Message):
"""Miner power stats"""
@dataclass(eq=False, repr=False)
class GetErrorsRequest(betterproto.Message):
pass
@dataclass(eq=False, repr=False)
class GetErrorsResponse(betterproto.Message):
errors: List["MinerError"] = betterproto.message_field(1)
"""Array of errors"""
@dataclass(eq=False, repr=False)
class Hashboard(betterproto.Message):
"""Structure to handle HB details"""
@@ -1467,6 +1564,9 @@ class Hashboard(betterproto.Message):
stats: "WorkSolverStats" = betterproto.message_field(8)
"""Hashboard stats"""
model: Optional[str] = betterproto.string_field(9, optional=True, group="_model")
"""Hashboard model"""
@dataclass(eq=False, repr=False)
class GetSupportArchiveRequest(betterproto.Message):
@@ -1530,6 +1630,108 @@ class HashboardEnableState(betterproto.Message):
"""Flag if hashboard is enabled or not"""
@dataclass(eq=False, repr=False)
class GetNetworkConfigurationRequest(betterproto.Message):
pass
@dataclass(eq=False, repr=False)
class GetNetworkConfigurationResponse(betterproto.Message):
network: "NetworkConfiguration" = betterproto.message_field(1)
@dataclass(eq=False, repr=False)
class SetNetworkConfigurationRequest(betterproto.Message):
dhcp: "Dhcp" = betterproto.message_field(1, group="protocol")
static: "Static" = betterproto.message_field(2, group="protocol")
hostname: Optional[str] = betterproto.string_field(
3, optional=True, group="_hostname"
)
"""Hostname. Existing value will be preserved if this field is not set."""
@dataclass(eq=False, repr=False)
class SetNetworkConfigurationResponse(betterproto.Message):
network: "NetworkConfiguration" = betterproto.message_field(1)
@dataclass(eq=False, repr=False)
class NetworkConfiguration(betterproto.Message):
dhcp: "Dhcp" = betterproto.message_field(1, group="protocol")
static: "Static" = betterproto.message_field(2, group="protocol")
hostname: str = betterproto.string_field(3)
"""Hostname"""
@dataclass(eq=False, repr=False)
class Dhcp(betterproto.Message):
pass
@dataclass(eq=False, repr=False)
class Static(betterproto.Message):
address: str = betterproto.string_field(1)
"""IP address"""
netmask: str = betterproto.string_field(2)
"""Netmask"""
gateway: str = betterproto.string_field(3)
"""Gateway"""
dns_servers: List[str] = betterproto.string_field(4)
"""DNS servers"""
@dataclass(eq=False, repr=False)
class GetNetworkInfoRequest(betterproto.Message):
pass
@dataclass(eq=False, repr=False)
class GetNetworkInfoResponse(betterproto.Message):
"""
Response message for GetCurrentNetworkConfiguration Represents the current
network configuration for the default network interface. Only IPv4 is
supported.
"""
name: str = betterproto.string_field(1)
"""Name of the network interface"""
mac_address: Optional[str] = betterproto.string_field(
2, optional=True, group="_mac_address"
)
"""MAC address of the network interface"""
hostname: Optional[str] = betterproto.string_field(
3, optional=True, group="_hostname"
)
"""Miner hostname"""
protocol: Optional["NetworkProtocol"] = betterproto.enum_field(
4, optional=True, group="_protocol"
)
"""Network protocol"""
dns_servers: List[str] = betterproto.string_field(5)
"""List of configured DNS servers"""
networks: List["IpNetwork"] = betterproto.message_field(6)
"""List of assigned IP addresses"""
default_gateway: Optional[str] = betterproto.string_field(
7, optional=True, group="_default_gateway"
)
"""Default gateway/route for the interface"""
@dataclass(eq=False, repr=False)
class IpNetwork(betterproto.Message):
address: str = betterproto.string_field(1)
netmask: str = betterproto.string_field(2)
class ActionsServiceStub(betterproto.ServiceStub):
async def start(
self,
@@ -2049,6 +2251,23 @@ class PoolServiceStub(betterproto.ServiceStub):
metadata=metadata,
)
async def set_pool_groups(
self,
set_pool_groups_request: "SetPoolGroupsRequest",
*,
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> "SetPoolGroupsResponse":
return await self._unary_unary(
"/braiins.bos.v1.PoolService/SetPoolGroups",
set_pool_groups_request,
SetPoolGroupsResponse,
timeout=timeout,
deadline=deadline,
metadata=metadata,
)
class ConfigurationServiceStub(betterproto.ServiceStub):
async def get_miner_configuration(
@@ -2158,6 +2377,23 @@ class MinerServiceStub(betterproto.ServiceStub):
metadata=metadata,
)
async def get_errors(
self,
get_errors_request: "GetErrorsRequest",
*,
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> "GetErrorsResponse":
return await self._unary_unary(
"/braiins.bos.v1.MinerService/GetErrors",
get_errors_request,
GetErrorsResponse,
timeout=timeout,
deadline=deadline,
metadata=metadata,
)
async def get_hashboards(
self,
get_hashboards_request: "GetHashboardsRequest",
@@ -2228,7 +2464,61 @@ class MinerServiceStub(betterproto.ServiceStub):
)
class NetworkServiceStub(betterproto.ServiceStub):
async def get_network_configuration(
self,
get_network_configuration_request: "GetNetworkConfigurationRequest",
*,
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> "GetNetworkConfigurationResponse":
return await self._unary_unary(
"/braiins.bos.v1.NetworkService/GetNetworkConfiguration",
get_network_configuration_request,
GetNetworkConfigurationResponse,
timeout=timeout,
deadline=deadline,
metadata=metadata,
)
async def set_network_configuration(
self,
set_network_configuration_request: "SetNetworkConfigurationRequest",
*,
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> "SetNetworkConfigurationResponse":
return await self._unary_unary(
"/braiins.bos.v1.NetworkService/SetNetworkConfiguration",
set_network_configuration_request,
SetNetworkConfigurationResponse,
timeout=timeout,
deadline=deadline,
metadata=metadata,
)
async def get_network_info(
self,
get_network_info_request: "GetNetworkInfoRequest",
*,
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> "GetNetworkInfoResponse":
return await self._unary_unary(
"/braiins.bos.v1.NetworkService/GetNetworkInfo",
get_network_info_request,
GetNetworkInfoResponse,
timeout=timeout,
deadline=deadline,
metadata=metadata,
)
class ActionsServiceBase(ServiceBase):
async def start(self, start_request: "StartRequest") -> "StartResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
@@ -2357,26 +2647,23 @@ class ActionsServiceBase(ServiceBase):
RebootRequest,
RebootResponse,
),
"/braiins.bos.v1.ActionsService/SetLocateDeviceStatus": (
grpclib.const.Handler(
self.__rpc_set_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
SetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
)
"/braiins.bos.v1.ActionsService/SetLocateDeviceStatus": grpclib.const.Handler(
self.__rpc_set_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
SetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
),
"/braiins.bos.v1.ActionsService/GetLocateDeviceStatus": (
grpclib.const.Handler(
self.__rpc_get_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
GetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
)
"/braiins.bos.v1.ActionsService/GetLocateDeviceStatus": grpclib.const.Handler(
self.__rpc_get_locate_device_status,
grpclib.const.Cardinality.UNARY_UNARY,
GetLocateDeviceStatusRequest,
LocateDeviceStatusResponse,
),
}
class AuthenticationServiceBase(ServiceBase):
async def login(self, login_request: "LoginRequest") -> "LoginResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
@@ -2417,6 +2704,7 @@ class AuthenticationServiceBase(ServiceBase):
class CoolingServiceBase(ServiceBase):
async def get_cooling_state(
self, get_cooling_state_request: "GetCoolingStateRequest"
) -> "GetCoolingStateResponse":
@@ -2461,6 +2749,7 @@ class CoolingServiceBase(ServiceBase):
class PerformanceServiceBase(ServiceBase):
async def get_tuner_state(
self, get_tuner_state_request: "GetTunerStateRequest"
) -> "GetTunerStateResponse":
@@ -2648,21 +2937,17 @@ class PerformanceServiceBase(ServiceBase):
GetTunerStateRequest,
GetTunerStateResponse,
),
"/braiins.bos.v1.PerformanceService/ListTargetProfiles": (
grpclib.const.Handler(
self.__rpc_list_target_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
ListTargetProfilesRequest,
ListTargetProfilesResponse,
)
"/braiins.bos.v1.PerformanceService/ListTargetProfiles": grpclib.const.Handler(
self.__rpc_list_target_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
ListTargetProfilesRequest,
ListTargetProfilesResponse,
),
"/braiins.bos.v1.PerformanceService/SetDefaultPowerTarget": (
grpclib.const.Handler(
self.__rpc_set_default_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultPowerTargetRequest,
SetPowerTargetResponse,
)
"/braiins.bos.v1.PerformanceService/SetDefaultPowerTarget": grpclib.const.Handler(
self.__rpc_set_default_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultPowerTargetRequest,
SetPowerTargetResponse,
),
"/braiins.bos.v1.PerformanceService/SetPowerTarget": grpclib.const.Handler(
self.__rpc_set_power_target,
@@ -2670,53 +2955,41 @@ class PerformanceServiceBase(ServiceBase):
SetPowerTargetRequest,
SetPowerTargetResponse,
),
"/braiins.bos.v1.PerformanceService/IncrementPowerTarget": (
grpclib.const.Handler(
self.__rpc_increment_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementPowerTargetRequest,
SetPowerTargetResponse,
)
"/braiins.bos.v1.PerformanceService/IncrementPowerTarget": grpclib.const.Handler(
self.__rpc_increment_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementPowerTargetRequest,
SetPowerTargetResponse,
),
"/braiins.bos.v1.PerformanceService/DecrementPowerTarget": (
grpclib.const.Handler(
self.__rpc_decrement_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementPowerTargetRequest,
SetPowerTargetResponse,
)
"/braiins.bos.v1.PerformanceService/DecrementPowerTarget": grpclib.const.Handler(
self.__rpc_decrement_power_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementPowerTargetRequest,
SetPowerTargetResponse,
),
"/braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget": (
grpclib.const.Handler(
self.__rpc_set_default_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultHashrateTargetRequest,
SetHashrateTargetResponse,
)
"/braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget": grpclib.const.Handler(
self.__rpc_set_default_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetDefaultHashrateTargetRequest,
SetHashrateTargetResponse,
),
"/braiins.bos.v1.PerformanceService/SetHashrateTarget": (
grpclib.const.Handler(
self.__rpc_set_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetHashrateTargetRequest,
SetHashrateTargetResponse,
)
"/braiins.bos.v1.PerformanceService/SetHashrateTarget": grpclib.const.Handler(
self.__rpc_set_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
SetHashrateTargetRequest,
SetHashrateTargetResponse,
),
"/braiins.bos.v1.PerformanceService/IncrementHashrateTarget": (
grpclib.const.Handler(
self.__rpc_increment_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementHashrateTargetRequest,
SetHashrateTargetResponse,
)
"/braiins.bos.v1.PerformanceService/IncrementHashrateTarget": grpclib.const.Handler(
self.__rpc_increment_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
IncrementHashrateTargetRequest,
SetHashrateTargetResponse,
),
"/braiins.bos.v1.PerformanceService/DecrementHashrateTarget": (
grpclib.const.Handler(
self.__rpc_decrement_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementHashrateTargetRequest,
SetHashrateTargetResponse,
)
"/braiins.bos.v1.PerformanceService/DecrementHashrateTarget": grpclib.const.Handler(
self.__rpc_decrement_hashrate_target,
grpclib.const.Cardinality.UNARY_UNARY,
DecrementHashrateTargetRequest,
SetHashrateTargetResponse,
),
"/braiins.bos.v1.PerformanceService/SetDPS": grpclib.const.Handler(
self.__rpc_set_dps,
@@ -2724,34 +2997,29 @@ class PerformanceServiceBase(ServiceBase):
SetDpsRequest,
SetDpsResponse,
),
"/braiins.bos.v1.PerformanceService/SetPerformanceMode": (
grpclib.const.Handler(
self.__rpc_set_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
SetPerformanceModeRequest,
PerformanceMode,
)
"/braiins.bos.v1.PerformanceService/SetPerformanceMode": grpclib.const.Handler(
self.__rpc_set_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
SetPerformanceModeRequest,
PerformanceMode,
),
"/braiins.bos.v1.PerformanceService/GetActivePerformanceMode": (
grpclib.const.Handler(
self.__rpc_get_active_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
GetPerformanceModeRequest,
PerformanceMode,
)
"/braiins.bos.v1.PerformanceService/GetActivePerformanceMode": grpclib.const.Handler(
self.__rpc_get_active_performance_mode,
grpclib.const.Cardinality.UNARY_UNARY,
GetPerformanceModeRequest,
PerformanceMode,
),
"/braiins.bos.v1.PerformanceService/RemoveTunedProfiles": (
grpclib.const.Handler(
self.__rpc_remove_tuned_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
RemoveTunedProfilesRequest,
RemoveTunedProfilesResponse,
)
"/braiins.bos.v1.PerformanceService/RemoveTunedProfiles": grpclib.const.Handler(
self.__rpc_remove_tuned_profiles,
grpclib.const.Cardinality.UNARY_UNARY,
RemoveTunedProfilesRequest,
RemoveTunedProfilesResponse,
),
}
class PoolServiceBase(ServiceBase):
async def get_pool_groups(
self, get_pool_groups_request: "GetPoolGroupsRequest"
) -> "GetPoolGroupsResponse":
@@ -2772,6 +3040,11 @@ class PoolServiceBase(ServiceBase):
) -> "RemovePoolGroupResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def set_pool_groups(
self, set_pool_groups_request: "SetPoolGroupsRequest"
) -> "SetPoolGroupsResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def __rpc_get_pool_groups(
self,
stream: "grpclib.server.Stream[GetPoolGroupsRequest, GetPoolGroupsResponse]",
@@ -2804,6 +3077,14 @@ class PoolServiceBase(ServiceBase):
response = await self.remove_pool_group(request)
await stream.send_message(response)
async def __rpc_set_pool_groups(
self,
stream: "grpclib.server.Stream[SetPoolGroupsRequest, SetPoolGroupsResponse]",
) -> None:
request = await stream.recv_message()
response = await self.set_pool_groups(request)
await stream.send_message(response)
def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
return {
"/braiins.bos.v1.PoolService/GetPoolGroups": grpclib.const.Handler(
@@ -2830,10 +3111,17 @@ class PoolServiceBase(ServiceBase):
RemovePoolGroupRequest,
RemovePoolGroupResponse,
),
"/braiins.bos.v1.PoolService/SetPoolGroups": grpclib.const.Handler(
self.__rpc_set_pool_groups,
grpclib.const.Cardinality.UNARY_UNARY,
SetPoolGroupsRequest,
SetPoolGroupsResponse,
),
}
class ConfigurationServiceBase(ServiceBase):
async def get_miner_configuration(
self, get_miner_configuration_request: "GetMinerConfigurationRequest"
) -> "GetMinerConfigurationResponse":
@@ -2862,26 +3150,23 @@ class ConfigurationServiceBase(ServiceBase):
def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
return {
"/braiins.bos.v1.ConfigurationService/GetMinerConfiguration": (
grpclib.const.Handler(
self.__rpc_get_miner_configuration,
grpclib.const.Cardinality.UNARY_UNARY,
GetMinerConfigurationRequest,
GetMinerConfigurationResponse,
)
"/braiins.bos.v1.ConfigurationService/GetMinerConfiguration": grpclib.const.Handler(
self.__rpc_get_miner_configuration,
grpclib.const.Cardinality.UNARY_UNARY,
GetMinerConfigurationRequest,
GetMinerConfigurationResponse,
),
"/braiins.bos.v1.ConfigurationService/GetConstraints": (
grpclib.const.Handler(
self.__rpc_get_constraints,
grpclib.const.Cardinality.UNARY_UNARY,
GetConstraintsRequest,
GetConstraintsResponse,
)
"/braiins.bos.v1.ConfigurationService/GetConstraints": grpclib.const.Handler(
self.__rpc_get_constraints,
grpclib.const.Cardinality.UNARY_UNARY,
GetConstraintsRequest,
GetConstraintsResponse,
),
}
class LicenseServiceBase(ServiceBase):
async def get_license_state(
self, get_license_state_request: "GetLicenseStateRequest"
) -> "GetLicenseStateResponse":
@@ -2907,6 +3192,7 @@ class LicenseServiceBase(ServiceBase):
class MinerServiceBase(ServiceBase):
async def get_miner_status(
self, get_miner_status_request: "GetMinerStatusRequest"
) -> AsyncIterator["GetMinerStatusResponse"]:
@@ -2923,6 +3209,11 @@ class MinerServiceBase(ServiceBase):
) -> "GetMinerStatsResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def get_errors(
self, get_errors_request: "GetErrorsRequest"
) -> "GetErrorsResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def get_hashboards(
self, get_hashboards_request: "GetHashboardsRequest"
) -> "GetHashboardsResponse":
@@ -2971,6 +3262,13 @@ class MinerServiceBase(ServiceBase):
response = await self.get_miner_stats(request)
await stream.send_message(response)
async def __rpc_get_errors(
self, stream: "grpclib.server.Stream[GetErrorsRequest, GetErrorsResponse]"
) -> None:
request = await stream.recv_message()
response = await self.get_errors(request)
await stream.send_message(response)
async def __rpc_get_hashboards(
self,
stream: "grpclib.server.Stream[GetHashboardsRequest, GetHashboardsResponse]",
@@ -3026,6 +3324,12 @@ class MinerServiceBase(ServiceBase):
GetMinerStatsRequest,
GetMinerStatsResponse,
),
"/braiins.bos.v1.MinerService/GetErrors": grpclib.const.Handler(
self.__rpc_get_errors,
grpclib.const.Cardinality.UNARY_UNARY,
GetErrorsRequest,
GetErrorsResponse,
),
"/braiins.bos.v1.MinerService/GetHashboards": grpclib.const.Handler(
self.__rpc_get_hashboards,
grpclib.const.Cardinality.UNARY_UNARY,
@@ -3051,3 +3355,67 @@ class MinerServiceBase(ServiceBase):
DisableHashboardsResponse,
),
}
class NetworkServiceBase(ServiceBase):
async def get_network_configuration(
self, get_network_configuration_request: "GetNetworkConfigurationRequest"
) -> "GetNetworkConfigurationResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def set_network_configuration(
self, set_network_configuration_request: "SetNetworkConfigurationRequest"
) -> "SetNetworkConfigurationResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def get_network_info(
self, get_network_info_request: "GetNetworkInfoRequest"
) -> "GetNetworkInfoResponse":
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
async def __rpc_get_network_configuration(
self,
stream: "grpclib.server.Stream[GetNetworkConfigurationRequest, GetNetworkConfigurationResponse]",
) -> None:
request = await stream.recv_message()
response = await self.get_network_configuration(request)
await stream.send_message(response)
async def __rpc_set_network_configuration(
self,
stream: "grpclib.server.Stream[SetNetworkConfigurationRequest, SetNetworkConfigurationResponse]",
) -> None:
request = await stream.recv_message()
response = await self.set_network_configuration(request)
await stream.send_message(response)
async def __rpc_get_network_info(
self,
stream: "grpclib.server.Stream[GetNetworkInfoRequest, GetNetworkInfoResponse]",
) -> None:
request = await stream.recv_message()
response = await self.get_network_info(request)
await stream.send_message(response)
def __mapping__(self) -> Dict[str, grpclib.const.Handler]:
return {
"/braiins.bos.v1.NetworkService/GetNetworkConfiguration": grpclib.const.Handler(
self.__rpc_get_network_configuration,
grpclib.const.Cardinality.UNARY_UNARY,
GetNetworkConfigurationRequest,
GetNetworkConfigurationResponse,
),
"/braiins.bos.v1.NetworkService/SetNetworkConfiguration": grpclib.const.Handler(
self.__rpc_set_network_configuration,
grpclib.const.Cardinality.UNARY_UNARY,
SetNetworkConfigurationRequest,
SetNetworkConfigurationResponse,
),
"/braiins.bos.v1.NetworkService/GetNetworkInfo": grpclib.const.Handler(
self.__rpc_get_network_info,
grpclib.const.Cardinality.UNARY_UNARY,
GetNetworkInfoRequest,
GetNetworkInfoResponse,
),
}

View File

@@ -15,9 +15,12 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
import hashlib
import json
from pathlib import Path
from typing import Any
import aiofiles
import httpx
from pyasic import settings
@@ -46,6 +49,14 @@ class ePICWebAPI(BaseWebAPI):
async with httpx.AsyncClient(transport=settings.transport()) as client:
for retry_cnt in range(settings.get("get_data_retries", 1)):
try:
if parameters.get("form") is not None:
form_data = parameters["form"]
form_data.add_field("password", self.pwd)
response = await client.post(
f"http://{self.ip}:{self.port}/{command}",
timeout=5,
data=form_data,
)
if post:
response = await client.post(
f"http://{self.ip}:{self.port}/{command}",
@@ -135,3 +146,22 @@ class ePICWebAPI(BaseWebAPI):
async def capabilities(self) -> dict:
return await self.send_command("capabilities")
async def system_update(self, file: Path | str, keep_settings: bool = True):
"""Perform a system update by uploading a firmware file and sending a
command to initiate the update."""
# calculate the SHA256 checksum of the firmware file
sha256_hash = hashlib.sha256()
async with aiofiles.open(str(file), "rb") as f:
while chunk := await f.read(8192):
sha256_hash.update(chunk)
checksum = sha256_hash.hexdigest()
# prepare the multipart/form-data request
form_data = aiohttp.FormData()
form_data.add_field("checksum", checksum)
form_data.add_field("keepsettings", str(keep_settings).lower())
form_data.add_field("update.zip", open(file, "rb"), filename="update.zip")
await self.send_command("systemupdate", form=form_data)

View File

@@ -140,3 +140,6 @@ class VNishWebAPI(BaseWebAPI):
async def autotune_presets(self) -> dict:
return await self.send_command("autotune/presets")
async def find_miner(self) -> dict:
return await self.send_command("find-miner", privileged=True)

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.57.4"
version = "0.59.2"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"
@@ -10,17 +10,18 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
httpx = ">=0.26.0"
asyncssh = "^2.14.2"
passlib = "^1.7.4"
pyaml = "^23.12.0"
toml = "^0.10.2"
betterproto = "2.0.0b6"
asyncssh = ">=2.14.2"
passlib = ">=1.7.4"
pyaml = ">=23.12.0"
toml = ">=0.10.2"
betterproto = ">=2.0.0b6"
aiofiles = ">=23.2.1"
[tool.poetry.group.dev]
optional = true
[tool.poetry.group.dev.dependencies]
pre-commit = "^3.1.0"
pre-commit = "^3.5.0"
isort = "^5.12.0"
[tool.poetry.group.docs]

View File

@@ -20,10 +20,10 @@ from pyasic.config import (
MinerConfig,
MiningModeConfig,
PoolConfig,
PowerScalingConfig,
ScalingConfig,
TemperatureConfig,
)
from pyasic.config.power_scaling import PowerScalingShutdown
from pyasic.config.mining.scaling import ScalingShutdown
class TestConfig(unittest.TestCase):
@@ -40,11 +40,13 @@ class TestConfig(unittest.TestCase):
),
fan_mode=FanModeConfig.manual(speed=90, minimum_fans=2),
temperature=TemperatureConfig(target=70, danger=120),
mining_mode=MiningModeConfig.power_tuning(power=3000),
power_scaling=PowerScalingConfig.enabled(
power_step=100,
minimum_power=2000,
shutdown_enabled=PowerScalingShutdown.enabled(duration=3),
mining_mode=MiningModeConfig.power_tuning(
power=3000,
scaling=ScalingConfig(
step=100,
minimum=2000,
shutdown=ScalingShutdown(enabled=True, duration=3),
),
),
)
@@ -76,12 +78,11 @@ class TestConfig(unittest.TestCase):
"mode": "power_tuning",
"power": 3000,
"algo": {"mode": "standard"},
},
"power_scaling": {
"mode": "enabled",
"power_step": 100,
"minimum_power": 2000,
"shutdown_enabled": {"mode": "enabled", "duration": 3},
"scaling": {
"step": 100,
"minimum": 2000,
"shutdown": {"enabled": True, "duration": 3},
},
},
}

View File

@@ -71,5 +71,5 @@ class TestFanConfig(unittest.TestCase):
fan_mode=fan_mode,
):
conf = fan_mode()
boser_conf = conf.as_boser()
boser_conf = conf.as_boser
self.assertEqual(conf, FanModeConfig.from_boser(boser_conf))

View File

@@ -55,8 +55,8 @@ class MinersTest(unittest.TestCase):
"expected_hashrate",
"uptime",
"wattage",
"voltage",
"wattage_limit",
"pools",
]
)
warnings.filterwarnings("ignore")