Compare commits

..

35 Commits

Author SHA1 Message Date
Upstream Data
cbf7eeb08d version: bump version number 2024-11-26 08:26:26 -07:00
Upstream Data
d34b35a82d bug: add luxos.supports_shutdown value 2024-11-26 08:25:25 -07:00
Upstream Data
5d57f35475 bug: fix possible case where unidentified miner type can raise and error 2024-11-25 13:00:49 -07:00
Brett Rowan
c95491ea45 version: bump version number 2024-11-22 10:49:47 -07:00
Brett Rowan
e9ec43fac9 bug: fix some more issues with type hints causing warnings 2024-11-22 10:49:24 -07:00
Brett Rowan
42bde081c4 bug: fix some issues with type hints causing warnings 2024-11-22 10:44:09 -07:00
Brett Rowan
bfb72aec1b bug: fix LookupError with AntminerOld 2024-11-22 10:41:34 -07:00
Brett Rowan
b2f36b2f0b bug: fix type hints 2024-11-22 10:41:12 -07:00
Upstream Data
f75c07401b refactor: remove unused imports 2024-11-21 15:32:30 -07:00
Upstream Data
01b96227e0 refactor: more optimizations to hashrate types with metaclass 2024-11-21 15:30:01 -07:00
Upstream Data
82552390c8 refactor: optimize hashrate algo units slightly 2024-11-21 14:55:04 -07:00
Upstream Data
b0651e26b8 version: bump version number 2024-11-21 13:44:48 -07:00
Brett Rowan
c00802e311 feature: add alternate algorithm handlers (#240)
* feature: handle all hashrate algorithm conversions for antminers

* feature: handle all hashrate algorithm conversions for auradine

* feature: handle all hashrate algorithm conversions for avalonminers

* feature: handle all hashrate algorithm conversions for bitaxe

* feature: handle all hashrate algorithm conversions for epic

* feature: handle all hashrate algorithm conversions for goldshell

* refactor: clean up imports

* feature: handle all hashrate algorithm conversions for hammer

* feature: handle all hashrate algorithm conversions for iceriver

* feature: handle all hashrate algorithm conversions for innosilicon

* feature: handle all hashrate algorithm conversions for whatsminer

* tests: update tests to check if miners have board, fan, and algo values

* feature: finish updating all miners with boards, fans, and algos

* feature: update algorithm default values

* feature: add algorithm hashrate values

* feature: improve hashrate types, and use `self.algo` inside miners

---------

Co-authored-by: Upstream Data <brett@upstreamdata.ca>
2024-11-21 13:44:17 -07:00
Brett Rowan
d66739e2c9 feature: swap from @dataclass to pydantic BaseModel (#238)
* feature: switch almost everything to pydantic BaseModel

* feature: swap more dataclasses over to pydantic models

* feature: use more model serializers to make data handle better

* bug: fix some serialization issues with `None` values

* bug: fix some initialization problems with miner mode config

* bug: fix new BOS+ pool parsing

* bug: fix new BOS+ board temperature parsing serialization error

* bug: more misc fixes with serialization, extra methods, and hashrate types

* bug: add explicit type conversions to hashrate types

* bug: fix epic pool URL parsing

* bug: fix positional args in hashboards

* bug: fix epic missing url scheme

* convert board temp to int

---------

Co-authored-by: Upstream Data <brett@upstreamdata.ca>
Co-authored-by: John-Paul Compagnone <jpcompagnone@epicblockchain.io>
2024-11-20 13:42:41 -07:00
Upstream Data
65ed565220 tests: add hammer miner tests 2024-11-20 10:22:53 -07:00
Upstream Data
db6499800b bug: fix raising warnings for partially supported None miners 2024-11-19 14:23:23 -07:00
Upstream Data
cc97ceee61 version: bump version number 2024-11-18 09:01:59 -07:00
Upstream Data
3fa1cb18d9 feature: add support for antminer D7 2024-11-18 08:22:30 -07:00
Upstream Data
cb3c50d007 version: bump version number 2024-11-14 11:16:25 -07:00
Upstream Data
2523ef8484 bug: fix some issues with hammer miners 2024-11-14 11:16:09 -07:00
Upstream Data
01342738b0 version: bump version number 2024-11-14 10:26:50 -07:00
Upstream Data
a9dee4a911 feature: add support for bitaxe gamma 2024-11-14 10:26:00 -07:00
Upstream Data
883ffe20b4 bug: fix bmminer subclass pools data location 2024-11-14 10:25:38 -07:00
Upstream Data
261527a380 feature: add support for hammer D10 and hammer discovery 2024-11-14 08:45:59 -07:00
Upstream Data
924b62e0d5 feature: add support for hammer miner 2024-11-14 08:45:59 -07:00
Temi
76a870c2ed feat: Add _get_pools method for Bmminer. (#233)
* backends: Add _get_pools for Bmminer

* [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>
2024-11-13 10:18:07 -07:00
Upstream Data
309356243b version: bump version number 2024-11-12 14:28:51 -07:00
Upstream Data
e9b4cc9bd6 feature: add Antminer D9 support 2024-11-12 14:28:31 -07:00
Upstream Data
648c54de93 bug: add iceriver KS5 chip count 2024-11-12 14:25:13 -07:00
Upstream Data
e1ce96ab1b bug: add innosilicon A11 chip count 2024-11-12 14:24:05 -07:00
Brett Rowan
86860a8dc4 version: bump version number 2024-11-08 09:18:34 -07:00
Jacob Roy
5212641f45 add json serialization to the Scheme class (#232) 2024-11-06 15:13:30 -07:00
Upstream Data
52432e6043 version: bump version number 2024-11-06 09:06:11 -07:00
Upstream Data
727e484860 docs: update docs 2024-11-06 09:05:08 -07:00
Upstream Data
6c091756d2 feature: add support for Iceriver KS5 and submodels 2024-11-06 09:04:38 -07:00
223 changed files with 3879 additions and 672 deletions

View File

@@ -43,3 +43,24 @@
show_root_heading: false
heading_level: 4
## KS5 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5L (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5L
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5M (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5M
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -688,6 +688,9 @@ details {
<li><a href="../iceriver/KSX#ks3-stock">KS3 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3l-stock">KS3L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3m-stock">KS3M (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5-stock">KS5 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5l-stock">KS5L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5m-stock">KS5M (Stock)</a></li>
</ul>
</details>
</ul>

173
poetry.lock generated
View File

@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]]
name = "aiofiles"
@@ -11,6 +11,17 @@ files = [
{file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"},
]
[[package]]
name = "annotated-types"
version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
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"},
]
[[package]]
name = "anyio"
version = "4.6.2.post1"
@@ -380,13 +391,13 @@ files = [
[[package]]
name = "httpcore"
version = "1.0.6"
version = "1.0.7"
description = "A minimal low-level HTTP client."
optional = false
python-versions = ">=3.8"
files = [
{file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"},
{file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"},
{file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"},
{file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"},
]
[package.dependencies]
@@ -437,13 +448,13 @@ files = [
[[package]]
name = "identify"
version = "2.6.1"
version = "2.6.2"
description = "File identification library for Python"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
files = [
{file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"},
{file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"},
{file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"},
{file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"},
]
[package.extras]
@@ -837,13 +848,13 @@ files = [
[[package]]
name = "packaging"
version = "24.1"
version = "24.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
]
[[package]]
@@ -936,6 +947,130 @@ files = [
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[[package]]
name = "pydantic"
version = "2.9.2"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"},
{file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"},
]
[package.dependencies]
annotated-types = ">=0.6.0"
pydantic-core = "2.23.4"
typing-extensions = [
{version = ">=4.12.2", markers = "python_version >= \"3.13\""},
{version = ">=4.6.1", markers = "python_version < \"3.13\""},
]
[package.extras]
email = ["email-validator (>=2.0.0)"]
timezone = ["tzdata"]
[[package]]
name = "pydantic-core"
version = "2.23.4"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
files = [
{file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"},
{file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"},
{file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"},
{file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"},
{file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"},
{file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"},
{file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"},
{file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"},
{file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"},
{file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"},
{file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"},
{file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"},
{file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"},
{file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"},
{file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"},
{file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"},
{file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"},
{file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"},
{file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"},
{file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"},
{file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"},
{file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"},
{file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"},
{file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"},
{file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"},
{file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"},
{file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"},
{file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"},
{file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"},
{file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"},
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"},
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"},
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"},
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"},
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"},
{file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"},
{file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"},
{file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"},
{file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"},
{file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"},
{file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"},
{file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"},
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"},
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"},
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"},
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"},
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"},
{file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"},
{file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"},
{file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"},
{file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"},
{file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"},
{file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"},
{file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"},
{file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"},
]
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pymdown-extensions"
version = "10.12"
@@ -1068,13 +1203,13 @@ files = [
[[package]]
name = "tomli"
version = "2.0.2"
version = "2.1.0"
description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
files = [
{file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"},
{file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"},
{file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"},
{file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"},
]
[[package]]
@@ -1163,13 +1298,13 @@ watchmedo = ["PyYAML (>=3.10)"]
[[package]]
name = "zipp"
version = "3.20.2"
version = "3.21.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
files = [
{file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"},
{file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"},
{file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
{file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
]
[package.extras]
@@ -1183,4 +1318,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "d611b5e8b0c5611d6ee916cedfb7f07f20dfc90a675ebaed04188e8b3c96aabe"
content-hash = "0ae7d5fe3858e93c8a8715b11876be854ce8bf39685316ca1bee759bf0896ce6"

View File

@@ -13,25 +13,28 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import asdict, dataclass, field
from pyasic.config.fans import FanModeConfig
from pyasic.config.mining import MiningModeConfig
from pydantic import BaseModel, Field
from pyasic.config.fans import FanMode, FanModeConfig
from pyasic.config.mining import MiningMode, MiningModeConfig
from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig
from pyasic.config.temperature import TemperatureConfig
from pyasic.misc import merge_dicts
@dataclass
class MinerConfig:
class MinerConfig(BaseModel):
"""Represents the configuration for a miner including pool configuration,
fan mode, temperature settings, mining mode, and power scaling."""
pools: PoolConfig = field(default_factory=PoolConfig.default)
fan_mode: FanModeConfig = field(default_factory=FanModeConfig.default)
temperature: TemperatureConfig = field(default_factory=TemperatureConfig.default)
mining_mode: MiningModeConfig = field(default_factory=MiningModeConfig.default)
class Config:
arbitrary_types_allowed = True
pools: PoolConfig = Field(default_factory=PoolConfig.default)
fan_mode: FanMode = Field(default_factory=FanModeConfig.default)
temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default)
mining_mode: MiningMode = Field(default_factory=MiningModeConfig.default)
def __getitem__(self, item):
try:
@@ -41,7 +44,7 @@ class MinerConfig:
def as_dict(self) -> dict:
"""Converts the MinerConfig object to a dictionary."""
return asdict(self)
return self.model_dump()
def as_am_modern(self, user_suffix: str = None) -> dict:
"""Generates the configuration in the format suitable for modern Antminers."""
@@ -107,7 +110,7 @@ class MinerConfig:
}
def as_boser(self, user_suffix: str = None) -> dict:
""" "Generates the configuration in the format suitable for BOSer."""
"""Generates the configuration in the format suitable for BOSer."""
return {
**self.fan_mode.as_boser(),
**self.temperature.as_boser(),
@@ -156,6 +159,9 @@ class MinerConfig:
**self.pools.as_luxos(user_suffix=user_suffix),
}
def as_hammer(self, *args, **kwargs) -> dict:
return self.as_am_modern(*args, **kwargs)
@classmethod
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from a dictionary."""
@@ -276,3 +282,7 @@ class MinerConfig:
),
pools=PoolConfig.from_luxos(rpc_pools=rpc_pools, rpc_groups=rpc_groups),
)
@classmethod
def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
return cls.from_am_modern(*args, **kwargs)

View File

@@ -15,9 +15,10 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import asdict, dataclass
from enum import Enum
from pydantic import BaseModel
class MinerConfigOption(Enum):
@classmethod
@@ -80,14 +81,13 @@ class MinerConfigOption(Enum):
raise KeyError
@dataclass
class MinerConfigValue:
class MinerConfigValue(BaseModel):
@classmethod
def from_dict(cls, dict_conf: dict | None):
return cls()
def as_dict(self) -> dict:
return asdict(self)
return self.model_dump()
def as_am_modern(self) -> dict:
return {}

View File

@@ -15,14 +15,15 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TypeVar, Union
from pydantic import Field
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@dataclass
class FanModeNormal(MinerConfigValue):
mode: str = field(init=False, default="normal")
mode: str = Field(init=False, default="normal")
minimum_fans: int = 1
minimum_speed: int = 0
@@ -87,9 +88,8 @@ class FanModeNormal(MinerConfigValue):
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
@dataclass
class FanModeManual(MinerConfigValue):
mode: str = field(init=False, default="manual")
mode: str = Field(init=False, default="manual")
speed: int = 100
minimum_fans: int = 1
@@ -151,9 +151,8 @@ class FanModeManual(MinerConfigValue):
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
@dataclass
class FanModeImmersion(MinerConfigValue):
mode: str = field(init=False, default="immersion")
mode: str = Field(init=False, default="immersion")
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "FanModeImmersion":
@@ -273,7 +272,7 @@ class FanModeConfig(MinerConfigOption):
keys = temperature_conf.keys()
if "auto" in keys:
if "minimumRequiredFans" in keys:
return cls.normal(temperature_conf["minimumRequiredFans"])
return cls.normal(minimum_fans=temperature_conf["minimumRequiredFans"])
return cls.normal()
if "manual" in keys:
conf = {}
@@ -300,7 +299,9 @@ class FanModeConfig(MinerConfigOption):
mode = web_config["general-config"]["environment-profile"]
if mode == "AirCooling":
if web_config["advance-config"]["override-fan-control"]:
return cls.manual(web_config["advance-config"]["fan-fixed-percent"])
return cls.manual(
speed=web_config["advance-config"]["fan-fixed-percent"]
)
return cls.normal()
return cls.immersion()
except LookupError:
@@ -333,3 +334,9 @@ class FanModeConfig(MinerConfigOption):
except LookupError:
pass
return cls.default()
FanMode = TypeVar(
"FanMode",
bound=Union[FanModeNormal, FanModeManual, FanModeImmersion],
)

View File

@@ -15,7 +15,8 @@
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass, field
from dataclasses import field
from typing import TypeVar, Union
from pyasic import settings
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@@ -34,11 +35,10 @@ from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
TunerPerformanceMode,
)
from .algo import TunerAlgo
from .algo import TunerAlgo, TunerAlgoType
from .scaling import ScalingConfig
@dataclass
class MiningModeNormal(MinerConfigValue):
mode: str = field(init=False, default="normal")
@@ -74,7 +74,6 @@ class MiningModeNormal(MinerConfigValue):
return {"autotunerset": {"enabled": False}}
@dataclass
class MiningModeSleep(MinerConfigValue):
mode: str = field(init=False, default="sleep")
@@ -107,7 +106,6 @@ class MiningModeSleep(MinerConfigValue):
}
@dataclass
class MiningModeLPM(MinerConfigValue):
mode: str = field(init=False, default="low")
@@ -130,7 +128,6 @@ class MiningModeLPM(MinerConfigValue):
return {"settings": {"level": 1}}
@dataclass
class MiningModeHPM(MinerConfigValue):
mode: str = field(init=False, default="high")
@@ -150,12 +147,14 @@ class MiningModeHPM(MinerConfigValue):
return {"mode": {"mode": "turbo"}}
@dataclass
class MiningModePowerTune(MinerConfigValue):
class Config:
arbitrary_types_allowed = True
mode: str = field(init=False, default="power_tuning")
power: int = None
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig = None
power: int | None = None
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig | None = None
@classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
@@ -247,11 +246,13 @@ class MiningModePowerTune(MinerConfigValue):
return {"autotunerset": {"enabled": True}}
@dataclass
class MiningModeHashrateTune(MinerConfigValue):
class Config:
arbitrary_types_allowed = True
mode: str = field(init=False, default="hashrate_tuning")
hashrate: int = None
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
algo: TunerAlgoType = field(default_factory=TunerAlgo.default)
scaling: ScalingConfig = None
@classmethod
@@ -343,7 +344,6 @@ class MiningModeHashrateTune(MinerConfigValue):
return {"autotunerset": {"enabled": True}}
@dataclass
class ManualBoardSettings(MinerConfigValue):
freq: float
volt: float
@@ -358,7 +358,6 @@ class ManualBoardSettings(MinerConfigValue):
return {"miner-mode": 0}
@dataclass
class MiningModeManual(MinerConfigValue):
mode: str = field(init=False, default="manual")
@@ -521,7 +520,7 @@ class MiningModeConfig(MinerConfigOption):
if autotuning_conf.get("psu_power_limit") is not None:
# old autotuning conf
return cls.power_tuning(
autotuning_conf["psu_power_limit"],
power=autotuning_conf["psu_power_limit"],
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
if autotuning_conf.get("mode") is not None:
@@ -530,7 +529,7 @@ class MiningModeConfig(MinerConfigOption):
if mode == "power_target":
if autotuning_conf.get("power_target") is not None:
return cls.power_tuning(
autotuning_conf["power_target"],
power=autotuning_conf["power_target"],
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
)
return cls.power_tuning(
@@ -539,7 +538,7 @@ class MiningModeConfig(MinerConfigOption):
if mode == "hashrate_target":
if autotuning_conf.get("hashrate_target") is not None:
return cls.hashrate_tuning(
autotuning_conf["hashrate_target"],
hashrate=autotuning_conf["hashrate_target"],
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
)
return cls.hashrate_tuning(
@@ -556,7 +555,7 @@ class MiningModeConfig(MinerConfigOption):
if mode_settings["preset"] == "disabled":
return MiningModeManual.from_vnish(mode_settings)
else:
return cls.power_tuning(int(mode_settings["preset"]))
return cls.power_tuning(power=int(mode_settings["preset"]))
@classmethod
def from_boser(cls, grpc_miner_conf: dict):
@@ -571,7 +570,7 @@ class MiningModeConfig(MinerConfigOption):
if tuner_conf["tunerMode"] == 1:
if tuner_conf.get("powerTarget") is not None:
return cls.power_tuning(
tuner_conf["powerTarget"]["watt"],
power=tuner_conf["powerTarget"]["watt"],
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
)
return cls.power_tuning(
@@ -581,7 +580,7 @@ class MiningModeConfig(MinerConfigOption):
if tuner_conf["tunerMode"] == 2:
if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning(
int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
scaling=ScalingConfig.from_boser(
grpc_miner_conf, mode="hashrate"
),
@@ -592,13 +591,13 @@ class MiningModeConfig(MinerConfigOption):
if tuner_conf.get("powerTarget") is not None:
return cls.power_tuning(
tuner_conf["powerTarget"]["watt"],
power=tuner_conf["powerTarget"]["watt"],
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
)
if tuner_conf.get("hashrateTarget") is not None:
return cls.hashrate_tuning(
int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
)
@@ -617,9 +616,9 @@ class MiningModeConfig(MinerConfigOption):
if mode_data.get("Mode") == "turbo":
return cls.high()
if mode_data.get("Ths") is not None:
return cls.hashrate_tuning(mode_data["Ths"])
return cls.hashrate_tuning(hashrate=mode_data["Ths"])
if mode_data.get("Power") is not None:
return cls.power_tuning(mode_data["Power"])
return cls.power_tuning(power=mode_data["Power"])
except LookupError:
return cls.default()
@@ -647,3 +646,17 @@ class MiningModeConfig(MinerConfigOption):
except LookupError:
pass
return cls.default()
MiningMode = TypeVar(
"MiningMode",
bound=Union[
MiningModeNormal,
MiningModeHPM,
MiningModeLPM,
MiningModeSleep,
MiningModeManual,
MiningModePowerTune,
MiningModeHashrateTune,
],
)

View File

@@ -1,11 +1,11 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import TypeVar, Union
from pyasic.config.base import MinerConfigOption, MinerConfigValue
@dataclass
class StandardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
@@ -13,7 +13,6 @@ class StandardTuneAlgo(MinerConfigValue):
return VOptAlgo().as_epic()
@dataclass
class VOptAlgo(MinerConfigValue):
mode: str = field(init=False, default="voltage_optimizer")
@@ -21,7 +20,6 @@ class VOptAlgo(MinerConfigValue):
return "VoltageOptimizer"
@dataclass
class BoardTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="board_tune")
@@ -29,7 +27,6 @@ class BoardTuneAlgo(MinerConfigValue):
return "BoardTune"
@dataclass
class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="chip_tune")
@@ -37,7 +34,6 @@ class ChipTuneAlgo(MinerConfigValue):
return "ChipTune"
@dataclass
class TunerAlgo(MinerConfigOption):
standard = StandardTuneAlgo
voltage_optimizer = VOptAlgo
@@ -45,11 +41,11 @@ class TunerAlgo(MinerConfigOption):
chip_tune = ChipTuneAlgo
@classmethod
def default(cls):
def default(cls) -> TunerAlgoType:
return cls.standard()
@classmethod
def from_dict(cls, dict_conf: dict | None):
def from_dict(cls, dict_conf: dict | None) -> TunerAlgoType:
mode = dict_conf.get("mode")
if mode is None:
return cls.default()
@@ -57,3 +53,14 @@ class TunerAlgo(MinerConfigOption):
cls_attr = getattr(cls, mode)
if cls_attr is not None:
return cls_attr().from_dict(dict_conf)
TunerAlgoType = TypeVar(
"TunerAlgoType",
bound=Union[
StandardTuneAlgo,
VOptAlgo,
BoardTuneAlgo,
ChipTuneAlgo,
],
)

View File

@@ -20,7 +20,6 @@ from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
@dataclass
class ScalingShutdown(MinerConfigValue):
enabled: bool = False
duration: int = None
@@ -35,7 +34,9 @@ class ScalingShutdown(MinerConfigValue):
def from_bosminer(cls, power_scaling_conf: dict):
sd_enabled = power_scaling_conf.get("shutdown_enabled")
if sd_enabled is not None:
return cls(sd_enabled, power_scaling_conf.get("shutdown_duration"))
return cls(
enabled=sd_enabled, duration=power_scaling_conf.get("shutdown_duration")
)
return None
@classmethod
@@ -43,9 +44,12 @@ class ScalingShutdown(MinerConfigValue):
sd_enabled = power_scaling_conf.get("shutdownEnabled")
if sd_enabled is not None:
try:
return cls(sd_enabled, power_scaling_conf["shutdownDuration"]["hours"])
return cls(
enabled=sd_enabled,
duration=power_scaling_conf["shutdownDuration"]["hours"],
)
except KeyError:
return cls(sd_enabled)
return cls(enabled=sd_enabled)
return None
def as_bosminer(self) -> dict:
@@ -60,7 +64,6 @@ class ScalingShutdown(MinerConfigValue):
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
@dataclass
class ScalingConfig(MinerConfigValue):
step: int = None
minimum: int = None

View File

@@ -17,9 +17,10 @@ from __future__ import annotations
import random
import string
from dataclasses import dataclass, field
from typing import List
from pydantic import Field
from pyasic.config.base import MinerConfigValue
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
PoolConfiguration,
@@ -30,7 +31,6 @@ from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
)
@dataclass
class Pool(MinerConfigValue):
url: str
user: str
@@ -235,11 +235,10 @@ class Pool(MinerConfigValue):
)
@dataclass
class PoolGroup(MinerConfigValue):
pools: list[Pool] = field(default_factory=list)
pools: list[Pool] = Field(default_factory=list)
quota: int = 1
name: str = None
name: str | None = None
def __post_init__(self):
if self.name is None:
@@ -254,7 +253,7 @@ class PoolGroup(MinerConfigValue):
if len(self.pools) > idx:
pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix))
else:
pools.append(Pool("", "", "").as_am_modern())
pools.append(Pool(url="", user="", password="").as_am_modern())
idx += 1
return pools
@@ -267,7 +266,7 @@ class PoolGroup(MinerConfigValue):
**self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix)
)
else:
pools.update(**Pool("", "", "").as_wm(idx=idx + 1))
pools.update(**Pool(url="", user="", password="").as_wm(idx=idx + 1))
idx += 1
return pools
@@ -280,7 +279,9 @@ class PoolGroup(MinerConfigValue):
**self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix)
)
else:
pools.update(**Pool("", "", "").as_am_old(idx=idx + 1))
pools.update(
**Pool(url="", user="", password="").as_am_old(idx=idx + 1)
)
idx += 1
return pools
@@ -290,7 +291,7 @@ class PoolGroup(MinerConfigValue):
def as_avalon(self, user_suffix: str = None) -> str:
if len(self.pools) > 0:
return self.pools[0].as_avalon(user_suffix=user_suffix)
return Pool("", "", "").as_avalon()
return Pool(url="", user="", password="").as_avalon()
def as_inno(self, user_suffix: str = None) -> dict:
pools = {}
@@ -301,7 +302,7 @@ class PoolGroup(MinerConfigValue):
**self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix)
)
else:
pools.update(**Pool("", "", "").as_inno(idx=idx + 1))
pools.update(**Pool(url="", user="", password="").as_inno(idx=idx + 1))
idx += 1
return pools
@@ -371,11 +372,11 @@ class PoolGroup(MinerConfigValue):
@classmethod
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
return cls([Pool.from_goldshell(p) for p in web_pools])
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
@classmethod
def from_inno(cls, web_pools: list) -> "PoolGroup":
return cls([Pool.from_inno(p) for p in web_pools])
return cls(pools=[Pool.from_inno(p) for p in web_pools])
@classmethod
def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup":
@@ -389,7 +390,7 @@ class PoolGroup(MinerConfigValue):
@classmethod
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
return cls([Pool.from_vnish(p) for p in web_settings_pools])
return cls(pools=[Pool.from_vnish(p) for p in web_settings_pools])
@classmethod
def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup":
@@ -424,9 +425,8 @@ class PoolGroup(MinerConfigValue):
)
@dataclass
class PoolConfig(MinerConfigValue):
groups: List[PoolGroup] = field(default_factory=list)
groups: List[PoolGroup] = Field(default_factory=list)
@classmethod
def default(cls) -> "PoolConfig":
@@ -538,38 +538,38 @@ class PoolConfig(MinerConfigValue):
return PoolConfig.default()
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
return cls([PoolGroup.from_api(pool_data)])
return cls(groups=[PoolGroup.from_api(pool_data)])
@classmethod
def from_epic(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["StratumConfigs"]
return cls([PoolGroup.from_epic(pool_data)])
return cls(groups=[PoolGroup.from_epic(pool_data)])
@classmethod
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
pool_data = web_conf["pools"]
return cls([PoolGroup.from_am_modern(pool_data)])
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
@classmethod
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
return cls([PoolGroup.from_goldshell(web_pools)])
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
@classmethod
def from_inno(cls, web_pools: list) -> "PoolConfig":
return cls([PoolGroup.from_inno(web_pools)])
return cls(groups=[PoolGroup.from_inno(web_pools)])
@classmethod
def from_bosminer(cls, toml_conf: dict) -> "PoolConfig":
if toml_conf.get("group") is None:
return cls()
return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
return cls(groups=[PoolGroup.from_bosminer(g) for g in toml_conf["group"]])
@classmethod
def from_vnish(cls, web_settings: dict) -> "PoolConfig":
try:
return cls([PoolGroup.from_vnish(web_settings["miner"]["pools"])])
return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])])
except LookupError:
return cls()

View File

@@ -20,11 +20,10 @@ from dataclasses import dataclass
from pyasic.config.base import MinerConfigValue
@dataclass
class TemperatureConfig(MinerConfigValue):
target: int = None
hot: int = None
danger: int = None
target: int | None = None
hot: int | None = None
danger: int | None = None
@classmethod
def default(cls):

View File

@@ -15,25 +15,24 @@
# ------------------------------------------------------------------------------
import copy
import json
import time
from dataclasses import asdict, dataclass, field, fields
from datetime import datetime, timezone
from typing import Any, List, Union
from pydantic import BaseModel, Field, computed_field, field_serializer
from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune
from pyasic.data.pools import PoolMetrics
from pyasic.data.pools import PoolMetrics, Scheme
from pyasic.device.algorithm.hashrate import AlgoHashRateType
from .boards import HashBoard
from .device import DeviceInfo
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
from .fans import Fan
from .hashrate import AlgoHashRate, HashUnit
@dataclass
class MinerData:
class MinerData(BaseModel):
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
Attributes:
@@ -50,7 +49,6 @@ class MinerData:
fw_ver: The current firmware version on the miner as a str.
hostname: The network hostname of the miner as a str.
hashrate: The hashrate of the miner in TH/s as a float. Calculated automatically.
_hashrate: Backup for hashrate found via API instead of hashboards.
expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics.
temperature_avg: The average temperature across the boards. Calculated automatically.
@@ -77,58 +75,44 @@ class MinerData:
# general
ip: str
_datetime: datetime = field(repr=False, default=None)
datetime: str = field(init=False)
timestamp: int = field(init=False)
raw_datetime: datetime = Field(
exclude=True, default_factory=datetime.now(timezone.utc).astimezone, repr=False
)
# about
device_info: DeviceInfo = None
make: str = field(init=False)
model: str = field(init=False)
firmware: str = field(init=False)
algo: str = field(init=False)
mac: str = None
api_ver: str = None
fw_ver: str = None
hostname: str = None
device_info: DeviceInfo | None = None
mac: str | None = None
api_ver: str | None = None
fw_ver: str | None = None
hostname: str | None = None
# hashrate
hashrate: AlgoHashRate = field(init=False)
_hashrate: AlgoHashRate = field(repr=False, default=None)
raw_hashrate: AlgoHashRateType = Field(exclude=True, default=None, repr=False)
# expected
expected_hashrate: float = None
expected_hashboards: int = None
expected_chips: int = None
expected_fans: int = None
# % expected
percent_expected_chips: float = field(init=False)
percent_expected_hashrate: float = field(init=False)
percent_expected_wattage: float = field(init=False)
expected_hashrate: AlgoHashRateType | None = None
expected_hashboards: int | None = None
expected_chips: int | None = None
expected_fans: int | None = None
# temperature
temperature_avg: int = field(init=False)
env_temp: float = None
env_temp: int | None = None
# power
wattage: int = None
wattage_limit: int = field(init=False)
voltage: float = None
_wattage_limit: int = field(repr=False, default=None)
wattage: int | None = None
voltage: float | None = None
raw_wattage_limit: int | None = Field(exclude=True, default=None, repr=False)
# fans
fans: List[Fan] = field(default_factory=list)
fan_psu: int = None
fans: List[Fan] = Field(default_factory=list)
fan_psu: int | None = None
# boards
hashboards: List[HashBoard] = field(default_factory=list)
total_chips: int = field(init=False)
nominal: bool = field(init=False)
hashboards: List[HashBoard] = Field(default_factory=list)
# config
config: MinerConfig = None
fault_light: Union[bool, None] = None
config: MinerConfig | None = None
fault_light: bool | None = None
# errors
errors: List[
@@ -138,26 +122,21 @@ class MinerData:
X19Error,
InnosiliconError,
]
] = field(default_factory=list)
] = Field(default_factory=list)
# mining state
is_mining: bool = True
uptime: int = None
efficiency: int = field(init=False)
uptime: int | None = None
# pools
pools: list[PoolMetrics] = field(default_factory=list)
pools: list[PoolMetrics] = Field(default_factory=list)
@classmethod
def fields(cls):
return [f.name for f in fields(cls) if not f.name.startswith("_")]
@staticmethod
def dict_factory(x):
return {k: v for (k, v) in x if not k.startswith("_")}
return list(cls.model_fields.keys())
def __post_init__(self):
self._datetime = datetime.now(timezone.utc).astimezone()
self.raw_datetime = datetime.now(timezone.utc).astimezone()
def get(self, __key: str, default: Any = None):
try:
@@ -185,19 +164,19 @@ class MinerData:
def __floordiv__(self, other):
cp = copy.deepcopy(self)
for key in self:
for key in self.fields():
item = getattr(self, key)
if isinstance(item, int):
setattr(cp, key, item // other)
if isinstance(item, float):
setattr(cp, key, round(item / other, 2))
setattr(cp, key, item / other)
return cp
def __add__(self, other):
if not isinstance(other, MinerData):
raise TypeError("Cannot add MinerData to non MinerData type.")
cp = copy.deepcopy(self)
for key in self:
for key in self.fields():
item = getattr(self, key)
other_item = getattr(other, key)
if item is None:
@@ -217,34 +196,49 @@ class MinerData:
setattr(cp, key, item & other_item)
return cp
@computed_field # type: ignore[misc]
@property
def hashrate(self): # noqa - Skip PyCharm inspection
def hashrate(self) -> AlgoHashRateType:
if len(self.hashboards) > 0:
hr_data = []
for item in self.hashboards:
if item.hashrate is not None:
hr_data.append(item.hashrate)
if len(hr_data) > 0:
return sum(hr_data, start=type(hr_data[0])(0))
return self._hashrate
return sum(hr_data, start=type(hr_data[0])(rate=0))
return self.raw_hashrate
@field_serializer("hashrate")
def serialize_hashrate(self, hashrate: AlgoHashRateType | None) -> float:
if hashrate is not None:
return float(hashrate)
@field_serializer("expected_hashrate")
def serialize_expected_hashrate(
self, expected_hashrate: AlgoHashRateType | None, _info
) -> float:
if expected_hashrate is not None:
return float(expected_hashrate)
@hashrate.setter
def hashrate(self, val):
self._hashrate = val
self.raw_hashrate = val
@computed_field # type: ignore[misc]
@property
def wattage_limit(self): # noqa - Skip PyCharm inspection
def wattage_limit(self) -> int:
if self.config is not None:
if isinstance(self.config.mining_mode, MiningModePowerTune):
return self.config.mining_mode.power
return self._wattage_limit
return self.raw_wattage_limit
@wattage_limit.setter
def wattage_limit(self, val: int):
self._wattage_limit = val
self.raw_wattage_limit = val
@computed_field # type: ignore[misc]
@property
def total_chips(self): # noqa - Skip PyCharm inspection
def total_chips(self) -> int | None:
if len(self.hashboards) > 0:
chip_data = []
for item in self.hashboards:
@@ -254,34 +248,25 @@ class MinerData:
return sum(chip_data)
return None
@total_chips.setter
def total_chips(self, val):
pass
@computed_field # type: ignore[misc]
@property
def nominal(self): # noqa - Skip PyCharm inspection
def nominal(self) -> bool | None:
if self.total_chips is None or self.expected_chips is None:
return None
return self.expected_chips == self.total_chips
@nominal.setter
def nominal(self, val):
pass
@computed_field # type: ignore[misc]
@property
def percent_expected_chips(self): # noqa - Skip PyCharm inspection
def percent_expected_chips(self) -> int | None:
if self.total_chips is None or self.expected_chips is None:
return None
if self.total_chips == 0 or self.expected_chips == 0:
return 0
return round((self.total_chips / self.expected_chips) * 100)
@percent_expected_chips.setter
def percent_expected_chips(self, val):
pass
@computed_field # type: ignore[misc]
@property
def percent_expected_hashrate(self): # noqa - Skip PyCharm inspection
def percent_expected_hashrate(self) -> int | None:
if self.hashrate is None or self.expected_hashrate is None:
return None
try:
@@ -289,12 +274,9 @@ class MinerData:
except ZeroDivisionError:
return 0
@percent_expected_hashrate.setter
def percent_expected_hashrate(self, val):
pass
@computed_field # type: ignore[misc]
@property
def percent_expected_wattage(self): # noqa - Skip PyCharm inspection
def percent_expected_wattage(self) -> int | None:
if self.wattage_limit is None or self.wattage is None:
return None
try:
@@ -302,12 +284,9 @@ class MinerData:
except ZeroDivisionError:
return 0
@percent_expected_wattage.setter
def percent_expected_wattage(self, val):
pass
@computed_field # type: ignore[misc]
@property
def temperature_avg(self): # noqa - Skip PyCharm inspection
def temperature_avg(self) -> int | None:
total_temp = 0
temp_count = 0
for hb in self.hashboards:
@@ -318,12 +297,9 @@ class MinerData:
return None
return round(total_temp / temp_count)
@temperature_avg.setter
def temperature_avg(self, val):
pass
@computed_field # type: ignore[misc]
@property
def efficiency(self): # noqa - Skip PyCharm inspection
def efficiency(self) -> int | None:
if self.hashrate is None or self.wattage is None:
return None
try:
@@ -331,67 +307,45 @@ class MinerData:
except ZeroDivisionError:
return 0
@efficiency.setter
def efficiency(self, val):
pass
@computed_field # type: ignore[misc]
@property
def datetime(self): # noqa - Skip PyCharm inspection
return self._datetime.isoformat()
@datetime.setter
def datetime(self, val):
pass
def datetime(self) -> str:
return self.raw_datetime.isoformat()
@computed_field # type: ignore[misc]
@property
def timestamp(self): # noqa - Skip PyCharm inspection
return int(time.mktime(self._datetime.timetuple()))
@timestamp.setter
def timestamp(self, val):
pass
def timestamp(self) -> int:
return int(time.mktime(self.raw_datetime.timetuple()))
@computed_field # type: ignore[misc]
@property
def make(self): # noqa - Skip PyCharm inspection
def make(self) -> str:
if self.device_info.make is not None:
return str(self.device_info.make)
@make.setter
def make(self, val):
pass
@computed_field # type: ignore[misc]
@property
def model(self): # noqa - Skip PyCharm inspection
def model(self) -> str:
if self.device_info.model is not None:
return str(self.device_info.model)
@model.setter
def model(self, val):
pass
@computed_field # type: ignore[misc]
@property
def firmware(self): # noqa - Skip PyCharm inspection
def firmware(self) -> str:
if self.device_info.firmware is not None:
return str(self.device_info.firmware)
@firmware.setter
def firmware(self, val):
pass
@computed_field # type: ignore[misc]
@property
def algo(self): # noqa - Skip PyCharm inspection
def algo(self) -> str:
if self.device_info.algo is not None:
return str(self.device_info.algo)
@algo.setter
def algo(self, val):
pass
def keys(self) -> list:
return [f.name for f in fields(self)]
return list(self.model_fields.keys())
def asdict(self) -> dict:
return asdict(self, dict_factory=self.dict_factory)
return self.model_dump()
def as_dict(self) -> dict:
"""Get this dataclass as a dictionary.
@@ -407,7 +361,7 @@ class MinerData:
Returns:
A JSON version of this class.
"""
return json.dumps(self.as_dict())
return self.model_dump_json()
def as_csv(self) -> str:
"""Get this dataclass as CSV.
@@ -436,7 +390,7 @@ class MinerData:
field_data = []
tags = ["ip", "mac", "model", "hostname"]
for attribute in self:
for attribute in self.fields():
if attribute in tags:
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
tag_data.append(f"{attribute}={escaped_data}")

View File

@@ -13,15 +13,16 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from .hashrate import AlgoHashRate
from pydantic import BaseModel, field_serializer
from pyasic.device.algorithm.hashrate import AlgoHashRateType
@dataclass
class HashBoard:
class HashBoard(BaseModel):
"""A Dataclass to standardize hashboard data.
Attributes:
@@ -39,16 +40,21 @@ class HashBoard:
"""
slot: int = 0
hashrate: AlgoHashRate = None
temp: int = None
chip_temp: int = None
chips: int = None
expected_chips: int = None
serial_number: str = None
hashrate: AlgoHashRateType | None = None
temp: float | None = None
chip_temp: float | None = None
chips: int | None = None
expected_chips: int | None = None
serial_number: str | None = None
missing: bool = True
tuned: bool = None
active: bool = None
voltage: float = None
tuned: bool | None = None
active: bool | None = None
voltage: float | None = None
@field_serializer("hashrate")
def serialize_hashrate(self, hashrate: AlgoHashRateType | None) -> float:
if hashrate is not None:
return float(hashrate)
def get(self, __key: str, default: Any = None):
try:

View File

@@ -1,14 +1,31 @@
from dataclasses import dataclass
from pydantic import BaseModel, ConfigDict, field_serializer
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.algorithm import MinerAlgoType
from pyasic.device.firmware import MinerFirmware
from pyasic.device.makes import MinerMake
from pyasic.device.models import MinerModel
from pyasic.device.models import MinerModelType
@dataclass
class DeviceInfo:
make: MinerMake = None
model: MinerModel = None
firmware: MinerFirmware = None
algo: MinerAlgo = None
class DeviceInfo(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
make: MinerMake | None = None
model: MinerModelType | None = None
firmware: MinerFirmware | None = None
algo: type[MinerAlgoType] | None = None
@field_serializer("make")
def serialize_make(self, make: MinerMake, _info):
return str(make)
@field_serializer("model")
def serialize_model(self, model: MinerModelType, _info):
return str(model)
@field_serializer("firmware")
def serialize_firmware(self, firmware: MinerFirmware, _info):
return str(firmware)
@field_serializer("algo")
def serialize_algo(self, algo: MinerAlgoType, _info):
return str(algo)

View File

@@ -13,12 +13,10 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import asdict, dataclass, fields
from pyasic.data.error_codes.base import BaseMinerError
@dataclass
class X19Error:
class X19Error(BaseMinerError):
"""A Dataclass to handle error codes of X19 miners.
Attributes:
@@ -28,10 +26,3 @@ class X19Error:
error_message: str
error_code: int = 0
@classmethod
def fields(cls):
return fields(cls)
def asdict(self):
return asdict(self)

View File

@@ -0,0 +1,18 @@
from pydantic import BaseModel
class BaseMinerError(BaseModel):
@classmethod
def fields(cls):
return list(cls.model_fields.keys())
def asdict(self) -> dict:
return self.model_dump()
def as_dict(self) -> dict:
"""Get this dataclass as a dictionary.
Returns:
A dictionary version of this class.
"""
return self.asdict()

View File

@@ -14,11 +14,10 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import asdict, dataclass, fields
from pyasic.data.error_codes.base import BaseMinerError
@dataclass
class BraiinsOSError:
class BraiinsOSError(BaseMinerError):
"""A Dataclass to handle error codes of BraiinsOS+ miners.
Attributes:
@@ -28,10 +27,3 @@ class BraiinsOSError:
error_message: str
error_code: int = 0
@classmethod
def fields(cls):
return fields(cls)
def asdict(self):
return asdict(self)

View File

@@ -14,11 +14,13 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import asdict, dataclass, field, fields
from pydantic import computed_field
from pyasic.data.error_codes.base import BaseMinerError
@dataclass
class InnosiliconError:
class InnosiliconError(BaseMinerError):
"""A Dataclass to handle error codes of Innosilicon miners.
Attributes:
@@ -27,25 +29,14 @@ class InnosiliconError:
"""
error_code: int
error_message: str = field(init=False)
@classmethod
def fields(cls):
return fields(cls)
@computed_field # type: ignore[misc]
@property
def error_message(self): # noqa - Skip PyCharm inspection
def error_message(self) -> str: # noqa - Skip PyCharm inspection
if self.error_code in ERROR_CODES:
return ERROR_CODES[self.error_code]
return "Unknown error type."
@error_message.setter
def error_message(self, val):
pass
def asdict(self):
return asdict(self)
ERROR_CODES = {
21: "The PLUG signal of the hash board is not detected.",

View File

@@ -13,12 +13,12 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pydantic import computed_field
from dataclasses import asdict, dataclass, field, fields
from pyasic.data.error_codes.base import BaseMinerError
@dataclass
class WhatsminerError:
class WhatsminerError(BaseMinerError):
"""A Dataclass to handle error codes of Whatsminers.
Attributes:
@@ -27,14 +27,10 @@ class WhatsminerError:
"""
error_code: int
error_message: str = field(init=False)
@classmethod
def fields(cls):
return fields(cls)
@computed_field # type: ignore[misc]
@property
def error_message(self): # noqa - Skip PyCharm inspection
def error_message(self) -> str: # noqa - Skip PyCharm inspection
if len(str(self.error_code)) == 6 and not str(self.error_code)[:1] == "1":
err_type = int(str(self.error_code)[:2])
err_subtype = int(str(self.error_code)[2:3])
@@ -74,13 +70,6 @@ class WhatsminerError:
except KeyError:
return "Unknown error type."
@error_message.setter
def error_message(self, val):
pass
def asdict(self):
return asdict(self)
ERROR_CODES = {
1: { # Fan error

View File

@@ -14,12 +14,12 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import dataclass
from typing import Any
from pydantic import BaseModel
@dataclass
class Fan:
class Fan(BaseModel):
"""A Dataclass to standardize fan data.
Attributes:

View File

@@ -1,15 +0,0 @@
from enum import Enum
from pyasic.data.hashrate.sha256 import SHA256HashRate
from pyasic.device.algorithm.sha256 import SHA256Unit
class AlgoHashRate(Enum):
SHA256 = SHA256HashRate
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
class HashUnit:
SHA256 = SHA256Unit

View File

@@ -1,54 +0,0 @@
from __future__ import annotations
from dataclasses import dataclass
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.algorithm.sha256 import SHA256Unit
@dataclass
class SHA256HashRate:
rate: float
unit: SHA256Unit = MinerAlgo.SHA256.unit.default
def __float__(self):
return float(self.rate)
def __int__(self):
return int(self.rate)
def __repr__(self):
return f"{self.rate} {str(self.unit)}"
def __round__(self, n: int = None):
return round(self.rate, n)
def __add__(self, other: SHA256HashRate | int | float) -> SHA256HashRate:
if isinstance(other, SHA256HashRate):
return SHA256HashRate(self.rate + other.into(self.unit).rate, self.unit)
return SHA256HashRate(self.rate + other, self.unit)
def __sub__(self, other: SHA256HashRate | int | float) -> SHA256HashRate:
if isinstance(other, SHA256HashRate):
return SHA256HashRate(self.rate - other.into(self.unit).rate, self.unit)
return SHA256HashRate(self.rate - other, self.unit)
def __truediv__(self, other: SHA256HashRate | int | float):
if isinstance(other, SHA256HashRate):
return SHA256HashRate(self.rate / other.into(self.unit).rate, self.unit)
return SHA256HashRate(self.rate / other, self.unit)
def __floordiv__(self, other: SHA256HashRate | int | float):
if isinstance(other, SHA256HashRate):
return SHA256HashRate(self.rate // other.into(self.unit).rate, self.unit)
return SHA256HashRate(self.rate // other, self.unit)
def __mul__(self, other: SHA256HashRate | int | float):
if isinstance(other, SHA256HashRate):
return SHA256HashRate(self.rate * other.into(self.unit).rate, self.unit)
return SHA256HashRate(self.rate * other, self.unit)
def into(self, other: SHA256Unit) -> SHA256HashRate:
return SHA256HashRate(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -1,8 +1,9 @@
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
from urllib.parse import urlparse
from pydantic import BaseModel, computed_field, model_serializer
class Scheme(Enum):
STRATUM_V1 = "stratum+tcp"
@@ -10,13 +11,16 @@ class Scheme(Enum):
STRATUM_V1_SSL = "stratum+ssl"
@dataclass
class PoolUrl:
class PoolUrl(BaseModel):
scheme: Scheme
host: str
port: int
pubkey: Optional[str] = None
@model_serializer
def serialize(self):
return str(self)
def __str__(self) -> str:
if self.scheme == Scheme.STRATUM_V2 and self.pubkey:
return f"{self.scheme.value}://{self.host}:{self.port}/{self.pubkey}"
@@ -36,8 +40,7 @@ class PoolUrl:
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
@dataclass
class PoolMetrics:
class PoolMetrics(BaseModel):
"""A dataclass to standardize pool metrics returned from miners.
Attributes:
@@ -63,18 +66,14 @@ class PoolMetrics:
alive: bool = None
index: int = None
user: str = None
pool_rejected_percent: float = field(init=False)
pool_stale_percent: float = field(init=False)
@computed_field # type: ignore[misc]
@property
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of rejected shares"""
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
@pool_rejected_percent.setter
def pool_rejected_percent(self, val):
pass
@computed_field # type: ignore[misc]
@property
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of stale shares."""
@@ -82,10 +81,6 @@ class PoolMetrics:
self.get_failures, self.accepted + self.rejected
)
@pool_stale_percent.setter
def pool_stale_percent(self, val):
pass
@staticmethod
def _calculate_percentage(value: int, total: int) -> float:
"""Calculate the percentage."""

View File

@@ -1,5 +1,26 @@
from pyasic.device.algorithm.sha256 import SHA256Algo
from .base import MinerAlgoType
from .blake256 import Blake256Algo
from .eaglesong import EaglesongAlgo
from .equihash import EquihashAlgo
from .ethash import EtHashAlgo
from .handshake import HandshakeAlgo
from .hashrate import *
from .hashrate.unit import *
from .kadena import KadenaAlgo
from .kheavyhash import KHeavyHashAlgo
from .scrypt import ScryptAlgo
from .sha256 import SHA256Algo
from .x11 import X11Algo
class MinerAlgo:
SHA256 = SHA256Algo
SCRYPT = ScryptAlgo
KHEAVYHASH = KHeavyHashAlgo
KADENA = KadenaAlgo
HANDSHAKE = HandshakeAlgo
X11 = X11Algo
BLAKE256 = Blake256Algo
EAGLESONG = EaglesongAlgo
ETHASH = EtHashAlgo
EQUIHASH = EquihashAlgo

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .hashrate.base import AlgoHashRateType
from .hashrate.unit.base import AlgoHashRateUnitType
class MinerAlgoMeta(type):
name: str
def __str__(cls):
return cls.name
class MinerAlgoType(metaclass=MinerAlgoMeta):
hashrate: type[AlgoHashRateType]
unit: type[AlgoHashRateUnitType]

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import Blake256HashRate
from .hashrate.unit import Blake256Unit
# make this json serializable
class Blake256Algo(MinerAlgoType):
hashrate: type[Blake256HashRate] = Blake256HashRate
unit: type[Blake256Unit] = Blake256Unit
name = "Blake256"

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import EaglesongHashRate
from .hashrate.unit import EaglesongUnit
# make this json serializable
class EaglesongAlgo(MinerAlgoType):
hashrate: type[EaglesongHashRate] = EaglesongHashRate
unit: type[EaglesongUnit] = EaglesongUnit
name = "Eaglesong"

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import EquihashHashRate
from .hashrate.unit import EquihashUnit
# make this json serializable
class EquihashAlgo(MinerAlgoType):
hashrate: type[EquihashHashRate] = EquihashHashRate
unit: type[EquihashUnit] = EquihashUnit
name = "Equihash"

View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import EtHashHashRate
from .hashrate.unit import EtHashUnit
class EtHashAlgo(MinerAlgoType):
hashrate: type[EtHashHashRate] = EtHashHashRate
unit: type[EtHashUnit] = EtHashUnit
name = "EtHash"

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import HandshakeHashRate
from .hashrate.unit import HandshakeUnit
# make this json serializable
class HandshakeAlgo(MinerAlgoType):
hashrate: type[HandshakeHashRate] = HandshakeHashRate
unit: type[HandshakeUnit] = HandshakeUnit
name = "Handshake"

View File

@@ -0,0 +1,24 @@
from .base import AlgoHashRateType
from .blake256 import Blake256HashRate
from .eaglesong import EaglesongHashRate
from .equihash import EquihashHashRate
from .ethash import EtHashHashRate
from .handshake import HandshakeHashRate
from .kadena import KadenaHashRate
from .kheavyhash import KHeavyHashHashRate
from .scrypt import ScryptHashRate
from .sha256 import SHA256HashRate
from .x11 import X11HashRate
class AlgoHashRate:
SHA256 = SHA256HashRate
SCRYPT = ScryptHashRate
KHEAVYHASH = KHeavyHashHashRate
KADENA = KadenaHashRate
HANDSHAKE = HandshakeHashRate
X11 = X11HashRate
BLAKE256 = Blake256HashRate
EAGLESONG = EaglesongHashRate
ETHASH = EtHashHashRate
EQUIHASH = EquihashHashRate

View File

@@ -0,0 +1,62 @@
from abc import ABC, abstractmethod
from pydantic import BaseModel
from typing_extensions import Self
from .unit.base import AlgoHashRateUnitType
class AlgoHashRateType(BaseModel, ABC):
unit: AlgoHashRateUnitType
rate: float
@abstractmethod
def into(self, other: "AlgoHashRateUnitType"):
pass
def __float__(self):
return float(self.rate)
def __int__(self):
return int(self.rate)
def __repr__(self):
return f"{self.rate} {str(self.unit)}"
def __round__(self, n: int = None):
return round(self.rate, n)
def __add__(self, other: Self | int | float) -> Self:
if isinstance(other, self.__class__):
return self.__class__(
rate=self.rate + other.into(self.unit).rate, unit=self.unit
)
return self.__class__(rate=self.rate + other, unit=self.unit)
def __sub__(self, other: Self | int | float) -> Self:
if isinstance(other, self.__class__):
return self.__class__(
rate=self.rate - other.into(self.unit).rate, unit=self.unit
)
return self.__class__(rate=self.rate - other, unit=self.unit)
def __truediv__(self, other: Self | int | float) -> Self:
if isinstance(other, self.__class__):
return self.__class__(
rate=self.rate / other.into(self.unit).rate, unit=self.unit
)
return self.__class__(rate=self.rate / other, unit=self.unit)
def __floordiv__(self, other: Self | int | float) -> Self:
if isinstance(other, self.__class__):
return self.__class__(
rate=self.rate // other.into(self.unit).rate, unit=self.unit
)
return self.__class__(rate=self.rate // other, unit=self.unit)
def __mul__(self, other: Self | int | float) -> Self:
if isinstance(other, self.__class__):
return self.__class__(
rate=self.rate * other.into(self.unit).rate, unit=self.unit
)
return self.__class__(rate=self.rate * other, unit=self.unit)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.blake256 import Blake256Unit
from .unit import HashUnit
class Blake256HashRate(AlgoHashRateType):
rate: float
unit: Blake256Unit = HashUnit.BLAKE256.default
def into(self, other: Blake256Unit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.eaglesong import EaglesongUnit
from .unit import HashUnit
class EaglesongHashRate(AlgoHashRateType):
rate: float
unit: EaglesongUnit = HashUnit.EAGLESONG.default
def into(self, other: EaglesongUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.equihash import EquihashUnit
from .unit import HashUnit
class EquihashHashRate(AlgoHashRateType):
rate: float
unit: EquihashUnit = HashUnit.ETHASH.default
def into(self, other: EquihashUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.ethash import EtHashUnit
from .unit import HashUnit
class EtHashHashRate(AlgoHashRateType):
rate: float
unit: EtHashUnit = HashUnit.ETHASH.default
def into(self, other: EtHashUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.handshake import HandshakeUnit
from .unit import HashUnit
class HandshakeHashRate(AlgoHashRateType):
rate: float
unit: HandshakeUnit = HashUnit.HANDSHAKE.default
def into(self, other: HandshakeUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.kadena import KadenaUnit
from .unit import HashUnit
class KadenaHashRate(AlgoHashRateType):
rate: float
unit: KadenaUnit = HashUnit.KADENA.default
def into(self, other: KadenaUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.kheavyhash import KHeavyHashUnit
from .unit import HashUnit
class KHeavyHashHashRate(AlgoHashRateType):
rate: float
unit: KHeavyHashUnit = HashUnit.KHEAVYHASH.default
def into(self, other: KHeavyHashUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit
from .unit import HashUnit
class ScryptHashRate(AlgoHashRateType):
rate: float
unit: ScryptUnit = HashUnit.SCRYPT.default
def into(self, other: ScryptUnit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.sha256 import SHA256Unit
from .unit import HashUnit
class SHA256HashRate(AlgoHashRateType):
rate: float
unit: SHA256Unit = HashUnit.SHA256.default
def into(self, other: SHA256Unit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,23 @@
from .blake256 import Blake256Unit
from .eaglesong import EaglesongUnit
from .equihash import EquihashUnit
from .ethash import EtHashUnit
from .handshake import HandshakeUnit
from .kadena import KadenaUnit
from .kheavyhash import KHeavyHashUnit
from .scrypt import ScryptUnit
from .sha256 import SHA256Unit
from .x11 import X11Unit
class HashUnit:
SHA256 = SHA256Unit
SCRYPT = ScryptUnit
KHEAVYHASH = KHeavyHashUnit
KADENA = KadenaUnit
HANDSHAKE = HandshakeUnit
X11 = X11Unit
BLAKE256 = Blake256Unit
EAGLESONG = EaglesongUnit
ETHASH = EtHashUnit
EQUIHASH = EquihashUnit

View File

@@ -0,0 +1,55 @@
from enum import IntEnum
class AlgoHashRateUnitType(IntEnum):
H: int
KH: int
MH: int
GH: int
TH: int
PH: int
EH: int
ZH: int
default: int
def __str__(self):
if self.value == self.H:
return "H/s"
if self.value == self.KH:
return "KH/s"
if self.value == self.MH:
return "MH/s"
if self.value == self.GH:
return "GH/s"
if self.value == self.TH:
return "TH/s"
if self.value == self.PH:
return "PH/s"
if self.value == self.EH:
return "EH/s"
if self.value == self.ZH:
return "ZH/s"
@classmethod
def from_str(cls, value: str):
if value == "H":
return cls.H
elif value == "KH":
return cls.KH
elif value == "MH":
return cls.MH
elif value == "GH":
return cls.GH
elif value == "TH":
return cls.TH
elif value == "PH":
return cls.PH
elif value == "EH":
return cls.EH
elif value == "ZH":
return cls.ZH
return cls.default
def __repr__(self):
return str(self)

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class Blake256Unit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class EaglesongUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -0,0 +1,34 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class EquihashUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = KH
def __str__(self):
if self.value == self.H:
return "Sol/s"
if self.value == self.KH:
return "KSol/s"
if self.value == self.MH:
return "MSol/s"
if self.value == self.GH:
return "GSol/s"
if self.value == self.TH:
return "TSol/s"
if self.value == self.PH:
return "PSol/s"
if self.value == self.EH:
return "ESol/s"
if self.value == self.ZH:
return "ZSol/s"

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class EtHashUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = MH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class HandshakeUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class KadenaUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class KHeavyHashUnit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from enum import IntEnum
class ScryptUnit(IntEnum):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = GH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class SHA256Unit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH

View File

@@ -0,0 +1,16 @@
from __future__ import annotations
from .base import AlgoHashRateUnitType
class X11Unit(AlgoHashRateUnitType):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = GH

View File

@@ -0,0 +1,18 @@
from __future__ import annotations
from typing_extensions import Self
from pyasic.device.algorithm.hashrate.base import AlgoHashRateType
from pyasic.device.algorithm.hashrate.unit.x11 import X11Unit
from .unit import HashUnit
class X11HashRate(AlgoHashRateType):
rate: float
unit: X11Unit = HashUnit.X11.default
def into(self, other: X11Unit) -> Self:
return self.__class__(
rate=self.rate / (other.value / self.unit.value), unit=other
)

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import KadenaHashRate
from .hashrate.unit import KadenaUnit
# make this json serializable
class KadenaAlgo(MinerAlgoType):
hashrate: type[KadenaHashRate] = KadenaHashRate
unit: type[KadenaUnit] = KadenaUnit
name = "Kadena"

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import KHeavyHashHashRate
from .hashrate.unit import KHeavyHashUnit
# make this json serializable
class KHeavyHashAlgo(MinerAlgoType):
hashrate: type[KHeavyHashHashRate] = KHeavyHashHashRate
unit: type[KHeavyHashUnit] = KHeavyHashUnit
name = "KHeavyHash"

View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import ScryptHashRate
from .hashrate.unit import ScryptUnit
class ScryptAlgo(MinerAlgoType):
hashrate: type[ScryptHashRate] = ScryptHashRate
unit: type[ScryptUnit] = ScryptUnit
name = "Scrypt"

View File

@@ -1,68 +1,13 @@
from __future__ import annotations
from enum import IntEnum
class SHA256Unit(IntEnum):
H = 1
KH = int(H) * 1000
MH = int(KH) * 1000
GH = int(MH) * 1000
TH = int(GH) * 1000
PH = int(TH) * 1000
EH = int(PH) * 1000
ZH = int(EH) * 1000
default = TH
def __str__(self):
if self.value == self.H:
return "H/s"
if self.value == self.KH:
return "KH/s"
if self.value == self.MH:
return "MH/s"
if self.value == self.GH:
return "GH/s"
if self.value == self.TH:
return "TH/s"
if self.value == self.PH:
return "PH/s"
if self.value == self.EH:
return "EH/s"
if self.value == self.ZH:
return "ZH/s"
@classmethod
def from_str(cls, value: str):
if value == "H":
return cls.H
elif value == "KH":
return cls.KH
elif value == "MH":
return cls.MH
elif value == "GH":
return cls.GH
elif value == "TH":
return cls.TH
elif value == "PH":
return cls.PH
elif value == "EH":
return cls.EH
elif value == "ZH":
return cls.ZH
return cls.default
def __repr__(self):
return str(self)
from .base import MinerAlgoType
from .hashrate import SHA256HashRate
from .hashrate.unit import SHA256Unit
# make this json serializable
class _SHA256Algo(str):
unit = SHA256Unit
class SHA256Algo(MinerAlgoType):
hashrate: type[SHA256HashRate] = SHA256HashRate
unit: type[SHA256Unit] = SHA256Unit
def __repr__(self):
return "SHA256Algo"
SHA256Algo = _SHA256Algo("SHA256")
name = "SHA256"

View File

@@ -0,0 +1,13 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import X11HashRate
from .hashrate.unit import X11Unit
# make this json serializable
class X11Algo(MinerAlgoType):
hashrate: type[X11HashRate] = X11HashRate
unit: type[X11Unit] = X11Unit
name = "X11"

View File

@@ -27,6 +27,7 @@ class MinerMake(str, Enum):
EPIC = "ePIC"
BITAXE = "BitAxe"
ICERIVER = "IceRiver"
HAMMER = "Hammer"
def __str__(self):
return self.value

View File

@@ -1,7 +1,11 @@
from enum import Enum
class AntminerModels(str, Enum):
class MinerModelType(str, Enum):
pass
class AntminerModels(MinerModelType):
D3 = "D3"
HS3 = "HS3"
L3Plus = "L3+"
@@ -11,11 +15,13 @@ class AntminerModels(str, Enum):
KS5 = "KS5"
L7 = "L7"
K7 = "K7"
D7 = "D7"
E9Pro = "E9Pro"
S9 = "S9"
S9i = "S9i"
S9j = "S9j"
T9 = "T9"
D9 = "D9"
Z15 = "Z15"
Z15Pro = "Z15 Pro"
S17 = "S17"
@@ -56,7 +62,7 @@ class AntminerModels(str, Enum):
return self.value
class WhatsminerModels(str, Enum):
class WhatsminerModels(MinerModelType):
M20V10 = "M20 V10"
M20SV10 = "M20S V10"
M20SV20 = "M20S V20"
@@ -277,7 +283,7 @@ class WhatsminerModels(str, Enum):
return self.value
class AvalonminerModels(str, Enum):
class AvalonminerModels(MinerModelType):
Avalon721 = "Avalon 721"
Avalon741 = "Avalon 741"
Avalon761 = "Avalon 761"
@@ -296,7 +302,7 @@ class AvalonminerModels(str, Enum):
return self.value
class InnosiliconModels(str, Enum):
class InnosiliconModels(MinerModelType):
T3HPlus = "T3H+"
A10X = "A10X"
A11 = "A11"
@@ -306,7 +312,7 @@ class InnosiliconModels(str, Enum):
return self.value
class GoldshellModels(str, Enum):
class GoldshellModels(MinerModelType):
CK5 = "CK5"
HS5 = "HS5"
KD5 = "KD5"
@@ -318,7 +324,7 @@ class GoldshellModels(str, Enum):
return self.value
class ePICModels(str, Enum):
class ePICModels(MinerModelType):
BM520i = "BlockMiner 520i"
BM720i = "BlockMiner 720i"
@@ -326,7 +332,7 @@ class ePICModels(str, Enum):
return self.value
class AuradineModels(str, Enum):
class AuradineModels(MinerModelType):
AT1500 = "AT1500"
AT2860 = "AT2860"
AT2880 = "AT2880"
@@ -339,22 +345,33 @@ class AuradineModels(str, Enum):
return self.value
class BitAxeModels(str, Enum):
class BitAxeModels(MinerModelType):
BM1366 = "Ultra"
BM1368 = "Supra"
BM1397 = "Max"
BM1370 = "Gamma"
def __str__(self):
return self.value
class IceRiverModels(str, Enum):
class IceRiverModels(MinerModelType):
KS0 = "KS0"
KS1 = "KS1"
KS2 = "KS2"
KS3 = "KS3"
KS3L = "KS3L"
KS3M = "KS3M"
KS5 = "KS5"
KS5L = "KS5L"
KS5M = "KS5M"
def __str__(self):
return self.value
class HammerModels(MinerModelType):
D10 = "D10"
def __str__(self):
return self.value
@@ -370,3 +387,4 @@ class MinerModel:
EPIC = ePICModels
BITAXE = BitAxeModels
ICERIVER = IceRiverModels
HAMMER = HammerModels

View File

@@ -0,0 +1,21 @@
# ------------------------------------------------------------------------------
# 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 AntminerModern
from pyasic.miners.device.models import D7
class BMMinerD7(AntminerModern, D7):
pass

View File

@@ -13,5 +13,6 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .D7 import BMMinerD7
from .K7 import BMMinerK7
from .L7 import BMMinerL7

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 AntminerModern
from pyasic.miners.device.models import D9
class BMMinerD9(AntminerModern, D9):
pass

View File

@@ -14,6 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .D9 import BMMinerD9
from .E9 import BMMinerE9Pro
from .S9 import BMMinerS9, BMMinerS9i, BMMinerS9j
from .T9 import BMMinerT9

View File

@@ -18,7 +18,8 @@ from typing import List, Optional
import asyncssh
from pyasic.data import AlgoHashRate, HashBoard, HashUnit
from pyasic.data import HashBoard
from pyasic.device.algorithm import AlgoHashRate, HashUnit
from pyasic.errors import APIError
from pyasic.miners.backends import Hiveon
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
@@ -62,6 +63,10 @@ HIVEON_T9_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -122,7 +127,7 @@ class HiveonT9(Hiveon, T9):
except (KeyError, IndexError):
pass
hashboards[board].hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.GH
rate=float(hashrate), unit=HashUnit.SHA256.GH
).into(self.algo.unit.default)
hashboards[board].chips = chips
@@ -168,4 +173,4 @@ class HiveonT9(Hiveon, T9):
pass
if not env_temp_list == []:
return round(float(sum(env_temp_list) / len(env_temp_list)), 2)
return round(sum(env_temp_list) / len(env_temp_list))

View File

@@ -17,16 +17,19 @@ from .antminer import AntminerModern, AntminerOld
from .auradine import Auradine
from .avalonminer import AvalonMiner
from .bfgminer import BFGMiner
from .bitaxe import BitAxe
from .bmminer import BMMiner
from .braiins_os import BOSer, BOSMiner
from .btminer import BTMiner
from .cgminer import CGMiner
from .epic import ePIC
from .goldshell import GoldshellMiner
from .hammer import BlackMiner
from .hiveon import Hiveon
from .iceriver import IceRiver
from .innosilicon import Innosilicon
from .luxminer import LUXMiner
from .marathon import MaraMiner
from .unknown import UnknownMiner
from .vnish import VNish
from .whatsminer import M2X, M3X, M5X, M6X

View File

@@ -16,12 +16,13 @@
import logging
from pathlib import Path
from typing import List, Optional, Union
from typing import List, Optional
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
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.errors import APIError
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.backends.cgminer import CGMiner
@@ -183,13 +184,13 @@ class AntminerModern(BMMiner):
async def stop_mining(self) -> bool:
cfg = await self.get_config()
cfg.miner_mode = MiningModeConfig.sleep
cfg.miner_mode = MiningModeConfig.sleep()
await self.send_config(cfg)
return True
async def resume_mining(self) -> bool:
cfg = await self.get_config()
cfg.miner_mode = MiningModeConfig.normal
cfg.miner_mode = MiningModeConfig.normal()
await self.send_config(cfg)
return True
@@ -239,7 +240,7 @@ class AntminerModern(BMMiner):
for item in web_summary["SUMMARY"][0]["status"]:
try:
if not item["status"] == "s":
errors.append(X19Error(item["msg"]))
errors.append(X19Error(error_message=item["msg"]))
except KeyError:
continue
except LookupError:
@@ -248,7 +249,7 @@ class AntminerModern(BMMiner):
async def _get_hashboards(self) -> List[HashBoard]:
hashboards = [
HashBoard(idx, expected_chips=self.expected_chips)
HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
@@ -260,8 +261,8 @@ class AntminerModern(BMMiner):
if rpc_stats is not None:
try:
for board in rpc_stats["STATS"][0]["chain"]:
hashboards[board["index"]].hashrate = AlgoHashRate.SHA256(
board["rate_real"], HashUnit.SHA256.GH
hashboards[board["index"]].hashrate = self.algo.hashrate(
rate=board["rate_real"], unit=self.algo.unit.GH
).into(self.algo.unit.default)
hashboards[board["index"]].chips = board["asic_num"]
board_temp_data = list(
@@ -317,8 +318,8 @@ class AntminerModern(BMMiner):
rate_unit = rpc_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
return self.algo.hashrate(
rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass
@@ -597,44 +598,52 @@ class AntminerOld(CGMiner):
pass
if rpc_stats is not None:
board_offset = -1
boards = rpc_stats["STATS"]
if len(boards) > 1:
for board_num in range(1, 16, 5):
for _b_num in range(5):
b = boards[1].get(f"chain_acn{board_num + _b_num}")
try:
board_offset = -1
boards = rpc_stats["STATS"]
if len(boards) > 1:
for board_num in range(1, 16, 5):
for _b_num in range(5):
b = boards[1].get(f"chain_acn{board_num + _b_num}")
if b and not b == 0 and board_offset == -1:
board_offset = board_num
if board_offset == -1:
board_offset = 1
if b and not b == 0 and board_offset == -1:
board_offset = board_num
if board_offset == -1:
board_offset = 1
for i in range(board_offset, board_offset + self.expected_hashboards):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
)
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
if chip_temp:
hashboard.chip_temp = round(chip_temp)
chip_temp = boards[1].get(f"temp{i}")
if chip_temp:
hashboard.chip_temp = round(chip_temp)
temp = boards[1].get(f"temp2_{i}")
if temp:
hashboard.temp = round(temp)
temp = boards[1].get(f"temp2_{i}")
if temp:
hashboard.temp = round(temp)
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = AlgoHashRate.SHA256(
float(hashrate), HashUnit.SHA256.GH
).into(self.algo.unit.default)
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.GH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
except LookupError:
return [
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
return hashboards

View File

@@ -18,7 +18,8 @@ from enum import Enum
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import (
DataFunction,
@@ -292,8 +293,9 @@ class Auradine(StockFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["MHS 5s"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["MHS 5s"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
@@ -321,10 +323,10 @@ class Auradine(StockFirmware):
try:
for board in rpc_devs["DEVS"]:
b_id = board["ID"] - 1
hashboards[b_id].hashrate = AlgoHashRate.SHA256(
board["MHS 5s"], HashUnit.SHA256.MH
hashboards[b_id].hashrate = self.algo.hashrate(
rate=float(board["MHS 5s"]), unit=self.algo.unit.MH
).into(self.algo.unit.default)
hashboards[b_id].temp = round(float(float(board["Temperature"])), 2)
hashboards[b_id].temp = round(float(board["Temperature"]))
hashboards[b_id].missing = False
except LookupError:
pass
@@ -390,7 +392,7 @@ class Auradine(StockFirmware):
if web_fan is not None:
try:
for fan in web_fan["Fan"]:
fans.append(Fan(round(fan["Speed"])))
fans.append(Fan(speed=round(fan["Speed"])))
except LookupError:
pass
return fans

View File

@@ -17,7 +17,8 @@
import re
from typing import List, Optional
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.backends.cgminer import CGMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
@@ -183,8 +184,8 @@ class AvalonMiner(CGMiner):
if rpc_devs is not None:
try:
return AlgoHashRate.SHA256(
rpc_devs["DEVS"][0]["MHS 1m"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_devs["DEVS"][0]["MHS 1m"]), unit=self.algo.unit.MH
).into(self.algo.unit.default)
except (KeyError, IndexError, ValueError, TypeError):
pass
@@ -216,8 +217,8 @@ class AvalonMiner(CGMiner):
try:
board_hr = parsed_stats["MGHS"][board]
hashboards[board].hashrate = AlgoHashRate.SHA256(
float(board_hr), HashUnit.SHA256.GH
hashboards[board].hashrate = self.algo.hashrate(
rate=float(board_hr), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except LookupError:
pass
@@ -252,8 +253,8 @@ class AvalonMiner(CGMiner):
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return AlgoHashRate.SHA256(
float(parsed_stats["GHSmm"][0]), HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(parsed_stats["GHSmm"][0]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass

View File

@@ -17,8 +17,9 @@
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -120,8 +121,9 @@ class BFGMiner(StockFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["MHS 20s"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["MHS 20s"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
@@ -166,8 +168,8 @@ class BFGMiner(StockFirmware):
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.GH
hashboard.hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.GH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
@@ -259,8 +261,8 @@ class BFGMiner(StockFirmware):
rate_unit = rpc_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
return self.algo.hashrate(
rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -1,8 +1,9 @@
from typing import List, Optional
from pyasic import APIError, MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.device import MinerFirmware
from pyasic.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
@@ -93,8 +94,8 @@ class BitAxe(BaseMiner):
if web_system_info is not None:
try:
return AlgoHashRate.SHA256(
web_system_info["hashRate"], HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except KeyError:
pass
@@ -123,8 +124,9 @@ class BitAxe(BaseMiner):
try:
return [
HashBoard(
hashrate=AlgoHashRate.SHA256(
web_system_info["hashRate"], HashUnit.SHA256.GH
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"),

View File

@@ -17,7 +17,9 @@
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -53,6 +55,10 @@ BMMINER_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -119,8 +125,9 @@ class BMMiner(StockFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["GHS 5s"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
@@ -178,8 +185,8 @@ class BMMiner(StockFirmware):
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.GH
hashboard.hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.GH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
@@ -240,8 +247,8 @@ class BMMiner(StockFirmware):
rate_unit = rpc_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
return self.algo.hashrate(
rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass
@@ -258,3 +265,33 @@ class BMMiner(StockFirmware):
return int(rpc_stats["STATS"][1]["Elapsed"])
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -29,9 +29,10 @@ except ImportError:
from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import BraiinsOSError, MinerErrorData
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate, AlgoHashRateType
from pyasic.errors import APIError
from pyasic.miners.data import (
DataFunction,
@@ -222,7 +223,7 @@ class BOSMiner(BraiinsOSFirmware):
cfg = await self.get_config()
if cfg is None:
return False
cfg.mining_mode = MiningModePowerTune(wattage)
cfg.mining_mode = MiningModePowerTune(power=wattage)
await self.send_config(cfg)
except APIError:
raise
@@ -362,8 +363,9 @@ class BOSMiner(BraiinsOSFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["MHS 1m"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except (KeyError, IndexError, ValueError, TypeError):
pass
@@ -434,8 +436,8 @@ class BOSMiner(BraiinsOSFirmware):
for board in rpc_devs["DEVS"]:
_id = board["ID"] - offset
hashboards[_id].hashrate = AlgoHashRate.SHA256(
board["MHS 1m"], HashUnit.SHA256.MH
hashboards[_id].hashrate = self.algo.hashrate(
rate=float(board["MHS 1m"]), unit=self.algo.unit.MH
).into(self.algo.unit.default)
except (IndexError, KeyError):
pass
@@ -481,7 +483,7 @@ class BOSMiner(BraiinsOSFirmware):
fans = []
for n in range(self.expected_fans):
try:
fans.append(Fan(rpc_fans["FANS"][n]["RPM"]))
fans.append(Fan(speed=rpc_fans["FANS"][n]["RPM"]))
except (IndexError, KeyError):
pass
return fans
@@ -512,7 +514,9 @@ class BOSMiner(BraiinsOSFirmware):
]:
_error = board["Status"].split(" {")[0]
_error = _error[0].lower() + _error[1:]
errors.append(BraiinsOSError(f"Slot {_id} {_error}"))
errors.append(
BraiinsOSError(error_message=f"Slot {_id} {_error}")
)
return errors
except (KeyError, IndexError):
pass
@@ -531,7 +535,7 @@ class BOSMiner(BraiinsOSFirmware):
async def _get_expected_hashrate(
self, rpc_devs: dict = None
) -> Optional[AlgoHashRate]:
) -> Optional[AlgoHashRateType]:
if rpc_devs is None:
try:
rpc_devs = await self.rpc.devs()
@@ -543,16 +547,21 @@ class BOSMiner(BraiinsOSFirmware):
hr_list = []
for board in rpc_devs["DEVS"]:
expected_hashrate = round(float(board["Nominal MHS"] / 1000000), 2)
expected_hashrate = float(board["Nominal MHS"])
if expected_hashrate:
hr_list.append(expected_hashrate)
if len(hr_list) == 0:
return AlgoHashRate.SHA256(0)
else:
return AlgoHashRate.SHA256(
(sum(hr_list) / len(hr_list)) * self.expected_hashboards
return self.algo.hashrate(
rate=float(0), unit=self.algo.unit.default
)
else:
return self.algo.hashrate(
rate=float(
(sum(hr_list) / len(hr_list)) * self.expected_hashboards
),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except (IndexError, KeyError):
pass
@@ -889,8 +898,9 @@ class BOSer(BraiinsOSFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["MHS 1m"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except (KeyError, IndexError, ValueError, TypeError):
pass
@@ -906,9 +916,11 @@ class BOSer(BraiinsOSFirmware):
if grpc_miner_details is not None:
try:
return AlgoHashRate.SHA256(
grpc_miner_details["stickerHashrate"]["gigahashPerSecond"],
HashUnit.SHA256.GH,
return self.algo.hashrate(
rate=float(
grpc_miner_details["stickerHashrate"]["gigahashPerSecond"]
),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except LookupError:
pass
@@ -933,18 +945,20 @@ class BOSer(BraiinsOSFirmware):
if board.get("chipsCount") is not None:
hashboards[idx].chips = board["chipsCount"]
if board.get("boardTemp") is not None:
hashboards[idx].temp = board["boardTemp"]["degreeC"]
hashboards[idx].temp = int(board["boardTemp"]["degreeC"])
if board.get("highestChipTemp") is not None:
hashboards[idx].chip_temp = board["highestChipTemp"]["temperature"][
"degreeC"
]
hashboards[idx].chip_temp = int(
board["highestChipTemp"]["temperature"]["degreeC"]
)
if board.get("stats") is not None:
if not board["stats"]["realHashrate"]["last5S"] == {}:
hashboards[idx].hashrate = AlgoHashRate.SHA256(
board["stats"]["realHashrate"]["last5S"][
"gigahashPerSecond"
],
HashUnit.SHA256.GH,
hashboards[idx].hashrate = self.algo.hashrate(
rate=float(
board["stats"]["realHashrate"]["last5S"][
"gigahashPerSecond"
]
),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
hashboards[idx].missing = False
@@ -993,7 +1007,7 @@ class BOSer(BraiinsOSFirmware):
fans = []
for n in range(self.expected_fans):
try:
fans.append(Fan(grpc_cooling_state["fans"][n]["rpm"]))
fans.append(Fan(speed=grpc_cooling_state["fans"][n]["rpm"]))
except (IndexError, KeyError):
pass
return fans
@@ -1024,7 +1038,9 @@ class BOSer(BraiinsOSFirmware):
]:
_error = board["Status"].split(" {")[0]
_error = _error[0].lower() + _error[1:]
errors.append(BraiinsOSError(f"Slot {_id} {_error}"))
errors.append(
BraiinsOSError(error_message=f"Slot {_id} {_error}")
)
return errors
except LookupError:
pass
@@ -1085,7 +1101,7 @@ class BOSer(BraiinsOSFirmware):
for group in grpc_pool_groups["poolGroups"]:
for idx, pool_info in enumerate(group["pools"]):
pool_data = PoolMetrics(
url=pool_info["url"],
url=PoolUrl.from_str(pool_info["url"]),
user=pool_info["user"],
index=idx,
accepted=pool_info["stats"].get("acceptedShares", 0),

View File

@@ -21,9 +21,10 @@ from typing import List, Optional
import aiofiles
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, WhatsminerError
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -274,7 +275,7 @@ class BTMiner(StockFirmware):
cfg.mining_mode = MiningModeConfig.normal()
return cfg
cfg.mining_mode = MiningModeConfig.power_tuning(power_lim)
cfg.mining_mode = MiningModeConfig.power_tuning(power=power_lim)
self.config = cfg
return self.config
@@ -403,8 +404,9 @@ class BTMiner(StockFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["MHS 1m"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except LookupError:
pass
@@ -433,8 +435,8 @@ class BTMiner(StockFirmware):
self.expected_hashboards += 1
hashboards[board["ASC"]].chip_temp = round(board["Chip Temp Avg"])
hashboards[board["ASC"]].temp = round(board["Temperature"])
hashboards[board["ASC"]].hashrate = AlgoHashRate.SHA256(
board["MHS 1m"], HashUnit.SHA256.MH
hashboards[board["ASC"]].hashrate = self.algo.hashrate(
rate=float(board["MHS 1m"]), unit=self.algo.unit.MH
).into(self.algo.unit.default)
hashboards[board["ASC"]].chips = board["Effective Chips"]
hashboards[board["ASC"]].serial_number = board["PCB SN"]
@@ -498,8 +500,8 @@ class BTMiner(StockFirmware):
try:
if self.expected_fans > 0:
fans = [
Fan(rpc_summary["SUMMARY"][0].get("Fan Speed In", 0)),
Fan(rpc_summary["SUMMARY"][0].get("Fan Speed Out", 0)),
Fan(speed=rpc_summary["SUMMARY"][0].get("Fan Speed In", 0)),
Fan(speed=rpc_summary["SUMMARY"][0].get("Fan Speed Out", 0)),
]
except LookupError:
pass
@@ -583,8 +585,8 @@ class BTMiner(StockFirmware):
try:
expected_hashrate = rpc_summary["SUMMARY"][0]["Factory GHS"]
if expected_hashrate:
return AlgoHashRate.SHA256(
expected_hashrate, HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(expected_hashrate), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except LookupError:

View File

@@ -17,8 +17,8 @@
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, HashUnit
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -123,8 +123,9 @@ class CGMiner(StockFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["GHS 5s"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass

View File

@@ -18,9 +18,10 @@ from pathlib import Path
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.data.pools import PoolMetrics
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
@@ -234,9 +235,9 @@ class ePIC(ePICFirmware):
if web_summary["HBs"] is not None:
for hb in web_summary["HBs"]:
hashrate += hb["Hashrate"][0]
return AlgoHashRate.SHA256(hashrate, HashUnit.SHA256.MH).into(
HashUnit.SHA256.TH
)
return self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.MH
).into(self.algo.unit.TH)
except (LookupError, ValueError, TypeError):
pass
@@ -260,9 +261,9 @@ class ePIC(ePICFirmware):
ideal = hb["Hashrate"][1] / 100
hashrate += hb["Hashrate"][0] / ideal
return AlgoHashRate.SHA256(hashrate, HashUnit.SHA256.MH).into(
self.algo.unit.default
)
return self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.MH
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
@@ -293,7 +294,7 @@ class ePIC(ePICFirmware):
if web_summary is not None:
for fan in web_summary["Fans Rpm"]:
try:
fans.append(Fan(web_summary["Fans Rpm"][fan]))
fans.append(Fan(speed=web_summary["Fans Rpm"][fan]))
except (LookupError, ValueError, TypeError):
fans.append(Fan())
return fans
@@ -352,11 +353,11 @@ class ePIC(ePICFirmware):
hashrate = hb["Hashrate"][0]
# Update the Hashboard object
hb_list[hb["Index"]].missing = False
hb_list[hb["Index"]].hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.MH
hb_list[hb["Index"]].hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.MH
).into(self.algo.unit.default)
hb_list[hb["Index"]].chips = num_of_chips
hb_list[hb["Index"]].temp = hb["Temperature"]
hb_list[hb["Index"]].temp = int(hb["Temperature"])
hb_list[hb["Index"]].tuned = tuned
hb_list[hb["Index"]].active = active
hb_list[hb["Index"]].voltage = hb["Input Voltage"]
@@ -417,7 +418,7 @@ class ePIC(ePICFirmware):
try:
error = web_summary["Status"]["Last Error"]
if error is not None:
errors.append(X19Error(str(error)))
errors.append(X19Error(error_message=str(error)))
return errors
except KeyError:
pass
@@ -437,6 +438,10 @@ class ePIC(ePICFirmware):
web_summary.get("Session") is not None
and web_summary.get("Stratum") is not None
):
url = web_summary["Stratum"].get("Current Pool")
# TODO: when scheme gets put in, update this
if url is not None:
url = PoolUrl.from_str(f"stratum+tcp://{url}")
pool_data.append(
PoolMetrics(
accepted=web_summary["Session"].get("Accepted"),
@@ -445,7 +450,7 @@ class ePIC(ePICFirmware):
remote_failures=0,
active=web_summary["Stratum"].get("IsPoolConnected"),
alive=web_summary["Stratum"].get("IsPoolConnected"),
url=web_summary["Stratum"].get("Current Pool"),
url=url,
user=web_summary["Stratum"].get("Current User"),
index=web_summary["Stratum"].get("Config Id"),
)

View File

@@ -16,7 +16,7 @@
from typing import List
from pyasic.config import MinerConfig, MiningModeConfig
from pyasic.data import AlgoHashRate, HashBoard, HashUnit
from pyasic.data import HashBoard
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.backends import BFGMiner
@@ -162,8 +162,8 @@ class GoldshellMiner(BFGMiner):
if board.get("ID") is not None:
try:
b_id = board["ID"]
hashboards[b_id].hashrate = AlgoHashRate.SHA256(
board["MHS 20s"], HashUnit.SHA256.MH
hashboards[b_id].hashrate = self.algo.hashrate(
rate=float(board["MHS 20s"]), unit=self.algo.unit.MH
).into(self.algo.unit.default)
hashboards[b_id].temp = board["tstemp-2"]
hashboards[b_id].missing = False

View File

@@ -0,0 +1,474 @@
# ------------------------------------------------------------------------------
# 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 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.errors import APIError
from pyasic.miners.data import (
DataFunction,
DataLocations,
DataOptions,
RPCAPICommand,
WebAPICommand,
)
from pyasic.miners.device.firmware import StockFirmware
from pyasic.rpc.ccminer import CCMinerRPCAPI
from pyasic.web.hammer import HammerWebAPI
HAMMER_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",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.HOSTNAME): DataFunction(
"_get_hostname",
[WebAPICommand("web_get_system_info", "get_system_info")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_summary", "summary")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_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_conf", "get_miner_conf")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
class BlackMiner(StockFirmware):
"""Handler for Hammer miners."""
_rpc_cls = CCMinerRPCAPI
rpc: CCMinerRPCAPI
_web_cls = HammerWebAPI
web: HammerWebAPI
data_locations = HAMMER_DATA_LOC
async def get_config(self) -> MinerConfig:
data = await self.web.get_miner_conf()
if data:
self.config = MinerConfig.from_hammer(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_hammer(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, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None:
try:
rpc_version = await self.rpc.version()
except APIError:
pass
if rpc_version is not None:
try:
self.api_ver = rpc_version["VERSION"][0]["API"]
except LookupError:
pass
return self.api_ver
async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None:
try:
rpc_version = await self.rpc.version()
except APIError:
pass
if rpc_version is not None:
try:
self.fw_ver = rpc_version["VERSION"][0]["CompileTime"]
except LookupError:
pass
return self.fw_ver
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
# get hr from API
if rpc_summary is None:
try:
rpc_summary = await self.rpc.summary()
except APIError:
pass
if rpc_summary is not None:
try:
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["GHS 5s"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = []
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
board_offset = -1
boards = rpc_stats["STATS"]
if len(boards) > 1:
for board_num in range(1, 16, 5):
for _b_num in range(5):
b = boards[1].get(f"chain_acn{board_num + _b_num}")
if b and not b == 0 and board_offset == -1:
board_offset = board_num
if board_offset == -1:
board_offset = 1
real_slots = []
for i in range(board_offset, board_offset + 4):
try:
key = f"chain_acs{i}"
if boards[1].get(key, "") != "":
real_slots.append(i)
except LookupError:
pass
if len(real_slots) < 3:
real_slots = list(
range(board_offset, board_offset + self.expected_hashboards)
)
for i in real_slots:
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
)
chip_temp = boards[1].get(f"temp{i}")
if chip_temp:
hashboard.chip_temp = round(chip_temp)
temp = boards[1].get(f"temp2_{i}")
if temp:
hashboard.temp = round(temp)
hashrate = boards[1].get(f"chain_rate{i}")
if hashrate:
hashboard.hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.GH
).into(self.algo.unit.default)
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
except (LookupError, ValueError, TypeError):
pass
return hashboards
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
fans = [Fan() for _ in range(self.expected_fans)]
if rpc_stats is not None:
try:
fan_offset = -1
for fan_num in range(1, 8, 4):
for _f_num in range(4):
f = rpc_stats["STATS"][1].get(f"fan{fan_num + _f_num}", 0)
if f and not f == 0 and fan_offset == -1:
fan_offset = fan_num
if fan_offset == -1:
fan_offset = 1
for fan in range(self.expected_fans):
fans[fan].speed = rpc_stats["STATS"][1].get(
f"fan{fan_offset+fan}", 0
)
except LookupError:
pass
return fans
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_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, rpc_stats: dict = None
) -> Optional[AlgoHashRate]:
if rpc_stats is None:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats is not None:
try:
expected_rate = rpc_stats["STATS"][1]["total_rateideal"]
try:
rate_unit = rpc_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
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 set_static_ip(
self,
ip: str,
dns: str,
gateway: str,
subnet_mask: str = "255.255.255.0",
hostname: str = None,
):
if not hostname:
hostname = await self.get_hostname()
await self.web.set_network_conf(
ip=ip,
dns=dns,
gateway=gateway,
subnet_mask=subnet_mask,
hostname=hostname,
protocol=2,
)
async def set_dhcp(self, hostname: str = None):
if not hostname:
hostname = await self.get_hostname()
await self.web.set_network_conf(
ip="", dns="", gateway="", subnet_mask="", hostname=hostname, protocol=1
)
async def set_hostname(self, hostname: str):
cfg = await self.web.get_network_info()
dns = cfg["conf_dnsservers"]
gateway = cfg["conf_gateway"]
ip = cfg["conf_ipaddress"]
subnet_mask = cfg["conf_netmask"]
protocol = 1 if cfg["conf_nettype"] == "DHCP" else 2
await self.web.set_network_conf(
ip=ip,
dns=dns,
gateway=gateway,
subnet_mask=subnet_mask,
hostname=hostname,
protocol=protocol,
)
async def _is_mining(self, web_get_conf: dict = None) -> Optional[bool]:
if web_get_conf is None:
try:
web_get_conf = await self.web.get_miner_conf()
except APIError:
pass
if web_get_conf is not None:
try:
if str(web_get_conf["bitmain-work-mode"]).isdigit():
return (
False if int(web_get_conf["bitmain-work-mode"]) == 1 else True
)
return False
except LookupError:
pass
async def _get_uptime(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 int(rpc_stats["STATS"][1]["Elapsed"])
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None:
try:
rpc_pools = await self.rpc.pools()
except APIError:
pass
pools_data = []
if rpc_pools is not None:
try:
pools = rpc_pools.get("POOLS", [])
for pool_info in pools:
url = pool_info.get("URL")
pool_url = PoolUrl.from_str(url) if url else None
pool_data = PoolMetrics(
accepted=pool_info.get("Accepted"),
rejected=pool_info.get("Rejected"),
get_failures=pool_info.get("Get Failures"),
remote_failures=pool_info.get("Remote Failures"),
active=pool_info.get("Stratum Active"),
alive=pool_info.get("Status") == "Alive",
url=pool_url,
user=pool_info.get("User"),
index=pool_info.get("POOL"),
)
pools_data.append(pool_data)
except LookupError:
pass
return pools_data

View File

@@ -1,9 +1,9 @@
from typing import List, Optional
from pyasic import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device import MinerAlgo
from pyasic.device.algorithm import AlgoHashRate, MinerAlgo
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.miners.device.firmware import StockFirmware
@@ -87,7 +87,9 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
return [Fan(spd) for spd in web_userpanel["userpanel"]["data"]["fans"]]
return [
Fan(speed=spd) for spd in web_userpanel["userpanel"]["data"]["fans"]
]
except (LookupError, ValueError, TypeError):
pass
@@ -129,8 +131,8 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
base_unit = web_userpanel["userpanel"]["data"]["unit"]
return AlgoHashRate.SHA256(
float(
return self.algo.hashrate(
rate=float(
web_userpanel["userpanel"]["data"]["rtpow"].replace(
base_unit, ""
)
@@ -185,8 +187,9 @@ class IceRiver(StockFirmware):
idx = int(board["no"] - 1)
hb_list[idx].chip_temp = round(board["outtmp"])
hb_list[idx].temp = round(board["intmp"])
hb_list[idx].hashrate = AlgoHashRate.SHA256(
float(board["rtpow"].replace("G", "")), HashUnit.SHA256.GH
hb_list[idx].hashrate = self.algo.hashrate(
rate=float(board["rtpow"].replace("G", "")),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
hb_list[idx].chips = int(board["chipnum"])
hb_list[idx].missing = False

View File

@@ -16,10 +16,11 @@
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.error_codes.innosilicon import InnosiliconError
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.data import (
@@ -186,20 +187,23 @@ class Innosilicon(CGMiner):
if web_get_all is not None:
try:
if "Hash Rate H" in web_get_all["total_hash"].keys():
return AlgoHashRate.SHA256(
web_get_all["total_hash"]["Hash Rate H"], HashUnit.SHA256.H
return self.algo.hashrate(
rate=float(web_get_all["total_hash"]["Hash Rate H"]),
unit=self.algo.unit.H,
).into(self.algo.unit.default)
elif "Hash Rate" in web_get_all["total_hash"].keys():
return AlgoHashRate.SHA256(
web_get_all["total_hash"]["Hash Rate"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(web_get_all["total_hash"]["Hash Rate"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except KeyError:
pass
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["MHS 1m"], HashUnit.SHA256.MH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["MHS 1m"]),
unit=self.algo.unit.MH,
).into(self.algo.unit.default)
except (KeyError, IndexError):
pass
@@ -252,8 +256,8 @@ class Innosilicon(CGMiner):
hashrate = board.get("Hash Rate H")
if hashrate:
hashboards[idx].hashrate = AlgoHashRate.SHA256(
hashrate, HashUnit.SHA256.H
hashboards[idx].hashrate = self.algo.hashrate(
rate=float(hashrate), unit=self.algo.unit.H
).into(self.algo.unit.default)
chip_temp = board.get("Temp max")

View File

@@ -17,8 +17,9 @@ import logging
from typing import List, Optional
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import LuxOSFirmware
@@ -75,6 +76,8 @@ class LUXMiner(LuxOSFirmware):
_rpc_cls = LUXMinerRPCAPI
rpc: LUXMinerRPCAPI
supports_shutdown = True
data_locations = LUXMINER_DATA_LOC
async def fault_light_on(self) -> bool:
@@ -178,15 +181,16 @@ class LUXMiner(LuxOSFirmware):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["GHS 5s"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = [
HashBoard(idx, expected_chips=self.expected_chips)
HashBoard(slot=idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
@@ -201,8 +205,9 @@ class LUXMiner(LuxOSFirmware):
board_stats = rpc_stats["STATS"][1]
for idx in range(3):
board_n = idx + 1
hashboards[idx].hashrate = AlgoHashRate.SHA256(
float(board_stats[f"chain_rate{board_n}"]), HashUnit.SHA256.GH
hashboards[idx].hashrate = self.algo.hashrate(
rate=float(board_stats[f"chain_rate{board_n}"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
hashboards[idx].chips = int(board_stats[f"chain_acn{board_n}"])
chip_temp_data = list(
@@ -253,7 +258,7 @@ class LUXMiner(LuxOSFirmware):
if rpc_fans is not None:
for fan in range(self.expected_fans):
try:
fans.append(Fan(rpc_fans["FANS"][fan]["RPM"]))
fans.append(Fan(speed=rpc_fans["FANS"][fan]["RPM"]))
except (LookupError, ValueError, TypeError):
fans.append(Fan())
return fans
@@ -274,8 +279,8 @@ class LUXMiner(LuxOSFirmware):
rate_unit = rpc_stats["STATS"][1]["rate_unit"]
except KeyError:
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
return self.algo.hashrate(
rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -2,8 +2,9 @@ from typing import List, Optional
from pyasic import MinerConfig
from pyasic.config import MiningModeConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data import Fan, HashBoard
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
from pyasic.miners.device.firmware import MaraFirmware
@@ -99,7 +100,7 @@ class MaraMiner(MaraFirmware):
async def set_power_limit(self, wattage: int) -> bool:
cfg = await self.get_config()
cfg.mining_mode = MiningModeConfig.power_tuning(wattage)
cfg.mining_mode = MiningModeConfig.power_tuning(power=wattage)
await self.send_config(cfg)
return True
@@ -178,14 +179,14 @@ class MaraMiner(MaraFirmware):
try:
for hb in web_hashboards["hashboards"]:
idx = hb["index"]
hashboards[idx].hashrate = AlgoHashRate.SHA256(
hb["hashrate_average"], HashUnit.SHA256.GH
hashboards[idx].hashrate = self.algo.hashrate(
rate=float(hb["hashrate_average"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
hashboards[idx].temp = round(
sum(hb["temperature_pcb"]) / len(hb["temperature_pcb"]), 2
sum(hb["temperature_pcb"]) / len(hb["temperature_pcb"])
)
hashboards[idx].chip_temp = round(
sum(hb["temperature_chip"]) / len(hb["temperature_chip"]), 2
sum(hb["temperature_chip"]) / len(hb["temperature_chip"])
)
hashboards[idx].chips = hb["asic_num"]
hashboards[idx].serial_number = hb["serial_number"]
@@ -242,8 +243,8 @@ class MaraMiner(MaraFirmware):
if web_brief is not None:
try:
return AlgoHashRate.SHA256(
web_brief["hashrate_realtime"], HashUnit.SHA256.TH
return self.algo.hashrate(
rate=float(web_brief["hashrate_realtime"]), unit=self.algo.unit.TH
).into(self.algo.unit.default)
except LookupError:
pass
@@ -259,7 +260,7 @@ class MaraMiner(MaraFirmware):
fans = []
for n in range(self.expected_fans):
try:
fans.append(Fan(web_fans["fans"][n]["current_speed"]))
fans.append(Fan(speed=web_fans["fans"][n]["current_speed"]))
except (IndexError, KeyError):
pass
return fans
@@ -290,8 +291,8 @@ class MaraMiner(MaraFirmware):
if web_brief is not None:
try:
return AlgoHashRate.SHA256(
web_brief["hashrate_ideal"], HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(web_brief["hashrate_ideal"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -15,8 +15,9 @@
from typing import List, Optional, Tuple
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard
from pyasic.data import Fan, HashBoard
from pyasic.data.error_codes import MinerErrorData
from pyasic.device.algorithm import AlgoHashRate
from pyasic.miners.base import BaseMiner
from pyasic.rpc.unknown import UnknownRPCAPI

View File

@@ -17,7 +17,7 @@
from typing import Optional
from pyasic import MinerConfig
from pyasic.data import AlgoHashRate, HashUnit
from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.data import (
@@ -80,6 +80,10 @@ VNISH_DATA_LOC = DataLocations(
"_is_mining",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
@@ -203,8 +207,9 @@ class VNish(VNishFirmware, BMMiner):
if rpc_summary is not None:
try:
return AlgoHashRate.SHA256(
rpc_summary["SUMMARY"][0]["GHS 5s"], HashUnit.SHA256.GH
return self.algo.hashrate(
rate=float(rpc_summary["SUMMARY"][0]["GHS 5s"]),
unit=self.algo.unit.GH,
).into(self.algo.unit.default)
except (LookupError, ValueError, TypeError):
pass

View File

@@ -19,14 +19,15 @@ import warnings
from typing import List, Optional, Protocol, Tuple, Type, TypeVar, Union
from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, MinerData
from pyasic.data import Fan, HashBoard, MinerData
from pyasic.data.device import DeviceInfo
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.pools import PoolMetrics
from pyasic.device import MinerModel
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.algorithm import MinerAlgoType
from pyasic.device.algorithm.hashrate import AlgoHashRate
from pyasic.device.firmware import MinerFirmware
from pyasic.device.makes import MinerMake
from pyasic.device.models import MinerModelType
from pyasic.errors import APIError
from pyasic.logger import logger
from pyasic.miners.data import DataLocations, DataOptions, RPCAPICommand, WebAPICommand
@@ -43,13 +44,13 @@ class MinerProtocol(Protocol):
ssh: _ssh_cls = None
make: MinerMake = None
raw_model: MinerModel = None
raw_model: MinerModelType = None
firmware: MinerFirmware = None
algo = MinerAlgo.SHA256
algo: type[MinerAlgoType] = None
expected_hashboards: int = 3
expected_hashboards: int = None
expected_chips: int = None
expected_fans: int = 2
expected_fans: int = None
data_locations: DataLocations = None
@@ -528,7 +529,11 @@ class MinerProtocol(Protocol):
expected_fans=self.expected_fans,
hashboards=[
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
for i in range(
self.expected_hashboards
if self.expected_hashboards is not None
else 0
)
],
)

View File

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

View File

@@ -1,3 +1,4 @@
from .BM1366 import BitAxeUltra
from .BM1368 import BitAxeSupra
from .BM1370 import BitAxeGamma
from .BM1397 import BitAxeMax

View File

@@ -52,3 +52,7 @@ class BitAxeMake(BaseMiner):
class IceRiverMake(BaseMiner):
make = MinerMake.ICERIVER
class HammerMake(BaseMiner):
make = MinerMake.HAMMER

View File

@@ -19,6 +19,7 @@ from .auradine import *
from .avalonminer import *
from .epic import *
from .goldshell import *
from .hammer import *
from .iceriver import *
from .innosilicon import *
from .whatsminer import *

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -21,9 +22,15 @@ class Z15(AntMinerMake):
raw_model = MinerModel.ANTMINER.Z15
expected_chips = 3
expected_hashboards = 3
expected_fans = 2
algo = MinerAlgo.EQUIHASH
class Z15Pro(AntMinerMake):
raw_model = MinerModel.ANTMINER.Z15Pro
expected_chips = 6
expected_hashboards = 3
expected_fans = 2
algo = MinerAlgo.EQUIHASH

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -22,6 +23,8 @@ class S17(AntMinerMake):
expected_chips = 48
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S17Plus(AntMinerMake):
@@ -29,6 +32,8 @@ class S17Plus(AntMinerMake):
expected_chips = 65
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S17Pro(AntMinerMake):
@@ -36,6 +41,8 @@ class S17Pro(AntMinerMake):
expected_chips = 48
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S17e(AntMinerMake):
@@ -43,3 +50,5 @@ class S17e(AntMinerMake):
expected_chips = 135
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -22,6 +23,8 @@ class T17(AntMinerMake):
expected_chips = 30
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class T17Plus(AntMinerMake):
@@ -29,6 +32,8 @@ class T17Plus(AntMinerMake):
expected_chips = 44
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class T17e(AntMinerMake):
@@ -36,3 +41,5 @@ class T17e(AntMinerMake):
expected_chips = 78
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -22,6 +23,8 @@ class S19(AntMinerMake):
expected_chips = 76
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19NoPIC(AntMinerMake):
@@ -29,6 +32,8 @@ class S19NoPIC(AntMinerMake):
expected_chips = 88
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19Pro(AntMinerMake):
@@ -36,6 +41,8 @@ class S19Pro(AntMinerMake):
expected_chips = 114
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19i(AntMinerMake):
@@ -43,6 +50,8 @@ class S19i(AntMinerMake):
expected_chips = 80
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19Plus(AntMinerMake):
@@ -50,6 +59,8 @@ class S19Plus(AntMinerMake):
expected_chips = 80
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19ProPlus(AntMinerMake):
@@ -57,6 +68,8 @@ class S19ProPlus(AntMinerMake):
expected_chips = 120
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19XP(AntMinerMake):
@@ -64,6 +77,8 @@ class S19XP(AntMinerMake):
expected_chips = 110
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19a(AntMinerMake):
@@ -71,6 +86,8 @@ class S19a(AntMinerMake):
expected_chips = 72
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19aPro(AntMinerMake):
@@ -78,6 +95,8 @@ class S19aPro(AntMinerMake):
expected_chips = 100
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19j(AntMinerMake):
@@ -85,6 +104,8 @@ class S19j(AntMinerMake):
expected_chips = 114
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jNoPIC(AntMinerMake):
@@ -92,6 +113,8 @@ class S19jNoPIC(AntMinerMake):
expected_chips = 88
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jPro(AntMinerMake):
@@ -99,6 +122,8 @@ class S19jPro(AntMinerMake):
expected_chips = 126
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jProNoPIC(AntMinerMake):
@@ -106,6 +131,8 @@ class S19jProNoPIC(AntMinerMake):
expected_chips = 126
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jProPlus(AntMinerMake):
@@ -113,6 +140,8 @@ class S19jProPlus(AntMinerMake):
expected_chips = 120
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jProPlusNoPIC(AntMinerMake):
@@ -120,6 +149,8 @@ class S19jProPlusNoPIC(AntMinerMake):
expected_chips = 120
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19kPro(AntMinerMake):
@@ -127,6 +158,8 @@ class S19kPro(AntMinerMake):
expected_chips = 77
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19kProNoPIC(AntMinerMake):
@@ -134,6 +167,8 @@ class S19kProNoPIC(AntMinerMake):
expected_chips = 77
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19L(AntMinerMake):
@@ -141,6 +176,8 @@ class S19L(AntMinerMake):
expected_chips = 76
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19Hydro(AntMinerMake):
@@ -149,6 +186,7 @@ class S19Hydro(AntMinerMake):
expected_chips = 104
expected_hashboards = 4
expected_fans = 0
algo = MinerAlgo.SHA256
class S19ProHydro(AntMinerMake):
@@ -157,6 +195,7 @@ class S19ProHydro(AntMinerMake):
expected_chips = 180
expected_hashboards = 4
expected_fans = 0
algo = MinerAlgo.SHA256
class S19ProPlusHydro(AntMinerMake):
@@ -165,6 +204,7 @@ class S19ProPlusHydro(AntMinerMake):
expected_chips = 180
expected_hashboards = 4
expected_fans = 0
algo = MinerAlgo.SHA256
class S19KPro(AntMinerMake):
@@ -172,3 +212,5 @@ class S19KPro(AntMinerMake):
expected_chips = 77
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -22,3 +23,5 @@ class T19(AntMinerMake):
expected_chips = 76
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -22,6 +23,8 @@ class S21(AntMinerMake):
expected_chips = 108
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S21Pro(AntMinerMake):
@@ -29,3 +32,5 @@ class S21Pro(AntMinerMake):
expected_chips = 65
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -22,3 +23,5 @@ class T21(AntMinerMake):
expected_chips = 108
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -21,3 +22,6 @@ class D3(AntMinerMake):
raw_model = MinerModel.ANTMINER.D3
expected_chips = 60
expected_hashboards = 3
expected_fans = 4
algo = MinerAlgo.X11

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -21,3 +22,6 @@ class HS3(AntMinerMake):
raw_model = MinerModel.ANTMINER.HS3
expected_chips = 92
expected_hashboards = 3
expected_fans = 4
algo = MinerAlgo.HANDSHAKE

View File

@@ -13,6 +13,7 @@
# 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 AntMinerMake
@@ -21,3 +22,6 @@ class KA3(AntMinerMake):
raw_model = MinerModel.ANTMINER.KA3
expected_chips = 92
expected_hashboards = 3
expected_fans = 4
algo = MinerAlgo.KADENA

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