Compare commits

...

113 Commits

Author SHA1 Message Date
Ryan Heideman
83035a869b feature: Add Support for Avalon Nano 3s (#329) 2025-04-12 15:34:45 -06:00
Brett Rowan
4c104a59ff version: bump version number 2025-04-01 09:24:20 -06:00
Brett Rowan
e708ae3728 bug: remove MSKminer web identification 2025-04-01 09:23:51 -06:00
Wilfred Allyn
a4352816ee bug: pass atmset param as bool 2025-04-01 07:48:42 -06:00
Will Jackson
336bd9c002 bug: update set_dps configuration to handle optional power and hashrate parameters (#319)
* bug: update set_dps configuration to handle optional power and hashrate parameters
2025-03-27 11:09:47 -06:00
Wilfred Allyn
e3c917efde feature: set supports_autotuning True for luxminer 2025-03-26 08:06:38 -06:00
Brett Rowan
4d71012ed6 version: bump version number
Closes: #310
2025-03-20 10:46:30 -06:00
Will Jackson
1acdba8ae0 bug: fix set_dps configuration to handle power target parameters correctly (#318)
* bug: fix set_dps configuration to handle power target parameters correctly

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-20 10:45:48 -06:00
Brett Rowan
5567f26c03 version: bump version number
Closes: #316, #310
2025-03-20 09:20:51 -06:00
Brett Rowan
0027485582 bug: fix set_dps tuple
cc: #310
2025-03-20 09:20:23 -06:00
Brett Rowan
c9bcb7bab6 feature: add support for S21+
cc: #316
2025-03-19 13:28:03 -06:00
John-Paul Compagnone
0ee261930e epic: remove guard on expected_hashboards 2025-03-18 09:35:13 -06:00
John-Paul Compagnone
3cfc8dded9 epic: remove v1 from ELITE miner 2025-03-18 09:35:13 -06:00
John-Paul Compagnone
655cf6d0ac epic: add BlockMiner eLITE, S19k Pro Dualie 2025-03-18 09:35:13 -06:00
Brett Rowan
640dc6d8c2 version: bump version number 2025-03-18 09:00:40 -06:00
Brett Rowan
a572fedb4d bug: fix some cases where bosminer config could be invalid 2025-03-18 09:00:16 -06:00
Brett Rowan
6c46a7cd71 bug: handle cases where hashboards and fans can be None with unknown types 2025-03-18 08:18:34 -06:00
Brett Rowan
49f42172da version: bump version number 2025-03-17 14:19:47 -06:00
Brett Rowan
83be80e4bc bug: handle connection errors in grpc for boser 2025-03-17 14:19:28 -06:00
Brett Rowan
0be9e9d519 feature: add fault_light to influx 2025-03-17 14:17:37 -06:00
Upstream Data
4694c0f774 bug: fix error code handling in list 2025-03-06 11:45:38 -07:00
Brett Rowan
11012b310b bug: fix delimiter handling 2025-03-04 21:27:28 -07:00
Brett Rowan
8852fab3ee feature: allow the user to pass a delimiter to as_influx 2025-03-04 20:14:55 -07:00
Brett Rowan
ec1b2ca162 bug: fix bad " " handling in influx 2025-03-04 19:58:44 -07:00
Upstream Data
1f9d4c8c10 bug: swap to lower case booleans 2025-03-04 19:51:48 -07:00
Upstream Data
5bb04b2af4 refactor: round hashrate to 2 decimals for influx 2025-03-04 19:51:48 -07:00
Upstream Data
2e192a1536 feature: update support for influxdb line protocol 2025-03-04 19:51:48 -07:00
Brett Rowan
4da8044bc7 bug: fix bad type hint 2025-03-04 19:51:37 -07:00
Brett Rowan
76078e4d0e bug: fix wattage limit parsing on luxos 2025-03-04 19:51:13 -07:00
Upstream Data
276a476fab bug: fix json decode error possibility with avalon nanos 2025-03-03 15:04:22 -07:00
pre-commit-ci[bot]
e0abed4f93 [pre-commit.ci] pre-commit autoupdate (#307)
updates:
- [github.com/pycqa/isort: 6.0.0 → 6.0.1](https://github.com/pycqa/isort/compare/6.0.0...6.0.1)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-03-03 14:56:47 -07:00
Ganey
4a67bd5d99 feat: add powerlimit to avalon nano (#302)
* feat: add powerlimit to avalon nano

add power limit to avalon nano

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-02-26 15:23:18 -07:00
pre-commit-ci[bot]
eb48d04939 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/python-poetry/poetry: 2.0.1 → 2.1.1](https://github.com/python-poetry/poetry/compare/2.0.1...2.1.1)
2025-02-18 08:44:43 -07:00
Upstream Data
c4697728e4 version: bump version number 2025-02-13 15:38:53 -07:00
Upstream Data
de64172073 feature: add support for Hiveon S19kPro 2025-02-13 15:38:28 -07:00
Upstream Data
4d7a13433b version: bump version number 2025-02-12 16:08:23 -07:00
Adrian
e14a4791b2 it turns out there are 3 hashboards per miner (#293) 2025-02-12 16:07:46 -07:00
Upstream Data
0e76d4550b version: bump version number 2025-02-11 15:47:56 -07:00
Upstream Data
2d424025e9 feature: add MAC address to mskminer 2025-02-11 15:47:37 -07:00
Upstream Data
bf4903ce4b refactor: remove some unused imports 2025-02-11 09:38:31 -07:00
Upstream Data
4f7f6bf045 version: bump version number 2025-02-11 09:34:57 -07:00
Upstream Data
824890ec97 feature: add very basic support for MSKminer 2025-02-11 09:34:23 -07:00
Upstream Data
ce9d7ffb0f bug: fix issue with vnish preset parsing on some miners 2025-02-11 09:00:59 -07:00
Upstream Data
183b4934c1 refactor: remote unused import in test 2025-02-06 11:44:15 -07:00
Upstream Data
3d2b260b17 version: bump version number 2025-02-06 11:42:33 -07:00
Upstream Data
f88c1734eb bug: fix some issues with avalon 1566, and add tests 2025-02-06 11:42:17 -07:00
Upstream Data
b897ca8363 version: bump version number 2025-02-06 11:33:08 -07:00
Upstream Data
dba341fdae feature: add support for avalon 1566 2025-02-06 11:32:45 -07:00
Upstream Data
837794bd57 version: bump version number 2025-02-05 10:45:56 -07:00
Upstream Data
36d16c7235 feature: add support for elphapex configs 2025-02-05 10:45:35 -07:00
Upstream Data
7797023689 feature: add support for elphapex pools 2025-02-05 08:28:28 -07:00
Upstream Data
1021f200ec version: bump version number 2025-02-05 08:07:35 -07:00
Upstream Data
197d6568e3 bug: fix DG1+ naming 2025-02-05 08:07:14 -07:00
Upstream Data
71c8905674 version: bump version number 2025-02-04 16:00:18 -07:00
Upstream Data
15c3806fbf refactor: cleanup some imports 2025-02-04 15:59:32 -07:00
Upstream Data
a5c42c9c2b feature: add support for Elphapex DG1+ 2025-02-04 15:59:32 -07:00
Upstream Data
90d8a795e6 version: bump version number 2025-02-03 09:39:49 -07:00
Upstream Data
07cf1b134c feature: add support for vnish error codes 2025-02-03 09:39:30 -07:00
Upstream Data
53e1f33fa6 version: bump version number 2025-02-03 09:32:44 -07:00
Upstream Data
16ab2bd4e3 feature: add support for luckyminer LV07 2025-02-03 09:32:16 -07:00
Upstream Data
de728f12d2 version: bump version number 2025-02-03 09:27:43 -07:00
Upstream Data
8092e12dfb feature: add support for S19X88 Hiveon 2025-02-03 09:27:18 -07:00
b-rowan
e70c3e9f79 version: bump version number 2025-01-30 17:56:47 -07:00
b-rowan
39a97f2914 feature: add mac address support for avalon nano 3 2025-01-30 17:56:16 -07:00
b-rowan
35af74ad1a version: bump version number 2025-01-30 07:14:50 -07:00
Erik Olof Gunnar Andersson
15fa91fb98 bug: don’t timeout forever on _socket_ping (#288)
* Don't timeout forever on _socket_ping

* Use factory_get_timeout to configure number of allowed retries

* Add warning log
2025-01-30 07:05:29 -07:00
James Hilliard
084987a3e1 bug: Add cryptography direct dependency (#286) 2025-01-29 10:01:10 -07:00
b-rowan
e93cc77a58 ci: update pre-commit hooks 2025-01-28 21:29:43 -07:00
b-rowan
788d43c51c version: bump version number 2025-01-28 21:27:17 -07:00
Adrian
74c22b82ce bug: add miner algo type for S21 Hydro (#285) 2025-01-28 21:20:39 -07:00
Upstream Data
1f46ce1b9a version: bump version number 2025-01-28 09:36:56 -07:00
Adrian
7964336a0c feature: add support for S21 Hydro (#283) 2025-01-28 09:36:15 -07:00
Upstream Data
a24fc07c2a ci: update pre-commit to run docs generation when committing. 2025-01-28 08:51:13 -07:00
Upstream Data
4c64481d3b bug: do not run unittest when running in precommit.ci 2025-01-28 08:33:02 -07:00
Upstream Data
66fb5834f0 version: bump version number 2025-01-23 13:37:28 -07:00
Upstream Data
db05cc1d97 feature: add support for S21 Pro BOS 2025-01-23 13:37:04 -07:00
Brett Rowan
418e3ce26e Merge pull request #281 from UpstreamData/fix_hammer_pass 2025-01-23 07:42:20 -07:00
John-Paul Compagnone
5a0bb11a44 fix hammer default password 2025-01-23 08:35:37 -05:00
Upstream Data
39f9d087db version: bump version number 2025-01-20 09:27:40 -07:00
Upstream Data
067a376f94 feature: add support for luckyminer LV08 2025-01-20 09:27:16 -07:00
Upstream Data
a5d6e122f9 version: bump version number 2025-01-20 09:01:04 -07:00
Upstream Data
b160fd75ba refactor: reformat some files 2025-01-20 09:00:45 -07:00
Upstream Data
d1007d3ae8 version: bump version number 2025-01-20 08:58:14 -07:00
Upstream Data
33803e89e2 feature: add support for Vnish S19i
Fixes: #279
2025-01-20 08:21:24 -07:00
Upstream Data
10a44b9877 bug: fix invlaid import in luxminer.py 2025-01-20 08:21:12 -07:00
pre-commit-ci[bot]
bbd883f639 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-01-19 08:18:27 -07:00
Wilfred Allyn
8e2ad478e9 refactor: simplify get_wattage_limit for luxos 2025-01-19 08:18:27 -07:00
Wilfred Allyn
957981a9c6 feature: get active preset from luxos 2025-01-19 08:18:27 -07:00
Wilfred Allyn
13a67dfdd1 feature: add _get_wattage_limit for luxos 2025-01-19 08:18:27 -07:00
Wilfred Allyn
e86f2b62c5 docs: fix issues for docs warnings 2025-01-18 08:03:11 -07:00
Wilfred Allyn
88b4d2cac3 docs: add instructions for building docs locally 2025-01-17 09:31:36 -07:00
Upstream Data
5842ef3d97 version: bump version number 2025-01-16 11:02:57 -07:00
Upstream Data
339a689267 bug: fix possible None value from miner type when parsing hammer miners 2025-01-16 11:02:39 -07:00
Brett Rowan
a0764806c4 version: bump version number 2025-01-15 22:23:41 -07:00
Brett Rowan
6eec0f6d44 bug: fix a few bad type hints in config models 2025-01-15 22:22:53 -07:00
Upstream Data
0854f7833c docs: update supported miners list and generation tool to use unique 2025-01-15 10:56:25 -07:00
Upstream Data
b6edc85679 feature: add support for some weird antminer submodel namings for S21 2025-01-15 09:47:14 -07:00
Upstream Data
ff11ebc304 bug: fix antminer KS5 using iceriver backend 2025-01-15 08:25:02 -07:00
Wilfred Allyn
f3681f1aa5 feature: add support for Vnish S19j Pro A 2025-01-15 07:36:29 -07:00
Wilfred Allyn
1a7411edb3 feature: add support for Vnish T21 and update docs 2025-01-15 07:34:47 -07:00
Upstream Data
f2a4a5d524 version: bump version number 2025-01-08 11:24:08 -07:00
Wilfred Allyn
624a3c5919 feature: revise atmset input params 2025-01-08 11:22:46 -07:00
Wilfred Allyn
2ec8054d24 feature: check if atm enabled before settting power 2025-01-08 11:22:46 -07:00
Wilfred Allyn
d148ccfe5f feature: add atm, atmset commands to luxos rpc 2025-01-08 11:22:46 -07:00
Wilfred Allyn
b6c29d16f9 feature: add set_power_limit for luxos 2025-01-08 11:22:46 -07:00
Wilfred Allyn
53a3bb13af feature: remove deprecated board_n param from profileset 2025-01-08 11:22:46 -07:00
John-Paul Compagnone
16e74e659c add expected_hashrate for bitaxe devices 2025-01-07 09:59:51 -07:00
Upstream Data
730caca23f version: bump version number 2025-01-07 08:57:09 -07:00
Upstream Data
dc126b2953 bug: fix a bug with config on S9 sometimes not having quota set. 2025-01-07 08:56:39 -07:00
Brett Rowan
51abdf0b2d version: bump version number 2025-01-04 11:50:08 -07:00
Brett Rowan
b367b2d293 bug: fix an issue with parsing disabled fan mode on BOS+ 2025-01-04 11:49:49 -07:00
Upstream Data
96f52a4b35 version: bump version number 2025-01-02 16:04:35 -07:00
John-Paul Compagnone
5236e02af2 fix hammer test, enforce default hr unit 2025-01-02 16:04:23 -07:00
135 changed files with 4878 additions and 1051 deletions

View File

@@ -1,7 +1,15 @@
ci:
skip:
- poetry-lock
- unittest
- generate-docs
repos:
- repo: https://github.com/python-poetry/poetry
rev: 2.1.1
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-install
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
@@ -15,11 +23,11 @@ repos:
exclude: ^mkdocs\.yml$
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 24.10.0
rev: 25.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.13.2
rev: 6.0.1
hooks:
- id: isort
name: isort (python)
@@ -30,6 +38,12 @@ repos:
name: unittest
entry: python -m unittest discover
language: system
'types': [python]
types: [ python ]
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
pass_filenames: false
- id: generate-docs
name: generate-docs
entry: python docs/generate_miners.py
language: system
types: [ python ]
pass_filenames: false

View File

@@ -50,6 +50,13 @@ poetry install --with dev
pre-commit install
```
##### Building Documentation Locally
```
poetry install --with docs
python docs/generate_miners.py
poetry run mkdocs serve
```
---
## Getting started

View File

@@ -2,6 +2,7 @@ import asyncio
import importlib
import os
import warnings
from pathlib import Path
from pyasic.miners.factory import MINER_CLASSES, MinerTypes
@@ -51,12 +52,19 @@ def backend_str(backend: MinerTypes) -> str:
return "Mara Firmware Miners"
case MinerTypes.BITAXE:
return "Stock Firmware BitAxe Miners"
case MinerTypes.LUCKYMINER:
return "Stock Firmware Lucky Miners"
case MinerTypes.ICERIVER:
return "Stock Firmware IceRiver Miners"
case MinerTypes.HAMMER:
return "Stock Firmware Hammer Miners"
case MinerTypes.VOLCMINER:
return "Stock Firmware Volcminers"
case MinerTypes.ELPHAPEX:
return "Stock Firmware Elphapex Miners"
case MinerTypes.MSKMINER:
return "MSKMiner Firmware Miners"
raise TypeError("Unknown miner backend, cannot generate docs")
def create_url_str(mtype: str):
@@ -121,27 +129,28 @@ BACKEND_TYPE_CLOSER = """
</details>"""
m_data = {}
done = []
for m in MINER_CLASSES:
for t in MINER_CLASSES[m]:
if t is not None:
for t in sorted(MINER_CLASSES[m], key=lambda x: x or ""):
if t is not None and MINER_CLASSES[m][t] not in done:
miner = MINER_CLASSES[m][t]
if make(miner) not in m_data:
m_data[make(miner)] = {}
if model_type(miner) not in m_data[make(miner)]:
m_data[make(miner)][model_type(miner)] = []
m_data[make(miner)][model_type(miner)].append(miner)
done.append(miner)
async def create_directory_structure(directory, data):
def create_directory_structure(directory, data):
if not os.path.exists(directory):
os.makedirs(directory)
for key, value in data.items():
subdirectory = os.path.join(directory, key)
if isinstance(value, dict):
await create_directory_structure(subdirectory, value)
create_directory_structure(subdirectory, value)
elif isinstance(value, list):
file_path = os.path.join(subdirectory + ".md")
@@ -162,7 +171,7 @@ async def create_directory_structure(directory, data):
)
async def create_supported_types(directory):
def create_supported_types(directory):
with open(os.path.join(directory, "supported_types.md"), "w") as file:
file.write(SUPPORTED_TYPES_HEADER)
for mback in MINER_CLASSES:
@@ -179,7 +188,7 @@ async def create_supported_types(directory):
for mtype in backend_types:
file.write(MINER_TYPE_HEADER.format(mtype))
for minstance in backend_types[mtype]:
model = await minstance("1.1.1.1").get_model()
model = minstance("1.1.1.1").model
file.write(
MINER_DETAILS.format(
make(minstance), mtype, create_url_str(model), model
@@ -189,6 +198,7 @@ async def create_supported_types(directory):
file.write(BACKEND_TYPE_CLOSER)
root_directory = os.path.join(os.getcwd(), "miners")
asyncio.run(create_directory_structure(root_directory, m_data))
asyncio.run(create_supported_types(root_directory))
if __name__ == "__main__":
root_directory = Path(__file__).parent.joinpath("miners")
create_directory_structure(root_directory, m_data)
create_supported_types(root_directory)

View File

@@ -14,19 +14,6 @@
show_root_heading: false
heading_level: 0
## S17+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17 Pro (Stock)
- [ ] Shutdowns
@@ -40,6 +27,19 @@
show_root_heading: false
heading_level: 0
## S17+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17e (Stock)
- [ ] Shutdowns
@@ -105,19 +105,6 @@
show_root_heading: false
heading_level: 0
## S17+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17 Pro (BOS+)
- [x] Shutdowns
@@ -131,6 +118,19 @@
show_root_heading: false
heading_level: 0
## S17+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17e (BOS+)
- [x] Shutdowns
@@ -183,19 +183,6 @@
show_root_heading: false
heading_level: 0
## S17+ (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S17 Pro (VNish)
- [x] Shutdowns
@@ -209,3 +196,16 @@
show_root_heading: false
heading_level: 0
## S17+ (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -14,14 +14,14 @@
show_root_heading: false
heading_level: 0
## S19L (Stock)
## S19 Hydro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro
handler: python
options:
show_root_heading: false
@@ -40,136 +40,6 @@
show_root_heading: false
heading_level: 0
## S19j (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19i (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j No PIC (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 XP (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Hydro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro Hydro (Stock)
- [x] Shutdowns
@@ -196,14 +66,92 @@
show_root_heading: false
heading_level: 0
## S19K Pro (Stock)
## S19 XP (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19a
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19aPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19i (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPro
handler: python
options:
show_root_heading: false
@@ -222,6 +170,58 @@
show_root_heading: false
heading_level: 0
## S19j No PIC (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19K Pro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19L (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19L
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19ProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (Stock)
- [x] Shutdowns
@@ -248,19 +248,6 @@
show_root_heading: false
heading_level: 0
## S19+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro (BOS+)
- [x] Shutdowns
@@ -274,6 +261,45 @@
show_root_heading: false
heading_level: 0
## S19 Pro+ Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19ProPlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 XP (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (BOS+)
- [x] Shutdowns
@@ -313,19 +339,6 @@
show_root_heading: false
heading_level: 0
## S19j No PIC (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (BOS+)
- [x] Shutdowns
@@ -359,19 +372,6 @@
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro+ (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jProPlus
handler: python
options:
@@ -391,14 +391,14 @@
show_root_heading: false
heading_level: 0
## S19k Pro No PIC (BOS+)
## S19j No PIC (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19kProNoPIC
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19jNoPIC
handler: python
options:
show_root_heading: false
@@ -417,32 +417,6 @@
show_root_heading: false
heading_level: 0
## S19 XP (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro+ Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19ProPlusHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (BOS+)
- [x] Shutdowns
@@ -469,19 +443,6 @@
show_root_heading: false
heading_level: 0
## S19 No PIC (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro (VNish)
- [x] Shutdowns
@@ -495,40 +456,14 @@
show_root_heading: false
heading_level: 0
## S19j (VNish)
## S19 Pro Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProHydro
handler: python
options:
show_root_heading: false
@@ -560,14 +495,40 @@
show_root_heading: false
heading_level: 0
## S19 Pro Hydro (VNish)
## S19i (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProHydro
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19i
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19j
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19jPro
handler: python
options:
show_root_heading: false
@@ -586,6 +547,19 @@
show_root_heading: false
heading_level: 0
## S19 No PIC (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (VNish)
- [x] Shutdowns
@@ -625,6 +599,19 @@
show_root_heading: false
heading_level: 0
## S19 XP (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (ePIC)
- [x] Shutdowns
@@ -651,6 +638,19 @@
show_root_heading: false
heading_level: 0
## S19j Pro Dual (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19jProDual
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro+ (ePIC)
- [x] Shutdowns
@@ -677,27 +677,27 @@
show_root_heading: false
heading_level: 0
## S19 XP (ePIC)
## S19k Pro Dual (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP
::: pyasic.miners.antminer.epic.X19.S19.ePICS19kProDual
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro Dual (ePIC)
## S19 (Hive)
- [x] Shutdowns
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.epic.X19.S19.ePICS19jProDual
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19
handler: python
options:
show_root_heading: false
@@ -716,14 +716,40 @@
show_root_heading: false
heading_level: 0
## S19 (Hive)
## S19K Pro (Hive)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19kPro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 No PIC (Hive)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19NoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 No PIC (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.mskminer.X19.S19.MSKMinerS19NoPIC
handler: python
options:
show_root_heading: false
@@ -755,6 +781,19 @@
show_root_heading: false
heading_level: 0
## S19 XP (LuxOS)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (LuxOS)
- [x] Shutdowns
@@ -794,19 +833,6 @@
show_root_heading: false
heading_level: 0
## S19 XP (LuxOS)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [x] Presets
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## T19 (LuxOS)
- [x] Shutdowns
@@ -846,6 +872,19 @@
show_root_heading: false
heading_level: 0
## S19 XP (MaraFW)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j (MaraFW)
- [ ] Shutdowns
@@ -859,19 +898,6 @@
show_root_heading: false
heading_level: 0
## S19j No PIC (MaraFW)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19jNoPIC
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j Pro (MaraFW)
- [ ] Shutdowns
@@ -885,14 +911,14 @@
show_root_heading: false
heading_level: 0
## S19 XP (MaraFW)
## S19j No PIC (MaraFW)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19XP
::: pyasic.miners.antminer.marathon.X19.S19.MaraS19jNoPIC
handler: python
options:
show_root_heading: false

View File

@@ -14,6 +14,19 @@
show_root_heading: false
heading_level: 0
## S21 Hydro (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 Pro (Stock)
- [x] Shutdowns
@@ -27,6 +40,19 @@
show_root_heading: false
heading_level: 0
## S21+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## T21 (Stock)
- [x] Shutdowns
@@ -53,6 +79,19 @@
show_root_heading: false
heading_level: 0
## S21 Pro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Pro
handler: python
options:
show_root_heading: false
heading_level: 0
## T21 (BOS+)
- [x] Shutdowns
@@ -79,6 +118,19 @@
show_root_heading: false
heading_level: 0
## T21 (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X21.T21.VNishT21
handler: python
options:
show_root_heading: false
heading_level: 0
## S21 (ePIC)
- [x] Shutdowns

View File

@@ -27,19 +27,6 @@
show_root_heading: false
heading_level: 0
## L3+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
handler: python
options:
show_root_heading: false
heading_level: 0
## KA3 (Stock)
- [x] Shutdowns
@@ -66,14 +53,14 @@
show_root_heading: false
heading_level: 0
## L3+ (VNish)
## L3+ (Stock)
- [x] Shutdowns
- [ ] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
handler: python
options:
show_root_heading: false

View File

@@ -27,3 +27,16 @@
show_root_heading: false
heading_level: 0
## KS5 (Stock)
- [ ] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5Pro
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -1,14 +1,14 @@
# pyasic
## X7 Models
## L7 (Stock)
## D7 (Stock)
- [ ] Shutdowns
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
handler: python
options:
show_root_heading: false
@@ -27,14 +27,14 @@
show_root_heading: false
heading_level: 0
## D7 (Stock)
## L7 (Stock)
- [x] Shutdowns
- [ ] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
handler: python
options:
show_root_heading: false

View File

@@ -1,6 +1,19 @@
# pyasic
## X9 Models
## D9 (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
handler: python
options:
show_root_heading: false
heading_level: 0
## E9Pro (Stock)
- [ ] Shutdowns
@@ -14,14 +27,14 @@
show_root_heading: false
heading_level: 0
## D9 (Stock)
## L9 (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
handler: python
options:
show_root_heading: false
@@ -79,19 +92,6 @@
show_root_heading: false
heading_level: 0
## L9 (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
handler: python
options:
show_root_heading: false
heading_level: 0
## S9 (BOS+)
- [x] Shutdowns

View File

@@ -0,0 +1,16 @@
# pyasic
## A15X Models
## Avalon 1566 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.avalonminer.cgminer.A15X.A1566.CGMinerAvalon1566
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -14,3 +14,16 @@
show_root_heading: false
heading_level: 0
## Avalon Nano 3s (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3s
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -1,19 +1,6 @@
# pyasic
## BM Models
## Supra (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
handler: python
options:
show_root_heading: false
heading_level: 0
## Ultra (Stock)
- [ ] Shutdowns
@@ -27,14 +14,14 @@
show_root_heading: false
heading_level: 0
## Max (Stock)
## Supra (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
handler: python
options:
show_root_heading: false
@@ -53,3 +40,16 @@
show_root_heading: false
heading_level: 0
## Max (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -27,3 +27,16 @@
show_root_heading: false
heading_level: 0
## BlockMiner eLITE 1.0 (ePIC)
- [x] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMinerELITE1
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,16 @@
# pyasic
## DGX Models
## DG1+ (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1Plus
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -0,0 +1,29 @@
# pyasic
## LV Models
## LV07 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.luckyminer.espminer.LV.LV07.LuckyMinerLV07
handler: python
options:
show_root_heading: false
heading_level: 0
## LV08 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.luckyminer.espminer.LV.LV08.LuckyMinerLV08
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -32,6 +32,7 @@ details {
<ul>
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
</ul>
</details>
<details>
@@ -100,8 +101,12 @@ details {
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-stock">S21 (Stock)</a></li>
<li><a href="../antminer/X21#s21_1-stock">S21+ (Stock)</a></li>
<li><a href="../antminer/X21#s21-pro-stock">S21 Pro (Stock)</a></li>
<li><a href="../antminer/X21#t21-stock">T21 (Stock)</a></li>
<li><a href="../antminer/X21#s21-hydro-stock">S21 Hydro (Stock)</a></li>
</ul>
</details>
</ul>
@@ -549,6 +554,15 @@ details {
<ul>
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
</ul>
<ul>
<li><a href="../avalonminer/nano#avalon-nano-3s-stock">Avalon Nano 3s (Stock)</a></li>
</ul>
</details>
<details>
<summary>A15X Series:</summary>
<ul>
<li><a href="../avalonminer/A15X#avalon-1566-stock">Avalon 1566 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
@@ -649,6 +663,7 @@ details {
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
<li><a href="../antminer/X21#s21-pro-bos_1">S21 Pro (BOS+)</a></li>
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
</ul>
</details>
@@ -691,6 +706,8 @@ details {
<li><a href="../antminer/X19#s19-no-pic-vnish">S19 No PIC (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
<li><a href="../antminer/X19#s19i-vnish">S19i (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19a-vnish">S19a (VNish)</a></li>
@@ -703,6 +720,7 @@ details {
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#t21-vnish">T21 (VNish)</a></li>
<li><a href="../antminer/X21#s21-vnish">S21 (VNish)</a></li>
</ul>
</details>
@@ -722,6 +740,7 @@ details {
<li><a href="../antminer/X19#s19k-pro-epic">S19k Pro (ePIC)</a></li>
<li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li>
<li><a href="../antminer/X19#s19j-pro-dual-epic">S19j Pro Dual (ePIC)</a></li>
<li><a href="../antminer/X19#s19k-pro-dual-epic">S19k Pro Dual (ePIC)</a></li>
</ul>
</details>
<details>
@@ -737,6 +756,7 @@ details {
<ul>
<li><a href="../blockminer/blockminer#blockminer-520i-epic">BlockMiner 520i (ePIC)</a></li>
<li><a href="../blockminer/blockminer#blockminer-720i-epic">BlockMiner 720i (ePIC)</a></li>
<li><a href="../blockminer/blockminer#blockminer-elite-1.0-epic">BlockMiner eLITE 1.0 (ePIC)</a></li>
</ul>
</details>
</ul>
@@ -755,6 +775,19 @@ details {
<ul>
<li><a href="../antminer/X19#s19j-pro-hive">S19j Pro (Hive)</a></li>
<li><a href="../antminer/X19#s19-hive">S19 (Hive)</a></li>
<li><a href="../antminer/X19#s19k-pro-hive">S19K Pro (Hive)</a></li>
<li><a href="../antminer/X19#s19-no-pic-hive">S19 No PIC (Hive)</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>MSKMiner Firmware Miners:</summary>
<ul>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="../antminer/X19#s19-no-pic-stock">S19 No PIC (Stock)</a></li>
</ul>
</details>
</ul>
@@ -854,6 +887,18 @@ details {
</ul>
</details>
<details>
<summary>Stock Firmware Lucky Miners:</summary>
<ul>
<details>
<summary>LV Series:</summary>
<ul>
<li><a href="../luckyminer/LV#lv08-stock">LV08 (Stock)</a></li>
<li><a href="../luckyminer/LV#lv07-stock">LV07 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
<summary>Stock Firmware IceRiver Miners:</summary>
<ul>
<details>
@@ -894,3 +939,14 @@ details {
</details>
</ul>
</details>
<details>
<summary>Stock Firmware Elphapex Miners:</summary>
<ul>
<details>
<summary>DGX Series:</summary>
<ul>
<li><a href="../elphapex/DGX#dg1_1-stock">DG1+ (Stock)</a></li>
</ul>
</details>
</ul>
</details>

374
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
[[package]]
name = "aiofiles"
@@ -6,6 +6,7 @@ version = "24.1.0"
description = "File support for asyncio."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"},
{file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
@@ -17,6 +18,7 @@ version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
@@ -24,13 +26,14 @@ files = [
[[package]]
name = "anyio"
version = "4.7.0"
version = "4.8.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"},
{file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"},
{file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"},
{file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"},
]
[package.dependencies]
@@ -41,18 +44,19 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"]
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1) ; python_version >= \"3.10\"", "uvloop (>=0.21) ; platform_python_implementation == \"CPython\" and platform_system != \"Windows\" and python_version < \"3.14\""]
trio = ["trio (>=0.26.1)"]
[[package]]
name = "asyncssh"
version = "2.19.0"
version = "2.20.0"
description = "AsyncSSH: Asynchronous SSHv2 client and server library"
optional = false
python-versions = ">=3.6"
groups = ["main"]
files = [
{file = "asyncssh-2.19.0-py3-none-any.whl", hash = "sha256:bb82ac30ff0cb4393fbaf1114e606ad7a4f13d6c4bdaed423c033ee26b455228"},
{file = "asyncssh-2.19.0.tar.gz", hash = "sha256:723dead4d068b558708dc66a4ca7e7a93a813aa9416036eccb9af4c03ae2cf30"},
{file = "asyncssh-2.20.0-py3-none-any.whl", hash = "sha256:af6888d937c07a4bf31293335a6166b4d87608cdb5957b49547da6ad87ecf174"},
{file = "asyncssh-2.20.0.tar.gz", hash = "sha256:020b6e384b2328ef8683908ad8e73de9ec2b9b62fd964571ea957bba98412983"},
]
[package.dependencies]
@@ -70,17 +74,37 @@ pywin32 = ["pywin32 (>=227)"]
[[package]]
name = "babel"
version = "2.16.0"
version = "2.17.0"
description = "Internationalization utilities"
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"},
{file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"},
{file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"},
{file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"},
]
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
[[package]]
name = "backrefs"
version = "5.8"
description = "A wrapper around re and regex that adds additional back references."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "backrefs-5.8-py310-none-any.whl", hash = "sha256:c67f6638a34a5b8730812f5101376f9d41dc38c43f1fdc35cb54700f6ed4465d"},
{file = "backrefs-5.8-py311-none-any.whl", hash = "sha256:2e1c15e4af0e12e45c8701bd5da0902d326b2e200cafcd25e49d9f06d44bb61b"},
{file = "backrefs-5.8-py312-none-any.whl", hash = "sha256:bbef7169a33811080d67cdf1538c8289f76f0942ff971222a16034da88a73486"},
{file = "backrefs-5.8-py313-none-any.whl", hash = "sha256:e3a63b073867dbefd0536425f43db618578528e3896fb77be7141328642a1585"},
{file = "backrefs-5.8-py39-none-any.whl", hash = "sha256:a66851e4533fb5b371aa0628e1fee1af05135616b86140c9d787a2ffdf4b8fdc"},
{file = "backrefs-5.8.tar.gz", hash = "sha256:2cab642a205ce966af3dd4b38ee36009b31fa9502a35fd61d59ccc116e40a6bd"},
]
[package.extras]
extras = ["regex"]
[[package]]
name = "betterproto"
@@ -88,6 +112,7 @@ version = "2.0.0b7"
description = "A better Protobuf / gRPC generator & library"
optional = false
python-versions = "<4.0,>=3.7"
groups = ["main"]
files = [
{file = "betterproto-2.0.0b7-py3-none-any.whl", hash = "sha256:401ab8055e2f814e77b9c88a74d0e1ae3d1e8a969cced6aeb1b59f71ad63fbd2"},
{file = "betterproto-2.0.0b7.tar.gz", hash = "sha256:1b1458ca5278d519bcd62556a4c236f998a91d503f0f71c67b0b954747052af2"},
@@ -104,13 +129,14 @@ rust-codec = ["betterproto-rust-codec (==0.1.1)"]
[[package]]
name = "certifi"
version = "2024.12.14"
version = "2025.1.31"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
groups = ["main", "docs"]
files = [
{file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
{file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
{file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
{file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
]
[[package]]
@@ -119,6 +145,8 @@ version = "1.17.1"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"},
{file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"},
@@ -198,6 +226,7 @@ version = "3.4.0"
description = "Validate configuration and produce human readable error messages."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
{file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
@@ -209,6 +238,7 @@ version = "3.4.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
@@ -310,6 +340,7 @@ version = "8.1.8"
description = "Composable command line interface toolkit"
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"},
{file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"},
@@ -324,6 +355,7 @@ version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["docs"]
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -335,6 +367,7 @@ version = "43.0.3"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"},
{file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"},
@@ -384,6 +417,7 @@ version = "0.3.9"
description = "Distribution utilities"
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
{file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
@@ -395,6 +429,8 @@ version = "1.2.2"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
groups = ["main"]
markers = "python_version < \"3.11\""
files = [
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
@@ -405,19 +441,20 @@ test = ["pytest (>=6)"]
[[package]]
name = "filelock"
version = "3.16.1"
version = "3.17.0"
description = "A platform independent file lock."
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
{file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
{file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"},
{file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"},
]
[package.extras]
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
typing = ["typing-extensions (>=4.12.2)"]
docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
typing = ["typing-extensions (>=4.12.2) ; python_version < \"3.11\""]
[[package]]
name = "ghp-import"
@@ -425,6 +462,7 @@ version = "2.1.0"
description = "Copy your docs directly to the gh-pages branch."
optional = false
python-versions = "*"
groups = ["docs"]
files = [
{file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"},
{file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"},
@@ -438,13 +476,14 @@ dev = ["flake8", "markdown", "twine", "wheel"]
[[package]]
name = "griffe"
version = "1.5.4"
version = "1.6.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.9"
groups = ["docs"]
files = [
{file = "griffe-1.5.4-py3-none-any.whl", hash = "sha256:ed33af890586a5bebc842fcb919fc694b3dc1bc55b7d9e0228de41ce566b4a1d"},
{file = "griffe-1.5.4.tar.gz", hash = "sha256:073e78ad3e10c8378c2f798bd4ef87b92d8411e9916e157fd366a17cc4fd4e52"},
{file = "griffe-1.6.0-py3-none-any.whl", hash = "sha256:9f1dfe035d4715a244ed2050dfbceb05b1f470809ed4f6bb10ece5a7302f8dd1"},
{file = "griffe-1.6.0.tar.gz", hash = "sha256:eb5758088b9c73ad61c7ac014f3cdfb4c57b5c2fcbfca69996584b702aefa354"},
]
[package.dependencies]
@@ -456,6 +495,7 @@ version = "0.4.7"
description = "Pure-Python gRPC implementation for asyncio"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"},
]
@@ -473,6 +513,7 @@ version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
@@ -480,28 +521,30 @@ files = [
[[package]]
name = "h2"
version = "4.1.0"
description = "HTTP/2 State-Machine based protocol implementation"
version = "4.2.0"
description = "Pure-Python HTTP/2 protocol implementation"
optional = false
python-versions = ">=3.6.1"
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"},
{file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"},
{file = "h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0"},
{file = "h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f"},
]
[package.dependencies]
hpack = ">=4.0,<5"
hyperframe = ">=6.0,<7"
hpack = ">=4.1,<5"
hyperframe = ">=6.1,<7"
[[package]]
name = "hpack"
version = "4.0.0"
description = "Pure-Python HPACK header compression"
version = "4.1.0"
description = "Pure-Python HPACK header encoding"
optional = false
python-versions = ">=3.6.1"
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"},
{file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"},
{file = "hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496"},
{file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"},
]
[[package]]
@@ -510,6 +553,7 @@ version = "1.0.7"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"},
{file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"},
@@ -531,6 +575,7 @@ version = "0.28.1"
description = "The next generation HTTP client."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
{file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
@@ -543,7 +588,7 @@ httpcore = "==1.*"
idna = "*"
[package.extras]
brotli = ["brotli", "brotlicffi"]
brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"]
@@ -551,24 +596,26 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "hyperframe"
version = "6.0.1"
description = "HTTP/2 framing layer for Python"
version = "6.1.0"
description = "Pure-Python HTTP/2 framing"
optional = false
python-versions = ">=3.6.1"
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
{file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
{file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"},
{file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"},
]
[[package]]
name = "identify"
version = "2.6.4"
version = "2.6.8"
description = "File identification library for Python"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af"},
{file = "identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac"},
{file = "identify-2.6.8-py2.py3-none-any.whl", hash = "sha256:83657f0f766a3c8d0eaea16d4ef42494b39b34629a4b3192a9d020d349b3e255"},
{file = "identify-2.6.8.tar.gz", hash = "sha256:61491417ea2c0c5c670484fd8abbb34de34cdae1e5f39a73ee65e48e4bb663fc"},
]
[package.extras]
@@ -580,6 +627,7 @@ version = "3.10"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.6"
groups = ["main", "docs"]
files = [
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
@@ -590,25 +638,27 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
[[package]]
name = "importlib-metadata"
version = "8.5.0"
version = "8.6.1"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
groups = ["docs"]
markers = "python_version < \"3.10\""
files = [
{file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"},
{file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"},
{file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
{file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
]
[package.dependencies]
zipp = ">=3.20"
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
enabler = ["pytest-enabler (>=2.2)"]
perf = ["ipython"]
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
test = ["flufl.flake8", "importlib_resources (>=1.3) ; python_version < \"3.9\"", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
type = ["pytest-mypy"]
[[package]]
@@ -617,6 +667,7 @@ version = "5.13.2"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
@@ -631,6 +682,7 @@ version = "3.1.5"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
groups = ["docs"]
files = [
{file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
{file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
@@ -648,6 +700,7 @@ version = "3.7"
description = "Python implementation of John Gruber's Markdown."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"},
{file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"},
@@ -666,6 +719,7 @@ version = "3.0.2"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"},
{file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"},
@@ -736,6 +790,7 @@ version = "1.3.4"
description = "A deep merge function for 🐍."
optional = false
python-versions = ">=3.6"
groups = ["docs"]
files = [
{file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
{file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
@@ -747,6 +802,7 @@ version = "1.6.1"
description = "Project documentation with Markdown."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"},
{file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"},
@@ -770,17 +826,18 @@ watchdog = ">=2.0"
[package.extras]
i18n = ["babel (>=2.9.0)"]
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4) ; platform_system == \"Windows\"", "ghp-import (==1.0)", "importlib-metadata (==4.4) ; python_version < \"3.10\"", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"]
[[package]]
name = "mkdocs-autorefs"
version = "1.2.0"
version = "1.4.0"
description = "Automatically link across pages in MkDocs."
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"},
{file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"},
{file = "mkdocs_autorefs-1.4.0-py3-none-any.whl", hash = "sha256:bad19f69655878d20194acd0162e29a89c3f7e6365ffe54e72aa3fd1072f240d"},
{file = "mkdocs_autorefs-1.4.0.tar.gz", hash = "sha256:a9c0aa9c90edbce302c09d050a3c4cb7c76f8b7b2c98f84a7a05f53d00392156"},
]
[package.dependencies]
@@ -794,6 +851,7 @@ version = "0.2.0"
description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file"
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"},
{file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"},
@@ -807,17 +865,19 @@ pyyaml = ">=5.1"
[[package]]
name = "mkdocs-material"
version = "9.5.49"
version = "9.6.7"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "mkdocs_material-9.5.49-py3-none-any.whl", hash = "sha256:c3c2d8176b18198435d3a3e119011922f3e11424074645c24019c2dcf08a360e"},
{file = "mkdocs_material-9.5.49.tar.gz", hash = "sha256:3671bb282b4f53a1c72e08adbe04d2481a98f85fed392530051f80ff94a9621d"},
{file = "mkdocs_material-9.6.7-py3-none-any.whl", hash = "sha256:8a159e45e80fcaadd9fbeef62cbf928569b93df954d4dc5ba76d46820caf7b47"},
{file = "mkdocs_material-9.6.7.tar.gz", hash = "sha256:3e2c1fceb9410056c2d91f334a00cdea3215c28750e00c691c1e46b2a33309b4"},
]
[package.dependencies]
babel = ">=2.10,<3.0"
backrefs = ">=5.7.post1,<6.0"
colorama = ">=0.4,<1.0"
jinja2 = ">=3.0,<4.0"
markdown = ">=3.2,<4.0"
@@ -826,11 +886,10 @@ mkdocs-material-extensions = ">=1.3,<2.0"
paginate = ">=0.5,<1.0"
pygments = ">=2.16,<3.0"
pymdown-extensions = ">=10.2,<11.0"
regex = ">=2022.4"
requests = ">=2.26,<3.0"
[package.extras]
git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"]
git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"]
imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"]
recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"]
@@ -840,6 +899,7 @@ version = "1.3.1"
description = "Extension pack for Python Markdown and MkDocs Material."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"},
{file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"},
@@ -851,6 +911,7 @@ version = "0.26.2"
description = "Automatic documentation from sources, for MkDocs."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "mkdocstrings-0.26.2-py3-none-any.whl", hash = "sha256:1248f3228464f3b8d1a15bd91249ce1701fe3104ac517a5f167a0e01ca850ba5"},
{file = "mkdocstrings-0.26.2.tar.gz", hash = "sha256:34a8b50f1e6cfd29546c6c09fbe02154adfb0b361bb758834bf56aa284ba876e"},
@@ -880,6 +941,7 @@ version = "1.13.0"
description = "A Python handler for mkdocstrings."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "mkdocstrings_python-1.13.0-py3-none-any.whl", hash = "sha256:b88bbb207bab4086434743849f8e796788b373bd32e7bfefbf8560ac45d88f97"},
{file = "mkdocstrings_python-1.13.0.tar.gz", hash = "sha256:2dbd5757e8375b9720e81db16f52f1856bf59905428fd7ef88005d1370e2f64c"},
@@ -896,6 +958,7 @@ version = "6.1.0"
description = "multidict implementation"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"},
{file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"},
@@ -1000,6 +1063,7 @@ version = "1.9.1"
description = "Node.js virtual environment builder"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
files = [
{file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
@@ -1011,6 +1075,7 @@ version = "24.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
@@ -1022,6 +1087,7 @@ version = "0.5.7"
description = "Divides large result sets into pages for easier browsing"
optional = false
python-versions = "*"
groups = ["docs"]
files = [
{file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"},
{file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"},
@@ -1037,6 +1103,7 @@ version = "1.7.4"
description = "comprehensive password hashing framework supporting over 30 schemes"
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"},
{file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"},
@@ -1054,6 +1121,7 @@ version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
@@ -1065,6 +1133,7 @@ version = "4.3.6"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
python-versions = ">=3.8"
groups = ["dev", "docs"]
files = [
{file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
{file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
@@ -1077,13 +1146,14 @@ type = ["mypy (>=1.11.2)"]
[[package]]
name = "pre-commit"
version = "4.0.1"
version = "4.1.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"},
{file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"},
{file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"},
{file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"},
]
[package.dependencies]
@@ -1099,6 +1169,7 @@ version = "25.1.0"
description = "PyYAML-based module to produce a bit more pretty and readable YAML-serialized data"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "pyaml-25.1.0-py3-none-any.whl", hash = "sha256:f7b40629d2dae88035657c860f539db3525ddd0120a11e0bcb44d47d5968b3bc"},
{file = "pyaml-25.1.0.tar.gz", hash = "sha256:33a93ac49218f57e020b81e280d2706cea554ac5a76445ac79add760d019c709"},
@@ -1116,6 +1187,8 @@ version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "platform_python_implementation != \"PyPy\""
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
@@ -1123,13 +1196,14 @@ files = [
[[package]]
name = "pydantic"
version = "2.10.4"
version = "2.10.6"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"},
{file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"},
{file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"},
{file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"},
]
[package.dependencies]
@@ -1139,7 +1213,7 @@ typing-extensions = ">=4.12.2"
[package.extras]
email = ["email-validator (>=2.0.0)"]
timezone = ["tzdata"]
timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
[[package]]
name = "pydantic-core"
@@ -1147,6 +1221,7 @@ version = "2.27.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"},
{file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"},
@@ -1255,13 +1330,14 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pygments"
version = "2.18.0"
version = "2.19.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
{file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
]
[package.extras]
@@ -1269,13 +1345,14 @@ windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pymdown-extensions"
version = "10.13"
version = "10.14.3"
description = "Extension pack for Python Markdown."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "pymdown_extensions-10.13-py3-none-any.whl", hash = "sha256:80bc33d715eec68e683e04298946d47d78c7739e79d808203df278ee8ef89428"},
{file = "pymdown_extensions-10.13.tar.gz", hash = "sha256:e0b351494dc0d8d14a1f52b39b1499a00ef1566b4ba23dc74f1eba75c736f5dd"},
{file = "pymdown_extensions-10.14.3-py3-none-any.whl", hash = "sha256:05e0bee73d64b9c71a4ae17c72abc2f700e8bc8403755a00580b49a4e9f189e9"},
{file = "pymdown_extensions-10.14.3.tar.gz", hash = "sha256:41e576ce3f5d650be59e900e4ceff231e0aed2a88cf30acaee41e02f063a061b"},
]
[package.dependencies]
@@ -1283,7 +1360,7 @@ markdown = ">=3.6"
pyyaml = "*"
[package.extras]
extra = ["pygments (>=2.12)"]
extra = ["pygments (>=2.19.1)"]
[[package]]
name = "python-dateutil"
@@ -1291,6 +1368,7 @@ version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
groups = ["main", "docs"]
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@@ -1305,6 +1383,7 @@ version = "6.0.2"
description = "YAML parser and emitter for Python"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev", "docs"]
files = [
{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"},
@@ -1367,6 +1446,7 @@ version = "0.1"
description = "A custom YAML tag for referencing environment variables in YAML files. "
optional = false
python-versions = ">=3.6"
groups = ["docs"]
files = [
{file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
{file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
@@ -1375,115 +1455,13 @@ files = [
[package.dependencies]
pyyaml = "*"
[[package]]
name = "regex"
version = "2024.11.6"
description = "Alternative regular expression module, to replace re."
optional = false
python-versions = ">=3.8"
files = [
{file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"},
{file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"},
{file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"},
{file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"},
{file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"},
{file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"},
{file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"},
{file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"},
{file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"},
{file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"},
{file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"},
{file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"},
{file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"},
{file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"},
{file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"},
{file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"},
{file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"},
{file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"},
{file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"},
{file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"},
{file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"},
{file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"},
{file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"},
{file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"},
{file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"},
{file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"},
{file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"},
{file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"},
{file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"},
{file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"},
{file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"},
{file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"},
{file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"},
{file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"},
{file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"},
{file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"},
{file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"},
{file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"},
{file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"},
{file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"},
{file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"},
{file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"},
{file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"},
{file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"},
{file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"},
{file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"},
{file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"},
{file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"},
{file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"},
{file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"},
{file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"},
{file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"},
{file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"},
{file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"},
{file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"},
{file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"},
{file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"},
{file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"},
{file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"},
{file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"},
{file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"},
{file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"},
{file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"},
{file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"},
{file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"},
{file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"},
{file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"},
{file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"},
{file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"},
{file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"},
{file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"},
{file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"},
{file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"},
{file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"},
{file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"},
{file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"},
{file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"},
{file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"},
{file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"},
{file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"},
{file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"},
{file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"},
{file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"},
{file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"},
{file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"},
{file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"},
{file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"},
{file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"},
{file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"},
{file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"},
{file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"},
{file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"},
{file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"},
{file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"},
]
[[package]]
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.8"
groups = ["docs"]
files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
@@ -1505,6 +1483,7 @@ version = "1.17.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
groups = ["main", "docs"]
files = [
{file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
{file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
@@ -1516,6 +1495,7 @@ version = "1.3.1"
description = "Sniff out which async library your code is running under"
optional = false
python-versions = ">=3.7"
groups = ["main"]
files = [
{file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
{file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
@@ -1527,6 +1507,8 @@ version = "2.2.1"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version < \"3.11\""
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -1564,13 +1546,14 @@ files = [
[[package]]
name = "tomli-w"
version = "1.1.0"
version = "1.2.0"
description = "A lil' TOML writer"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "tomli_w-1.1.0-py3-none-any.whl", hash = "sha256:1403179c78193e3184bfaade390ddbd071cba48a32a2e62ba11aae47490c63f7"},
{file = "tomli_w-1.1.0.tar.gz", hash = "sha256:49e847a3a304d516a169a601184932ef0f6b61623fe680f836a2aa7128ed0d33"},
{file = "tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90"},
{file = "tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021"},
]
[[package]]
@@ -1579,10 +1562,12 @@ version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
groups = ["main", "docs"]
files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
markers = {docs = "python_version < \"3.10\""}
[[package]]
name = "urllib3"
@@ -1590,26 +1575,28 @@ version = "2.3.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"},
{file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "virtualenv"
version = "20.28.0"
version = "20.29.2"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"},
{file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"},
{file = "virtualenv-20.29.2-py3-none-any.whl", hash = "sha256:febddfc3d1ea571bdb1dc0f98d7b45d24def7428214d4fb73cc486c9568cce6a"},
{file = "virtualenv-20.29.2.tar.gz", hash = "sha256:fdaabebf6d03b5ba83ae0a02cfe96f48a716f4fae556461d180825866f75b728"},
]
[package.dependencies]
@@ -1619,7 +1606,7 @@ platformdirs = ">=3.9.1,<5"
[package.extras]
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
[[package]]
name = "watchdog"
@@ -1627,6 +1614,7 @@ version = "6.0.0"
description = "Filesystem events monitoring"
optional = false
python-versions = ">=3.9"
groups = ["docs"]
files = [
{file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"},
{file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"},
@@ -1669,20 +1657,22 @@ version = "3.21.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.9"
groups = ["docs"]
markers = "python_version < \"3.10\""
files = [
{file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
{file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\""]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
test = ["big-O", "importlib-resources ; python_version < \"3.9\"", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"]
type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "c0aa00dd5f3b52bbac53eb765be2bca2ec7f9429e835d6b2fe6bf207f2f39974"
lock-version = "2.1"
python-versions = ">3.9, <4.0"
content-hash = "67a8e0d34c0d1af0f8e4d617d80fd5afc8d197e5d5444f78fd82e4f716a52965"

View File

@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import importlib.metadata
from pyasic import settings
from pyasic.config import MinerConfig
from pyasic.data import MinerData
@@ -22,3 +24,5 @@ from pyasic.network import MinerNetwork
from pyasic.rpc import *
from pyasic.ssh import *
from pyasic.web import *
__version__ = importlib.metadata.version("pyasic")

View File

@@ -46,7 +46,7 @@ class MinerConfig(BaseModel):
"""Converts the MinerConfig object to a dictionary."""
return self.model_dump()
def as_am_modern(self, user_suffix: str = None) -> dict:
def as_am_modern(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for modern Antminers."""
return {
**self.fan_mode.as_am_modern(),
@@ -56,7 +56,27 @@ class MinerConfig(BaseModel):
**self.temperature.as_am_modern(),
}
def as_wm(self, user_suffix: str = None) -> dict:
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for modern Hiveon."""
return {
**self.fan_mode.as_hiveon_modern(),
"freq-level": "100",
**self.mining_mode.as_hiveon_modern(),
**self.pools.as_hiveon_modern(user_suffix=user_suffix),
**self.temperature.as_hiveon_modern(),
}
def as_elphapex(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for modern Elphapex."""
return {
**self.fan_mode.as_elphapex(),
"fc-freq-level": "100",
**self.mining_mode.as_elphapex(),
**self.pools.as_elphapex(user_suffix=user_suffix),
**self.temperature.as_elphapex(),
}
def as_wm(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Whatsminers."""
return {
**self.fan_mode.as_wm(),
@@ -65,7 +85,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_wm(),
}
def as_am_old(self, user_suffix: str = None) -> dict:
def as_am_old(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for old versions of Antminers."""
return {
**self.fan_mode.as_am_old(),
@@ -74,7 +94,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_am_old(),
}
def as_goldshell(self, user_suffix: str = None) -> dict:
def as_goldshell(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Goldshell miners."""
return {
**self.fan_mode.as_goldshell(),
@@ -83,7 +103,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_goldshell(),
}
def as_avalon(self, user_suffix: str = None) -> dict:
def as_avalon(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Avalonminers."""
return {
**self.fan_mode.as_avalon(),
@@ -92,7 +112,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_avalon(),
}
def as_inno(self, user_suffix: str = None) -> dict:
def as_inno(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Innosilicon miners."""
return {
**self.fan_mode.as_inno(),
@@ -101,7 +121,7 @@ class MinerConfig(BaseModel):
**self.temperature.as_inno(),
}
def as_bosminer(self, user_suffix: str = None) -> dict:
def as_bosminer(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the bosminer.toml format."""
return {
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
@@ -109,7 +129,7 @@ class MinerConfig(BaseModel):
**self.pools.as_bosminer(user_suffix=user_suffix),
}
def as_boser(self, user_suffix: str = None) -> dict:
def as_boser(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for BOSer."""
return {
**self.fan_mode.as_boser(),
@@ -118,7 +138,7 @@ class MinerConfig(BaseModel):
**self.pools.as_boser(user_suffix=user_suffix),
}
def as_epic(self, user_suffix: str = None) -> dict:
def as_epic(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for ePIC miners."""
return {
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
@@ -126,7 +146,7 @@ class MinerConfig(BaseModel):
**self.pools.as_epic(user_suffix=user_suffix),
}
def as_auradine(self, user_suffix: str = None) -> dict:
def as_auradine(self, user_suffix: str | None = None) -> dict:
"""Generates the configuration in the format suitable for Auradine miners."""
return {
**self.fan_mode.as_auradine(),
@@ -135,7 +155,7 @@ class MinerConfig(BaseModel):
**self.pools.as_auradine(user_suffix=user_suffix),
}
def as_mara(self, user_suffix: str = None) -> dict:
def as_mara(self, user_suffix: str | None = None) -> dict:
return {
**self.fan_mode.as_mara(),
**self.temperature.as_mara(),
@@ -143,15 +163,15 @@ class MinerConfig(BaseModel):
**self.pools.as_mara(user_suffix=user_suffix),
}
def as_bitaxe(self, user_suffix: str = None) -> dict:
def as_espminer(self, user_suffix: str | None = 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),
**self.fan_mode.as_espminer(),
**self.temperature.as_espminer(),
**self.mining_mode.as_espminer(),
**self.pools.as_espminer(user_suffix=user_suffix),
}
def as_luxos(self, user_suffix: str = None) -> dict:
def as_luxos(self, user_suffix: str | None = None) -> dict:
return {
**self.fan_mode.as_luxos(),
**self.temperature.as_luxos(),
@@ -159,7 +179,7 @@ class MinerConfig(BaseModel):
**self.pools.as_luxos(user_suffix=user_suffix),
}
def as_vnish(self, user_suffix: str = None) -> dict:
def as_vnish(self, user_suffix: str | None = None) -> dict:
main_cfg = {
"miner": {
**self.fan_mode.as_vnish(),
@@ -199,6 +219,24 @@ class MinerConfig(BaseModel):
fan_mode=FanModeConfig.from_am_modern(web_conf),
)
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for Hiveon."""
return cls(
pools=PoolConfig.from_hiveon_modern(web_conf),
mining_mode=MiningModeConfig.from_hiveon_modern(web_conf),
fan_mode=FanModeConfig.from_hiveon_modern(web_conf),
)
@classmethod
def from_elphapex(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for modern Antminers."""
return cls(
pools=PoolConfig.from_elphapex(web_conf),
mining_mode=MiningModeConfig.from_elphapex(web_conf),
fan_mode=FanModeConfig.from_elphapex(web_conf),
)
@classmethod
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
@@ -272,10 +310,10 @@ class MinerConfig(BaseModel):
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig":
def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
return cls(
pools=PoolConfig.from_bitaxe(web_system_info),
fan_mode=FanModeConfig.from_bitaxe(web_system_info),
pools=PoolConfig.from_espminer(web_system_info),
fan_mode=FanModeConfig.from_espminer(web_system_info),
)
@classmethod
@@ -308,7 +346,3 @@ class MinerConfig(BaseModel):
@classmethod
def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
return cls.from_am_modern(*args, **kwargs)
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
return cls.from_am_modern(web_conf)

View File

@@ -28,6 +28,9 @@ class MinerConfigOption(Enum):
def as_am_modern(self) -> dict:
return self.value.as_am_modern()
def as_hiveon_modern(self) -> dict:
return self.value.as_hiveon_modern()
def as_am_old(self) -> dict:
return self.value.as_am_old()
@@ -61,12 +64,15 @@ class MinerConfigOption(Enum):
def as_mara(self) -> dict:
return self.value.as_mara()
def as_bitaxe(self) -> dict:
return self.value.as_bitaxe()
def as_espminer(self) -> dict:
return self.value.as_espminer()
def as_luxos(self) -> dict:
return self.value.as_luxos()
def as_elphapex(self) -> dict:
return self.value.as_elphapex()
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
@@ -92,6 +98,9 @@ class MinerConfigValue(BaseModel):
def as_am_modern(self) -> dict:
return {}
def as_hiveon_modern(self) -> dict:
return {}
def as_am_old(self) -> dict:
return {}
@@ -125,12 +134,15 @@ class MinerConfigValue(BaseModel):
def as_mara(self) -> dict:
return {}
def as_bitaxe(self) -> dict:
def as_espminer(self) -> dict:
return {}
def as_luxos(self) -> dict:
return {}
def as_elphapex(self) -> dict:
return {}
def __getitem__(self, item):
try:
return getattr(self, item)

View File

@@ -55,6 +55,12 @@ class FanModeNormal(MinerConfigValue):
def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
def as_hiveon_modern(self) -> dict:
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
def as_elphapex(self) -> dict:
return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"}
def as_bosminer(self) -> dict:
return {
"temp_control": {"mode": "auto"},
@@ -81,7 +87,7 @@ class FanModeNormal(MinerConfigValue):
},
}
def as_bitaxe(self) -> dict:
def as_espminer(self) -> dict:
return {"autoFanspeed": 1}
def as_luxos(self) -> dict:
@@ -135,6 +141,12 @@ class FanModeManual(MinerConfigValue):
def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
def as_hiveon_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
def as_elphapex(self) -> dict:
return {"fc-fan-ctrl": True, "fc-fan-pwm": str(self.speed)}
def as_bosminer(self) -> dict:
return {
"temp_control": {"mode": "manual"},
@@ -156,7 +168,7 @@ class FanModeManual(MinerConfigValue):
},
}
def as_bitaxe(self) -> dict:
def as_espminer(self) -> dict:
return {"autoFanspeed": 0, "fanspeed": self.speed}
def as_luxos(self) -> dict:
@@ -185,6 +197,12 @@ class FanModeImmersion(MinerConfigValue):
def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
def as_hiveon_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
def as_elphapex(self) -> dict:
return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"}
def as_bosminer(self) -> dict:
return {
"fan_control": {"min_fans": 0},
@@ -239,6 +257,34 @@ class FanModeConfig(MinerConfigOption):
else:
return cls.default()
@classmethod
def from_hiveon_modern(cls, web_conf: dict):
if web_conf.get("bitmain-fan-ctrl") is not None:
fan_manual = web_conf["bitmain-fan-ctrl"]
if fan_manual:
speed = int(web_conf["bitmain-fan-pwm"])
if speed == 0:
return cls.immersion()
return cls.manual(speed=speed)
else:
return cls.normal()
else:
return cls.default()
@classmethod
def from_elphapex(cls, web_conf: dict):
if web_conf.get("fc-fan-ctrl") is not None:
fan_manual = web_conf["fc-fan-ctrl"]
if fan_manual:
speed = int(web_conf["fc-fan-pwm"])
if speed == 0:
return cls.immersion()
return cls.manual(speed=speed)
else:
return cls.normal()
else:
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
try:
@@ -308,6 +354,12 @@ class FanModeConfig(MinerConfigOption):
if "minimumRequiredFans" in keys:
conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"])
return cls.manual(**conf)
if "disabled" in keys:
conf = {}
if "fanSpeedRatio" in temperature_conf["disabled"].keys():
conf["speed"] = int(temperature_conf["disabled"]["fanSpeedRatio"])
return cls.manual(**conf)
return cls.default()
@classmethod
def from_auradine(cls, web_fan: dict):
@@ -336,7 +388,7 @@ class FanModeConfig(MinerConfigOption):
return cls.default()
@classmethod
def from_bitaxe(cls, web_system_info: dict):
def from_espminer(cls, web_system_info: dict):
if web_system_info["autofanspeed"] == 1:
return cls.normal()
else:

View File

@@ -52,6 +52,14 @@ class MiningModeNormal(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict:
return {"mode": self.mode}
@@ -74,6 +82,9 @@ class MiningModeNormal(MinerConfigValue):
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": False}}
def as_bosminer(self) -> dict:
return {"autotuning": {"enabled": True}}
class MiningModeSleep(MinerConfigValue):
mode: str = field(init=False, default="sleep")
@@ -87,6 +98,14 @@ class MiningModeSleep(MinerConfigValue):
return {"miner-mode": "1"}
return {"miner-mode": 1}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "1"}
return {"miner-mode": 1}
def as_elphapex(self) -> dict:
return {"miner-mode": 1}
def as_wm(self) -> dict:
return {"mode": self.mode}
@@ -119,6 +138,14 @@ class MiningModeLPM(MinerConfigValue):
return {"miner-mode": "3"}
return {"miner-mode": 3}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "3"}
return {"miner-mode": 3}
def as_elphapex(self) -> dict:
return {"miner-mode": 3}
def as_wm(self) -> dict:
return {"mode": self.mode}
@@ -141,6 +168,14 @@ class MiningModeHPM(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict:
return {"mode": self.mode}
@@ -174,6 +209,14 @@ class MiningModePowerTune(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_wm(self) -> dict:
if self.power is not None:
return {"mode": self.mode, self.mode: {"wattage": self.power}}
@@ -215,18 +258,18 @@ class MiningModePowerTune(MinerConfigValue):
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
power_target_kwargs = {}
if self.scaling.step is not None:
power_target_kwargs["power_step"] = Power(self.scaling.step)
if self.scaling.minimum is not None:
power_target_kwargs["min_power_target"] = Power(self.scaling.minimum)
cfg["set_dps"] = SetDpsRequest(
save_action=SaveAction.SAVE_AND_APPLY,
enable=True,
**sd_cfg,
target=DpsTarget(
power_target=DpsPowerTarget(
power_step=Power(self.scaling.step),
min_power_target=Power(self.scaling.minimum),
)
),
),
target=DpsTarget(power_target=DpsPowerTarget(**power_target_kwargs)),
)
return cfg
def as_auradine(self) -> dict:
@@ -273,13 +316,20 @@ class MiningModeHashrateTune(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_bosminer(self) -> dict:
conf = {"enabled": True, "mode": "hashrate_target"}
if self.hashrate is not None:
conf["hashrate_target"] = self.hashrate
return {"autotuning": conf}
@property
def as_boser(self) -> dict:
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
@@ -299,18 +349,24 @@ class MiningModeHashrateTune(MinerConfigValue):
sd_cfg = {}
if self.scaling.shutdown is not None:
sd_cfg = self.scaling.shutdown.as_boser()
cfg["set_dps"] = (
SetDpsRequest(
hashrate_target_kwargs = {}
if self.scaling.step is not None:
hashrate_target_kwargs["hashrate_step"] = TeraHashrate(
self.scaling.step
)
if self.scaling.minimum is not None:
hashrate_target_kwargs["min_hashrate_target"] = TeraHashrate(
self.scaling.minimum
)
cfg["set_dps"] = SetDpsRequest(
save_action=SaveAction.SAVE_AND_APPLY,
enable=True,
**sd_cfg,
target=DpsTarget(
hashrate_target=DpsHashrateTarget(
hashrate_step=TeraHashrate(self.scaling.step),
min_hashrate_target=TeraHashrate(self.scaling.minimum),
)
),
hashrate_target=DpsHashrateTarget(**hashrate_target_kwargs)
),
)
return cfg
def as_auradine(self) -> dict:
@@ -368,20 +424,25 @@ class MiningModePreset(MinerConfigValue):
)
@classmethod
def from_luxos(
cls, rpc_config: dict, rpc_profiles: list[dict]
) -> "MiningModePreset":
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> "MiningModePreset":
active_preset = cls.get_active_preset_from_luxos(rpc_config, rpc_profiles)
return cls(
active_preset=active_preset,
available_presets=[
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
],
)
@classmethod
def get_active_preset_from_luxos(
cls, rpc_config: dict, rpc_profiles: dict
) -> MiningPreset:
active_preset = None
active_profile = rpc_config["CONFIG"][0]["Profile"]
for profile in rpc_profiles["PROFILES"]:
if profile["Profile Name"] == active_profile:
active_preset = profile
return cls(
active_preset=MiningPreset.from_luxos(active_preset),
available_presets=[
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
],
)
return MiningPreset.from_luxos(active_preset)
class ManualBoardSettings(MinerConfigValue):
@@ -397,6 +458,14 @@ class ManualBoardSettings(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_hiveon_modern(self) -> dict:
if settings.get("antminer_mining_mode_as_str", False):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {"freq": self.freq}
@@ -421,6 +490,9 @@ class MiningModeManual(MinerConfigValue):
return {"miner-mode": "0"}
return {"miner-mode": 0}
def as_elphapex(self) -> dict:
return {"miner-mode": 0}
def as_vnish(self) -> dict:
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
return {
@@ -518,6 +590,34 @@ class MiningModeConfig(MinerConfigOption):
return cls.low()
return cls.default()
@classmethod
def from_hiveon_modern(cls, web_conf: dict):
if web_conf.get("bitmain-work-mode") is not None:
work_mode = web_conf["bitmain-work-mode"]
if work_mode == "":
return cls.default()
if int(work_mode) == 0:
return cls.normal()
elif int(work_mode) == 1:
return cls.sleep()
elif int(work_mode) == 3:
return cls.low()
return cls.default()
@classmethod
def from_elphapex(cls, web_conf: dict):
if web_conf.get("fc-work-mode") is not None:
work_mode = web_conf["fc-work-mode"]
if work_mode == "":
return cls.default()
if int(work_mode) == 0:
return cls.normal()
elif int(work_mode) == 1:
return cls.sleep()
elif int(work_mode) == 3:
return cls.low()
return cls.default()
@classmethod
def from_epic(cls, web_conf: dict):
try:
@@ -600,6 +700,7 @@ class MiningModeConfig(MinerConfigOption):
return cls.hashrate_tuning(
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
return cls.default()
@classmethod
def from_vnish(cls, web_settings: dict, web_presets: list[dict]):
@@ -705,7 +806,11 @@ class MiningModeConfig(MinerConfigOption):
@classmethod
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict):
return MiningModePreset.from_luxos(rpc_config, rpc_profiles)
preset_info = MiningModePreset.from_luxos(rpc_config, rpc_profiles)
return cls.preset(
active_preset=preset_info.active_preset,
available_presets=preset_info.available_presets,
)
MiningMode = TypeVar(

View File

@@ -1,6 +1,6 @@
from __future__ import annotations
from dataclasses import dataclass, field
from dataclasses import field
from typing import TypeVar, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue

View File

@@ -24,7 +24,14 @@ class MiningPreset(MinerConfigValue):
hashrate = None
else:
power = hr_power_split[0].replace("watt", "").strip()
hashrate = hr_power_split[1].replace("TH", "").replace(" LC", "").strip()
hashrate = (
hr_power_split[1]
.replace("TH", "")
.replace("GH", "")
.replace("MH", "")
.replace(" LC", "")
.strip()
)
tuned = web_preset["status"] == "tuned"
modded_psu = web_preset["modded_psu_required"]
return cls(

View File

@@ -15,14 +15,12 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
class ScalingShutdown(MinerConfigValue):
enabled: bool = False
duration: int = None
duration: int | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingShutdown":
@@ -65,9 +63,9 @@ class ScalingShutdown(MinerConfigValue):
class ScalingConfig(MinerConfigValue):
step: int = None
minimum: int = None
shutdown: ScalingShutdown = None
step: int | None = None
minimum: int | None = None
shutdown: ScalingShutdown | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "ScalingConfig":
@@ -81,7 +79,7 @@ class ScalingConfig(MinerConfigValue):
return cls(**cls_conf)
@classmethod
def from_bosminer(cls, toml_conf: dict, mode: str = None):
def from_bosminer(cls, toml_conf: dict, mode: str | None = None):
if mode == "power":
return cls._from_bosminer_power(toml_conf)
if mode == "hashrate":
@@ -106,7 +104,7 @@ class ScalingConfig(MinerConfigValue):
return cls(step=power_step, minimum=min_power, shutdown=sd_mode)
@classmethod
def from_boser(cls, grpc_miner_conf: dict, mode: str = None):
def from_boser(cls, grpc_miner_conf: dict, mode: str | None = None):
if mode == "power":
return cls._from_boser_power(grpc_miner_conf)
if mode == "hashrate":

View File

@@ -43,6 +43,20 @@ class Pool(MinerConfigValue):
"pass": self.password,
}
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
return {
"url": self.url,
"user": f"{self.user}{user_suffix or ''}",
"pass": self.password,
}
def as_elphapex(self, user_suffix: str | None = None) -> dict:
return {
"url": self.url,
"user": f"{self.user}{user_suffix or ''}",
"pass": self.password,
}
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
return {
f"pool_{idx}": self.url,
@@ -102,7 +116,7 @@ class Pool(MinerConfigValue):
"pass": self.password,
}
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
def as_espminer(self, user_suffix: str | None = None) -> dict:
return {
"stratumURL": self.url,
"stratumUser": f"{self.user}{user_suffix or ''}",
@@ -146,6 +160,18 @@ class Pool(MinerConfigValue):
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
@classmethod
def from_hiveon_modern(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
@classmethod
def from_elphapex(cls, web_pool: dict) -> "Pool":
return cls(
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
)
# TODO: check if this is accurate, user/username, pass/password
@classmethod
def from_goldshell(cls, web_pool: dict) -> "Pool":
@@ -164,7 +190,7 @@ class Pool(MinerConfigValue):
return cls(
url=toml_pool_conf["url"],
user=toml_pool_conf["user"],
password=toml_pool_conf["password"],
password=toml_pool_conf.get("password", ""),
)
@classmethod
@@ -192,7 +218,7 @@ class Pool(MinerConfigValue):
)
@classmethod
def from_bitaxe(cls, web_system_info: dict) -> "Pool":
def from_espminer(cls, web_system_info: dict) -> "Pool":
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
return cls(
url=url,
@@ -235,6 +261,28 @@ class PoolGroup(MinerConfigValue):
idx += 1
return pools
def as_hiveon_modern(self, user_suffix: str | None = None) -> list:
pools = []
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.append(self.pools[idx].as_hiveon_modern(user_suffix=user_suffix))
else:
pools.append(Pool(url="", user="", password="").as_hiveon_modern())
idx += 1
return pools
def as_elphapex(self, user_suffix: str | None = None) -> list:
pools = []
idx = 0
while idx < 3:
if len(self.pools) > idx:
pools.append(self.pools[idx].as_elphapex(user_suffix=user_suffix))
else:
pools.append(Pool(url="", user="", password="").as_elphapex())
idx += 1
return pools
def as_wm(self, user_suffix: str | None = None) -> dict:
pools = {}
idx = 0
@@ -306,8 +354,8 @@ class PoolGroup(MinerConfigValue):
def as_mara(self, user_suffix: str | None = None) -> list:
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
def as_espminer(self, user_suffix: str | None = None) -> dict:
return self.pools[0].as_espminer(user_suffix=user_suffix)
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
return PoolGroupConfiguration(
@@ -351,6 +399,20 @@ class PoolGroup(MinerConfigValue):
pools.append(Pool.from_am_modern(pool))
return cls(pools=pools)
@classmethod
def from_hiveon_modern(cls, web_pool_list: list) -> "PoolGroup":
pools = []
for pool in web_pool_list:
pools.append(Pool.from_hiveon_modern(pool))
return cls(pools=pools)
@classmethod
def from_elphapex(cls, web_pool_list: list) -> "PoolGroup":
pools = []
for pool in web_pool_list:
pools.append(Pool.from_elphapex(pool))
return cls(pools=pools)
@classmethod
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
@@ -364,7 +426,7 @@ class PoolGroup(MinerConfigValue):
if toml_group_conf.get("pool") is not None:
return cls(
name=toml_group_conf["name"],
quota=toml_group_conf.get("quota"),
quota=toml_group_conf.get("quota", 1),
pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]],
)
return cls()
@@ -395,8 +457,8 @@ class PoolGroup(MinerConfigValue):
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)])
def from_espminer(cls, web_system_info: dict) -> "PoolGroup":
return cls(pools=[Pool.from_espminer(web_system_info)])
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup":
@@ -436,6 +498,16 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_am_modern()}
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_hiveon_modern(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_hiveon_modern()}
def as_elphapex(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_elphapex(user_suffix=user_suffix)}
return {"pools": PoolGroup().as_elphapex()}
def as_wm(self, user_suffix: str | None = None) -> dict:
if len(self.groups) > 0:
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
@@ -507,8 +579,8 @@ class PoolConfig(MinerConfigValue):
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
return {"pools": []}
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
def as_espminer(self, user_suffix: str | None = None) -> dict:
return self.groups[0].as_espminer(user_suffix=user_suffix)
def as_luxos(self, user_suffix: str | None = None) -> dict:
return {}
@@ -533,10 +605,28 @@ class PoolConfig(MinerConfigValue):
@classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
try:
pool_data = web_conf["pools"]
except KeyError:
return cls(groups=[])
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
@classmethod
def from_hiveon_modern(cls, web_conf: dict) -> "PoolConfig":
try:
pool_data = web_conf["pools"]
except KeyError:
return cls(groups=[])
return cls(groups=[PoolGroup.from_hiveon_modern(pool_data)])
@classmethod
def from_elphapex(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"]
return cls(groups=[PoolGroup.from_elphapex(pool_data)])
@classmethod
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
@@ -576,8 +666,8 @@ class PoolConfig(MinerConfigValue):
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)])
def from_espminer(cls, web_system_info: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_espminer(web_system_info)])
@classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":

View File

@@ -15,8 +15,6 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue

View File

@@ -16,7 +16,7 @@
import copy
import time
from datetime import datetime, timezone
from typing import Any, List, Union
from typing import Any
from pydantic import BaseModel, Field, computed_field
@@ -24,12 +24,11 @@ from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune
from pyasic.data.pools import PoolMetrics, Scheme
from pyasic.device.algorithm.hashrate import AlgoHashRateType
from pyasic.device.algorithm.hashrate.base import GenericHashrate
from ..device.algorithm.hashrate.unit.base import GenericUnit
from .boards import HashBoard
from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .error_codes.base import BaseMinerError
from .fans import Fan
@@ -109,25 +108,18 @@ class MinerData(BaseModel):
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False)
# fans
fans: List[Fan] = Field(default_factory=list)
fans: list[Fan] = Field(default_factory=list)
fan_psu: int | None = None
# boards
hashboards: List[HashBoard] = Field(default_factory=list)
hashboards: list[HashBoard] = Field(default_factory=list)
# config
config: MinerConfig | None = None
fault_light: bool | None = None
# errors
errors: List[
Union[
WhatsminerError,
BraiinsOSError,
X19Error,
InnosiliconError,
]
] = Field(default_factory=list)
errors: list[BaseMinerError] = Field(default_factory=list)
# mining state
is_mining: bool = True
@@ -137,8 +129,10 @@ class MinerData(BaseModel):
pools: list[PoolMetrics] = Field(default_factory=list)
@classmethod
def fields(cls):
return list(cls.model_fields.keys())
def fields(cls) -> set:
all_fields = set(cls.model_fields.keys())
all_fields.update(set(cls.model_computed_fields.keys()))
return all_fields
def get(self, __key: str, default: Any = None):
try:
@@ -309,25 +303,25 @@ class MinerData(BaseModel):
@computed_field # type: ignore[misc]
@property
def make(self) -> str:
def make(self) -> str | None:
if self.device_info.make is not None:
return str(self.device_info.make)
@computed_field # type: ignore[misc]
@property
def model(self) -> str:
def model(self) -> str | None:
if self.device_info.model is not None:
return str(self.device_info.model)
@computed_field # type: ignore[misc]
@property
def firmware(self) -> str:
def firmware(self) -> str | None:
if self.device_info.firmware is not None:
return str(self.device_info.firmware)
@computed_field # type: ignore[misc]
@property
def algo(self) -> str:
def algo(self) -> str | None:
if self.device_info.algo is not None:
return str(self.device_info.algo)
@@ -367,7 +361,9 @@ class MinerData(BaseModel):
data_list = [str(data[item]) for item in data]
return ",".join(data_list)
def as_influxdb(self, measurement_name: str = "miner_data") -> str:
def as_influxdb(
self, measurement_name: str = "miner_data", level_delimiter: str = "."
) -> str:
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
Parameters:
@@ -376,54 +372,127 @@ class MinerData(BaseModel):
Returns:
A influxdb line protocol version of this class.
"""
tag_data = [measurement_name]
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
return f"{key}={round(float(value), 2)}"
def serialize_list(key: str, value: list[Any]) -> str | None:
if len(value) == 0:
return None
list_field_data = []
for idx, list_field_val in enumerate(value):
item_serialization_func = serialization_map.get(
type(list_field_val), lambda _k, _v: None
)
item_serialized = item_serialization_func(
f"{key}{level_delimiter}{idx}", list_field_val
)
if item_serialized is not None:
list_field_data.append(item_serialized)
continue
for dt in serialization_map_instance:
if item_serialized is None:
if isinstance(list_field_val, dt):
item_serialized = serialization_map_instance[dt](
f"{key}{level_delimiter}{idx}", list_field_val
)
if item_serialized is not None:
list_field_data.append(item_serialized)
return ",".join(list_field_data)
def serialize_miner_error(key: str, value: BaseMinerError):
return value.as_influxdb(key, level_delimiter=level_delimiter)
def serialize_fan(key: str, value: Fan) -> str:
return f"{key}{level_delimiter}speed={value.speed}"
def serialize_hashboard(key: str, value: HashBoard) -> str:
return value.as_influxdb(key, level_delimiter=level_delimiter)
def serialize_bool(key: str, value: bool):
return f"{key}={str(value).lower()}"
def serialize_pool_metrics(key: str, value: PoolMetrics):
return value.as_influxdb(key, level_delimiter=level_delimiter)
include = [
"uptime",
"expected_hashrate",
"hashrate",
"hashboards",
"temperature_avg",
"env_temp",
"wattage",
"wattage_limit",
"voltage",
"fans",
"expected_fans",
"fan_psu",
"total_chips",
"expected_chips",
"efficiency",
"fault_light",
"is_mining",
"errors",
"pools",
]
serialization_map_instance = {
AlgoHashRateType: serialize_algo_hash_rate,
BaseMinerError: serialize_miner_error,
}
serialization_map = {
int: serialize_int,
float: serialize_float,
str: serialize_str,
bool: serialize_bool,
list: serialize_list,
Fan: serialize_fan,
HashBoard: serialize_hashboard,
PoolMetrics: serialize_pool_metrics,
}
tag_data = [
measurement_name,
f"ip={str(self.ip)}",
f"mac={str(self.mac)}",
f"make={str(self.make)}",
f"model={str(self.model)}",
f"firmware={str(self.firmware)}",
f"algo={str(self.algo)}",
]
field_data = []
tags = ["ip", "mac", "model", "hostname"]
for attribute in self.fields():
if attribute in tags:
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
tag_data.append(f"{attribute}={escaped_data}")
continue
elif str(attribute).startswith("_"):
continue
elif isinstance(self[attribute], str):
field_data.append(f'{attribute}="{self[attribute]}"')
continue
elif isinstance(self[attribute], bool):
field_data.append(f"{attribute}={str(self[attribute]).lower()}")
continue
elif isinstance(self[attribute], int):
field_data.append(f"{attribute}={self[attribute]}")
continue
elif isinstance(self[attribute], float):
field_data.append(f"{attribute}={self[attribute]}")
continue
elif attribute == "errors":
for idx, item in enumerate(self[attribute]):
field_data.append(f'error_{idx+1}="{item.error_message}"')
elif attribute == "hashboards":
for idx, item in enumerate(self[attribute]):
field_data.append(
f"hashboard_{idx+1}_hashrate={item.get('hashrate', 0.0)}"
for field in include:
field_val = getattr(self, field)
serialization_func = serialization_map.get(
type(field_val), lambda _k, _v: None
)
field_data.append(
f"hashboard_{idx+1}_temperature={item.get('temp', 0)}"
serialized = serialization_func(field, field_val)
if serialized is not None:
field_data.append(serialized)
continue
for datatype in serialization_map_instance:
if serialized is None:
if isinstance(field_val, datatype):
serialized = serialization_map_instance[datatype](
field, field_val
)
field_data.append(
f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}"
)
field_data.append(f"hashboard_{idx+1}_chips={item.get('chips', 0)}")
field_data.append(
f"hashboard_{idx+1}_expected_chips={item.get('expected_chips', 0)}"
)
elif attribute == "fans":
for idx, item in enumerate(self[attribute]):
if item.speed is not None:
field_data.append(f"fan_{idx+1}={item.speed}")
if serialized is not None:
field_data.append(serialized)
tags_str = ",".join(tag_data)
field_str = ",".join(field_data)
timestamp = str(self.timestamp * 1e9)
tags_str = ",".join(tag_data).replace(" ", "\\ ")
field_str = ",".join(field_data).replace(" ", "\\ ")
timestamp = str(self.timestamp * 10**9)
return " ".join([tags_str, field_str, timestamp])

View File

@@ -17,7 +17,7 @@ from __future__ import annotations
from typing import Any
from pydantic import BaseModel, field_serializer
from pydantic import BaseModel
from pyasic.device.algorithm.hashrate import AlgoHashRateType
@@ -51,6 +51,12 @@ class HashBoard(BaseModel):
active: bool | None = None
voltage: float | None = None
@classmethod
def fields(cls) -> set:
all_fields = set(cls.model_fields.keys())
all_fields.update(set(cls.model_computed_fields.keys()))
return all_fields
def get(self, __key: str, default: Any = None):
try:
val = self.__getitem__(__key)
@@ -65,3 +71,63 @@ class HashBoard(BaseModel):
return getattr(self, item)
except AttributeError:
raise KeyError(f"{item}")
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
return f"{key}={round(float(value), 2)}"
def serialize_bool(key: str, value: bool):
return f"{key}={str(value).lower()}"
serialization_map_instance = {
AlgoHashRateType: serialize_algo_hash_rate,
}
serialization_map = {
int: serialize_int,
float: serialize_float,
str: serialize_str,
bool: serialize_bool,
}
include = [
"hashrate",
"temp",
"chip_temp",
"chips",
"expected_chips",
"tuned",
"active",
"voltage",
]
field_data = []
for field in include:
field_val = getattr(self, field)
serialization_func = serialization_map.get(
type(field_val), lambda _k, _v: None
)
serialized = serialization_func(
f"{key_root}{level_delimiter}{field}", field_val
)
if serialized is not None:
field_data.append(serialized)
continue
for datatype in serialization_map_instance:
if serialized is None:
if isinstance(field_val, datatype):
serialized = serialization_map_instance[datatype](
f"{key_root}{level_delimiter}{field}", field_val
)
if serialized is not None:
field_data.append(serialized)
return ",".join(field_data)

View File

@@ -18,9 +18,15 @@ from typing import TypeVar
from .bos import BraiinsOSError
from .innosilicon import InnosiliconError
from .vnish import VnishError
from .whatsminer import WhatsminerError
from .X19 import X19Error
MinerErrorData = TypeVar(
"MinerErrorData", WhatsminerError, BraiinsOSError, X19Error, InnosiliconError
"MinerErrorData",
WhatsminerError,
BraiinsOSError,
X19Error,
InnosiliconError,
VnishError,
)

View File

@@ -16,3 +16,17 @@ class BaseMinerError(BaseModel):
A dictionary version of this class.
"""
return self.asdict()
def as_influxdb(self, root_key: str, level_delimiter: str = ".") -> str:
field_data = []
if self.error_code is not None:
field_data.append(
f"{root_key}{level_delimiter}error_code={self.error_code}"
)
if self.error_message is not None:
field_data.append(
f'{root_key}{level_delimiter}error_message="{self.error_message}"'
)
return ",".join(field_data)

View File

@@ -0,0 +1,28 @@
# ------------------------------------------------------------------------------
# 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.data.error_codes.base import BaseMinerError
class VnishError(BaseMinerError):
"""A Dataclass to handle error codes of Vnish miners.
Attributes:
error_message: The error message as a string.
error_code: The error code as an int. 0 if the message is not assigned a code.
"""
error_message: str
error_code: int = 0

View File

@@ -92,3 +92,51 @@ class PoolMetrics(BaseModel):
if total == 0:
return 0
return (value / total) * 100
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
def serialize_int(key: str, value: int) -> str:
return f"{key}={value}"
def serialize_float(key: str, value: float) -> str:
return f"{key}={value}"
def serialize_str(key: str, value: str) -> str:
return f'{key}="{value}"'
def serialize_pool_url(key: str, value: str) -> str:
return f'{key}="{str(value)}"'
def serialize_bool(key: str, value: bool):
return f"{key}={str(value).lower()}"
serialization_map = {
int: serialize_int,
float: serialize_float,
str: serialize_str,
bool: serialize_bool,
PoolUrl: serialize_pool_url,
}
include = [
"url",
"accepted",
"rejected",
"active",
"alive",
"user",
]
field_data = []
for field in include:
field_val = getattr(self, field)
serialization_func = serialization_map.get(
type(field_val), lambda _k, _v: None
)
serialized = serialization_func(
f"{key_root}{level_delimiter}{field}", field_val
)
if serialized is not None:
field_data.append(serialized)
return ",".join(field_data)

View File

@@ -26,9 +26,11 @@ class MinerMake(str, Enum):
AURADINE = "Auradine"
EPIC = "ePIC"
BITAXE = "BitAxe"
LUCKYMINER = "LuckyMiner"
ICERIVER = "IceRiver"
HAMMER = "Hammer"
VOLCMINER = "VolcMiner"
ELPHAPEX = "Elphapex"
BRAIINS = "Braiins"
def __str__(self):

View File

@@ -13,6 +13,7 @@ class AntminerModels(MinerModelType):
KS3 = "KS3"
DR5 = "DR5"
KS5 = "KS5"
KS5Pro = "KS5Pro"
L7 = "L7"
K7 = "K7"
D7 = "D7"
@@ -57,7 +58,9 @@ class AntminerModels(MinerModelType):
S19jXP = "S19j XP"
T19 = "T19"
S21 = "S21"
S21Plus = "S21+"
S21Pro = "S21 Pro"
S21Hydro = "S21 Hydro"
T21 = "T21"
def __str__(self):
@@ -446,7 +449,9 @@ class AvalonminerModels(MinerModelType):
Avalon1166Pro = "Avalon 1166 Pro"
Avalon1126Pro = "Avalon 1126 Pro"
Avalon1246 = "Avalon 1246"
Avalon1566 = "Avalon 1566"
AvalonNano3 = "Avalon Nano 3"
AvalonNano3s = "Avalon Nano 3s"
def __str__(self):
return self.value
@@ -477,7 +482,9 @@ class GoldshellModels(MinerModelType):
class ePICModels(MinerModelType):
BM520i = "BlockMiner 520i"
BM720i = "BlockMiner 720i"
eLITE1 = "BlockMiner eLITE 1.0"
S19jProDual = "S19j Pro Dual"
S19kProDual = "S19k Pro Dual"
def __str__(self):
return self.value
@@ -506,6 +513,14 @@ class BitAxeModels(MinerModelType):
return self.value
class LuckyMinerModels(MinerModelType):
LV07 = "LV07"
LV08 = "LV08"
def __str__(self):
return self.value
class IceRiverModels(MinerModelType):
KS0 = "KS0"
KS1 = "KS1"
@@ -540,6 +555,10 @@ class BraiinsModels(MinerModelType):
BMM101 = "BMM101"
class ElphapexModels(MinerModelType):
DG1Plus = "DG1+"
class MinerModel:
ANTMINER = AntminerModels
WHATSMINER = WhatsminerModels
@@ -549,7 +568,9 @@ class MinerModel:
AURADINE = AuradineModels
EPIC = ePICModels
BITAXE = BitAxeModels
LUCKYMINER = LuckyMinerModels
ICERIVER = IceRiverModels
HAMMER = HammerModels
VOLCMINER = VolcMinerModels
ELPHAPEX = ElphapexModels
BRAIINS = BraiinsModels

View File

@@ -21,4 +21,5 @@ from .epic import *
from .hiveon import *
from .luxos import *
from .marathon import *
from .mskminer import *
from .vnish import *

View File

@@ -15,12 +15,20 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import AntminerModern
from pyasic.miners.device.models import S21, S21Pro
from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21Pro
class BMMinerS21(AntminerModern, S21):
pass
class BMMinerS21Plus(AntminerModern, S21Plus):
pass
class BMMinerS21Pro(AntminerModern, S21Pro):
pass
class BMMinerS21Hydro(AntminerModern, S21Hydro):
pass

View File

@@ -13,5 +13,5 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .S21 import BMMinerS21, BMMinerS21Pro
from .S21 import BMMinerS21, BMMinerS21Hydro, BMMinerS21Plus, BMMinerS21Pro
from .T21 import BMMinerT21

View File

@@ -14,8 +14,12 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.miners.backends import AntminerModern
from pyasic.miners.device.models import KS5
from pyasic.miners.device.models.antminer import KS5, KS5Pro
class BMMinerKS5(AntminerModern, KS5):
supports_shutdown = False
class BMMinerKS5Pro(AntminerModern, KS5Pro):
supports_shutdown = False

View File

@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .KS5 import BMMinerKS5
from .KS5 import BMMinerKS5, BMMinerKS5Pro

View File

@@ -15,8 +15,12 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import BOSer
from pyasic.miners.device.models import S21
from pyasic.miners.device.models import S21, S21Pro
class BOSMinerS21(BOSer, S21):
pass
class BOSMinerS21Pro(BOSer, S21Pro):
pass

View File

@@ -14,5 +14,5 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .S21 import BOSMinerS21
from .S21 import BOSMinerS21, BOSMinerS21Pro
from .T21 import BOSMinerT21

View File

@@ -59,3 +59,9 @@ class ePICS19jProDual(ePIC, S19jPro):
raw_model = MinerModel.EPIC.S19jProDual
expected_fans = S19jPro.expected_fans * 2
expected_hashboards = S19jPro.expected_hashboards * 2
class ePICS19kProDual(ePIC, S19kPro):
raw_model = MinerModel.EPIC.S19kProDual
expected_fans = S19kPro.expected_fans * 2
expected_hashboards = S19kPro.expected_hashboards * 2

View File

@@ -21,6 +21,7 @@ from .S19 import (
ePICS19jProDual,
ePICS19jProPlus,
ePICS19kPro,
ePICS19kProDual,
ePICS19Pro,
ePICS19XP,
)

View File

@@ -27,6 +27,7 @@ from pyasic.miners.device.models import (
S19jNoPIC,
S19jPro,
S19KPro,
S19NoPIC,
S19Plus,
S19Pro,
S19ProHydro,
@@ -39,6 +40,10 @@ class HiveonS19(HiveonModern, S19):
pass
class HiveonS19NoPIC(HiveonModern, S19NoPIC):
pass
class HiveonS19Plus(HiveonModern, S19Plus):
pass
@@ -95,5 +100,5 @@ class HiveonS19ProPlusHydro(HiveonModern, S19ProPlusHydro):
pass
class HiveonS19KPro(HiveonModern, S19KPro):
class HiveonS19kPro(HiveonModern, S19KPro):
pass

View File

@@ -23,8 +23,9 @@ from .S19 import (
HiveonS19j,
HiveonS19jNoPIC,
HiveonS19jPro,
HiveonS19KPro,
HiveonS19kPro,
HiveonS19L,
HiveonS19NoPIC,
HiveonS19Plus,
HiveonS19Pro,
HiveonS19ProHydro,

View File

@@ -16,8 +16,6 @@
from typing import List, Optional
import asyncssh
from pyasic.data import HashBoard
from pyasic.device.algorithm import AlgoHashRate, HashUnit
from pyasic.errors import APIError

View File

@@ -0,0 +1,24 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.miners.backends.mskminer import MSKMiner
from pyasic.miners.device.models import (
S19NoPIC,
)
class MSKMinerS19NoPIC(MSKMiner, S19NoPIC):
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 .S19 import MSKMinerS19NoPIC

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 .X19 import *

View File

@@ -20,6 +20,7 @@ from pyasic.miners.device.models import (
S19XP,
S19a,
S19aPro,
S19i,
S19j,
S19jPro,
S19kPro,
@@ -53,6 +54,10 @@ class VNishS19aPro(VNish, S19aPro):
pass
class VNishS19i(VNish, S19i):
pass
class VNishS19j(VNish, S19j):
pass

View File

@@ -18,6 +18,7 @@ from .S19 import (
VNishS19,
VNishS19a,
VNishS19aPro,
VNishS19i,
VNishS19j,
VNishS19jPro,
VNishS19kPro,

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 T21
class VNishT21(VNish, T21):
pass

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------
from .S21 import VNishS21
from .T21 import VNishT21

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 AvalonMiner
from pyasic.miners.device.models import Avalon1566
class CGMinerAvalon1566(AvalonMiner, Avalon1566):
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 .A1566 import CGMinerAvalon1566

View File

@@ -20,4 +20,5 @@ from .A9X import *
from .A10X import *
from .A11X import *
from .A12X import *
from .A15X import *
from .nano import *

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .nano3 import CGMinerAvalonNano3
from .nano3 import CGMinerAvalonNano3, CGMinerAvalonNano3s

View File

@@ -13,10 +13,99 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import Optional
from pyasic import APIError
from pyasic.miners.backends import AvalonMiner
from pyasic.miners.device.models import AvalonNano3
from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.miners.device.models import AvalonNano3, AvalonNano3s
from pyasic.web.avalonminer import AvalonMinerWebAPI
AVALON_NANO_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_minerinfo", "get_minerinfo")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_devs", "devs")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"_get_env_temp",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
class CGMinerAvalonNano3(AvalonMiner, AvalonNano3):
_web_cls = AvalonMinerWebAPI
web: AvalonMinerWebAPI
data_locations = AVALON_NANO_DATA_LOC
async def _get_mac(self, web_minerinfo: dict) -> Optional[dict]:
if web_minerinfo is None:
try:
web_minerinfo = await self.web.minerinfo()
except APIError:
pass
if web_minerinfo is not None:
try:
mac = web_minerinfo.get("mac")
if mac is not None:
return mac.upper()
except (KeyError, ValueError):
pass
class CGMinerAvalonNano3s(AvalonMiner, AvalonNano3s):
pass

View File

@@ -22,14 +22,17 @@ from .bmminer import BMMiner
from .braiins_os import BOSer, BOSMiner
from .btminer import BTMiner
from .cgminer import CGMiner
from .elphapex import ElphapexMiner
from .epic import ePIC
from .goldshell import GoldshellMiner
from .hammer import BlackMiner
from .hiveon import HiveonModern, HiveonOld
from .iceriver import IceRiver
from .innosilicon import Innosilicon
from .luckyminer import LuckyMiner
from .luxminer import LUXMiner
from .marathon import MaraMiner
from .mskminer import MSKMiner
from .unknown import UnknownMiner
from .vnish import VNish
from .whatsminer import M2X, M3X, M5X, M6X, M7X

View File

@@ -75,6 +75,10 @@ ANTMINER_MODERN_DATA_LOC = DataLocations(
"_get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[],
),
str(DataOptions.IS_MINING): DataFunction(
"_is_mining",
[WebAPICommand("web_get_conf", "get_miner_conf")],
@@ -248,6 +252,9 @@ class AntminerModern(BMMiner):
return errors
async def _get_hashboards(self) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
@@ -561,6 +568,9 @@ class AntminerOld(CGMiner):
pass
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
@@ -589,6 +599,8 @@ class AntminerOld(CGMiner):
return fans_data
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = []
if rpc_stats is None:

View File

@@ -303,6 +303,9 @@ class Auradine(StockFirmware):
async def _get_hashboards(
self, rpc_devs: dict = None, web_ipreport: dict = None
) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -382,6 +385,9 @@ class Auradine(StockFirmware):
pass
async def _get_fans(self, web_fan: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_fan is None:
try:
web_fan = await self.web.get_fan()

View File

@@ -57,6 +57,10 @@ AVALON_DATA_LOC = DataLocations(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
@@ -100,6 +104,23 @@ class AvalonMiner(CGMiner):
return True
return False
async def set_power_limit(self, wattage: int) -> bool:
try:
if wattage < 3:
limit = wattage
elif wattage > 100:
limit = 2
elif wattage > 80:
limit = 1
else:
limit = 0
data = await self.rpc.ascset(0, "worklevel,set", 1)
except APIError:
return False
if data["STATUS"][0]["Msg"] == "ASC 0 set OK":
return True
return False
async def reboot(self) -> bool:
try:
data = await self.rpc.restart()
@@ -191,6 +212,9 @@ class AvalonMiner(CGMiner):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -289,7 +313,25 @@ class AvalonMiner(CGMiner):
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["WALLPOWER"][0])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()

View File

@@ -129,6 +129,9 @@ class BFGMiner(StockFirmware):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = []
if rpc_stats is None:
@@ -185,6 +188,9 @@ class BFGMiner(StockFirmware):
return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()

View File

@@ -1,208 +1,7 @@
from typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.device.algorithm import AlgoHashRate
from pyasic.device.firmware 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")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_system_info", "system/info")],
),
}
)
from pyasic.miners.backends.espminer import ESPMiner
class BitAxe(BaseMiner):
class BitAxe(ESPMiner):
"""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 self.algo.hashrate(
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.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=self.algo.hashrate(
rate=float(web_system_info["hashRate"]),
unit=self.algo.unit.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
async def _get_mac(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["macAddr"].upper()
except KeyError:
pass

View File

@@ -133,6 +133,9 @@ class BMMiner(StockFirmware):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = []
if rpc_stats is None:
@@ -202,6 +205,9 @@ class BMMiner(StockFirmware):
return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()

View File

@@ -376,6 +376,9 @@ class BOSMiner(BraiinsOSFirmware):
rpc_devdetails: dict = None,
rpc_devs: dict = None,
) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -473,6 +476,9 @@ class BOSMiner(BraiinsOSFirmware):
pass
async def _get_fans(self, rpc_fans: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_fans is None:
try:
rpc_fans = await self.rpc.fans()
@@ -926,6 +932,9 @@ class BOSer(BraiinsOSFirmware):
pass
async def _get_hashboards(self, grpc_hashboards: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -997,6 +1006,9 @@ class BOSer(BraiinsOSFirmware):
pass
async def _get_fans(self, grpc_cooling_state: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if grpc_cooling_state is None:
try:
grpc_cooling_state = await self.web.get_cooling_state()

View File

@@ -412,6 +412,9 @@ class BTMiner(StockFirmware):
pass
async def _get_hashboards(self, rpc_devs: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -490,6 +493,9 @@ class BTMiner(StockFirmware):
async def _get_fans(
self, rpc_summary: dict = None, rpc_get_psu: dict = None
) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()

View File

@@ -0,0 +1,380 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import Fan, HashBoard, X19Error
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
WebAPICommand,
)
from pyasic.miners.device.firmware import StockFirmware
from pyasic.web.elphapex import ElphapexWebAPI
ELPHAPEX_DATA_LOC = DataLocations(
**{
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.HOSTNAME): DataFunction(
"_get_hostname",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[WebAPICommand("web_stats", "stats")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[WebAPICommand("web_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_stats", "stats")],
),
str(DataOptions.ERRORS): DataFunction(
"_get_errors",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[WebAPICommand("web_get_blink_status", "get_blink_status")],
),
str(DataOptions.IS_MINING): DataFunction(
"_is_mining",
[WebAPICommand("web_get_miner_conf", "get_miner_conf")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[WebAPICommand("web_pools", "pools")],
),
}
)
class ElphapexMiner(StockFirmware):
"""Handler for Elphapex miners."""
_web_cls = ElphapexWebAPI
web: ElphapexWebAPI
data_locations = ELPHAPEX_DATA_LOC
async def get_config(self) -> MinerConfig:
data = await self.web.get_miner_conf()
if data:
self.config = MinerConfig.from_elphapex(data)
return self.config
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config
await self.web.set_miner_conf(config.as_elphapex(user_suffix=user_suffix))
async def fault_light_on(self) -> bool:
data = await self.web.blink(blink=True)
if data:
if data.get("code") == "B000":
self.light = True
return self.light
async def fault_light_off(self) -> bool:
data = await self.web.blink(blink=False)
if data:
if data.get("code") == "B100":
self.light = False
return self.light
async def reboot(self) -> bool:
data = await self.web.reboot()
if data:
return True
return False
async def _get_api_ver(self, web_summary: dict = None) -> Optional[str]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
self.api_ver = web_summary["STATUS"]["api_version"]
except LookupError:
pass
return self.api_ver
async def _get_fw_ver(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()
except APIError:
pass
if web_get_system_info is not None:
try:
self.fw_ver = (
web_get_system_info["system_filesystem_version"]
.upper()
.split("V")[-1]
)
except LookupError:
pass
return self.fw_ver
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()
except APIError:
pass
if web_get_system_info is not None:
try:
return web_get_system_info["hostname"]
except KeyError:
pass
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()
except APIError:
pass
if web_get_system_info is not None:
try:
return web_get_system_info["macaddr"]
except KeyError:
pass
try:
data = await self.web.get_network_info()
if data:
return data["macaddr"]
except KeyError:
pass
async def _get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
errors = []
if web_summary is not None:
try:
for item in web_summary["SUMMARY"][0]["status"]:
try:
if not item["status"] == "s":
errors.append(X19Error(error_message=item["msg"]))
except KeyError:
continue
except LookupError:
pass
return errors
async def _get_hashboards(self, web_stats: dict | None = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
if web_stats is None:
try:
web_stats = await self.web.stats()
except APIError:
return hashboards
if web_stats is not None:
try:
for board in web_stats["STATS"][0]["chain"]:
hashboards[board["index"]].hashrate = self.algo.hashrate(
rate=board["rate_real"], unit=self.algo.unit.MH
).into(self.algo.unit.default)
hashboards[board["index"]].chips = board["asic_num"]
board_temp_data = list(
filter(lambda x: not x == 0, board["temp_pcb"])
)
hashboards[board["index"]].temp = sum(board_temp_data) / len(
board_temp_data
)
chip_temp_data = list(
filter(lambda x: not x == "", board["temp_chip"])
)
hashboards[board["index"]].chip_temp = sum(
[int(i) / 1000 for i in chip_temp_data]
) / len(chip_temp_data)
hashboards[board["index"]].serial_number = board["sn"]
hashboards[board["index"]].missing = False
except LookupError:
pass
return hashboards
async def _get_fault_light(
self, web_get_blink_status: dict = None
) -> Optional[bool]:
if self.light:
return self.light
if web_get_blink_status is None:
try:
web_get_blink_status = await self.web.get_blink_status()
except APIError:
pass
if web_get_blink_status is not None:
try:
self.light = web_get_blink_status["blink"]
except KeyError:
pass
return self.light
async def _get_expected_hashrate(
self, web_stats: dict = None
) -> Optional[AlgoHashRate]:
if web_stats is None:
try:
web_stats = await self.web.stats()
except APIError:
pass
if web_stats is not None:
try:
expected_rate = web_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = web_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "MH"
return self.algo.hashrate(
rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass
async def _is_mining(self, web_get_miner_conf: dict = None) -> Optional[bool]:
if web_get_miner_conf is None:
try:
web_get_miner_conf = await self.web.get_miner_conf()
except APIError:
pass
if web_get_miner_conf is not None:
try:
if str(web_get_miner_conf["fc-work-mode"]).isdigit():
return (
False if int(web_get_miner_conf["fc-work-mode"]) == 1 else True
)
return False
except LookupError:
pass
async def _get_uptime(self, web_summary: dict = None) -> Optional[int]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
return int(web_summary["SUMMARY"][1]["elapsed"])
except LookupError:
pass
async def _get_fans(self, web_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_stats is None:
try:
web_stats = await self.web.stats()
except APIError:
pass
fans = [Fan() for _ in range(self.expected_fans)]
if web_stats is not None:
for fan_n in range(self.expected_fans):
try:
fans[fan_n].speed = int(web_stats["STATS"][0]["fan"][fan_n])
except LookupError:
pass
return fans
async def _get_pools(self, web_pools: list = None) -> List[PoolMetrics]:
if web_pools is None:
try:
web_pools = await self.web.pools()
except APIError:
return []
active_pool_index = None
highest_priority = float("inf")
for pool_info in web_pools["POOLS"]:
if (
pool_info.get("status") == "Alive"
and pool_info.get("priority", float("inf")) < highest_priority
):
highest_priority = pool_info["priority"]
active_pool_index = pool_info["index"]
pools_data = []
if web_pools is not None:
try:
for pool_info in web_pools["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("stale"),
remote_failures=pool_info.get("discarded"),
active=pool_info.get("index") == active_pool_index,
alive=pool_info.get("status") == "Alive",
url=pool_url,
user=pool_info.get("user"),
index=pool_info.get("index"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -21,7 +21,7 @@ from pyasic.config import MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.device.algorithm import AlgoHashRate, ScryptAlgo
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
@@ -138,6 +138,8 @@ class ePIC(ePICFirmware):
await self.web.set_ptune_algo(conf["ptune"])
## Pools
if self.algo == ScryptAlgo:
conf["pools"]["coin"] = "Ltc"
await self.web.set_pools(conf["pools"])
except APIError:
pass
@@ -283,6 +285,9 @@ class ePIC(ePICFirmware):
pass
async def _get_fans(self, web_summary: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_summary is None:
try:
web_summary = await self.web.summary()
@@ -302,6 +307,9 @@ class ePIC(ePICFirmware):
async def _get_hashboards(
self, web_summary: dict = None, web_capabilities: dict = None
) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_summary is None:
try:
web_summary = await self.web.summary()

View File

@@ -0,0 +1,241 @@
from typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import Fan, HashBoard
from pyasic.device.algorithm import AlgoHashRate
from pyasic.device.firmware import MinerFirmware
from pyasic.miners.base import BaseMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.web.espminer import ESPMinerWebAPI
ESPMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[WebAPICommand("web_system_info", "system/info")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_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")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_system_info", "system/info")],
),
}
)
class ESPMiner(BaseMiner):
"""Handler for ESPMiner"""
web: ESPMinerWebAPI
_web_cls = ESPMinerWebAPI
firmware = MinerFirmware.STOCK
data_locations = ESPMINER_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_espminer(web_system_info)
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
await self.web.update_settings(**config.as_espminer())
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 self.algo.hashrate(
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except KeyError:
pass
async def _get_expected_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:
expected_hashrate = (
web_system_info.get("smallCoreCount")
* web_system_info.get("asicCount")
* web_system_info.get("frequency")
)
return self.algo.hashrate(
rate=float(expected_hashrate), unit=self.algo.unit.MH
).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 self.expected_hashboards is None:
return []
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=self.algo.hashrate(
rate=float(web_system_info["hashRate"]),
unit=self.algo.unit.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 self.expected_fans is None:
return []
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
async def _get_mac(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["macAddr"].upper()
except KeyError:
pass

View File

@@ -145,6 +145,9 @@ class GoldshellMiner(BFGMiner):
async def _get_hashboards(
self, rpc_devs: dict = None, rpc_devdetails: dict = None
) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if rpc_devs is None:
try:
rpc_devs = await self.rpc.devs()

View File

@@ -180,6 +180,9 @@ class BlackMiner(StockFirmware):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = []
if rpc_stats is None:
@@ -229,7 +232,7 @@ class BlackMiner(StockFirmware):
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.GH
rate=float(hashrate), unit=self.algo.unit.MH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
@@ -245,6 +248,9 @@ class BlackMiner(StockFirmware):
return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
@@ -358,7 +364,7 @@ class BlackMiner(StockFirmware):
try:
expected_rate = rpc_stats["STATS"][1].get("total_rateideal")
if expected_rate is None:
return self.sticker_hashrate
return self.sticker_hashrate.into(self.algo.unit.default)
try:
rate_unit = rpc_stats["STATS"][1]["rate_unit"]
except KeyError:

View File

@@ -79,6 +79,9 @@ class IceRiver(StockFirmware):
return MinerConfig.from_iceriver(web_userpanel)
async def _get_fans(self, web_userpanel: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_userpanel is None:
try:
web_userpanel = await self.web.userpanel()
@@ -170,6 +173,9 @@ class IceRiver(StockFirmware):
pass
async def _get_hashboards(self, web_userpanel: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_userpanel is None:
try:
web_userpanel = await self.web.userpanel()

View File

@@ -211,6 +211,9 @@ class Innosilicon(CGMiner):
async def _get_hashboards(
self, rpc_stats: dict = None, web_get_all: dict = None
) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
if web_get_all:
web_get_all = web_get_all["all"]
@@ -304,6 +307,9 @@ class Innosilicon(CGMiner):
return wattage
async def _get_fans(self, web_get_all: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_get_all:
web_get_all = web_get_all["all"]

View File

@@ -0,0 +1,7 @@
from pyasic.miners.backends.espminer import ESPMiner
class LuckyMiner(ESPMiner):
"""Handler for LuckyMiner"""
pass

View File

@@ -17,6 +17,7 @@ import logging
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePreset
from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
@@ -47,6 +48,13 @@ LUXMINER_DATA_LOC = DataLocations(
"_get_wattage",
[RPCAPICommand("rpc_power", "power")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[
RPCAPICommand("rpc_config", "config"),
RPCAPICommand("rpc_profiles", "profiles"),
],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_fans", "fans")],
@@ -78,6 +86,7 @@ class LUXMiner(LuxOSFirmware):
supports_shutdown = True
supports_presets = True
supports_autotuning = True
data_locations = LUXMINER_DATA_LOC
@@ -144,14 +153,14 @@ class LUXMiner(LuxOSFirmware):
rpc_profiles=data.get("profiles", [{}])[0],
)
async def upgrade_firmware(self) -> bool:
async def upgrade_firmware(self, *args, **kwargs) -> bool:
"""
Upgrade the firmware on a LuxOS miner by calling the 'updaterun' API command.
Returns:
bool: True if the firmware upgrade was successfully initiated, False otherwise.
"""
try:
await self.rpc.upgraderun()
await self.rpc.updaterun()
logging.info(f"{self.ip}: Firmware upgrade initiated successfully.")
return True
@@ -160,6 +169,44 @@ class LUXMiner(LuxOSFirmware):
return False
async def atm_enabled(self) -> Optional[bool]:
try:
result = await self.rpc.atm()
return result["ATM"][0]["Enabled"]
except (APIError, LookupError):
pass
async def set_power_limit(self, wattage: int) -> bool:
config = await self.get_config()
valid_presets = {
preset.name: preset.power
for preset in config.mining_mode.available_presets
if preset.power <= wattage
}
# Set power to highest preset <= wattage
# If ATM enabled, must disable it before setting power limit
new_preset = max(valid_presets, key=valid_presets.get)
re_enable_atm = False
try:
if await self.atm_enabled():
re_enable_atm = True
await self.rpc.atmset(enabled=False)
result = await self.rpc.profileset(new_preset)
if re_enable_atm:
await self.rpc.atmset(enabled=True)
except APIError:
raise
except Exception as e:
logging.warning(f"{self} - Failed to set power limit: {e}")
return False
if result["PROFILE"][0]["Profile"] == new_preset:
return True
else:
return False
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
##################################################
@@ -194,6 +241,9 @@ class LUXMiner(LuxOSFirmware):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
@@ -251,7 +301,21 @@ class LUXMiner(LuxOSFirmware):
except (LookupError, ValueError, TypeError):
pass
async def _get_wattage_limit(
self, rpc_config: dict = None, rpc_profiles: list[dict] = None
) -> Optional[int]:
try:
active_preset = MiningModePreset.get_active_preset_from_luxos(
rpc_config, rpc_profiles
)
return active_preset.power
except (LookupError, ValueError, TypeError):
pass
async def _get_fans(self, rpc_fans: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_fans is None:
try:
rpc_fans = await self.rpc.fans()

View File

@@ -164,6 +164,9 @@ class MaraMiner(MaraFirmware):
pass
async def _get_hashboards(self, web_hashboards: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
hashboards = [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
@@ -250,6 +253,9 @@ class MaraMiner(MaraFirmware):
pass
async def _get_fans(self, web_fans: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if web_fans is None:
try:
web_fans = await self.web.fans()

View File

@@ -0,0 +1,110 @@
from typing import Optional
from pyasic import APIError
from pyasic.device.algorithm import AlgoHashRate
from pyasic.miners.backends import BMMiner
from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.web.mskminer import MSKMinerWebAPI
MSKMINER_DATA_LOC = DataLocations(
**{
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.MAC): DataFunction(
"_get_mac",
[WebAPICommand("web_info_v1", "info_v1")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
class MSKMiner(BMMiner):
"""Handler for MSKMiner"""
data_locations = MSKMINER_DATA_LOC
web: MSKMinerWebAPI
_web_cls = MSKMinerWebAPI
async def _get_hashrate(self, rpc_stats: dict = None) -> Optional[AlgoHashRate]:
# get hr from API
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
return self.algo.hashrate(
rate=float(rpc_stats["STATS"][0]["total_rate"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
return rpc_stats["STATS"][0]["total_power"]
except (LookupError, ValueError, TypeError):
pass
async def _get_mac(self, web_info_v1: dict = None) -> Optional[str]:
if web_info_v1 is None:
try:
web_info_v1 = await self.web.info_v1()
except APIError:
pass
if web_info_v1 is not None:
try:
return web_info_v1["network_info"]["result"]["macaddr"].upper()
except (LookupError, ValueError, TypeError):
pass

View File

@@ -97,7 +97,7 @@ class UnknownMiner(BaseMiner):
return None
async def _get_fans(self) -> List[Fan]:
return [Fan(), Fan(), Fan(), Fan()]
return []
async def _get_fan_psu(self) -> Optional[int]:
return None

View File

@@ -15,9 +15,10 @@
# ------------------------------------------------------------------------------
import logging
from typing import Optional
from typing import List, Optional
from pyasic import MinerConfig
from pyasic.data.error_codes import MinerErrorData, VnishError
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.backends.bmminer import BMMiner
@@ -85,6 +86,10 @@ VNISH_DATA_LOC = DataLocations(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
str(DataOptions.ERRORS): DataFunction(
"_get_errors",
[WebAPICommand("web_summary", "summary")],
),
}
)
@@ -266,6 +271,24 @@ class VNish(VNishFirmware, BMMiner):
except LookupError:
pass
async def _get_errors(self, web_summary: dict = None) -> List[MinerErrorData]:
errors = []
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
return errors
if web_summary is not None:
chains = web_summary.get("miner", {}).get("chains", [])
for chain in chains:
state = chain.get("status", {}).get("state")
description = chain.get("status", {}).get("description", "")
if state == "failure":
errors.append(VnishError(error_message=description))
return errors
async def get_config(self) -> MinerConfig:
try:
web_settings = await self.web.settings()

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .blockminer import ePICBlockMiner520i, ePICBlockMiner720i
from .blockminer import *

View File

@@ -15,7 +15,11 @@
# ------------------------------------------------------------------------------
from pyasic.miners.backends import ePIC
from pyasic.miners.device.models import BlockMiner520i, BlockMiner720i
from pyasic.miners.device.models import (
BlockMiner520i,
BlockMiner720i,
BlockMinerELITE1,
)
class ePICBlockMiner520i(ePIC, BlockMiner520i):
@@ -24,3 +28,7 @@ class ePICBlockMiner520i(ePIC, BlockMiner520i):
class ePICBlockMiner720i(ePIC, BlockMiner720i):
pass
class ePICBlockMinerELITE1(ePIC, BlockMinerELITE1):
pass

View File

@@ -50,6 +50,10 @@ class BitAxeMake(BaseMiner):
make = MinerMake.BITAXE
class LuckyMinerMake(BaseMiner):
make = MinerMake.LUCKYMINER
class IceRiverMake(BaseMiner):
make = MinerMake.ICERIVER
@@ -64,3 +68,7 @@ class VolcMinerMake(BaseMiner):
class BraiinsMake(BaseMiner):
make = MinerMake.BRAIINS
class ElphapexMake(BaseMiner):
make = MinerMake.ELPHAPEX

View File

@@ -18,6 +18,7 @@ from .antminer import *
from .auradine import *
from .avalonminer import *
from .braiins import *
from .elphapex import *
from .epic import *
from .goldshell import *
from .hammer import *

View File

@@ -27,6 +27,15 @@ class S21(AntMinerMake):
algo = MinerAlgo.SHA256
class S21Plus(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21Plus
expected_chips = 55
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S21Pro(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21Pro
@@ -34,3 +43,12 @@ class S21Pro(AntMinerMake):
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S21Hydro(AntMinerMake):
raw_model = MinerModel.ANTMINER.S21Hydro
expected_chips = 216
expected_hashboards = 3
expected_fans = 0
algo = MinerAlgo.SHA256

View File

@@ -14,5 +14,5 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .S21 import S21, S21Pro
from .S21 import S21, S21Hydro, S21Plus, S21Pro
from .T21 import T21

View File

@@ -25,3 +25,12 @@ class KS5(AntMinerMake):
expected_hashboards = 3
expected_fans = 4
algo = MinerAlgo.KHEAVYHASH
class KS5Pro(AntMinerMake):
raw_model = MinerModel.ANTMINER.KS5
expected_chips = 92
expected_hashboards = 3
expected_fans = 4
algo = MinerAlgo.KHEAVYHASH

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .DR5 import DR5
from .KS5 import KS5
from .KS5 import KS5, KS5Pro

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# 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.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import AvalonMinerMake
class Avalon1566(AvalonMinerMake):
raw_model = MinerModel.AVALONMINER.Avalon1566
expected_chips = 160
expected_fans = 2
expected_hashboards = 3
algo = MinerAlgo.SHA256

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 .A1566 import Avalon1566

View File

@@ -20,4 +20,5 @@ from .A9X import *
from .A10X import *
from .A11X import *
from .A12X import *
from .A15X import *
from .nano import *

View File

@@ -1 +1 @@
from .nano3 import AvalonNano3
from .nano3 import AvalonNano3, AvalonNano3s

View File

@@ -10,3 +10,12 @@ class AvalonNano3(AvalonMinerMake):
expected_chips = 10
expected_fans = 1
algo = MinerAlgo.SHA256
class AvalonNano3s(AvalonMinerMake):
raw_model = MinerModel.AVALONMINER.AvalonNano3s
expected_hashboards = 1
expected_chips = 12
expected_fans = 1
algo = MinerAlgo.SHA256

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2024 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.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import ElphapexMake
class DG1Plus(ElphapexMake):
raw_model = MinerModel.ELPHAPEX.DG1Plus
expected_chips = 204
expected_hashboards = 4
expected_fans = 4
algo = MinerAlgo.SCRYPT

View File

@@ -0,0 +1 @@
from .DG1 import DG1Plus

View File

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

View File

@@ -19,3 +19,12 @@ class BlockMiner720i(ePICMake):
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class BlockMinerELITE1(ePICMake):
raw_model = MinerModel.EPIC.eLITE1
expected_chips = 105
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SCRYPT

View File

@@ -0,0 +1,12 @@
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import LuckyMinerMake
class LV07(LuckyMinerMake):
raw_model = MinerModel.LUCKYMINER.LV07
expected_hashboards = 1
expected_chips = 1
expected_fans = 1
algo = MinerAlgo.SHA256

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