Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0651e26b8 | ||
|
|
c00802e311 | ||
|
|
d66739e2c9 | ||
|
|
65ed565220 | ||
|
|
db6499800b | ||
|
|
cc97ceee61 | ||
|
|
3fa1cb18d9 | ||
|
|
cb3c50d007 | ||
|
|
2523ef8484 | ||
|
|
01342738b0 | ||
|
|
a9dee4a911 | ||
|
|
883ffe20b4 | ||
|
|
261527a380 | ||
|
|
924b62e0d5 | ||
|
|
76a870c2ed | ||
|
|
309356243b | ||
|
|
e9b4cc9bd6 | ||
|
|
648c54de93 | ||
|
|
e1ce96ab1b |
173
poetry.lock
generated
173
poetry.lock
generated
@@ -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"
|
||||
|
||||
@@ -13,25 +13,25 @@
|
||||
# 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)
|
||||
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 +41,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 +107,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 +156,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 +279,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)
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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,6 @@ class FanModeConfig(MinerConfigOption):
|
||||
except LookupError:
|
||||
pass
|
||||
return cls.default()
|
||||
|
||||
|
||||
FanMode = TypeVar("FanMode", bound=Union[*[v.value for v in FanModeConfig]])
|
||||
|
||||
@@ -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,11 @@ class MiningModeHPM(MinerConfigValue):
|
||||
return {"mode": {"mode": "turbo"}}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MiningModePowerTune(MinerConfigValue):
|
||||
mode: str = field(init=False, default="power_tuning")
|
||||
power: int = None
|
||||
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
|
||||
scaling: ScalingConfig = None
|
||||
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 +243,10 @@ class MiningModePowerTune(MinerConfigValue):
|
||||
return {"autotunerset": {"enabled": True}}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MiningModeHashrateTune(MinerConfigValue):
|
||||
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 +338,6 @@ class MiningModeHashrateTune(MinerConfigValue):
|
||||
return {"autotunerset": {"enabled": True}}
|
||||
|
||||
|
||||
@dataclass
|
||||
class ManualBoardSettings(MinerConfigValue):
|
||||
freq: float
|
||||
volt: float
|
||||
@@ -358,7 +352,6 @@ class ManualBoardSettings(MinerConfigValue):
|
||||
return {"miner-mode": 0}
|
||||
|
||||
|
||||
@dataclass
|
||||
class MiningModeManual(MinerConfigValue):
|
||||
mode: str = field(init=False, default="manual")
|
||||
|
||||
@@ -521,7 +514,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 +523,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 +532,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 +549,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 +564,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 +574,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 +585,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 +610,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 +640,6 @@ class MiningModeConfig(MinerConfigOption):
|
||||
except LookupError:
|
||||
pass
|
||||
return cls.default()
|
||||
|
||||
|
||||
MiningMode = TypeVar("MiningMode", bound=Union[*[v.value for v in MiningModeConfig]])
|
||||
|
||||
@@ -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,6 @@ 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[*[v.value for v in TunerAlgo]])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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, 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,30 +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.value if isinstance(v, Scheme) else 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:
|
||||
@@ -189,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:
|
||||
@@ -221,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:
|
||||
@@ -258,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:
|
||||
@@ -293,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:
|
||||
@@ -306,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:
|
||||
@@ -322,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:
|
||||
@@ -335,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.
|
||||
@@ -411,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.
|
||||
@@ -440,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}")
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
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 +39,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: int | None = None
|
||||
chip_temp: int | 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:
|
||||
|
||||
@@ -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: 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
18
pyasic/data/error_codes/base.py
Normal file
18
pyasic/data/error_codes/base.py
Normal 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()
|
||||
@@ -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)
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
)
|
||||
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
9
pyasic/device/algorithm/base.py
Normal file
9
pyasic/device/algorithm/base.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .hashrate.base import AlgoHashRateType
|
||||
from .hashrate.unit.base import AlgoHashRateUnitType
|
||||
|
||||
|
||||
class MinerAlgoType(str):
|
||||
hashrate: type[AlgoHashRateType]
|
||||
unit: type[AlgoHashRateUnitType]
|
||||
17
pyasic/device/algorithm/blake256.py
Normal file
17
pyasic/device/algorithm/blake256.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = Blake256HashRate
|
||||
unit = Blake256Unit
|
||||
|
||||
def __repr__(self):
|
||||
return "Blake256Algo"
|
||||
|
||||
|
||||
Blake256Algo = _Blake256Algo("Blake256")
|
||||
17
pyasic/device/algorithm/eaglesong.py
Normal file
17
pyasic/device/algorithm/eaglesong.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = EaglesongHashRate
|
||||
unit = EaglesongUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "EaglesongAlgo"
|
||||
|
||||
|
||||
EaglesongAlgo = _EaglesongAlgo("Eaglesong")
|
||||
17
pyasic/device/algorithm/equihash.py
Normal file
17
pyasic/device/algorithm/equihash.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = EquihashHashRate
|
||||
unit = EquihashUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "EquihashAlgo"
|
||||
|
||||
|
||||
EquihashAlgo = _EquihashAlgo("Equihash")
|
||||
17
pyasic/device/algorithm/ethash.py
Normal file
17
pyasic/device/algorithm/ethash.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import MinerAlgoType
|
||||
from .hashrate import EtHashHashRate
|
||||
from .hashrate.unit import EtHashUnit
|
||||
|
||||
|
||||
# make this json serializable
|
||||
class _EtHashAlgo(MinerAlgoType):
|
||||
hashrate = EtHashHashRate
|
||||
unit = EtHashUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "EtHashAlgo"
|
||||
|
||||
|
||||
EtHashAlgo = _EtHashAlgo("EtHash")
|
||||
17
pyasic/device/algorithm/handshake.py
Normal file
17
pyasic/device/algorithm/handshake.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = HandshakeHashRate
|
||||
unit = HandshakeUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "HandshakeAlgo"
|
||||
|
||||
|
||||
HandshakeAlgo = _HandshakeAlgo("Handshake")
|
||||
24
pyasic/device/algorithm/hashrate/__init__.py
Normal file
24
pyasic/device/algorithm/hashrate/__init__.py
Normal 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
|
||||
26
pyasic/device/algorithm/hashrate/base.py
Normal file
26
pyasic/device/algorithm/hashrate/base.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
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)
|
||||
51
pyasic/device/algorithm/hashrate/blake256.py
Normal file
51
pyasic/device/algorithm/hashrate/blake256.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: Blake256HashRate | int | float) -> Blake256HashRate:
|
||||
if isinstance(other, Blake256HashRate):
|
||||
return Blake256HashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return Blake256HashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: Blake256HashRate | int | float) -> Blake256HashRate:
|
||||
if isinstance(other, Blake256HashRate):
|
||||
return Blake256HashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return Blake256HashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: Blake256HashRate | int | float):
|
||||
if isinstance(other, Blake256HashRate):
|
||||
return Blake256HashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return Blake256HashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: Blake256HashRate | int | float):
|
||||
if isinstance(other, Blake256HashRate):
|
||||
return Blake256HashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return Blake256HashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: Blake256HashRate | int | float):
|
||||
if isinstance(other, Blake256HashRate):
|
||||
return Blake256HashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return Blake256HashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: Blake256Unit) -> Blake256HashRate:
|
||||
return Blake256HashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/eaglesong.py
Normal file
51
pyasic/device/algorithm/hashrate/eaglesong.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: EaglesongHashRate | int | float) -> EaglesongHashRate:
|
||||
if isinstance(other, EaglesongHashRate):
|
||||
return EaglesongHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EaglesongHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: EaglesongHashRate | int | float) -> EaglesongHashRate:
|
||||
if isinstance(other, EaglesongHashRate):
|
||||
return EaglesongHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EaglesongHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: EaglesongHashRate | int | float):
|
||||
if isinstance(other, EaglesongHashRate):
|
||||
return EaglesongHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EaglesongHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: EaglesongHashRate | int | float):
|
||||
if isinstance(other, EaglesongHashRate):
|
||||
return EaglesongHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EaglesongHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: EaglesongHashRate | int | float):
|
||||
if isinstance(other, EaglesongHashRate):
|
||||
return EaglesongHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EaglesongHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: EaglesongUnit) -> EaglesongHashRate:
|
||||
return EaglesongHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/equihash.py
Normal file
51
pyasic/device/algorithm/hashrate/equihash.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: EquihashHashRate | int | float) -> EquihashHashRate:
|
||||
if isinstance(other, EquihashHashRate):
|
||||
return EquihashHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EquihashHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: EquihashHashRate | int | float) -> EquihashHashRate:
|
||||
if isinstance(other, EquihashHashRate):
|
||||
return EquihashHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EquihashHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: EquihashHashRate | int | float):
|
||||
if isinstance(other, EquihashHashRate):
|
||||
return EquihashHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EquihashHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: EquihashHashRate | int | float):
|
||||
if isinstance(other, EquihashHashRate):
|
||||
return EquihashHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EquihashHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: EquihashHashRate | int | float):
|
||||
if isinstance(other, EquihashHashRate):
|
||||
return EquihashHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EquihashHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: EquihashUnit) -> EquihashHashRate:
|
||||
return EquihashHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/ethash.py
Normal file
51
pyasic/device/algorithm/hashrate/ethash.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: EtHashHashRate | int | float) -> EtHashHashRate:
|
||||
if isinstance(other, EtHashHashRate):
|
||||
return EtHashHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EtHashHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: EtHashHashRate | int | float) -> EtHashHashRate:
|
||||
if isinstance(other, EtHashHashRate):
|
||||
return EtHashHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EtHashHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: EtHashHashRate | int | float):
|
||||
if isinstance(other, EtHashHashRate):
|
||||
return EtHashHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EtHashHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: EtHashHashRate | int | float):
|
||||
if isinstance(other, EtHashHashRate):
|
||||
return EtHashHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EtHashHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: EtHashHashRate | int | float):
|
||||
if isinstance(other, EtHashHashRate):
|
||||
return EtHashHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return EtHashHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: EtHashUnit) -> EtHashHashRate:
|
||||
return EtHashHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/handshake.py
Normal file
51
pyasic/device/algorithm/hashrate/handshake.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: HandshakeHashRate | int | float) -> HandshakeHashRate:
|
||||
if isinstance(other, HandshakeHashRate):
|
||||
return HandshakeHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return HandshakeHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: HandshakeHashRate | int | float) -> HandshakeHashRate:
|
||||
if isinstance(other, HandshakeHashRate):
|
||||
return HandshakeHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return HandshakeHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: HandshakeHashRate | int | float):
|
||||
if isinstance(other, HandshakeHashRate):
|
||||
return HandshakeHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return HandshakeHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: HandshakeHashRate | int | float):
|
||||
if isinstance(other, HandshakeHashRate):
|
||||
return HandshakeHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return HandshakeHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: HandshakeHashRate | int | float):
|
||||
if isinstance(other, HandshakeHashRate):
|
||||
return HandshakeHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return HandshakeHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: HandshakeUnit) -> HandshakeHashRate:
|
||||
return HandshakeHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/kadena.py
Normal file
51
pyasic/device/algorithm/hashrate/kadena.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: KadenaHashRate | int | float) -> KadenaHashRate:
|
||||
if isinstance(other, KadenaHashRate):
|
||||
return KadenaHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KadenaHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: KadenaHashRate | int | float) -> KadenaHashRate:
|
||||
if isinstance(other, KadenaHashRate):
|
||||
return KadenaHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KadenaHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: KadenaHashRate | int | float):
|
||||
if isinstance(other, KadenaHashRate):
|
||||
return KadenaHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KadenaHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: KadenaHashRate | int | float):
|
||||
if isinstance(other, KadenaHashRate):
|
||||
return KadenaHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KadenaHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: KadenaHashRate | int | float):
|
||||
if isinstance(other, KadenaHashRate):
|
||||
return KadenaHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KadenaHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: KadenaUnit) -> KadenaHashRate:
|
||||
return KadenaHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/kheavyhash.py
Normal file
51
pyasic/device/algorithm/hashrate/kheavyhash.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: KHeavyHashHashRate | int | float) -> KHeavyHashHashRate:
|
||||
if isinstance(other, KHeavyHashHashRate):
|
||||
return KHeavyHashHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KHeavyHashHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: KHeavyHashHashRate | int | float) -> KHeavyHashHashRate:
|
||||
if isinstance(other, KHeavyHashHashRate):
|
||||
return KHeavyHashHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KHeavyHashHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: KHeavyHashHashRate | int | float):
|
||||
if isinstance(other, KHeavyHashHashRate):
|
||||
return KHeavyHashHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KHeavyHashHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: KHeavyHashHashRate | int | float):
|
||||
if isinstance(other, KHeavyHashHashRate):
|
||||
return KHeavyHashHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KHeavyHashHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: KHeavyHashHashRate | int | float):
|
||||
if isinstance(other, KHeavyHashHashRate):
|
||||
return KHeavyHashHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return KHeavyHashHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: KHeavyHashUnit) -> KHeavyHashHashRate:
|
||||
return KHeavyHashHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/scrypt.py
Normal file
51
pyasic/device/algorithm/hashrate/scrypt.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: ScryptHashRate | int | float) -> ScryptHashRate:
|
||||
if isinstance(other, ScryptHashRate):
|
||||
return ScryptHashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return ScryptHashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: ScryptHashRate | int | float) -> ScryptHashRate:
|
||||
if isinstance(other, ScryptHashRate):
|
||||
return ScryptHashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return ScryptHashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: ScryptHashRate | int | float):
|
||||
if isinstance(other, ScryptHashRate):
|
||||
return ScryptHashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return ScryptHashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: ScryptHashRate | int | float):
|
||||
if isinstance(other, ScryptHashRate):
|
||||
return ScryptHashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return ScryptHashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: ScryptHashRate | int | float):
|
||||
if isinstance(other, ScryptHashRate):
|
||||
return ScryptHashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return ScryptHashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: ScryptUnit) -> ScryptHashRate:
|
||||
return ScryptHashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
51
pyasic/device/algorithm/hashrate/sha256.py
Normal file
51
pyasic/device/algorithm/hashrate/sha256.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: SHA256HashRate | int | float) -> SHA256HashRate:
|
||||
if isinstance(other, SHA256HashRate):
|
||||
return SHA256HashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return SHA256HashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: SHA256HashRate | int | float) -> SHA256HashRate:
|
||||
if isinstance(other, SHA256HashRate):
|
||||
return SHA256HashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return SHA256HashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: SHA256HashRate | int | float):
|
||||
if isinstance(other, SHA256HashRate):
|
||||
return SHA256HashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return SHA256HashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: SHA256HashRate | int | float):
|
||||
if isinstance(other, SHA256HashRate):
|
||||
return SHA256HashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return SHA256HashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: SHA256HashRate | int | float):
|
||||
if isinstance(other, SHA256HashRate):
|
||||
return SHA256HashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return SHA256HashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: SHA256Unit) -> SHA256HashRate:
|
||||
return SHA256HashRate(
|
||||
rate=self.rate / (other.value / self.unit.value), unit=other
|
||||
)
|
||||
23
pyasic/device/algorithm/hashrate/unit/__init__.py
Normal file
23
pyasic/device/algorithm/hashrate/unit/__init__.py
Normal 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
|
||||
17
pyasic/device/algorithm/hashrate/unit/base.py
Normal file
17
pyasic/device/algorithm/hashrate/unit/base.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
|
||||
@classmethod
|
||||
def from_str(cls, value):
|
||||
return cls.default
|
||||
57
pyasic/device/algorithm/hashrate/unit/blake256.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/blake256.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/eaglesong.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/eaglesong.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/equihash.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/equihash.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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"
|
||||
|
||||
@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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/ethash.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/ethash.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/handshake.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/handshake.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/kadena.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/kadena.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/kheavyhash.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/kheavyhash.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/scrypt.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/scrypt.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/sha256.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/sha256.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
57
pyasic/device/algorithm/hashrate/unit/x11.py
Normal file
57
pyasic/device/algorithm/hashrate/unit/x11.py
Normal file
@@ -0,0 +1,57 @@
|
||||
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
|
||||
|
||||
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)
|
||||
49
pyasic/device/algorithm/hashrate/x11.py
Normal file
49
pyasic/device/algorithm/hashrate/x11.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
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 __add__(self, other: X11HashRate | int | float) -> X11HashRate:
|
||||
if isinstance(other, X11HashRate):
|
||||
return X11HashRate(
|
||||
rate=self.rate + other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return X11HashRate(rate=self.rate + other, unit=self.unit)
|
||||
|
||||
def __sub__(self, other: X11HashRate | int | float) -> X11HashRate:
|
||||
if isinstance(other, X11HashRate):
|
||||
return X11HashRate(
|
||||
rate=self.rate - other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return X11HashRate(rate=self.rate - other, unit=self.unit)
|
||||
|
||||
def __truediv__(self, other: X11HashRate | int | float):
|
||||
if isinstance(other, X11HashRate):
|
||||
return X11HashRate(
|
||||
rate=self.rate / other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return X11HashRate(rate=self.rate / other, unit=self.unit)
|
||||
|
||||
def __floordiv__(self, other: X11HashRate | int | float):
|
||||
if isinstance(other, X11HashRate):
|
||||
return X11HashRate(
|
||||
rate=self.rate // other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return X11HashRate(rate=self.rate // other, unit=self.unit)
|
||||
|
||||
def __mul__(self, other: X11HashRate | int | float):
|
||||
if isinstance(other, X11HashRate):
|
||||
return X11HashRate(
|
||||
rate=self.rate * other.into(self.unit).rate, unit=self.unit
|
||||
)
|
||||
return X11HashRate(rate=self.rate * other, unit=self.unit)
|
||||
|
||||
def into(self, other: X11Unit) -> X11HashRate:
|
||||
return X11HashRate(rate=self.rate / (other.value / self.unit.value), unit=other)
|
||||
17
pyasic/device/algorithm/kadena.py
Normal file
17
pyasic/device/algorithm/kadena.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = KadenaHashRate
|
||||
unit = KadenaUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "KadenaAlgo"
|
||||
|
||||
|
||||
KadenaAlgo = _KadenaAlgo("Kadena")
|
||||
17
pyasic/device/algorithm/kheavyhash.py
Normal file
17
pyasic/device/algorithm/kheavyhash.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = KHeavyHashHashRate
|
||||
unit = KHeavyHashUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "KHeavyHashAlgo"
|
||||
|
||||
|
||||
KHeavyHashAlgo = _KHeavyHashAlgo("KHeavyHash")
|
||||
17
pyasic/device/algorithm/scrypt.py
Normal file
17
pyasic/device/algorithm/scrypt.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .base import MinerAlgoType
|
||||
from .hashrate import ScryptHashRate
|
||||
from .hashrate.unit import ScryptUnit
|
||||
|
||||
|
||||
# make this json serializable
|
||||
class _ScryptAlgo(MinerAlgoType):
|
||||
hashrate = ScryptHashRate
|
||||
unit = ScryptUnit
|
||||
|
||||
def __repr__(self):
|
||||
return "ScryptAlgo"
|
||||
|
||||
|
||||
ScryptAlgo = _ScryptAlgo("Scrypt")
|
||||
@@ -1,64 +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):
|
||||
class _SHA256Algo(MinerAlgoType):
|
||||
hashrate = SHA256HashRate
|
||||
unit = SHA256Unit
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
17
pyasic/device/algorithm/x11.py
Normal file
17
pyasic/device/algorithm/x11.py
Normal file
@@ -0,0 +1,17 @@
|
||||
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 = X11HashRate
|
||||
unit = X11Unit
|
||||
|
||||
def __repr__(self):
|
||||
return "X11Algo"
|
||||
|
||||
|
||||
X11Algo = _X11Algo("X11")
|
||||
@@ -27,6 +27,7 @@ class MinerMake(str, Enum):
|
||||
EPIC = "ePIC"
|
||||
BITAXE = "BitAxe"
|
||||
ICERIVER = "IceRiver"
|
||||
HAMMER = "Hammer"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
@@ -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,16 +345,17 @@ 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"
|
||||
@@ -363,6 +370,13 @@ class IceRiverModels(str, Enum):
|
||||
return self.value
|
||||
|
||||
|
||||
class HammerModels(MinerModelType):
|
||||
D10 = "D10"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class MinerModel:
|
||||
ANTMINER = AntminerModels
|
||||
WHATSMINER = WhatsminerModels
|
||||
@@ -373,3 +387,4 @@ class MinerModel:
|
||||
EPIC = ePICModels
|
||||
BITAXE = BitAxeModels
|
||||
ICERIVER = IceRiverModels
|
||||
HAMMER = HammerModels
|
||||
|
||||
21
pyasic/miners/antminer/bmminer/X7/D7.py
Normal file
21
pyasic/miners/antminer/bmminer/X7/D7.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
22
pyasic/miners/antminer/bmminer/X9/D9.py
Normal file
22
pyasic/miners/antminer/bmminer/X9/D9.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -624,8 +625,8 @@ class AntminerOld(CGMiner):
|
||||
|
||||
hashrate = boards[1].get(f"chain_rate{i}")
|
||||
if hashrate:
|
||||
hashboard.hashrate = AlgoHashRate.SHA256(
|
||||
float(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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
474
pyasic/miners/backends/hammer.py
Normal file
474
pyasic/miners/backends/hammer.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
@@ -178,15 +179,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 +203,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 +256,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 +277,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: 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
|
||||
|
||||
@@ -495,6 +496,7 @@ class MinerProtocol(Protocol):
|
||||
function = getattr(self, getattr(self.data_locations, data_name).cmd)
|
||||
miner_data[data_name] = await function(**args_to_send)
|
||||
except Exception as e:
|
||||
raise e
|
||||
raise APIError(
|
||||
f"Failed to call {data_name} on {self} while getting data."
|
||||
) from e
|
||||
|
||||
6
pyasic/miners/bitaxe/espminer/BM/BM1370.py
Normal file
6
pyasic/miners/bitaxe/espminer/BM/BM1370.py
Normal 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
|
||||
@@ -1,3 +1,4 @@
|
||||
from .BM1366 import BitAxeUltra
|
||||
from .BM1368 import BitAxeSupra
|
||||
from .BM1370 import BitAxeGamma
|
||||
from .BM1397 import BitAxeMax
|
||||
|
||||
@@ -52,3 +52,7 @@ class BitAxeMake(BaseMiner):
|
||||
|
||||
class IceRiverMake(BaseMiner):
|
||||
make = MinerMake.ICERIVER
|
||||
|
||||
|
||||
class HammerMake(BaseMiner):
|
||||
make = MinerMake.HAMMER
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 KS3(AntMinerMake):
|
||||
|
||||
expected_chips = 92
|
||||
expected_fans = 2
|
||||
expected_hashboards = 3
|
||||
algo = MinerAlgo.KHEAVYHASH
|
||||
|
||||
@@ -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 L3Plus(AntMinerMake):
|
||||
raw_model = MinerModel.ANTMINER.L3Plus
|
||||
|
||||
expected_chips = 72
|
||||
expected_hashboards = 4
|
||||
expected_fans = 2
|
||||
algo = MinerAlgo.SCRYPT
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user