Compare commits
870 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f4054bf38 | ||
|
|
cd52d3aeaf | ||
|
|
66c9b3663e | ||
|
|
5f0e1da938 | ||
|
|
2bd031b33d | ||
|
|
e2f07818cc | ||
|
|
75056cfff5 | ||
|
|
7fbcb0dbd2 | ||
|
|
7329aeace2 | ||
|
|
e8c3953106 | ||
|
|
a1a7562bdb | ||
|
|
b2726c77a0 | ||
|
|
68299fa54d | ||
|
|
1466039e08 | ||
|
|
0aa72c6c85 | ||
|
|
24134a5991 | ||
|
|
038208efa6 | ||
|
|
7e319b79df | ||
|
|
a0fdec3ce5 | ||
|
|
1a8928de18 | ||
|
|
bce0058930 | ||
|
|
850656fce4 | ||
|
|
8bb35d6d7c | ||
|
|
e85f06dbc2 | ||
|
|
a566801316 | ||
|
|
e1a9cc5d19 | ||
|
|
27bb06de2b | ||
|
|
debd4d2d4d | ||
|
|
56ad6cbc6f | ||
|
|
3fa54213bf | ||
|
|
076958ec0e | ||
|
|
5319089ebe | ||
|
|
76a77b51e8 | ||
|
|
b099ff45d2 | ||
|
|
9bc3cc221a | ||
|
|
6418c2e102 | ||
|
|
aa9f3b2c45 | ||
|
|
bb1c98f061 | ||
|
|
d984431fe5 | ||
|
|
f1e4feb91e | ||
|
|
90c8986900 | ||
|
|
5457ae6cd5 | ||
|
|
aa3d105fcb | ||
|
|
77f59f6db6 | ||
|
|
3fa0d96fbb | ||
|
|
e55477a8b8 | ||
|
|
7d5744ae28 | ||
|
|
d4500be10c | ||
|
|
7ef2540133 | ||
|
|
1ea4f4e124 | ||
|
|
a8a0e4a5fe | ||
|
|
5f2f6e01da | ||
|
|
41b4c23d45 | ||
|
|
b4687f18fd | ||
|
|
2437421005 | ||
|
|
40ebc2773f | ||
|
|
b8ae238d23 | ||
|
|
2920639b70 | ||
|
|
bd9144b3de | ||
|
|
8f7a67d4dc | ||
|
|
a62bea33a7 | ||
|
|
406bcd179c | ||
|
|
aa87ef7d71 | ||
|
|
ec88fbf6aa | ||
|
|
e23c86a944 | ||
|
|
b81276cb19 | ||
|
|
8dcc72b1bb | ||
|
|
540572356f | ||
|
|
83035a869b | ||
|
|
4c104a59ff | ||
|
|
e708ae3728 | ||
|
|
a4352816ee | ||
|
|
336bd9c002 | ||
|
|
e3c917efde | ||
|
|
4d71012ed6 | ||
|
|
1acdba8ae0 | ||
|
|
5567f26c03 | ||
|
|
0027485582 | ||
|
|
c9bcb7bab6 | ||
|
|
0ee261930e | ||
|
|
3cfc8dded9 | ||
|
|
655cf6d0ac | ||
|
|
640dc6d8c2 | ||
|
|
a572fedb4d | ||
|
|
6c46a7cd71 | ||
|
|
49f42172da | ||
|
|
83be80e4bc | ||
|
|
0be9e9d519 | ||
|
|
4694c0f774 | ||
|
|
11012b310b | ||
|
|
8852fab3ee | ||
|
|
ec1b2ca162 | ||
|
|
1f9d4c8c10 | ||
|
|
5bb04b2af4 | ||
|
|
2e192a1536 | ||
|
|
4da8044bc7 | ||
|
|
76078e4d0e | ||
|
|
276a476fab | ||
|
|
e0abed4f93 | ||
|
|
4a67bd5d99 | ||
|
|
eb48d04939 | ||
|
|
c4697728e4 | ||
|
|
de64172073 | ||
|
|
4d7a13433b | ||
|
|
e14a4791b2 | ||
|
|
0e76d4550b | ||
|
|
2d424025e9 | ||
|
|
bf4903ce4b | ||
|
|
4f7f6bf045 | ||
|
|
824890ec97 | ||
|
|
ce9d7ffb0f | ||
|
|
183b4934c1 | ||
|
|
3d2b260b17 | ||
|
|
f88c1734eb | ||
|
|
b897ca8363 | ||
|
|
dba341fdae | ||
|
|
837794bd57 | ||
|
|
36d16c7235 | ||
|
|
7797023689 | ||
|
|
1021f200ec | ||
|
|
197d6568e3 | ||
|
|
71c8905674 | ||
|
|
15c3806fbf | ||
|
|
a5c42c9c2b | ||
|
|
90d8a795e6 | ||
|
|
07cf1b134c | ||
|
|
53e1f33fa6 | ||
|
|
16ab2bd4e3 | ||
|
|
de728f12d2 | ||
|
|
8092e12dfb | ||
|
|
e70c3e9f79 | ||
|
|
39a97f2914 | ||
|
|
35af74ad1a | ||
|
|
15fa91fb98 | ||
|
|
084987a3e1 | ||
|
|
e93cc77a58 | ||
|
|
788d43c51c | ||
|
|
74c22b82ce | ||
|
|
1f46ce1b9a | ||
|
|
7964336a0c | ||
|
|
a24fc07c2a | ||
|
|
4c64481d3b | ||
|
|
66fb5834f0 | ||
|
|
db05cc1d97 | ||
|
|
418e3ce26e | ||
|
|
5a0bb11a44 | ||
|
|
39f9d087db | ||
|
|
067a376f94 | ||
|
|
a5d6e122f9 | ||
|
|
b160fd75ba | ||
|
|
d1007d3ae8 | ||
|
|
33803e89e2 | ||
|
|
10a44b9877 | ||
|
|
bbd883f639 | ||
|
|
8e2ad478e9 | ||
|
|
957981a9c6 | ||
|
|
13a67dfdd1 | ||
|
|
e86f2b62c5 | ||
|
|
88b4d2cac3 | ||
|
|
5842ef3d97 | ||
|
|
339a689267 | ||
|
|
a0764806c4 | ||
|
|
6eec0f6d44 | ||
|
|
0854f7833c | ||
|
|
b6edc85679 | ||
|
|
ff11ebc304 | ||
|
|
f3681f1aa5 | ||
|
|
1a7411edb3 | ||
|
|
f2a4a5d524 | ||
|
|
624a3c5919 | ||
|
|
2ec8054d24 | ||
|
|
d148ccfe5f | ||
|
|
b6c29d16f9 | ||
|
|
53a3bb13af | ||
|
|
16e74e659c | ||
|
|
730caca23f | ||
|
|
dc126b2953 | ||
|
|
51abdf0b2d | ||
|
|
b367b2d293 | ||
|
|
96f52a4b35 | ||
|
|
5236e02af2 | ||
|
|
9ee52f77f9 | ||
|
|
26389f8ba0 | ||
|
|
8a0605bd3b | ||
|
|
fe9894919e | ||
|
|
bb399fe362 | ||
|
|
7ca7fe3e7e | ||
|
|
748279c25d | ||
|
|
23890fa10a | ||
|
|
8ab7df516e | ||
|
|
f7a0188104 | ||
|
|
66a8932ea3 | ||
|
|
56536fd258 | ||
|
|
1b4e6d4da0 | ||
|
|
2019bdaff2 | ||
|
|
b903cc6e5f | ||
|
|
c1a01b5f7b | ||
|
|
3427a8d15a | ||
|
|
25c08b9bc0 | ||
|
|
01263da52b | ||
|
|
5081319a2f | ||
|
|
ec5be00065 | ||
|
|
891e28bfe6 | ||
|
|
8e15b00e70 | ||
|
|
df71ab3282 | ||
|
|
1508f7873a | ||
|
|
77058ac692 | ||
|
|
1a4491ca56 | ||
|
|
d018724aa4 | ||
|
|
5b97bed704 | ||
|
|
55786b154d | ||
|
|
6ab9681dec | ||
|
|
89641c6316 | ||
|
|
136ff6a688 | ||
|
|
d918d93f4a | ||
|
|
8046c532a6 | ||
|
|
92820a362d | ||
|
|
9fd90031a9 | ||
|
|
2f2223a112 | ||
|
|
50e6cf9dfd | ||
|
|
1b5e3093e6 | ||
|
|
9e3578b4a2 | ||
|
|
a3087e1a96 | ||
|
|
4b16ea2ca2 | ||
|
|
5dd361c4ef | ||
|
|
098112742c | ||
|
|
cd31e0743e | ||
|
|
1a7d0bf7cc | ||
|
|
41b5ebf0f0 | ||
|
|
5436bede29 | ||
|
|
c01b3958dc | ||
|
|
c16367285f | ||
|
|
17eae253e6 | ||
|
|
ab30988614 | ||
|
|
9b8e547f86 | ||
|
|
3b8cbb9ff1 | ||
|
|
d39d278296 | ||
|
|
ea8b922367 | ||
|
|
0d1c8d80e0 | ||
|
|
494d25da97 | ||
|
|
0327d93a35 | ||
|
|
680584c468 | ||
|
|
c0dbafb198 | ||
|
|
97d2c4ac34 | ||
|
|
055d633c91 | ||
|
|
68c57f265f | ||
|
|
1ba0f8ed83 | ||
|
|
7f74b083d3 | ||
|
|
97c20dae0a | ||
|
|
09c7aff640 | ||
|
|
7e7cdc9615 | ||
|
|
b6fb0fd2b9 | ||
|
|
46788e7d14 | ||
|
|
5b4f84a241 | ||
|
|
0c56bfdf9e | ||
|
|
4a5d793cd6 | ||
|
|
1894ff1aea | ||
|
|
3ab9294000 | ||
|
|
5e0641634b | ||
|
|
a1975bc9b8 | ||
|
|
6a265f03f7 | ||
|
|
c3658f028f | ||
|
|
ba3c653a29 | ||
|
|
61fbc132ed | ||
|
|
3f9f232990 | ||
|
|
29c2398846 | ||
|
|
ecc161820d | ||
|
|
5fec3052f6 | ||
|
|
437ee774ab | ||
|
|
aed9e0e406 | ||
|
|
be96428823 | ||
|
|
446881b237 | ||
|
|
ceab8e55b5 | ||
|
|
e12f85c94d | ||
|
|
0c85f53177 | ||
|
|
0b524f9bd0 | ||
|
|
95db852636 | ||
|
|
93fa02412a | ||
|
|
edb77e9cd8 | ||
|
|
cbf7eeb08d | ||
|
|
d34b35a82d | ||
|
|
5d57f35475 | ||
|
|
c95491ea45 | ||
|
|
e9ec43fac9 | ||
|
|
42bde081c4 | ||
|
|
bfb72aec1b | ||
|
|
b2f36b2f0b | ||
|
|
f75c07401b | ||
|
|
01b96227e0 | ||
|
|
82552390c8 | ||
|
|
b0651e26b8 | ||
|
|
c00802e311 | ||
|
|
d66739e2c9 | ||
|
|
65ed565220 | ||
|
|
db6499800b | ||
|
|
cc97ceee61 | ||
|
|
3fa1cb18d9 | ||
|
|
cb3c50d007 | ||
|
|
2523ef8484 | ||
|
|
01342738b0 | ||
|
|
a9dee4a911 | ||
|
|
883ffe20b4 | ||
|
|
261527a380 | ||
|
|
924b62e0d5 | ||
|
|
76a870c2ed | ||
|
|
309356243b | ||
|
|
e9b4cc9bd6 | ||
|
|
648c54de93 | ||
|
|
e1ce96ab1b | ||
|
|
86860a8dc4 | ||
|
|
5212641f45 | ||
|
|
52432e6043 | ||
|
|
727e484860 | ||
|
|
6c091756d2 | ||
|
|
14533ce4fe | ||
|
|
82d1840039 | ||
|
|
8e6240cdba | ||
|
|
5749e173d1 | ||
|
|
7d682b62ac | ||
|
|
6739a1001f | ||
|
|
56e4a5307f | ||
|
|
88de27c9e7 | ||
|
|
a77113c4db | ||
|
|
c19945bb82 | ||
|
|
1756937d20 | ||
|
|
c7b7fe864b | ||
|
|
e7ebefd1bf | ||
|
|
4677efbc46 | ||
|
|
4b7a1a0495 | ||
|
|
cc4e7da4e5 | ||
|
|
a3d2d7d35e | ||
|
|
d67de98bd0 | ||
|
|
fd1a3e459b | ||
|
|
adcab694b5 | ||
|
|
2bb097272f | ||
|
|
896968dded | ||
|
|
56b8f7c5b3 | ||
|
|
0ed7559aef | ||
|
|
275d87e4fe | ||
|
|
c3ab814d77 | ||
|
|
05a8569205 | ||
|
|
b098cb8136 | ||
|
|
75fe7857e4 | ||
|
|
66797aced1 | ||
|
|
4a71e38078 | ||
|
|
9fb07e4fa3 | ||
|
|
74792771ec | ||
|
|
fa6e8a976d | ||
|
|
f20531cff5 | ||
|
|
8b1cbed9ce | ||
|
|
0194e13427 | ||
|
|
82d71abf54 | ||
|
|
e71cfadf6e | ||
|
|
18931c4e98 | ||
|
|
8622c080aa | ||
|
|
cb71b2a593 | ||
|
|
ff5956da41 | ||
|
|
acdafc2efd | ||
|
|
b8874092ad | ||
|
|
ad28ba0b3e | ||
|
|
0d90b60eef | ||
|
|
7c18c9f69c | ||
|
|
975560f46f | ||
|
|
bfe9cbf7d9 | ||
|
|
ccb5eb73db | ||
|
|
d143667bd6 | ||
|
|
87d809abc0 | ||
|
|
4dc5b1a541 | ||
|
|
ddd3e867f9 | ||
|
|
77480d3d69 | ||
|
|
0767c93002 | ||
|
|
e690e6dd3b | ||
|
|
d4665ed768 | ||
|
|
b90a92c0df | ||
|
|
50cfcf9796 | ||
|
|
5d204f09da | ||
|
|
4c0410322f | ||
|
|
fbb2b3f6e7 | ||
|
|
0f09fb49fc | ||
|
|
b0d063d6ed | ||
|
|
a68fe70af4 | ||
|
|
43c7ac281b | ||
|
|
a97ae55a06 | ||
|
|
4a3a6f4186 | ||
|
|
f976724ada | ||
|
|
2632bdaa30 | ||
|
|
91016d7b8c | ||
|
|
2b00e741ca | ||
|
|
d496c11d67 | ||
|
|
5880223517 | ||
|
|
394a5dcd0d | ||
|
|
7365275f46 | ||
|
|
0ecab5fdd4 | ||
|
|
ed0d9f73e4 | ||
|
|
28f4e16662 | ||
|
|
b9b0bff946 | ||
|
|
790718a5df | ||
|
|
96a0301f5e | ||
|
|
c57b019b7d | ||
|
|
af920c4dda | ||
|
|
f3d11788ed | ||
|
|
fd0e02af59 | ||
|
|
2a6c51d52c | ||
|
|
2d62e2070b | ||
|
|
b143bd70f0 | ||
|
|
605509c57c | ||
|
|
7036137b23 | ||
|
|
7a9ff535b4 | ||
|
|
f185bafe2a | ||
|
|
ab81d5d020 | ||
|
|
0965e6489b | ||
|
|
792e1c9cad | ||
|
|
a6721f971a | ||
|
|
8113d0e4e0 | ||
|
|
e3c7d3f8a2 | ||
|
|
6415de8c73 | ||
|
|
f2838cf31d | ||
|
|
fbd49b370d | ||
|
|
79f7296576 | ||
|
|
76f4ca5f89 | ||
|
|
477acda1c1 | ||
|
|
a57f343dcc | ||
|
|
36e9201ed4 | ||
|
|
c1525501d4 | ||
|
|
e4bb90a569 | ||
|
|
28642cc521 | ||
|
|
beae79ddec | ||
|
|
f02e10ab3d | ||
|
|
d0b9dff476 | ||
|
|
501e290839 | ||
|
|
a0daf37f80 | ||
|
|
8111b1ff4b | ||
|
|
754087afd6 | ||
|
|
5e16b6092c | ||
|
|
21636a75fa | ||
|
|
f124f5422a | ||
|
|
1e5d1a2528 | ||
|
|
1fcef07902 | ||
|
|
41e7dd8056 | ||
|
|
dccc35db5f | ||
|
|
0cfe59aa34 | ||
|
|
6fdd156fa3 | ||
|
|
e9fcf25ad3 | ||
|
|
a9422165ca | ||
|
|
0ea5ee8239 | ||
|
|
fba25cba61 | ||
|
|
343b5a1c50 | ||
|
|
63522aad81 | ||
|
|
b957aa7fba | ||
|
|
a71aa6868a | ||
|
|
6b50bf0cf7 | ||
|
|
d00444ec56 | ||
|
|
e7ed39fe39 | ||
|
|
168d68d0b2 | ||
|
|
63cddfdde3 | ||
|
|
4a642fd3da | ||
|
|
13c0407b2d | ||
|
|
794ed6d103 | ||
|
|
d0aeb5a6ce | ||
|
|
030f8c6079 | ||
|
|
7195e204ce | ||
|
|
962a328219 | ||
|
|
1cec2ca7f3 | ||
|
|
a3c4187411 | ||
|
|
18a2df5d9b | ||
|
|
6d66c793cb | ||
|
|
b434c8df1a | ||
|
|
2b8fa2fc2b | ||
|
|
1497d2abea | ||
|
|
a2ca79843d | ||
|
|
f6500e7d66 | ||
|
|
ea2fd0fc9a | ||
|
|
e2cbd30a99 | ||
|
|
151ea44b10 | ||
|
|
6487a0b08e | ||
|
|
552fdf9ec0 | ||
|
|
00cf1449f9 | ||
|
|
8ec88e385a | ||
|
|
cc29b2960a | ||
|
|
568ffd67c4 | ||
|
|
4b4670201a | ||
|
|
92f70c9a76 | ||
|
|
1d2dc3fddf | ||
|
|
c44150fd15 | ||
|
|
8664b53991 | ||
|
|
31aeca2340 | ||
|
|
34eec3ff2e | ||
|
|
e1416b5a4b | ||
|
|
3ca75729b9 | ||
|
|
73031eea65 | ||
|
|
1643c5b7ee | ||
|
|
ca5db726bd | ||
|
|
4bb4d32b48 | ||
|
|
fec7a89807 | ||
|
|
db2615a4eb | ||
|
|
eea5d5ba2a | ||
|
|
f405bbff4d | ||
|
|
dd8d895b50 | ||
|
|
dff4e98523 | ||
|
|
846bbb9033 | ||
|
|
e6f9a33b3c | ||
|
|
092126bded | ||
|
|
e5d5cb4dab | ||
|
|
bd76966d3a | ||
|
|
d46908a298 | ||
|
|
e4cec021b0 | ||
|
|
42d2d975db | ||
|
|
427f91d677 | ||
|
|
7a9c9237a3 | ||
|
|
9b431b020f | ||
|
|
ee1eece181 | ||
|
|
3627194f34 | ||
|
|
65cfb57605 | ||
|
|
8e4a547c77 | ||
|
|
a751efe7d5 | ||
|
|
e859895261 | ||
|
|
ae3d38603a | ||
|
|
fca72eb747 | ||
|
|
923e963369 | ||
|
|
7a3c9a3460 | ||
|
|
e649348af2 | ||
|
|
ba58e80ec3 | ||
|
|
45e2c9a403 | ||
|
|
bd9592c19c | ||
|
|
1bb597999d | ||
|
|
7803fa60f2 | ||
|
|
4adb7dc92c | ||
|
|
ba69a1de2c | ||
|
|
64265206c2 | ||
|
|
eec8f66b81 | ||
|
|
999e8ef318 | ||
|
|
30f385c2d9 | ||
|
|
87377fbe4c | ||
|
|
2a66602c2c | ||
|
|
8cc18ca272 | ||
|
|
677db8fd0d | ||
|
|
a458adc45f | ||
|
|
5a09ddcb04 | ||
|
|
4d9fde572e | ||
|
|
3e4b347506 | ||
|
|
927bbae0c1 | ||
|
|
7e3e1f19aa | ||
|
|
5a4b1b6ee1 | ||
|
|
25767aab8e | ||
|
|
b3a0949395 | ||
|
|
18e6fc2a3c | ||
|
|
4d45b6e50f | ||
|
|
eefb055a3f | ||
|
|
9c41a6b28f | ||
|
|
bf0e2e6cfe | ||
|
|
4a2adabe95 | ||
|
|
4031a42350 | ||
|
|
4698a806f0 | ||
|
|
aec53aa5f0 | ||
|
|
e15ddd020c | ||
|
|
6f4aead0d4 | ||
|
|
6b3bf31597 | ||
|
|
2ac118a008 | ||
|
|
c87880529c | ||
|
|
b12766321d | ||
|
|
3d6bee2d85 | ||
|
|
c8a8315ad0 | ||
|
|
dac9bcc3de | ||
|
|
7688288d05 | ||
|
|
46621d6b93 | ||
|
|
35700f7e57 | ||
|
|
08e6744595 | ||
|
|
2de3e5e328 | ||
|
|
51f2eb1b1d | ||
|
|
b4faf7c49e | ||
|
|
26d9562c18 | ||
|
|
d40d92c1ca | ||
|
|
7ea63643a9 | ||
|
|
0bd5c22681 | ||
|
|
8f0cf5b3a3 | ||
|
|
6458a71b5d | ||
|
|
dbdd23e37d | ||
|
|
313c324771 | ||
|
|
a9fd9343d8 | ||
|
|
8f41d4d0bc | ||
|
|
521853863b | ||
|
|
b7a5a647b3 | ||
|
|
4434f9ccad | ||
|
|
82a1cc3cfe | ||
|
|
2d92c2c0e2 | ||
|
|
6f10c91482 | ||
|
|
f2d6bce165 | ||
|
|
1ab05c7a5e | ||
|
|
61623cc44d | ||
|
|
a30a726324 | ||
|
|
0e90ad64cd | ||
|
|
53572c6236 | ||
|
|
67da56a03b | ||
|
|
be8633185d | ||
|
|
1d656da2a2 | ||
|
|
189deae3d1 | ||
|
|
46188ad52b | ||
|
|
19e232ddb9 | ||
|
|
5d90b7e938 | ||
|
|
3f90799544 | ||
|
|
1f70ec0d28 | ||
|
|
c26b78aa01 | ||
|
|
0debf16f7c | ||
|
|
d7f48d8f9f | ||
|
|
58c95559dd | ||
|
|
03caa9fe94 | ||
|
|
afd8697f07 | ||
|
|
fc77aded28 | ||
|
|
a5cafd1fb8 | ||
|
|
91d504fc1c | ||
|
|
db0444a8eb | ||
|
|
0cab872baf | ||
|
|
7f191eb2fd | ||
|
|
52adc2553b | ||
|
|
de49fd7e95 | ||
|
|
7eb61473a8 | ||
|
|
f6a134342a | ||
|
|
1d67e5ed68 | ||
|
|
7d4aa80966 | ||
|
|
742dde622b | ||
|
|
497aa6a42c | ||
|
|
40ebf42da1 | ||
|
|
2f8aea5285 | ||
|
|
53f545ba13 | ||
|
|
689b34611e | ||
|
|
d55c3f45ef | ||
|
|
5ac533616f | ||
|
|
96ea5f5d16 | ||
|
|
87526f5efc | ||
|
|
d31bafbc0e | ||
|
|
66bae47bb9 | ||
|
|
7a09b66d4e | ||
|
|
de5932184f | ||
|
|
2cba62e050 | ||
|
|
c7520d98e0 | ||
|
|
92e9f7bc08 | ||
|
|
e0becce349 | ||
|
|
a65c4ba215 | ||
|
|
4cd0c3357b | ||
|
|
d5cabf8af5 | ||
|
|
3120de757d | ||
|
|
0b69fe591e | ||
|
|
032288d062 | ||
|
|
1d6618c1c0 | ||
|
|
7126e03f0d | ||
|
|
c27556c809 | ||
|
|
99ff28d3e1 | ||
|
|
8ad6f60757 | ||
|
|
5f67b987a0 | ||
|
|
5842b3c4aa | ||
|
|
e2e1d2f2fd | ||
|
|
dd205c0f06 | ||
|
|
79e247c0cf | ||
|
|
836d045b65 | ||
|
|
2d029b65e6 | ||
|
|
993b7efeef | ||
|
|
7e02a6b932 | ||
|
|
60e38fb8bc | ||
|
|
4fc2757ffa | ||
|
|
c89b25d7c9 | ||
|
|
f2fc354f8c | ||
|
|
0e7eca339c | ||
|
|
5f76cb9f0a | ||
|
|
ed3a4bd32a | ||
|
|
a0a1b68f68 | ||
|
|
b7a81097a4 | ||
|
|
651bef8203 | ||
|
|
a9e5c99ab2 | ||
|
|
30216fdd5b | ||
|
|
341cc13d83 | ||
|
|
05a4ae6f04 | ||
|
|
5971d9fd83 | ||
|
|
3968f2275c | ||
|
|
84344ca883 | ||
|
|
59b80254eb | ||
|
|
06fdb19e0b | ||
|
|
75222e8cd2 | ||
|
|
5a067d60e7 | ||
|
|
2fbc4fcb4a | ||
|
|
13fe60504a | ||
|
|
cdc52c3605 | ||
|
|
a9db097355 | ||
|
|
ec5557cf88 | ||
|
|
a8ea84d2f3 | ||
|
|
58d369eedf | ||
|
|
f8f9dd7070 | ||
|
|
01b72e1ee6 | ||
|
|
e367e630b8 | ||
|
|
385cca6fc0 | ||
|
|
75a3a466a3 | ||
|
|
4bf08dbfe6 | ||
|
|
833d061315 | ||
|
|
a85558278d | ||
|
|
bf5087b06d | ||
|
|
ec58d13bae | ||
|
|
75993564ab | ||
|
|
f3ea169dec | ||
|
|
39b3fe5c25 | ||
|
|
3b2b586420 | ||
|
|
5c79c6cb0c | ||
|
|
bab4261bed | ||
|
|
e1d5c89388 | ||
|
|
5f6c8cca18 | ||
|
|
39db14b002 | ||
|
|
3be04c678b | ||
|
|
099ec35a8f | ||
|
|
113dfb9170 | ||
|
|
8d19e0ebbb | ||
|
|
ec064eba65 | ||
|
|
3451127761 | ||
|
|
b3be52ca77 | ||
|
|
b6ec6caa72 | ||
|
|
8e81e18622 | ||
|
|
6ea26e0e19 | ||
|
|
be446f94c1 | ||
|
|
3d94e30f22 | ||
|
|
d5a7ff3a46 | ||
|
|
bbfa97632d | ||
|
|
ecf0ce22d6 | ||
|
|
d56da007a5 | ||
|
|
2c86b2da7e | ||
|
|
c73b1ceb07 | ||
|
|
a320c8967d | ||
|
|
e21e340f60 | ||
|
|
f63d8f4b91 | ||
|
|
ba6a1606b6 | ||
|
|
51b0c0456f | ||
|
|
04bd03b496 | ||
|
|
bc5764c8ff | ||
|
|
1f2e066f4c | ||
|
|
74c457a694 | ||
|
|
4a1c53dfd7 | ||
|
|
81b77f8768 | ||
|
|
3b127b6862 | ||
|
|
2815d2ba11 | ||
|
|
1ff20fc6f0 | ||
|
|
797c847055 | ||
|
|
65c7f2f66f | ||
|
|
445d621590 | ||
|
|
d39ecfd6b4 | ||
|
|
36663471fb | ||
|
|
80293ac52f | ||
|
|
70b45f40f5 | ||
|
|
a511fabd9c | ||
|
|
8bc8f6f178 | ||
|
|
b790ad58a7 | ||
|
|
354ab793a2 | ||
|
|
59346d641f | ||
|
|
11d770771b | ||
|
|
1b6db7ed45 | ||
|
|
55c4e10fae | ||
|
|
77c06dad61 | ||
|
|
68d250d2f2 | ||
|
|
094a17ac68 | ||
|
|
dbcdeaa3de | ||
|
|
872cac811a | ||
|
|
d324c2fee9 | ||
|
|
577e8df612 | ||
|
|
4b54cf67ba | ||
|
|
0e00fe3114 | ||
|
|
15d1dc5bb6 | ||
|
|
2af0003843 | ||
|
|
3c227be170 | ||
|
|
e889780bad | ||
|
|
cc3d4fa805 | ||
|
|
743823f66e | ||
|
|
227e1e2d2d | ||
|
|
d6c8ff0910 | ||
|
|
bd20e051b0 | ||
|
|
2eb6697e9a | ||
|
|
c5817fcc36 | ||
|
|
f2391bcb2d | ||
|
|
bc3bd9e5da | ||
|
|
3f0959d75e | ||
|
|
31f7b56724 | ||
|
|
072954d755 | ||
|
|
d271e0f9c8 | ||
|
|
8f1408ce17 | ||
|
|
825d1f4cfb | ||
|
|
c6bcd7e05a | ||
|
|
5d80051f3b | ||
|
|
b71c448199 | ||
|
|
c82148412c | ||
|
|
a582ee63a0 | ||
|
|
db7c19c486 | ||
|
|
599f71da19 | ||
|
|
0995744d90 | ||
|
|
4073a27aba | ||
|
|
bec9c31c97 | ||
|
|
acdd615c53 | ||
|
|
8091617ee2 | ||
|
|
c25ff6fcef | ||
|
|
ab0dcd607b | ||
|
|
ce288e472f | ||
|
|
02d8f25daf | ||
|
|
a76d1c6149 | ||
|
|
17f5eade19 | ||
|
|
b6a2a5054b | ||
|
|
5984338c64 | ||
|
|
07d1c48e33 | ||
|
|
d2abae947c | ||
|
|
e4a0f2451a | ||
|
|
880c598b1a | ||
|
|
3632c2c4d8 | ||
|
|
09bc9686ae | ||
|
|
34584ab098 | ||
|
|
554d99ca08 | ||
|
|
5c5d688ffa | ||
|
|
c50d55e87c | ||
|
|
5e5516bfb3 | ||
|
|
4b068c57c5 | ||
|
|
203f199aec | ||
|
|
895f17aaf9 | ||
|
|
8a64ff3559 | ||
|
|
4c45d356c4 | ||
|
|
4dec329f11 | ||
|
|
b563ed118e | ||
|
|
75b2ec40b1 | ||
|
|
d9adaf6667 | ||
|
|
9343308f41 | ||
|
|
88769e40ae | ||
|
|
be45eb7400 | ||
|
|
2f719a03a4 | ||
|
|
64196f9754 | ||
|
|
49a77f1b79 | ||
|
|
3838c4f2f9 | ||
|
|
80d89c95b5 | ||
|
|
30cd8b5cfe | ||
|
|
c443170f78 | ||
|
|
a2c2aa2377 | ||
|
|
4f0eb49a02 | ||
|
|
a821357b4f | ||
|
|
3c7679a22d | ||
|
|
a52737e236 | ||
|
|
7c96bbe153 | ||
|
|
e8bbf22aa7 | ||
|
|
5ac8b27cb6 | ||
|
|
6c14902484 | ||
|
|
96aa346f00 | ||
|
|
c2b6cc7468 | ||
|
|
ac7f41be44 | ||
|
|
718b87fd12 | ||
|
|
5ad23c6cd0 | ||
|
|
66be443dc3 | ||
|
|
a9135e21d4 | ||
|
|
dd4c087749 | ||
|
|
aa1d7c1b6f | ||
|
|
b328a27f04 | ||
|
|
c5eed797ec | ||
|
|
4fd2199435 | ||
|
|
3226d47846 | ||
|
|
6c1931fe7e | ||
|
|
1dd87ac102 | ||
|
|
95d1e40b4f | ||
|
|
31682b7fae | ||
|
|
e6523fc7d5 | ||
|
|
91de12467b | ||
|
|
d81e3e9f88 | ||
|
|
49fc0f3c54 | ||
|
|
4b36044e56 | ||
|
|
90fb67f586 | ||
|
|
edf31ae7df | ||
|
|
af354fd8e2 | ||
|
|
6a2a3e836d | ||
|
|
41709e4706 | ||
|
|
b60c7a55d4 | ||
|
|
eed1973345 | ||
|
|
64774d2017 | ||
|
|
e9751d6cd1 | ||
|
|
e2b0a76e67 |
6
.github/workflows/python-publish.yml
vendored
6
.github/workflows/python-publish.yml
vendored
@@ -13,10 +13,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4.2.2
|
||||||
- name: Publish GH release
|
- name: Publish GH release
|
||||||
uses: softprops/action-gh-release@v0.1.14
|
uses: softprops/action-gh-release@v2.1.0
|
||||||
- name: Build using poetry and publish to PyPi
|
- name: Build using poetry and publish to PyPi
|
||||||
uses: JRubics/poetry-publish@v1.11
|
uses: JRubics/poetry-publish@v2.0
|
||||||
with:
|
with:
|
||||||
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
pypi_token: ${{ secrets.PYPI_API_KEY }}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ pyvenv.cfg
|
|||||||
bin/
|
bin/
|
||||||
lib/
|
lib/
|
||||||
.idea/
|
.idea/
|
||||||
|
.vs/
|
||||||
@@ -1,27 +1,57 @@
|
|||||||
|
ci:
|
||||||
|
skip:
|
||||||
|
- poetry-lock
|
||||||
|
- unittest
|
||||||
|
- generate-docs
|
||||||
repos:
|
repos:
|
||||||
|
- repo: https://github.com/python-poetry/poetry
|
||||||
|
rev: 2.2.1
|
||||||
|
hooks:
|
||||||
|
- id: poetry-check
|
||||||
|
- id: poetry-lock
|
||||||
|
- id: poetry-install
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.3.0
|
rev: v6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
|
name: check-yaml for mkdocs.yml
|
||||||
|
files: ^mkdocs\.yml$
|
||||||
|
args: [--unsafe]
|
||||||
|
- id: check-yaml
|
||||||
|
name: check-yaml for other YAML files
|
||||||
|
exclude: ^mkdocs\.yml$
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 22.6.0
|
rev: v0.13.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: ruff-check
|
||||||
- repo: https://github.com/pycqa/isort
|
args: [--fix]
|
||||||
rev: 5.10.1
|
- id: ruff-format
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
|
rev: v1.18.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: mypy
|
||||||
name: isort (python)
|
additional_dependencies:
|
||||||
|
[
|
||||||
|
betterproto==2.0.0b7,
|
||||||
|
httpx==0.28.1,
|
||||||
|
types-aiofiles==24.1.0.20250822,
|
||||||
|
types-passlib==1.7.7.20250602,
|
||||||
|
pydantic==2.11.9,
|
||||||
|
]
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
- id: unittest
|
- id: unittest
|
||||||
name: unittest
|
name: unittest
|
||||||
entry: python -m unittest discover
|
entry: python -m unittest discover
|
||||||
language: system
|
language: system
|
||||||
'types': [python]
|
types: [ python ]
|
||||||
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
|
args: ["-p '*test.py'"] # Probably this option is absolutely not needed.
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
stages: [commit]
|
- id: generate-docs
|
||||||
|
name: generate-docs
|
||||||
|
entry: python docs/generate_miners.py
|
||||||
|
language: system
|
||||||
|
types: [ python ]
|
||||||
|
pass_filenames: false
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
# .readthedocs.yaml
|
# .readthedocs.yaml
|
||||||
# Read the Docs configuration file
|
|
||||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
||||||
|
|
||||||
# Required
|
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
# Set the version of Python and other tools you might need
|
# Set the version of Python and other tools you might need
|
||||||
build:
|
build:
|
||||||
os: ubuntu-20.04
|
os: ubuntu-20.04
|
||||||
tools:
|
tools: { python: "3.11" }
|
||||||
python: "3.9"
|
jobs:
|
||||||
|
pre_create_environment:
|
||||||
|
- asdf plugin add poetry
|
||||||
|
- asdf install poetry latest
|
||||||
|
- asdf global poetry latest
|
||||||
|
- poetry config virtualenvs.create false
|
||||||
|
post_install:
|
||||||
|
- VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --only docs
|
||||||
|
|
||||||
mkdocs:
|
mkdocs:
|
||||||
configuration: mkdocs.yml
|
configuration: mkdocs.yml
|
||||||
|
|
||||||
# Optionally declare the Python requirements required to build your docs
|
|
||||||
python:
|
|
||||||
install:
|
|
||||||
- requirements: docs/requirements.txt
|
|
||||||
64
README.md
64
README.md
@@ -9,7 +9,7 @@
|
|||||||
[](https://github.com/UpstreamData/pyasic/commits/master/)
|
[](https://github.com/UpstreamData/pyasic/commits/master/)
|
||||||
|
|
||||||
[](https://github.com/psf/black)
|
[](https://github.com/psf/black)
|
||||||
[](https://pyasic.readthedocs.io/en/latest/)
|
[](https://docs.pyasic.org)
|
||||||
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -17,7 +17,45 @@
|
|||||||
|
|
||||||
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
||||||
|
|
||||||
[Click here to view supported miner types](miners/supported_types.md)
|
[Click here to view supported miner types](https://docs.pyasic.org/en/latest/miners/supported_types/)
|
||||||
|
|
||||||
|
---
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include:
|
||||||
|
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default. Use version 2.0+
|
||||||
|
|
||||||
|
```
|
||||||
|
poetry install
|
||||||
|
```
|
||||||
|
|
||||||
|
- [venv](https://docs.python.org/3/library/venv.html): included in Python standard library but has fewer features than other options
|
||||||
|
- [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv): [pyenv](https://github.com/pyenv/pyenv) plugin for managing virtualenvs
|
||||||
|
|
||||||
|
```
|
||||||
|
pyenv install <python version number>
|
||||||
|
pyenv virtualenv <python version number> <env name>
|
||||||
|
pyenv activate <env name>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [conda](https://docs.conda.io/en/latest/)
|
||||||
|
|
||||||
|
##### Installing `pyasic`
|
||||||
|
|
||||||
|
`python -m pip install pyasic` or `poetry install`
|
||||||
|
|
||||||
|
##### Additional Developer Setup
|
||||||
|
```
|
||||||
|
poetry install --with dev
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Building Documentation Locally
|
||||||
|
```
|
||||||
|
poetry install --with docs
|
||||||
|
python docs/generate_miners.py
|
||||||
|
poetry run mkdocs serve
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
## Getting started
|
## Getting started
|
||||||
@@ -224,24 +262,30 @@ if __name__ == "__main__":
|
|||||||
```python
|
```python
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
|
|
||||||
settings.update("default_antminer_password", "my_pwd")
|
settings.update("default_antminer_web_password", "my_pwd")
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Default values:
|
##### Default values:
|
||||||
```
|
```
|
||||||
"network_ping_retries": 1,
|
"network_ping_retries": 1,
|
||||||
"network_ping_timeout": 3,
|
"network_ping_timeout": 3,
|
||||||
"network_scan_threads": 300,
|
"network_scan_semaphore": None,
|
||||||
"factory_get_retries": 1,
|
"factory_get_retries": 1,
|
||||||
"factory_get_timeout": 3,
|
"factory_get_timeout": 3,
|
||||||
"get_data_retries": 1,
|
"get_data_retries": 1,
|
||||||
"api_function_timeout": 5,
|
"api_function_timeout": 5,
|
||||||
"default_whatsminer_password": "admin",
|
"antminer_mining_mode_as_str": False,
|
||||||
"default_innosilicon_password": "admin",
|
"default_whatsminer_rpc_password": "admin",
|
||||||
"default_antminer_password": "root",
|
"default_innosilicon_web_password": "admin",
|
||||||
"default_bosminer_password": "root",
|
"default_antminer_web_password": "root",
|
||||||
"default_vnish_password": "admin",
|
"default_bosminer_web_password": "root",
|
||||||
"default_goldshell_password": "123456789",
|
"default_vnish_web_password": "admin",
|
||||||
|
"default_goldshell_web_password": "123456789",
|
||||||
|
"default_auradine_web_password": "admin",
|
||||||
|
"default_epic_web_password": "letmein",
|
||||||
|
"default_hive_web_password": "admin",
|
||||||
|
"default_antminer_ssh_password": "miner",
|
||||||
|
"default_bosminer_ssh_password": "root",
|
||||||
|
|
||||||
# ADVANCED
|
# ADVANCED
|
||||||
# Only use this if you know what you are doing
|
# Only use this if you know what you are doing
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import asyncio
|
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pyasic.miners.miner_factory import MINER_CLASSES, MinerTypes
|
from pyasic.miners.factory import MINER_CLASSES, MinerTypes
|
||||||
|
|
||||||
warnings.filterwarnings("ignore")
|
warnings.filterwarnings("ignore")
|
||||||
|
|
||||||
@@ -27,6 +28,8 @@ def backend_str(backend: MinerTypes) -> str:
|
|||||||
match backend:
|
match backend:
|
||||||
case MinerTypes.ANTMINER:
|
case MinerTypes.ANTMINER:
|
||||||
return "Stock Firmware Antminers"
|
return "Stock Firmware Antminers"
|
||||||
|
case MinerTypes.AURADINE:
|
||||||
|
return "Stock Firmware Auradine Miners"
|
||||||
case MinerTypes.AVALONMINER:
|
case MinerTypes.AVALONMINER:
|
||||||
return "Stock Firmware Avalonminers"
|
return "Stock Firmware Avalonminers"
|
||||||
case MinerTypes.VNISH:
|
case MinerTypes.VNISH:
|
||||||
@@ -45,6 +48,23 @@ def backend_str(backend: MinerTypes) -> str:
|
|||||||
return "Stock Firmware Goldshells"
|
return "Stock Firmware Goldshells"
|
||||||
case MinerTypes.LUX_OS:
|
case MinerTypes.LUX_OS:
|
||||||
return "LuxOS Firmware Miners"
|
return "LuxOS Firmware Miners"
|
||||||
|
case MinerTypes.MARATHON:
|
||||||
|
return "Mara Firmware Miners"
|
||||||
|
case MinerTypes.BITAXE:
|
||||||
|
return "Stock Firmware BitAxe Miners"
|
||||||
|
case MinerTypes.LUCKYMINER:
|
||||||
|
return "Stock Firmware Lucky Miners"
|
||||||
|
case MinerTypes.ICERIVER:
|
||||||
|
return "Stock Firmware IceRiver Miners"
|
||||||
|
case MinerTypes.HAMMER:
|
||||||
|
return "Stock Firmware Hammer Miners"
|
||||||
|
case MinerTypes.VOLCMINER:
|
||||||
|
return "Stock Firmware Volcminers"
|
||||||
|
case MinerTypes.ELPHAPEX:
|
||||||
|
return "Stock Firmware Elphapex Miners"
|
||||||
|
case MinerTypes.MSKMINER:
|
||||||
|
return "MSKMiner Firmware Miners"
|
||||||
|
raise TypeError("Unknown miner backend, cannot generate docs")
|
||||||
|
|
||||||
|
|
||||||
def create_url_str(mtype: str):
|
def create_url_str(mtype: str):
|
||||||
@@ -59,11 +79,17 @@ def create_url_str(mtype: str):
|
|||||||
|
|
||||||
HEADER_FORMAT = "# pyasic\n## {} Models\n\n"
|
HEADER_FORMAT = "# pyasic\n## {} Models\n\n"
|
||||||
MINER_HEADER_FORMAT = "## {}\n"
|
MINER_HEADER_FORMAT = "## {}\n"
|
||||||
DATA_FORMAT = """::: {}
|
DATA_FORMAT = """
|
||||||
|
- [{}] Shutdowns
|
||||||
|
- [{}] Power Modes
|
||||||
|
- [{}] Setpoints
|
||||||
|
- [{}] Presets
|
||||||
|
|
||||||
|
::: {}
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
"""
|
"""
|
||||||
SUPPORTED_TYPES_HEADER = """# pyasic
|
SUPPORTED_TYPES_HEADER = """# pyasic
|
||||||
@@ -71,6 +97,8 @@ SUPPORTED_TYPES_HEADER = """# pyasic
|
|||||||
|
|
||||||
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
Supported miner types are here on this list. If your miner (or miner version) is not on this list, please feel free to [open an issue on GitHub](https://github.com/UpstreamData/pyasic/issues) to get it added.
|
||||||
|
|
||||||
|
Keep in mind that some functionality is only supported for specific miners or firmwares, please check the page for your miner to make sure the functionality you need is supported.
|
||||||
|
|
||||||
##### pyasic currently supports the following miners and subtypes:
|
##### pyasic currently supports the following miners and subtypes:
|
||||||
<style>
|
<style>
|
||||||
details {
|
details {
|
||||||
@@ -100,40 +128,50 @@ BACKEND_TYPE_CLOSER = """
|
|||||||
</ul>
|
</ul>
|
||||||
</details>"""
|
</details>"""
|
||||||
|
|
||||||
m_data = {}
|
m_data: dict[str, dict[str, list[type[Any]]]] = {}
|
||||||
|
done = []
|
||||||
|
|
||||||
for m in MINER_CLASSES:
|
for m in MINER_CLASSES:
|
||||||
for t in MINER_CLASSES[m]:
|
for t in sorted(MINER_CLASSES[m], key=lambda x: x or ""):
|
||||||
if t is not None:
|
if t is not None and MINER_CLASSES[m][t] not in done:
|
||||||
miner = MINER_CLASSES[m][t]
|
miner = MINER_CLASSES[m][t]
|
||||||
if make(miner) not in m_data:
|
if make(miner) not in m_data:
|
||||||
m_data[make(miner)] = {}
|
m_data[make(miner)] = {}
|
||||||
if model_type(miner) not in m_data[make(miner)]:
|
if model_type(miner) not in m_data[make(miner)]:
|
||||||
m_data[make(miner)][model_type(miner)] = []
|
m_data[make(miner)][model_type(miner)] = []
|
||||||
m_data[make(miner)][model_type(miner)].append(miner)
|
m_data[make(miner)][model_type(miner)].append(miner)
|
||||||
|
done.append(miner)
|
||||||
|
|
||||||
|
|
||||||
async def create_directory_structure(directory, data):
|
def create_directory_structure(directory, data):
|
||||||
if not os.path.exists(directory):
|
if not os.path.exists(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
|
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
subdirectory = os.path.join(directory, key)
|
subdirectory = os.path.join(directory, key)
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
await create_directory_structure(subdirectory, value)
|
create_directory_structure(subdirectory, value)
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
file_path = os.path.join(subdirectory + ".md")
|
file_path = os.path.join(subdirectory + ".md")
|
||||||
|
|
||||||
with open(file_path, "w") as file:
|
with open(file_path, "w") as file:
|
||||||
file.write(HEADER_FORMAT.format(key))
|
file.write(HEADER_FORMAT.format(key))
|
||||||
for item in value:
|
for item in value:
|
||||||
header = await item("1.1.1.1").get_model()
|
obj = item("1.1.1.1")
|
||||||
|
header = obj.model
|
||||||
file.write(MINER_HEADER_FORMAT.format(header))
|
file.write(MINER_HEADER_FORMAT.format(header))
|
||||||
file.write(DATA_FORMAT.format(path(item)))
|
file.write(
|
||||||
|
DATA_FORMAT.format(
|
||||||
|
"x" if obj.supports_shutdown else " ",
|
||||||
|
"x" if obj.supports_power_modes else " ",
|
||||||
|
"x" if obj.supports_autotuning else " ",
|
||||||
|
"x" if obj.supports_presets else " ",
|
||||||
|
path(item),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def create_supported_types(directory):
|
def create_supported_types(directory):
|
||||||
with open(os.path.join(directory, "supported_types.md"), "w") as file:
|
with open(os.path.join(directory, "supported_types.md"), "w") as file:
|
||||||
file.write(SUPPORTED_TYPES_HEADER)
|
file.write(SUPPORTED_TYPES_HEADER)
|
||||||
for mback in MINER_CLASSES:
|
for mback in MINER_CLASSES:
|
||||||
@@ -150,7 +188,7 @@ async def create_supported_types(directory):
|
|||||||
for mtype in backend_types:
|
for mtype in backend_types:
|
||||||
file.write(MINER_TYPE_HEADER.format(mtype))
|
file.write(MINER_TYPE_HEADER.format(mtype))
|
||||||
for minstance in backend_types[mtype]:
|
for minstance in backend_types[mtype]:
|
||||||
model = await minstance("1.1.1.1").get_model()
|
model = minstance("1.1.1.1").model
|
||||||
file.write(
|
file.write(
|
||||||
MINER_DETAILS.format(
|
MINER_DETAILS.format(
|
||||||
make(minstance), mtype, create_url_str(model), model
|
make(minstance), mtype, create_url_str(model), model
|
||||||
@@ -160,6 +198,7 @@ async def create_supported_types(directory):
|
|||||||
file.write(BACKEND_TYPE_CLOSER)
|
file.write(BACKEND_TYPE_CLOSER)
|
||||||
|
|
||||||
|
|
||||||
root_directory = os.path.join(os.getcwd(), "miners")
|
if __name__ == "__main__":
|
||||||
asyncio.run(create_directory_structure(root_directory, m_data))
|
root_directory = Path(__file__).parent.joinpath("miners")
|
||||||
asyncio.run(create_supported_types(root_directory))
|
create_directory_structure(root_directory, m_data)
|
||||||
|
create_supported_types(root_directory)
|
||||||
|
|||||||
190
docs/index.md
190
docs/index.md
@@ -11,120 +11,149 @@
|
|||||||
[](https://pyasic.readthedocs.io/en/latest/)
|
[](https://pyasic.readthedocs.io/en/latest/)
|
||||||
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
[](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
---
|
|
||||||
## Intro
|
## Intro
|
||||||
---
|
---
|
||||||
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast.
|
||||||
|
|
||||||
[Click here to view supported miner types](miners/supported_types.md)
|
[Click here to view supported miner types](miners/supported_types.md)
|
||||||
|
|
||||||
|
## Installation
|
||||||
---
|
---
|
||||||
|
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system.
|
||||||
|
`pyasic` can be installed directly from pip, either with `pip install pyasic`, or a different command if using a tool like `pypoetry`.
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
---
|
---
|
||||||
Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
|
Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP.
|
||||||
|
|
||||||
##### Scanning for miners
|
### Scanning for miners
|
||||||
To scan for miners in `pyasic`, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
|
To scan for miners in `pyasic`, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command.
|
||||||
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
|
The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found.
|
||||||
```python
|
```python3
|
||||||
import asyncio # asyncio for handling the async part
|
import asyncio# (1)!
|
||||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
from pyasic.network import MinerNetwork# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def scan_miners(): # define async scan function to allow awaiting
|
async def scan_miners():# (3)!
|
||||||
# create a miner network
|
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
||||||
# you can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
|
||||||
network = MinerNetwork.from_subnet("192.168.1.50/24") # this uses the 192.168.1.0-255 network
|
|
||||||
|
|
||||||
# scan for miners asynchronously
|
miners = await network.scan()# (5)!
|
||||||
# this will return the correct type of miners if they are supported with all functionality.
|
|
||||||
miners = await network.scan()
|
|
||||||
print(miners)
|
print(miners)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(scan_miners()) # run the scan asynchronously with asyncio.run()
|
asyncio.run(scan_miners())# (6)!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `MinerNetwork` handles the scanning.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Create a miner network.
|
||||||
|
You can pass in any IP and it will use that in a subnet with a /24 mask (255 IPs).
|
||||||
|
This uses the 192.168.1.0-255 network.
|
||||||
|
5. Scan for miners asynchronously.
|
||||||
|
This will return the correct type of miners (if they are supported) with all functionality.
|
||||||
|
6. Run the scan asynchronously with asyncio.run().
|
||||||
|
|
||||||
---
|
---
|
||||||
##### Creating miners based on IP
|
### Creating miners based on IP
|
||||||
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.miner_factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner].
|
If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner].
|
||||||
The function [`get_miner()`][pyasic.miners.get_miner] will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner.
|
The function [`get_miner()`][pyasic.miners.get_miner] will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner.
|
||||||
```python
|
```python
|
||||||
import asyncio # asyncio for handling the async part
|
import asyncio# (1)!
|
||||||
from pyasic import get_miner # handles miner creation
|
from pyasic import get_miner# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def get_miners(): # define async scan function to allow awaiting
|
async def get_miners():# (3)!
|
||||||
# get the miner with the miner factory
|
miner_1 = await get_miner("192.168.1.75")# (4)!
|
||||||
# the miner factory is a singleton, and will always use the same object and cache
|
|
||||||
# this means you can always call it as MinerFactory().get_miner(), or just get_miner()
|
|
||||||
miner_1 = await get_miner("192.168.1.75")
|
|
||||||
miner_2 = await get_miner("192.168.1.76")
|
miner_2 = await get_miner("192.168.1.76")
|
||||||
print(miner_1, miner_2)
|
print(miner_1, miner_2)
|
||||||
|
|
||||||
# can also gather these, since they are async
|
|
||||||
# gathering them will get them both at the same time
|
|
||||||
# this makes it much faster to get a lot of miners at a time
|
|
||||||
tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")]
|
tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")]
|
||||||
miners = await asyncio.gather(*tasks)
|
miners = await asyncio.gather(*tasks)# (5)!
|
||||||
print(miners)
|
print(miners)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run()
|
asyncio.run(get_miners())# (6)!
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `get_miner` handles the miner type selection.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Get the miner.
|
||||||
|
5. Can also gather these, since they are async.
|
||||||
|
Gathering them will get them both at the same time.
|
||||||
|
This makes it much faster to get a lot of miners at a time.
|
||||||
|
6. Get the miners asynchronously with asyncio.run().
|
||||||
|
|
||||||
## Data gathering
|
## Data gathering
|
||||||
---
|
---
|
||||||
Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`.
|
Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`.
|
||||||
This function will return an instance of the dataclass [`MinerData`][pyasic.data.MinerData] with all data it can gather from the miner.
|
This function will return an instance of the dataclass [`MinerData`][pyasic.data.MinerData] with all data it can gather from the miner.
|
||||||
Each piece of data in a [`MinerData`][pyasic.data.MinerData] instance can be referenced by getting it as an attribute, such as [`MinerData().hashrate`][pyasic.data.MinerData].
|
Each piece of data in a [`MinerData`][pyasic.data.MinerData] instance can be referenced by getting it as an attribute, such as [`MinerData().hashrate`][pyasic.data.MinerData].
|
||||||
|
|
||||||
##### One miner
|
### One miner
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio# (1)!
|
||||||
from pyasic import get_miner
|
from pyasic import get_miner# (2)!
|
||||||
|
|
||||||
async def gather_miner_data():
|
|
||||||
miner = await get_miner("192.168.1.75")
|
async def gather_miner_data():# (3)!
|
||||||
if miner is not None:
|
miner = await get_miner("192.168.1.75")# (4)!
|
||||||
miner_data = await miner.get_data()
|
if miner is not None:# (5)!
|
||||||
print(miner_data) # all data from the dataclass
|
miner_data = await miner.get_data()# (6)!
|
||||||
|
print(miner_data)# (7)!
|
||||||
print(miner_data.hashrate) # hashrate of the miner in TH/s
|
print(miner_data.hashrate) # hashrate of the miner in TH/s
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(gather_miner_data())
|
asyncio.run(gather_miner_data())# (9)!
|
||||||
```
|
```
|
||||||
---
|
|
||||||
##### Multiple miners
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `get_miner` handles the miner type selection.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Get the miner.
|
||||||
|
5. Make sure the miner exists.
|
||||||
|
If this result is `None`, the miner may be offline.
|
||||||
|
6. Get data from the miner.
|
||||||
|
7. All the data from the dataclass.
|
||||||
|
8. Hashrate of the miner, with unit information.
|
||||||
|
9. Get the miner data asynchronously with asyncio.run().
|
||||||
|
|
||||||
|
### Multiple miners
|
||||||
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
|
You can do something similar with multiple miners, with only needing to make a small change to get all the data at once.
|
||||||
```python
|
```python
|
||||||
import asyncio # asyncio for handling the async part
|
import asyncio# (1)!
|
||||||
from pyasic.network import MinerNetwork # miner network handles the scanning
|
from pyasic.network import MinerNetwork# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def gather_miner_data(): # define async scan function to allow awaiting
|
async def gather_miner_data():# (3)!
|
||||||
network = MinerNetwork.from_subnet("192.168.1.50/24")
|
network = MinerNetwork.from_subnet("192.168.1.50/24")# (4)!
|
||||||
miners = await network.scan()
|
miners = await network.scan()# (5)!
|
||||||
|
|
||||||
# we need to asyncio.gather() all the miners get_data() functions to make them run together
|
|
||||||
all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners])
|
all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners])
|
||||||
|
|
||||||
for miner_data in all_miner_data:
|
for miner_data in all_miner_data:
|
||||||
print(miner_data) # print out all the data one by one
|
print(miner_data)# (7)!
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(gather_miner_data())
|
asyncio.run(gather_miner_data())# (8)!
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `MinerNetwork` handles the scanning.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Create a miner network.
|
||||||
|
5. Scan for miners asynchronously.
|
||||||
|
6. Use `asyncio.gather()` with all the miners' `get_data()` functions to make them run together.
|
||||||
|
7. Print out the data one at a time.
|
||||||
|
8. Get the miner data asynchronously with asyncio.run().
|
||||||
|
|
||||||
## Miner control
|
## Miner control
|
||||||
---
|
---
|
||||||
`pyasic` exposes a standard interface for each miner using control functions.
|
`pyasic` exposes a standard interface for each miner using control functions.
|
||||||
Every miner class in `pyasic` must implement all the control functions defined in [`MinerProtocol`][pyasic.miners.base.MinerProtocol].
|
Every miner class in `pyasic` must implement all the following control functions.
|
||||||
|
|
||||||
These functions are
|
|
||||||
[`check_light`][pyasic.miners.base.MinerProtocol.check_light],
|
[`check_light`][pyasic.miners.base.MinerProtocol.check_light],
|
||||||
[`fault_light_off`][pyasic.miners.base.MinerProtocol.fault_light_off],
|
[`fault_light_off`][pyasic.miners.base.MinerProtocol.fault_light_off],
|
||||||
[`fault_light_on`][pyasic.miners.base.MinerProtocol.fault_light_on],
|
[`fault_light_on`][pyasic.miners.base.MinerProtocol.fault_light_on],
|
||||||
@@ -141,35 +170,41 @@ These functions are
|
|||||||
[`send_config`][pyasic.miners.base.MinerProtocol.send_config], and
|
[`send_config`][pyasic.miners.base.MinerProtocol.send_config], and
|
||||||
[`set_power_limit`][pyasic.miners.base.MinerProtocol.set_power_limit].
|
[`set_power_limit`][pyasic.miners.base.MinerProtocol.set_power_limit].
|
||||||
|
|
||||||
##### Usage
|
### Usage
|
||||||
```python
|
```python
|
||||||
import asyncio
|
import asyncio# (1)!
|
||||||
from pyasic import get_miner
|
from pyasic import get_miner# (2)!
|
||||||
|
|
||||||
|
|
||||||
async def set_fault_light():
|
async def set_fault_light():# (3)!
|
||||||
miner = await get_miner("192.168.1.20")
|
miner = await get_miner("192.168.1.20")# (4)!
|
||||||
|
|
||||||
# call control function
|
await miner.fault_light_on()# (5)!
|
||||||
await miner.fault_light_on()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(set_fault_light())
|
asyncio.run(set_fault_light())# (6)!
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
1. `asyncio` for handling the async part.
|
||||||
|
2. `get_miner` handles the miner type selection.
|
||||||
|
3. Define an async function to allow awaiting.
|
||||||
|
4. Get the miner.
|
||||||
|
5. Call the miner control function.
|
||||||
|
6. Call the control function asynchronously with asyncio.run().
|
||||||
|
|
||||||
|
|
||||||
## Helper dataclasses
|
## Helper dataclasses
|
||||||
---
|
---
|
||||||
|
|
||||||
##### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData]
|
||||||
|
|
||||||
`pyasic` implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
`pyasic` implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
##### [`MinerData`][pyasic.data.MinerData]
|
### [`MinerData`][pyasic.data.MinerData]
|
||||||
|
|
||||||
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns.
|
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`][pyasic.miners.base.MinerProtocol.get_data] function, and is used to have a consistent dataset across all returns.
|
||||||
|
|
||||||
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
|
||||||
|
|
||||||
@@ -188,13 +223,13 @@ average_data = sum(list_of_miner_data, start=MinerData("0.0.0.0"))/len(list_of_m
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
##### [`MinerConfig`][pyasic.config.MinerConfig]
|
### [`MinerConfig`][pyasic.config.MinerConfig]
|
||||||
|
|
||||||
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner.
|
||||||
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`](#get-config).
|
It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`][pyasic.miners.base.MinerProtocol.get_config].
|
||||||
|
|
||||||
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
|
||||||
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
In most cases these helper functions should not be used, as [`send_config()`][pyasic.miners.base.MinerProtocol.send_config] takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
|
||||||
|
|
||||||
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows:
|
||||||
```python
|
```python
|
||||||
@@ -216,7 +251,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
## Settings
|
## Settings
|
||||||
---
|
---
|
||||||
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows:
|
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows:
|
||||||
@@ -224,24 +258,30 @@ if __name__ == "__main__":
|
|||||||
```python
|
```python
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
|
|
||||||
settings.update("default_antminer_password", "my_pwd")
|
settings.update("default_antminer_web_password", "my_pwd")
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Default values:
|
### Default values:
|
||||||
```
|
```
|
||||||
"network_ping_retries": 1,
|
"network_ping_retries": 1,
|
||||||
"network_ping_timeout": 3,
|
"network_ping_timeout": 3,
|
||||||
"network_scan_threads": 300,
|
"network_scan_semaphore": None,
|
||||||
"factory_get_retries": 1,
|
"factory_get_retries": 1,
|
||||||
"factory_get_timeout": 3,
|
"factory_get_timeout": 3,
|
||||||
"get_data_retries": 1,
|
"get_data_retries": 1,
|
||||||
"api_function_timeout": 5,
|
"api_function_timeout": 5,
|
||||||
"default_whatsminer_password": "admin",
|
"antminer_mining_mode_as_str": False,
|
||||||
"default_innosilicon_password": "admin",
|
"default_whatsminer_rpc_password": "admin",
|
||||||
"default_antminer_password": "root",
|
"default_innosilicon_web_password": "admin",
|
||||||
"default_bosminer_password": "root",
|
"default_antminer_web_password": "root",
|
||||||
"default_vnish_password": "admin",
|
"default_bosminer_web_password": "root",
|
||||||
"default_goldshell_password": "123456789",
|
"default_vnish_web_password": "admin",
|
||||||
|
"default_goldshell_web_password": "123456789",
|
||||||
|
"default_auradine_web_password": "admin",
|
||||||
|
"default_epic_web_password": "letmein",
|
||||||
|
"default_hive_web_password": "admin",
|
||||||
|
"default_antminer_ssh_password": "miner",
|
||||||
|
"default_bosminer_ssh_password": "root",
|
||||||
|
|
||||||
# ADVANCED
|
# ADVANCED
|
||||||
# Only use this if you know what you are doing
|
# Only use this if you know what you are doing
|
||||||
|
|||||||
@@ -1,10 +1,29 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X15 Models
|
## X15 Models
|
||||||
|
|
||||||
## Z15
|
## Z15 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15
|
::: pyasic.miners.antminer.cgminer.X15.Z15.CGMinerZ15
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Z15 Pro (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X15.Z15.BMMinerZ15Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,115 +1,211 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X17 Models
|
## X17 Models
|
||||||
|
|
||||||
## S17
|
## S17 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## S17+
|
## S17 Pro (Stock)
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
|
|
||||||
handler: python
|
- [ ] Shutdowns
|
||||||
options:
|
- [ ] Power Modes
|
||||||
show_root_heading: false
|
- [ ] Setpoints
|
||||||
heading_level: 4
|
- [ ] Presets
|
||||||
|
|
||||||
## S17 Pro
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S17+ (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S17e (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S17e
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e
|
::: pyasic.miners.antminer.bmminer.X17.S17.BMMinerS17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T17 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T17
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
|
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T17+ (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T17+
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus
|
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T17e (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T17e
|
|
||||||
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e
|
::: pyasic.miners.antminer.bmminer.X17.T17.BMMinerT17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S17 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S17 (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## S17+ (BOS)
|
## S17 Pro (BOS+)
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
|
|
||||||
handler: python
|
- [x] Shutdowns
|
||||||
options:
|
- [ ] Power Modes
|
||||||
show_root_heading: false
|
- [x] Setpoints
|
||||||
heading_level: 4
|
- [ ] Presets
|
||||||
|
|
||||||
## S17 Pro (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Pro
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S17+ (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S17e (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S17e (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17e
|
::: pyasic.miners.antminer.bosminer.X17.S17.BOSMinerS17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T17 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T17 (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17
|
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T17+ (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T17+ (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17Plus
|
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T17e (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T17e (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17e
|
::: pyasic.miners.antminer.bosminer.X17.T17.BOSMinerT17e
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## S17+ (VNish)
|
|
||||||
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
## S17 Pro (VNish)
|
## S17 Pro (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Pro
|
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S17+ (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X17.S17.VNishS17Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
328
docs/miners/antminer/X21.md
Normal file
328
docs/miners/antminer/X21.md
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
# pyasic
|
||||||
|
## X21 Models
|
||||||
|
|
||||||
|
## S21 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Hydro (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Hydro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Pro (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21+ (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21+ Hydro (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X21.S21.BMMinerS21PlusHydro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T21 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X21.T21.BMMinerT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Hydro (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Hydro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Pro (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21+ (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21+ Hydro (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21PlusHydro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T21 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bosminer.X21.T21.BOSMinerT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Hydro (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Hydro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Pro (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21+ (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21+ Hydro (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21PlusHydro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T21 (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X21.T21.VNishT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 (ePIC)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.epic.X21.S21.ePICS21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 Pro (ePIC)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.epic.X21.S21.ePICS21Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T21 (ePIC)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.epic.X21.T21.ePICT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 (LuxOS)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.luxos.X21.S21.LUXMinerS21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T21 (LuxOS)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.luxos.X21.T21.LUXMinerT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S21 (MaraFW)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.marathon.X21.S21.MaraS21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T21 (MaraFW)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.marathon.X21.T21.MaraT21
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,31 +1,81 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X3 Models
|
## X3 Models
|
||||||
|
|
||||||
## D3
|
## D3 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3
|
::: pyasic.miners.antminer.cgminer.X3.D3.CGMinerD3
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## HS3 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## HS3
|
|
||||||
::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3
|
::: pyasic.miners.antminer.bmminer.X3.HS3.BMMinerHS3
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## KA3 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X3.KA3.BMMinerKA3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS3 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X3.KS3.BMMinerKS3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## L3+ (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## L3+
|
|
||||||
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
|
::: pyasic.miners.antminer.bmminer.X3.L3.BMMinerL3Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## L3+ (VNish)
|
## L3+ (VNish)
|
||||||
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X3.L3.VNishL3Plus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X5 Models
|
## X5 Models
|
||||||
|
|
||||||
## DR5
|
## DR5 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5
|
::: pyasic.miners.antminer.cgminer.X5.DR5.CGMinerDR5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS5 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS5 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,55 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X7 Models
|
## X7 Models
|
||||||
|
|
||||||
## L7
|
## D7 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## K7 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X7.K7.BMMinerK7
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## L7 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
|
::: pyasic.miners.antminer.bmminer.X7.L7.BMMinerL7
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## L7 (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X7.L7.VNishL7
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,59 +1,146 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X9 Models
|
## X9 Models
|
||||||
|
|
||||||
## E9Pro
|
## D9 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## E9Pro (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro
|
::: pyasic.miners.antminer.bmminer.X9.E9.BMMinerE9Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## L9 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.bmminer.X9.L9.BMMinerL9
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## S9 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S9
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S9i (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S9i
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9i
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S9j (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S9j
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j
|
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9j
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## T9 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T9
|
|
||||||
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
|
::: pyasic.miners.antminer.bmminer.X9.T9.BMMinerT9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## S9 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## S9 (BOS)
|
|
||||||
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
|
::: pyasic.miners.antminer.bosminer.X9.S9.BOSMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## L9 (VNish)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.vnish.X9.L9.VNishL9
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## T9 (Hive)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## T9 (Hiveon)
|
|
||||||
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
|
::: pyasic.miners.antminer.hiveon.X9.T9.HiveonT9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## S9 (LuxOS)
|
## S9 (LuxOS)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [x] Presets
|
||||||
|
|
||||||
::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9
|
::: pyasic.miners.antminer.luxos.X9.S9.LUXMinerS9
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
42
docs/miners/auradine/AD.md
Normal file
42
docs/miners/auradine/AD.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# pyasic
|
||||||
|
## AD Models
|
||||||
|
|
||||||
|
## AT1500 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AD.AT1.AuradineFluxAT1500
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## AT2860 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2860
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## AT2880 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AD.AT2.AuradineFluxAT2880
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
29
docs/miners/auradine/AI.md
Normal file
29
docs/miners/auradine/AI.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## AI Models
|
||||||
|
|
||||||
|
## AI2500 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AI.AI2.AuradineFluxAI2500
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## AI3680 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AI.AI3.AuradineFluxAI3680
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
29
docs/miners/auradine/AT.md
Normal file
29
docs/miners/auradine/AT.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## AT Models
|
||||||
|
|
||||||
|
## AD2500 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AT.AD2.AuradineFluxAD2500
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## AD3500 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.auradine.flux.AT.AD3.AuradineFluxAD3500
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,24 +1,42 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A10X Models
|
## A10X Models
|
||||||
|
|
||||||
## Avalon 1026
|
## Avalon 1026 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
|
::: pyasic.miners.avalonminer.cgminer.A10X.A1026.CGMinerAvalon1026
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 1047 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## Avalon 1047
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
|
::: pyasic.miners.avalonminer.cgminer.A10X.A1047.CGMinerAvalon1047
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 1066 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## Avalon 1066
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
|
::: pyasic.miners.avalonminer.cgminer.A10X.A1066.CGMinerAvalon1066
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,29 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A11X Models
|
## A11X Models
|
||||||
|
|
||||||
## Avalon 1166 Pro
|
## Avalon 1126 Pro (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.A11X.A1126.CGMinerAvalon1126Pro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 1166 Pro (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
|
::: pyasic.miners.avalonminer.cgminer.A11X.A1166.CGMinerAvalon1166Pro
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A12X Models
|
## A12X Models
|
||||||
|
|
||||||
## Avalon 1246
|
## Avalon 1246 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246
|
::: pyasic.miners.avalonminer.cgminer.A12X.A1246.CGMinerAvalon1246
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
16
docs/miners/avalonminer/A15X.md
Normal file
16
docs/miners/avalonminer/A15X.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## A15X Models
|
||||||
|
|
||||||
|
## Avalon 1566 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.A15X.A1566.CGMinerAvalon1566
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,24 +1,42 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A7X Models
|
## A7X Models
|
||||||
|
|
||||||
## Avalon 721
|
## Avalon 721 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
|
::: pyasic.miners.avalonminer.cgminer.A7X.A721.CGMinerAvalon721
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 741 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## Avalon 741
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
|
::: pyasic.miners.avalonminer.cgminer.A7X.A741.CGMinerAvalon741
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 761 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## Avalon 761
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
|
::: pyasic.miners.avalonminer.cgminer.A7X.A761.CGMinerAvalon761
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,42 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A8X Models
|
## A8X Models
|
||||||
|
|
||||||
## Avalon 821
|
## Avalon 821 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
|
::: pyasic.miners.avalonminer.cgminer.A8X.A821.CGMinerAvalon821
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 841 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## Avalon 841
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
|
::: pyasic.miners.avalonminer.cgminer.A8X.A841.CGMinerAvalon841
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon 851 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## Avalon 851
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
|
::: pyasic.miners.avalonminer.cgminer.A8X.A851.CGMinerAvalon851
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A9X Models
|
## A9X Models
|
||||||
|
|
||||||
## Avalon 921
|
## Avalon 921 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
|
::: pyasic.miners.avalonminer.cgminer.A9X.A921.CGMinerAvalon921
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
16
docs/miners/avalonminer/Q.md
Normal file
16
docs/miners/avalonminer/Q.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## Q Models
|
||||||
|
|
||||||
|
## Avalon Q Home (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.Q.Q.CGMinerAvalonQHome
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
29
docs/miners/avalonminer/nano.md
Normal file
29
docs/miners/avalonminer/nano.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## nano Models
|
||||||
|
|
||||||
|
## Avalon Nano 3s (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3s
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## Avalon Nano 3 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.nano.nano3.CGMinerAvalonNano3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## Hiveon Backend
|
## Modern Hiveon Backend
|
||||||
|
|
||||||
::: pyasic.miners.backends.hiveon.Hiveon
|
::: pyasic.miners.backends.hiveon.HiveonModern
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## Old Hiveon Backend
|
||||||
|
::: pyasic.miners.backends.hiveon.HiveonOld
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
|
|||||||
@@ -10,8 +10,3 @@ You may not instantiate this class on its own, only subclass from it.
|
|||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
::: pyasic.miners.base.MinerProtocol
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|||||||
55
docs/miners/bitaxe/BM.md
Normal file
55
docs/miners/bitaxe/BM.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# pyasic
|
||||||
|
## BM Models
|
||||||
|
|
||||||
|
## Ultra (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.bitaxe.espminer.BM.BM1366.BitAxeUltra
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## Supra (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.bitaxe.espminer.BM.BM1368.BitAxeSupra
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## Gamma (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.bitaxe.espminer.BM.BM1370.BitAxeGamma
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## Max (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.bitaxe.espminer.BM.BM1397.BitAxeMax
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
42
docs/miners/blockminer/blockminer.md
Normal file
42
docs/miners/blockminer/blockminer.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# pyasic
|
||||||
|
## blockminer Models
|
||||||
|
|
||||||
|
## BlockMiner 520i (ePIC)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner520i
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## BlockMiner 720i (ePIC)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMiner720i
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## BlockMiner eLITE 1.0 (ePIC)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.blockminer.epic.blockminer.blockminer.ePICBlockMinerELITE1
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
29
docs/miners/braiins/BMM.md
Normal file
29
docs/miners/braiins/BMM.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## BMM Models
|
||||||
|
|
||||||
|
## BMM100 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.braiins.braiins.BMM.BMM.BraiinsBMM100
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## BMM101 (BOS+)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [x] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.braiins.braiins.BMM.BMM.BraiinsBMM101
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
42
docs/miners/elphapex/DGX.md
Normal file
42
docs/miners/elphapex/DGX.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# pyasic
|
||||||
|
## DGX Models
|
||||||
|
|
||||||
|
## DG1 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## DG1+ (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1Plus
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## DG1Home (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1Home
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,91 +1,10 @@
|
|||||||
## Control functionality
|
## Control functionality
|
||||||
|
|
||||||
### Check Light
|
All control functionality is outlined by the [`MinerProtocol`][pyasic.miners.base.MinerProtocol] class.
|
||||||
::: pyasic.miners.base.MinerProtocol.check_light
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Fault Light Off
|
## Miner Protocol
|
||||||
::: pyasic.miners.base.MinerProtocol.fault_light_off
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Fault Light On
|
::: pyasic.miners.base.MinerProtocol
|
||||||
::: pyasic.miners.base.MinerProtocol.fault_light_on
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Get Config
|
|
||||||
::: pyasic.miners.base.MinerProtocol.get_config
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Get Data
|
|
||||||
::: pyasic.miners.base.MinerProtocol.get_data
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Get Errors
|
|
||||||
::: pyasic.miners.base.MinerProtocol.get_errors
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Get Hostname
|
|
||||||
::: pyasic.miners.base.MinerProtocol.get_hostname
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Get Model
|
|
||||||
::: pyasic.miners.base.MinerProtocol.get_model
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Reboot
|
|
||||||
::: pyasic.miners.base.MinerProtocol.reboot
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Restart Backend
|
|
||||||
::: pyasic.miners.base.MinerProtocol.restart_backend
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Stop Mining
|
|
||||||
::: pyasic.miners.base.MinerProtocol.stop_mining
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Resume Mining
|
|
||||||
::: pyasic.miners.base.MinerProtocol.resume_mining
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Is Mining
|
|
||||||
::: pyasic.miners.base.MinerProtocol.is_mining
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Send Config
|
|
||||||
::: pyasic.miners.base.MinerProtocol.send_config
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
### Set Power Limit
|
|
||||||
::: pyasic.miners.base.MinerProtocol.set_power_limit
|
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|||||||
16
docs/miners/goldshell/Byte.md
Normal file
16
docs/miners/goldshell/Byte.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## byte Models
|
||||||
|
|
||||||
|
## Byte (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.goldshell.bfgminer.byte.byte.GoldshellByte
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
16
docs/miners/goldshell/MiniDoge.md
Normal file
16
docs/miners/goldshell/MiniDoge.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## Mini Doge Models
|
||||||
|
|
||||||
|
## Mini Doge (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.goldshell.bfgminer.MiniDoge.MiniDoge.GoldshellMiniDoge
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,23 +1,42 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## X5 Models
|
## X5 Models
|
||||||
|
|
||||||
## CK5
|
## CK5 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5
|
::: pyasic.miners.goldshell.bfgminer.X5.CK5.GoldshellCK5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## HS5 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## HS5
|
|
||||||
::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5
|
::: pyasic.miners.goldshell.bfgminer.X5.HS5.GoldshellHS5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## KD5 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## KD5
|
|
||||||
::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5
|
::: pyasic.miners.goldshell.bfgminer.X5.KD5.GoldshellKD5
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
29
docs/miners/goldshell/XBox.md
Normal file
29
docs/miners/goldshell/XBox.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## XBox Models
|
||||||
|
|
||||||
|
## KD Box II (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxII
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KD Box Pro (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.goldshell.bfgminer.XBox.KDBox.GoldshellKDBoxPro
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## XMax Models
|
## XMax Models
|
||||||
|
|
||||||
## KD Max
|
## KD Max (Stock)
|
||||||
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.KDMax
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.goldshell.bfgminer.XMax.KDMax.GoldshellKDMax
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
16
docs/miners/goldshell/mini_doge.md
Normal file
16
docs/miners/goldshell/mini_doge.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## mini_doge Models
|
||||||
|
|
||||||
|
## Mini Doge (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.goldshell.bfgminer.mini_doge.mini_doge.GoldshellMiniDoge
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
16
docs/miners/hammer/DX.md
Normal file
16
docs/miners/hammer/DX.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## DX Models
|
||||||
|
|
||||||
|
## D10 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.hammer.blackminer.DX.D10.HammerD10
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
16
docs/miners/iceriver/ALX.md
Normal file
16
docs/miners/iceriver/ALX.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## ALX Models
|
||||||
|
|
||||||
|
## AL3 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.ALX.AL3.IceRiverAL3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
120
docs/miners/iceriver/KSX.md
Normal file
120
docs/miners/iceriver/KSX.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# pyasic
|
||||||
|
## KSX Models
|
||||||
|
|
||||||
|
## KS0 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS0.IceRiverKS0
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS1 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS1.IceRiverKS1
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS2 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS3 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS3L (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3L
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS3M (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3M
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS5 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS5L (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5L
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## KS5M (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5M
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## A10X Models
|
## A10X Models
|
||||||
|
|
||||||
## A10X
|
## A10X (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
|
::: pyasic.miners.innosilicon.cgminer.A10X.A10X.InnosiliconA10X
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
29
docs/miners/innosilicon/A11X.md
Normal file
29
docs/miners/innosilicon/A11X.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## A11X Models
|
||||||
|
|
||||||
|
## A11 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.innosilicon.cgminer.A11X.A11.InnosiliconA11
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## A11MX (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.innosilicon.cgminer.A11X.A11M.InnosiliconA11MX
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,9 +1,16 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## T3X Models
|
## T3X Models
|
||||||
|
|
||||||
## T3H+
|
## T3H+ (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
|
::: pyasic.miners.innosilicon.cgminer.T3X.T3H.InnosiliconT3HPlus
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
29
docs/miners/luckyminer/LV.md
Normal file
29
docs/miners/luckyminer/LV.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# pyasic
|
||||||
|
## LV Models
|
||||||
|
|
||||||
|
## LV07 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.luckyminer.espminer.LV.LV07.LuckyMinerLV07
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## LV08 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.luckyminer.espminer.LV.LV08.LuckyMinerLV08
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## Miner Factory
|
## Miner Factory
|
||||||
|
|
||||||
[`MinerFactory`][pyasic.miners.miner_factory.MinerFactory] is the way to create miner types in `pyasic`. The most important method is [`get_miner()`][pyasic.get_miner], which is mapped to [`pyasic.get_miner()`][pyasic.get_miner], and should be used from there.
|
[`MinerFactory`][pyasic.miners.factory.MinerFactory] is the way to create miner types in `pyasic`. The most important method is [`get_miner()`][pyasic.get_miner], which is mapped to [`pyasic.get_miner()`][pyasic.get_miner], and should be used from there.
|
||||||
|
|
||||||
The instance used for [`pyasic.get_miner()`][pyasic.get_miner] is `pyasic.miner_factory`.
|
The instance used for [`pyasic.get_miner()`][pyasic.get_miner] is `pyasic.miner_factory`.
|
||||||
|
|
||||||
[`MinerFactory`][pyasic.MinerFactory] also keeps a cache, which can be cleared if needed with `pyasic.miner_factory.clear_cached_miners()`.
|
[`MinerFactory`][pyasic.miners.factory.MinerFactory] also keeps a cache, which can be cleared if needed with `pyasic.miner_factory.clear_cached_miners()`.
|
||||||
|
|
||||||
Finally, there is functionality to get multiple miners without using `asyncio.gather()` explicitly. Use `pyasic.miner_factory.get_multiple_miners()` with a list of IPs as strings to get a list of miner instances. You can also get multiple miners with an `AsyncGenerator` by using `pyasic.miner_factory.get_miner_generator()`.
|
Finally, there is functionality to get multiple miners without using `asyncio.gather()` explicitly. Use `pyasic.miner_factory.get_multiple_miners()` with a list of IPs as strings to get a list of miner instances. You can also get multiple miners with an `AsyncGenerator` by using `pyasic.miner_factory.get_miner_generator()`.
|
||||||
|
|
||||||
::: pyasic.miners.miner_factory.MinerFactory
|
::: pyasic.miners.factory.MinerFactory
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
@@ -25,12 +25,12 @@ Finally, there is functionality to get multiple miners without using `asyncio.ga
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
## AnyMiner
|
## AnyMiner
|
||||||
::: pyasic.miners.miner_factory.AnyMiner
|
::: pyasic.miners.base.AnyMiner
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|
||||||
[`AnyMiner`][pyasic.miners.miner_factory.AnyMiner] is a placeholder type variable used for typing returns of functions.
|
[`AnyMiner`][pyasic.miners.base.AnyMiner] is a placeholder type variable used for typing returns of functions.
|
||||||
A function returning [`AnyMiner`][pyasic.miners.miner_factory.AnyMiner] will always return a subclass of [`BaseMiner`][pyasic.miners.BaseMiner],
|
A function returning [`AnyMiner`][pyasic.miners.base.AnyMiner] will always return a subclass of [`BaseMiner`][pyasic.miners.base.BaseMiner],
|
||||||
and is used to specify a function returning some arbitrary type of miner class instance.
|
and is used to specify a function returning some arbitrary type of miner class instance.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
16
docs/miners/volcminer/DX.md
Normal file
16
docs/miners/volcminer/DX.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## DX Models
|
||||||
|
|
||||||
|
## D1 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.volcminer.blackminer.DX.D1.VolcMinerD1
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,94 +1,172 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## M2X Models
|
## M2X Models
|
||||||
|
|
||||||
## M20 V10
|
## M20P V10 (Stock)
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
## M20S V10
|
- [x] Shutdowns
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
|
- [x] Power Modes
|
||||||
handler: python
|
- [ ] Setpoints
|
||||||
options:
|
- [ ] Presets
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
## M20S V20
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
## M20S V30
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
|
|
||||||
handler: python
|
|
||||||
options:
|
|
||||||
show_root_heading: false
|
|
||||||
heading_level: 4
|
|
||||||
|
|
||||||
## M20P V10
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
|
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## M20P V30 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## M20P V30
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
|
::: pyasic.miners.whatsminer.btminer.M2X.M20P.BTMinerM20PV30
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## M20S+ V30 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## M20S+ V30
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S_Plus.BTMinerM20SPlusV30
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## M21 V10
|
## M20S V10 (Stock)
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## M21S V20
|
## M20S V20 (Stock)
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV20
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## M21S V60
|
## M20S V30 (Stock)
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20S.BTMinerM20SV30
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
## M21S V70
|
## M20 V10 (Stock)
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M20.BTMinerM20V10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## M21S+ V20 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## M21S+ V20
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S_Plus.BTMinerM21SPlusV20
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
## M21S V20 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV20
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## M21S V60 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV60
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## M21S V70 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21S.BTMinerM21SV70
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## M21 V10 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M2X.M21.BTMinerM21V10
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
|
## M29 V10 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
## M29 V10
|
|
||||||
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
|
::: pyasic.miners.whatsminer.btminer.M2X.M29.BTMinerM29V10
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
heading_level: 0
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1212
docs/miners/whatsminer/M6X.md
Normal file
1212
docs/miners/whatsminer/M6X.md
Normal file
File diff suppressed because it is too large
Load Diff
16
docs/miners/whatsminer/M7X.md
Normal file
16
docs/miners/whatsminer/M7X.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## M7X Models
|
||||||
|
|
||||||
|
## M70 VM30 (Stock)
|
||||||
|
|
||||||
|
- [x] Shutdowns
|
||||||
|
- [x] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.whatsminer.btminer.M7X.M70.BTMinerM70VM30
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
jinja2<3.1.3
|
|
||||||
mkdocs
|
|
||||||
mkdocstrings[python]
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## Miner APIs
|
## Miner RPC APIs
|
||||||
Each miner has a unique API that is used to communicate with it.
|
Each miner has a unique RPC API that is used to communicate with it.
|
||||||
Each of these API types has commands that differ between them, and some commands have data that others do not.
|
Each of these API types has commands that differ between them, and some commands have data that others do not.
|
||||||
Each miner that is a subclass of [`BaseMiner`][pyasic.miners.BaseMiner] should have an API linked to it as `Miner.api`.
|
Each miner that is a subclass of [`BaseMiner`][pyasic.miners.base.BaseMiner] may have an API linked to it as `Miner.rpc`.
|
||||||
|
|
||||||
All API implementations inherit from [`BaseMinerRPCAPI`][pyasic.rpc.BaseMinerRPCAPI], which implements the basic communications protocols.
|
All RPC API implementations inherit from [`BaseMinerRPCAPI`][pyasic.rpc.base.BaseMinerRPCAPI], which implements the basic communications protocols.
|
||||||
|
|
||||||
[`BaseMinerRPCAPI`][pyasic.rpc.BaseMinerRPCAPI] should never be used unless inheriting to create a new miner API class for a new type of miner (which should be exceedingly rare).
|
[`BaseMinerRPCAPI`][pyasic.rpc.base.BaseMinerRPCAPI] should never be used unless inheriting to create a new miner API class for a new type of miner (which should be exceedingly rare).
|
||||||
[`BaseMinerRPCAPI`][pyasic.rpc.BaseMinerRPCAPI] cannot be instantiated directly, it will raise a `TypeError`.
|
[`BaseMinerRPCAPI`][pyasic.rpc.base.BaseMinerRPCAPI] cannot be instantiated directly, it will raise a `TypeError`.
|
||||||
Use these instead -
|
Use these instead -
|
||||||
|
|
||||||
#### [BFGMiner API][pyasic.rpc.bfgminer.BFGMinerRPCAPI]
|
#### [BFGMiner API][pyasic.rpc.bfgminer.BFGMinerRPCAPI]
|
||||||
@@ -21,7 +21,7 @@ Use these instead -
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
## BaseMinerRPCAPI
|
## BaseMinerRPCAPI
|
||||||
::: pyasic.rpc.BaseMinerRPCAPI
|
::: pyasic.rpc.base.BaseMinerRPCAPI
|
||||||
handler: python
|
handler: python
|
||||||
options:
|
options:
|
||||||
heading_level: 4
|
heading_level: 4
|
||||||
|
|||||||
@@ -12,13 +12,18 @@ Settings options:
|
|||||||
- `factory_get_timeout`
|
- `factory_get_timeout`
|
||||||
- `get_data_retries`
|
- `get_data_retries`
|
||||||
- `api_function_timeout`
|
- `api_function_timeout`
|
||||||
- `default_whatsminer_password`
|
- `antminer_mining_mode_as_str`
|
||||||
- `default_innosilicon_password`
|
- `default_whatsminer_rpc_password`
|
||||||
- `default_antminer_password`
|
- `default_innosilicon_web_password`
|
||||||
- `default_bosminer_password`
|
- `default_antminer_web_password`
|
||||||
- `default_vnish_password`
|
- `default_bosminer_web_password`
|
||||||
- `default_goldshell_password`
|
- `default_vnish_web_password`
|
||||||
- `socket_linger_time`
|
- `default_goldshell_web_password`
|
||||||
|
- `default_auradine_web_password`
|
||||||
|
- `default_epic_web_password`
|
||||||
|
- `default_hive_web_password`
|
||||||
|
- `default_antminer_ssh_password`
|
||||||
|
- `default_bosminer_ssh_password`
|
||||||
|
|
||||||
|
|
||||||
### get
|
### get
|
||||||
|
|||||||
14
docs/web/antminer.md
Normal file
14
docs/web/antminer.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# pyasic
|
||||||
|
## AntminerModernWebAPI
|
||||||
|
::: pyasic.web.antminer.AntminerModernWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
|
|
||||||
|
## AntminerOldWebAPI
|
||||||
|
::: pyasic.web.antminer.AntminerOldWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
27
docs/web/api.md
Normal file
27
docs/web/api.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# pyasic
|
||||||
|
## Miner Web APIs
|
||||||
|
Each miner has a unique Web API that is used to communicate with it.
|
||||||
|
Each of these API types has commands that differ between them, and some commands have data that others do not.
|
||||||
|
Each miner that is a subclass of [`BaseMiner`][pyasic.miners.base.BaseMiner] may have an API linked to it as `Miner.web`.
|
||||||
|
|
||||||
|
All API implementations inherit from [`BaseWebAPI`][pyasic.web.BaseWebAPI], which implements the basic communications protocols.
|
||||||
|
|
||||||
|
[`BaseWebAPI`][pyasic.web.BaseWebAPI] should never be used unless inheriting to create a new miner API class for a new type of miner (which should be exceedingly rare).
|
||||||
|
Use these instead -
|
||||||
|
|
||||||
|
#### [AntminerModerNWebAPI][pyasic.web.antminer.AntminerModernWebAPI]
|
||||||
|
#### [AntminerOldWebAPI][pyasic.web.antminer.AntminerOldWebAPI]
|
||||||
|
#### [AuradineWebAPI][pyasic.web.auradine.AuradineWebAPI]
|
||||||
|
#### [ePICWebAPI][pyasic.web.epic.ePICWebAPI]
|
||||||
|
#### [GoldshellWebAPI][pyasic.web.goldshell.GoldshellWebAPI]
|
||||||
|
#### [InnosiliconWebAPI][pyasic.web.innosilicon.InnosiliconWebAPI]
|
||||||
|
#### [MaraWebAPI][pyasic.web.marathon.MaraWebAPI]
|
||||||
|
#### [VNishWebAPI][pyasic.web.vnish.VNishWebAPI]
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## BaseWebAPI
|
||||||
|
::: pyasic.web.BaseWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
heading_level: 4
|
||||||
7
docs/web/auradine.md
Normal file
7
docs/web/auradine.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# pyasic
|
||||||
|
## AuradineWebAPI
|
||||||
|
::: pyasic.web.auradine.AuradineWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
7
docs/web/epic.md
Normal file
7
docs/web/epic.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# pyasic
|
||||||
|
## ePICWebAPI
|
||||||
|
::: pyasic.web.epic.ePICWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
7
docs/web/goldshell.md
Normal file
7
docs/web/goldshell.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# pyasic
|
||||||
|
## GoldshellWebAPI
|
||||||
|
::: pyasic.web.goldshell.GoldshellWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
7
docs/web/innosilicon.md
Normal file
7
docs/web/innosilicon.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# pyasic
|
||||||
|
## InnosiliconWebAPI
|
||||||
|
::: pyasic.web.innosilicon.InnosiliconWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
7
docs/web/marathon.md
Normal file
7
docs/web/marathon.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# pyasic
|
||||||
|
## MaraWebAPI
|
||||||
|
::: pyasic.web.marathon.MaraWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
7
docs/web/vnish.md
Normal file
7
docs/web/vnish.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# pyasic
|
||||||
|
## VNishWebAPI
|
||||||
|
::: pyasic.web.vnish.VNishWebAPI
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 4
|
||||||
57
mkdocs.yml
57
mkdocs.yml
@@ -1,5 +1,36 @@
|
|||||||
site_name: pyasic
|
site_name: pyasic
|
||||||
repo_url: https://github.com/UpstreamData/pyasic
|
repo_url: https://github.com/UpstreamData/pyasic
|
||||||
|
site_url: !ENV SITE_URL
|
||||||
|
theme:
|
||||||
|
name: material
|
||||||
|
features:
|
||||||
|
- content.code.copy
|
||||||
|
- content.code.annotate
|
||||||
|
palette:
|
||||||
|
- media: "(prefers-color-scheme)"
|
||||||
|
toggle:
|
||||||
|
icon: material/brightness-auto
|
||||||
|
name: Switch to light mode
|
||||||
|
- media: "(prefers-color-scheme: light)"
|
||||||
|
scheme: default
|
||||||
|
toggle:
|
||||||
|
icon: material/weather-night
|
||||||
|
name: Switch to dark mode
|
||||||
|
- media: "(prefers-color-scheme: dark)"
|
||||||
|
scheme: slate
|
||||||
|
toggle:
|
||||||
|
icon: material/weather-sunny
|
||||||
|
name: Switch to auto mode
|
||||||
|
markdown_extensions:
|
||||||
|
- pymdownx.highlight:
|
||||||
|
anchor_linenums: true
|
||||||
|
line_spans: __span
|
||||||
|
pygments_lang_class: true
|
||||||
|
- pymdownx.tasklist:
|
||||||
|
custom_checkbox: true
|
||||||
|
- pymdownx.inlinehilite
|
||||||
|
- pymdownx.snippets
|
||||||
|
- pymdownx.superfences
|
||||||
nav:
|
nav:
|
||||||
- Introduction: "index.md"
|
- Introduction: "index.md"
|
||||||
- Miners:
|
- Miners:
|
||||||
@@ -22,6 +53,15 @@ nav:
|
|||||||
- CGMiner: "rpc/cgminer.md"
|
- CGMiner: "rpc/cgminer.md"
|
||||||
- LUXMiner: "rpc/luxminer.md"
|
- LUXMiner: "rpc/luxminer.md"
|
||||||
- Unknown: "rpc/unknown.md"
|
- Unknown: "rpc/unknown.md"
|
||||||
|
- Web APIs:
|
||||||
|
- Intro: "web/api.md"
|
||||||
|
- Antminer: "web/antminer.md"
|
||||||
|
- Auradine: "web/auradine.md"
|
||||||
|
- ePIC: "web/epic.md"
|
||||||
|
- Goldshell: "web/goldshell.md"
|
||||||
|
- Innosilicon: "web/innosilicon.md"
|
||||||
|
- Marathon: "web/marathon.md"
|
||||||
|
- VNish: "web/vnish.md"
|
||||||
- Backends:
|
- Backends:
|
||||||
- BMMiner: "miners/backends/bmminer.md"
|
- BMMiner: "miners/backends/bmminer.md"
|
||||||
- BOSMiner: "miners/backends/bosminer.md"
|
- BOSMiner: "miners/backends/bosminer.md"
|
||||||
@@ -40,6 +80,9 @@ nav:
|
|||||||
- Antminer X15: "miners/antminer/X15.md"
|
- Antminer X15: "miners/antminer/X15.md"
|
||||||
- Antminer X17: "miners/antminer/X17.md"
|
- Antminer X17: "miners/antminer/X17.md"
|
||||||
- Antminer X19: "miners/antminer/X19.md"
|
- Antminer X19: "miners/antminer/X19.md"
|
||||||
|
- Antminer X21: "miners/antminer/X21.md"
|
||||||
|
- Braiins Mini Miners: "miners/braiins/BMM.md"
|
||||||
|
- Avalon Nano: "miners/avalonminer/nano.md"
|
||||||
- Avalon 7X: "miners/avalonminer/A7X.md"
|
- Avalon 7X: "miners/avalonminer/A7X.md"
|
||||||
- Avalon 8X: "miners/avalonminer/A8X.md"
|
- Avalon 8X: "miners/avalonminer/A8X.md"
|
||||||
- Avalon 9X: "miners/avalonminer/A9X.md"
|
- Avalon 9X: "miners/avalonminer/A9X.md"
|
||||||
@@ -49,10 +92,24 @@ nav:
|
|||||||
- Whatsminer M2X: "miners/whatsminer/M2X.md"
|
- Whatsminer M2X: "miners/whatsminer/M2X.md"
|
||||||
- Whatsminer M3X: "miners/whatsminer/M3X.md"
|
- Whatsminer M3X: "miners/whatsminer/M3X.md"
|
||||||
- Whatsminer M5X: "miners/whatsminer/M5X.md"
|
- Whatsminer M5X: "miners/whatsminer/M5X.md"
|
||||||
|
- Whatsminer M6X: "miners/whatsminer/M6X.md"
|
||||||
|
- Whatsminer M7X: "miners/whatsminer/M7X.md"
|
||||||
- Innosilicon T3X: "miners/innosilicon/T3X.md"
|
- Innosilicon T3X: "miners/innosilicon/T3X.md"
|
||||||
- Innosilicon A10X: "miners/innosilicon/A10X.md"
|
- Innosilicon A10X: "miners/innosilicon/A10X.md"
|
||||||
|
- Innosilicon A11X: "miners/innosilicon/A11X.md"
|
||||||
|
- Goldshell Byte: "miners/goldshell/Byte.md"
|
||||||
|
- Goldshell Mini Doge: "miners/goldshell/MiniDoge.md"
|
||||||
- Goldshell X5: "miners/goldshell/X5.md"
|
- Goldshell X5: "miners/goldshell/X5.md"
|
||||||
- Goldshell XMax: "miners/goldshell/XMax.md"
|
- Goldshell XMax: "miners/goldshell/XMax.md"
|
||||||
|
- Goldshell XBox: "miners/goldshell/XBox.md"
|
||||||
|
- Auradine AD: "miners/auradine/AD.md"
|
||||||
|
- Auradine AI: "miners/auradine/AI.md"
|
||||||
|
- Auradine AT: "miners/auradine/AT.md"
|
||||||
|
- Blockminer: "miners/blockminer/blockminer.md"
|
||||||
|
- BitAxe BM: "miners/bitaxe/BM.md"
|
||||||
|
- Hammer DX: "miners/hammer/DX.md"
|
||||||
|
- Iceriver KSX: "miners/iceriver/KSX.md"
|
||||||
|
- Volcminer DX: "miners/volcminer/DX.md"
|
||||||
- Base Miner: "miners/base_miner.md"
|
- Base Miner: "miners/base_miner.md"
|
||||||
- Settings:
|
- Settings:
|
||||||
- Settings: "settings/settings.md"
|
- Settings: "settings/settings.md"
|
||||||
|
|||||||
1712
poetry.lock
generated
Normal file
1712
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,47 +13,16 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
import importlib.metadata
|
||||||
|
|
||||||
from pyasic import settings
|
from pyasic import settings
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.data import (
|
from pyasic.data import MinerData
|
||||||
BraiinsOSError,
|
|
||||||
InnosiliconError,
|
|
||||||
MinerData,
|
|
||||||
WhatsminerError,
|
|
||||||
X19Error,
|
|
||||||
)
|
|
||||||
from pyasic.errors import APIError, APIWarning
|
from pyasic.errors import APIError, APIWarning
|
||||||
from pyasic.miners import get_miner
|
from pyasic.miners import *
|
||||||
from pyasic.miners.base import AnyMiner, DataOptions
|
|
||||||
from pyasic.miners.miner_factory import MinerFactory, miner_factory
|
|
||||||
from pyasic.miners.miner_listener import MinerListener
|
|
||||||
from pyasic.network import MinerNetwork
|
from pyasic.network import MinerNetwork
|
||||||
from pyasic.rpc.bmminer import BMMinerRPCAPI
|
from pyasic.rpc import *
|
||||||
from pyasic.rpc.bosminer import BOSMinerRPCAPI
|
from pyasic.ssh import *
|
||||||
from pyasic.rpc.btminer import BTMinerRPCAPI
|
from pyasic.web import *
|
||||||
from pyasic.rpc.cgminer import CGMinerRPCAPI
|
|
||||||
from pyasic.rpc.unknown import UnknownRPCAPI
|
|
||||||
|
|
||||||
__all__ = [
|
__version__ = importlib.metadata.version("pyasic")
|
||||||
"BMMinerRPCAPI",
|
|
||||||
"BOSMinerRPCAPI",
|
|
||||||
"BTMinerRPCAPI",
|
|
||||||
"CGMinerRPCAPI",
|
|
||||||
"UnknownRPCAPI",
|
|
||||||
"MinerConfig",
|
|
||||||
"MinerData",
|
|
||||||
"BraiinsOSError",
|
|
||||||
"InnosiliconError",
|
|
||||||
"WhatsminerError",
|
|
||||||
"X19Error",
|
|
||||||
"APIError",
|
|
||||||
"APIWarning",
|
|
||||||
"get_miner",
|
|
||||||
"AnyMiner",
|
|
||||||
"DataOptions",
|
|
||||||
"MinerFactory",
|
|
||||||
"miner_factory",
|
|
||||||
"MinerListener",
|
|
||||||
"MinerNetwork",
|
|
||||||
"settings",
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -13,166 +13,316 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from copy import deepcopy
|
|
||||||
from dataclasses import asdict, dataclass, field
|
|
||||||
|
|
||||||
from pyasic.config.fans import FanModeConfig
|
from typing import Any
|
||||||
from pyasic.config.mining import MiningModeConfig
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from pyasic.config.fans import (
|
||||||
|
FanModeConfig,
|
||||||
|
FanModeImmersion,
|
||||||
|
FanModeManual,
|
||||||
|
FanModeNormal,
|
||||||
|
)
|
||||||
|
from pyasic.config.mining import (
|
||||||
|
MiningModeConfig,
|
||||||
|
MiningModeHashrateTune,
|
||||||
|
MiningModeHPM,
|
||||||
|
MiningModeLPM,
|
||||||
|
MiningModeManual,
|
||||||
|
MiningModeNormal,
|
||||||
|
MiningModePowerTune,
|
||||||
|
MiningModePreset,
|
||||||
|
MiningModeSleep,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Type aliases for config field types
|
||||||
|
FanModeType = FanModeNormal | FanModeManual | FanModeImmersion | FanModeConfig
|
||||||
|
MiningModeType = (
|
||||||
|
MiningModeNormal
|
||||||
|
| MiningModeHPM
|
||||||
|
| MiningModeLPM
|
||||||
|
| MiningModeSleep
|
||||||
|
| MiningModeManual
|
||||||
|
| MiningModePowerTune
|
||||||
|
| MiningModeHashrateTune
|
||||||
|
| MiningModePreset
|
||||||
|
| MiningModeConfig
|
||||||
|
)
|
||||||
|
from pyasic.config.mining.scaling import ScalingConfig
|
||||||
from pyasic.config.pools import PoolConfig
|
from pyasic.config.pools import PoolConfig
|
||||||
from pyasic.config.power_scaling import PowerScalingConfig
|
|
||||||
from pyasic.config.temperature import TemperatureConfig
|
from pyasic.config.temperature import TemperatureConfig
|
||||||
|
from pyasic.misc import merge_dicts
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class MinerConfig(BaseModel):
|
||||||
class MinerConfig:
|
"""Represents the configuration for a miner including pool configuration,
|
||||||
pools: PoolConfig = field(default_factory=PoolConfig.default)
|
fan mode, temperature settings, mining mode, and power scaling."""
|
||||||
fan_mode: FanModeConfig = field(default_factory=FanModeConfig.default)
|
|
||||||
temperature: TemperatureConfig = field(default_factory=TemperatureConfig.default)
|
class Config:
|
||||||
mining_mode: MiningModeConfig = field(default_factory=MiningModeConfig.default)
|
arbitrary_types_allowed = True
|
||||||
power_scaling: PowerScalingConfig = field(
|
|
||||||
default_factory=PowerScalingConfig.default
|
pools: PoolConfig = Field(default_factory=PoolConfig.default)
|
||||||
)
|
fan_mode: FanModeType = Field(default_factory=FanModeConfig.default)
|
||||||
|
temperature: TemperatureConfig = Field(default_factory=TemperatureConfig.default)
|
||||||
|
mining_mode: MiningModeType = Field(default_factory=MiningModeConfig.default)
|
||||||
|
|
||||||
|
def __getitem__(self, item: str) -> Any:
|
||||||
|
try:
|
||||||
|
return getattr(self, item)
|
||||||
|
except AttributeError:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
return asdict(self)
|
"""Converts the MinerConfig object to a dictionary."""
|
||||||
|
return self.model_dump()
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str = None) -> dict:
|
def as_am_modern(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for modern Antminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_am_modern(),
|
**self.fan_mode.as_am_modern(),
|
||||||
"freq-level": "100",
|
"freq-level": "100",
|
||||||
**self.mining_mode.as_am_modern(),
|
**self.mining_mode.as_am_modern(),
|
||||||
**self.pools.as_am_modern(user_suffix=user_suffix),
|
**self.pools.as_am_modern(user_suffix=user_suffix),
|
||||||
**self.temperature.as_am_modern(),
|
**self.temperature.as_am_modern(),
|
||||||
**self.power_scaling.as_am_modern(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> dict:
|
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for modern Hiveon."""
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_hiveon_modern(),
|
||||||
|
"freq-level": "100",
|
||||||
|
**self.mining_mode.as_hiveon_modern(),
|
||||||
|
**self.pools.as_hiveon_modern(user_suffix=user_suffix),
|
||||||
|
**self.temperature.as_hiveon_modern(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for modern Elphapex."""
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_elphapex(),
|
||||||
|
"fc-freq-level": "100",
|
||||||
|
**self.mining_mode.as_elphapex(),
|
||||||
|
**self.pools.as_elphapex(user_suffix=user_suffix),
|
||||||
|
**self.temperature.as_elphapex(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_wm(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for Whatsminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_wm(),
|
**self.fan_mode.as_wm(),
|
||||||
**self.mining_mode.as_wm(),
|
**self.mining_mode.as_wm(),
|
||||||
**self.pools.as_wm(user_suffix=user_suffix),
|
**self.pools.as_wm(user_suffix=user_suffix),
|
||||||
**self.temperature.as_wm(),
|
**self.temperature.as_wm(),
|
||||||
**self.power_scaling.as_wm(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str = None) -> dict:
|
def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for Whatsminers running BTMiner V3."""
|
||||||
|
return {
|
||||||
|
"set.miner.pools": self.pools.as_btminer_v3(),
|
||||||
|
**self.mining_mode.as_btminer_v3(),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_am_old(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for old versions of Antminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_am_old(),
|
**self.fan_mode.as_am_old(),
|
||||||
**self.mining_mode.as_am_old(),
|
**self.mining_mode.as_am_old(),
|
||||||
**self.pools.as_am_old(user_suffix=user_suffix),
|
**self.pools.as_am_old(user_suffix=user_suffix),
|
||||||
**self.temperature.as_am_old(),
|
**self.temperature.as_am_old(),
|
||||||
**self.power_scaling.as_am_old(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str = None) -> dict:
|
def as_goldshell(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for Goldshell miners."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_goldshell(),
|
**self.fan_mode.as_goldshell(),
|
||||||
**self.mining_mode.as_goldshell(),
|
**self.mining_mode.as_goldshell(),
|
||||||
**self.pools.as_goldshell(user_suffix=user_suffix),
|
**self.pools.as_goldshell(user_suffix=user_suffix),
|
||||||
**self.temperature.as_goldshell(),
|
**self.temperature.as_goldshell(),
|
||||||
**self.power_scaling.as_goldshell(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str = None) -> dict:
|
def as_avalon(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for Avalonminers."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_avalon(),
|
**self.fan_mode.as_avalon(),
|
||||||
**self.mining_mode.as_avalon(),
|
**self.mining_mode.as_avalon(),
|
||||||
**self.pools.as_avalon(user_suffix=user_suffix),
|
**self.pools.as_avalon(user_suffix=user_suffix),
|
||||||
**self.temperature.as_avalon(),
|
**self.temperature.as_avalon(),
|
||||||
**self.power_scaling.as_avalon(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str = None) -> dict:
|
def as_inno(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for Innosilicon miners."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_inno(),
|
**self.fan_mode.as_inno(),
|
||||||
**self.mining_mode.as_inno(),
|
**self.mining_mode.as_inno(),
|
||||||
**self.pools.as_inno(user_suffix=user_suffix),
|
**self.pools.as_inno(user_suffix=user_suffix),
|
||||||
**self.temperature.as_inno(),
|
**self.temperature.as_inno(),
|
||||||
**self.power_scaling.as_inno(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str = None) -> dict:
|
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the bosminer.toml format."""
|
||||||
return {
|
return {
|
||||||
**merge(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
|
**merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()),
|
||||||
**self.mining_mode.as_bosminer(),
|
**self.mining_mode.as_bosminer(),
|
||||||
**self.pools.as_bosminer(user_suffix=user_suffix),
|
**self.pools.as_bosminer(user_suffix=user_suffix),
|
||||||
**self.power_scaling.as_bosminer(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str = None) -> dict:
|
def as_boser(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for BOSer."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_boser(),
|
**self.fan_mode.as_boser(),
|
||||||
**self.temperature.as_boser(),
|
**self.temperature.as_boser(),
|
||||||
**self.mining_mode.as_boser(),
|
**self.mining_mode.as_boser(),
|
||||||
**self.pools.as_boser(user_suffix=user_suffix),
|
**self.pools.as_boser(user_suffix=user_suffix),
|
||||||
**self.power_scaling.as_boser(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_epic(self, user_suffix: str = None) -> dict:
|
def as_epic(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for ePIC miners."""
|
||||||
return {
|
return {
|
||||||
**self.fan_mode.as_epic(),
|
**merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
|
||||||
**self.temperature.as_epic(),
|
|
||||||
**self.mining_mode.as_epic(),
|
**self.mining_mode.as_epic(),
|
||||||
**self.pools.as_epic(user_suffix=user_suffix),
|
**self.pools.as_epic(user_suffix=user_suffix),
|
||||||
**self.power_scaling.as_epic(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for Auradine miners."""
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_auradine(),
|
||||||
|
**self.temperature.as_auradine(),
|
||||||
|
**self.mining_mode.as_auradine(),
|
||||||
|
**self.pools.as_auradine(user_suffix=user_suffix),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_mara(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_mara(),
|
||||||
|
**self.temperature.as_mara(),
|
||||||
|
**self.mining_mode.as_mara(),
|
||||||
|
**self.pools.as_mara(user_suffix=user_suffix),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_espminer(),
|
||||||
|
**self.temperature.as_espminer(),
|
||||||
|
**self.mining_mode.as_espminer(),
|
||||||
|
**self.pools.as_espminer(user_suffix=user_suffix),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_luxos(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_luxos(),
|
||||||
|
**self.temperature.as_luxos(),
|
||||||
|
**self.mining_mode.as_luxos(),
|
||||||
|
**self.pools.as_luxos(user_suffix=user_suffix),
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
||||||
|
main_cfg = {
|
||||||
|
"miner": {
|
||||||
|
**self.fan_mode.as_vnish(),
|
||||||
|
**self.temperature.as_vnish(),
|
||||||
|
**self.mining_mode.as_vnish(),
|
||||||
|
**self.pools.as_vnish(user_suffix=user_suffix),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isinstance(self.fan_mode, FanModeNormal):
|
||||||
|
main_cfg["miner"]["cooling"]["mode"]["param"] = self.temperature.target
|
||||||
|
return main_cfg
|
||||||
|
|
||||||
|
def as_hammer(self, *args, **kwargs) -> dict:
|
||||||
|
return self.as_am_modern(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
|
def from_dict(cls, dict_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from a dictionary."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_dict(dict_conf.get("pools")),
|
pools=PoolConfig.from_dict(dict_conf.get("pools")),
|
||||||
mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
|
mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")),
|
||||||
fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
|
fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")),
|
||||||
temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
|
temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")),
|
||||||
power_scaling=PowerScalingConfig.from_dict(dict_conf.get("power_scaling")),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api(cls, api_pools: dict) -> "MinerConfig":
|
def from_api(cls, api_pools: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from API pool data."""
|
||||||
return cls(pools=PoolConfig.from_api(api_pools))
|
return cls(pools=PoolConfig.from_api(api_pools))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_modern(cls, web_conf: dict) -> "MinerConfig":
|
def from_am_modern(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for modern Antminers."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_am_modern(web_conf),
|
pools=PoolConfig.from_am_modern(web_conf),
|
||||||
mining_mode=MiningModeConfig.from_am_modern(web_conf),
|
mining_mode=MiningModeConfig.from_am_modern(web_conf),
|
||||||
fan_mode=FanModeConfig.from_am_modern(web_conf),
|
fan_mode=FanModeConfig.from_am_modern(web_conf),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for Hiveon."""
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_hiveon_modern(web_conf),
|
||||||
|
mining_mode=MiningModeConfig.from_hiveon_modern(web_conf),
|
||||||
|
fan_mode=FanModeConfig.from_hiveon_modern(web_conf),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for modern Antminers."""
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_elphapex(web_conf),
|
||||||
|
mining_mode=MiningModeConfig.from_elphapex(web_conf),
|
||||||
|
fan_mode=FanModeConfig.from_elphapex(web_conf),
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
|
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
|
||||||
return cls.from_am_modern(web_conf)
|
return cls.from_am_modern(web_conf)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_conf: dict) -> "MinerConfig":
|
def from_goldshell(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for Goldshell miners."""
|
||||||
return cls(pools=PoolConfig.from_am_modern(web_conf))
|
return cls(pools=PoolConfig.from_am_modern(web_conf))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_goldshell_list(cls, web_conf: list) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for Goldshell miners."""
|
||||||
|
return cls(pools=PoolConfig.from_goldshell(web_conf))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_goldshell_byte(cls, web_conf: list) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for Goldshell Byte miners."""
|
||||||
|
return cls(pools=PoolConfig.from_goldshell_byte(web_conf))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_inno(cls, web_pools: list) -> "MinerConfig":
|
def from_inno(cls, web_pools: list) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for Innosilicon miners."""
|
||||||
return cls(pools=PoolConfig.from_inno(web_pools))
|
return cls(pools=PoolConfig.from_inno(web_pools))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_conf: dict) -> "MinerConfig":
|
def from_bosminer(cls, toml_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from the bosminer.toml file, same as the `as_bosminer` dumps a dict for writing to that file as toml."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_bosminer(toml_conf),
|
pools=PoolConfig.from_bosminer(toml_conf),
|
||||||
mining_mode=MiningModeConfig.from_bosminer(toml_conf),
|
mining_mode=MiningModeConfig.from_bosminer(toml_conf),
|
||||||
fan_mode=FanModeConfig.from_bosminer(toml_conf),
|
fan_mode=FanModeConfig.from_bosminer(toml_conf),
|
||||||
temperature=TemperatureConfig.from_bosminer(toml_conf),
|
temperature=TemperatureConfig.from_bosminer(toml_conf),
|
||||||
power_scaling=PowerScalingConfig.from_bosminer(toml_conf),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_miner_conf: dict) -> "MinerConfig":
|
def from_boser(cls, grpc_miner_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from gRPC configuration for BOSer."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_boser(grpc_miner_conf),
|
pools=PoolConfig.from_boser(grpc_miner_conf),
|
||||||
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
|
mining_mode=MiningModeConfig.from_boser(grpc_miner_conf),
|
||||||
fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
|
fan_mode=FanModeConfig.from_boser(grpc_miner_conf),
|
||||||
temperature=TemperatureConfig.from_boser(grpc_miner_conf),
|
temperature=TemperatureConfig.from_boser(grpc_miner_conf),
|
||||||
power_scaling=PowerScalingConfig.from_boser(grpc_miner_conf),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict) -> "MinerConfig":
|
def from_epic(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for ePIC miners."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_epic(web_conf),
|
pools=PoolConfig.from_epic(web_conf),
|
||||||
fan_mode=FanModeConfig.from_epic(web_conf),
|
fan_mode=FanModeConfig.from_epic(web_conf),
|
||||||
@@ -181,21 +331,81 @@ class MinerConfig:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict) -> "MinerConfig":
|
def from_vnish(
|
||||||
|
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
|
||||||
|
) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web settings for VNish miners."""
|
||||||
return cls(
|
return cls(
|
||||||
pools=PoolConfig.from_vnish(web_settings),
|
pools=PoolConfig.from_vnish(web_settings),
|
||||||
fan_mode=FanModeConfig.from_vnish(web_settings),
|
fan_mode=FanModeConfig.from_vnish(web_settings),
|
||||||
temperature=TemperatureConfig.from_vnish(web_settings),
|
temperature=TemperatureConfig.from_vnish(web_settings),
|
||||||
mining_mode=MiningModeConfig.from_vnish(web_settings),
|
mining_mode=MiningModeConfig.from_vnish(
|
||||||
|
web_settings, web_presets, web_perf_summary
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_auradine(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for Auradine miners."""
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_api(web_conf["pools"]),
|
||||||
|
fan_mode=FanModeConfig.from_auradine(web_conf["fan"]),
|
||||||
|
mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]),
|
||||||
|
)
|
||||||
|
|
||||||
def merge(a: dict, b: dict) -> dict:
|
@classmethod
|
||||||
result = deepcopy(a)
|
def from_mara(cls, web_miner_config: dict) -> "MinerConfig":
|
||||||
for b_key, b_val in b.items():
|
return cls(
|
||||||
a_val = result.get(b_key)
|
pools=PoolConfig.from_mara(web_miner_config),
|
||||||
if isinstance(a_val, dict) and isinstance(b_val, dict):
|
fan_mode=FanModeConfig.from_mara(web_miner_config),
|
||||||
result[b_key] = merge(a_val, b_val)
|
mining_mode=MiningModeConfig.from_mara(web_miner_config),
|
||||||
else:
|
)
|
||||||
result[b_key] = deepcopy(b_val)
|
|
||||||
return result
|
@classmethod
|
||||||
|
def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_espminer(web_system_info),
|
||||||
|
fan_mode=FanModeConfig.from_espminer(web_system_info),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_iceriver(cls, web_userpanel: dict) -> "MinerConfig":
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_iceriver(web_userpanel),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(
|
||||||
|
cls,
|
||||||
|
rpc_tempctrl: dict,
|
||||||
|
rpc_fans: dict,
|
||||||
|
rpc_pools: dict,
|
||||||
|
rpc_groups: dict,
|
||||||
|
rpc_config: dict,
|
||||||
|
rpc_profiles: dict,
|
||||||
|
) -> "MinerConfig":
|
||||||
|
return cls(
|
||||||
|
temperature=TemperatureConfig.from_luxos(rpc_tempctrl=rpc_tempctrl),
|
||||||
|
fan_mode=FanModeConfig.from_luxos(
|
||||||
|
rpc_tempctrl=rpc_tempctrl, rpc_fans=rpc_fans
|
||||||
|
),
|
||||||
|
pools=PoolConfig.from_luxos(rpc_pools=rpc_pools, rpc_groups=rpc_groups),
|
||||||
|
mining_mode=MiningModeConfig.from_luxos(
|
||||||
|
rpc_config=rpc_config, rpc_profiles=rpc_profiles
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hammer(cls, *args, **kwargs) -> "MinerConfig":
|
||||||
|
return cls.from_am_modern(*args, **kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_btminer_v3(
|
||||||
|
cls, rpc_pools: dict, rpc_settings: dict, rpc_device_info: dict
|
||||||
|
) -> "MinerConfig":
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_btminer_v3(rpc_pools=rpc_pools["msg"]),
|
||||||
|
mining_mode=MiningModeConfig.from_btminer_v3(
|
||||||
|
rpc_device_info=rpc_device_info, rpc_settings=rpc_settings
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|||||||
@@ -13,19 +13,25 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from dataclasses import asdict, dataclass
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Union
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
class MinerConfigOption(Enum):
|
class MinerConfigOption(Enum):
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]):
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return self.value.as_am_modern()
|
return self.value.as_am_modern()
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
return self.value.as_hiveon_modern()
|
||||||
|
|
||||||
def as_am_old(self) -> dict:
|
def as_am_old(self) -> dict:
|
||||||
return self.value.as_am_old()
|
return self.value.as_am_old()
|
||||||
|
|
||||||
@@ -45,7 +51,7 @@ class MinerConfigOption(Enum):
|
|||||||
return self.value.as_bosminer()
|
return self.value.as_bosminer()
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
def as_boser(self) -> dict:
|
||||||
return self.value.as_boser()
|
return self.value.as_boser
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
def as_epic(self) -> dict:
|
||||||
return self.value.as_epic()
|
return self.value.as_epic()
|
||||||
@@ -53,6 +59,21 @@ class MinerConfigOption(Enum):
|
|||||||
def as_vnish(self) -> dict:
|
def as_vnish(self) -> dict:
|
||||||
return self.value.as_vnish()
|
return self.value.as_vnish()
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return self.value.as_auradine()
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return self.value.as_mara()
|
||||||
|
|
||||||
|
def as_espminer(self) -> dict:
|
||||||
|
return self.value.as_espminer()
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return self.value.as_luxos()
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return self.value.as_elphapex()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
return self.value(*args, **kwargs)
|
return self.value(*args, **kwargs)
|
||||||
|
|
||||||
@@ -60,42 +81,74 @@ class MinerConfigOption(Enum):
|
|||||||
def default(cls):
|
def default(cls):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
try:
|
||||||
|
return getattr(self, item)
|
||||||
|
except AttributeError:
|
||||||
|
raise KeyError
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MinerConfigValue:
|
class MinerConfigValue(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]):
|
def from_dict(cls, dict_conf: dict):
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self) -> dict:
|
||||||
return asdict(self)
|
return self.model_dump()
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_am_old(self) -> dict:
|
def as_hiveon_modern(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
def as_am_old(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_inno(self) -> dict:
|
def as_wm(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_goldshell(self) -> dict:
|
def as_btminer_v3(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_avalon(self) -> dict:
|
def as_inno(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_goldshell(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
def as_avalon(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_epic(self) -> dict:
|
def as_bosminer(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
def as_boser(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def as_epic(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_vnish(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_auradine(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_mara(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_espminer(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_luxos(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_elphapex(self, *args: Any, **kwargs: Any) -> Any:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
try:
|
||||||
|
return getattr(self, item)
|
||||||
|
except AttributeError:
|
||||||
|
raise KeyError
|
||||||
|
|||||||
@@ -13,20 +13,22 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from dataclasses import dataclass, field
|
from __future__ import annotations
|
||||||
from typing import Union
|
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FanModeNormal(MinerConfigValue):
|
class FanModeNormal(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="normal")
|
mode: str = Field(init=False, default="normal")
|
||||||
minimum_fans: int = 1
|
minimum_fans: int = 1
|
||||||
minimum_speed: int = 0
|
minimum_speed: int = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeNormal":
|
def from_dict(cls, dict_conf: dict) -> FanModeNormal:
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
if dict_conf.get("minimum_fans") is not None:
|
if dict_conf.get("minimum_fans") is not None:
|
||||||
cls_conf["minimum_fans"] = dict_conf["minimum_fans"]
|
cls_conf["minimum_fans"] = dict_conf["minimum_fans"]
|
||||||
@@ -35,7 +37,7 @@ class FanModeNormal(MinerConfigValue):
|
|||||||
return cls(**cls_conf)
|
return cls(**cls_conf)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_cooling_settings: dict):
|
def from_vnish(cls, web_cooling_settings: dict) -> FanModeNormal:
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
if web_cooling_settings.get("fan_min_count") is not None:
|
if web_cooling_settings.get("fan_min_count") is not None:
|
||||||
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
|
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
|
||||||
@@ -43,21 +45,74 @@ class FanModeNormal(MinerConfigValue):
|
|||||||
cls_conf["minimum_speed"] = web_cooling_settings["fan_min_duty"]
|
cls_conf["minimum_speed"] = web_cooling_settings["fan_min_duty"]
|
||||||
return cls(**cls_conf)
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_fan_conf: dict):
|
||||||
|
cls_conf = {}
|
||||||
|
if toml_fan_conf.get("min_fans") is not None:
|
||||||
|
cls_conf["minimum_fans"] = toml_fan_conf["min_fans"]
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {"temp_control": {"mode": "auto"}}
|
return {
|
||||||
|
"temp_control": {"mode": "auto"},
|
||||||
|
"fan_control": {"min_fans": self.minimum_fans},
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {
|
||||||
|
"fans": {
|
||||||
|
"Auto": {
|
||||||
|
"Idle Speed": (
|
||||||
|
self.minimum_speed if not self.minimum_speed == 0 else 100
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"general-config": {"environment-profile": "AirCooling"},
|
||||||
|
"advance-config": {
|
||||||
|
"override-fan-control": False,
|
||||||
|
"fan-fixed-percent": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_espminer(self) -> dict:
|
||||||
|
return {"autoFanspeed": 1}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {
|
||||||
|
"cooling": {
|
||||||
|
"fan_min_count": self.minimum_fans,
|
||||||
|
"fan_min_duty": self.minimum_speed,
|
||||||
|
"mode": {
|
||||||
|
"name": "auto",
|
||||||
|
"param": None, # Target temp, must be set later...
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FanModeManual(MinerConfigValue):
|
class FanModeManual(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="manual")
|
mode: str = Field(init=False, default="manual")
|
||||||
speed: int = 100
|
speed: int = 100
|
||||||
minimum_fans: int = 1
|
minimum_fans: int = 1
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeManual":
|
def from_dict(cls, dict_conf: dict) -> FanModeManual:
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
if dict_conf.get("speed") is not None:
|
if dict_conf.get("speed") is not None:
|
||||||
cls_conf["speed"] = dict_conf["speed"]
|
cls_conf["speed"] = dict_conf["speed"]
|
||||||
@@ -66,7 +121,7 @@ class FanModeManual(MinerConfigValue):
|
|||||||
return cls(**cls_conf)
|
return cls(**cls_conf)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_fan_conf: dict) -> "FanModeManual":
|
def from_bosminer(cls, toml_fan_conf: dict) -> FanModeManual:
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
if toml_fan_conf.get("min_fans") is not None:
|
if toml_fan_conf.get("min_fans") is not None:
|
||||||
cls_conf["minimum_fans"] = toml_fan_conf["min_fans"]
|
cls_conf["minimum_fans"] = toml_fan_conf["min_fans"]
|
||||||
@@ -75,7 +130,7 @@ class FanModeManual(MinerConfigValue):
|
|||||||
return cls(**cls_conf)
|
return cls(**cls_conf)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_cooling_settings: dict) -> "FanModeManual":
|
def from_vnish(cls, web_cooling_settings: dict) -> FanModeManual:
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
if web_cooling_settings.get("fan_min_count") is not None:
|
if web_cooling_settings.get("fan_min_count") is not None:
|
||||||
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
|
cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"]
|
||||||
@@ -84,7 +139,13 @@ class FanModeManual(MinerConfigValue):
|
|||||||
return cls(**cls_conf)
|
return cls(**cls_conf)
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)}
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"fc-fan-ctrl": True, "fc-fan-pwm": str(self.speed)}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
@@ -92,20 +153,72 @@ class FanModeManual(MinerConfigValue):
|
|||||||
"fan_control": {"min_fans": self.minimum_fans, "speed": self.speed},
|
"fan_control": {"min_fans": self.minimum_fans, "speed": self.speed},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"fan": {"percentage": self.speed}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {"fans": {"Manual": {"speed": self.speed}}}
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"general-config": {"environment-profile": "AirCooling"},
|
||||||
|
"advance-config": {
|
||||||
|
"override-fan-control": True,
|
||||||
|
"fan-fixed-percent": self.speed,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_espminer(self) -> dict:
|
||||||
|
return {"autoFanspeed": 0, "fanspeed": self.speed}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {
|
||||||
|
"cooling": {
|
||||||
|
"fan_min_count": self.minimum_fans,
|
||||||
|
"fan_min_duty": self.speed,
|
||||||
|
"mode": {
|
||||||
|
"name": "manual",
|
||||||
|
"param": self.speed, # Speed value
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class FanModeImmersion(MinerConfigValue):
|
class FanModeImmersion(MinerConfigValue):
|
||||||
mode: str = field(init=False, default="immersion")
|
mode: str = Field(init=False, default="immersion")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeImmersion":
|
def from_dict(cls, dict_conf: dict | None) -> FanModeImmersion:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"}
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {"temp_control": {"mode": "disabled"}}
|
return {
|
||||||
|
"fan_control": {"min_fans": 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"fan": {"percentage": 0}}
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {"general-config": {"environment-profile": "OilImmersionCooling"}}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"fanset": {"speed": 0, "min_fans": 0}}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {"cooling": {"mode": {"name": "immers"}}}
|
||||||
|
|
||||||
|
|
||||||
class FanModeConfig(MinerConfigOption):
|
class FanModeConfig(MinerConfigOption):
|
||||||
@@ -118,7 +231,7 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
return cls.normal()
|
return cls.normal()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]):
|
def from_dict(cls, dict_conf: dict | None):
|
||||||
if dict_conf is None:
|
if dict_conf is None:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
@@ -126,16 +239,47 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
if mode is None:
|
if mode is None:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
clsattr = getattr(cls, mode)
|
cls_attr = getattr(cls, mode)
|
||||||
if clsattr is not None:
|
if cls_attr is not None:
|
||||||
return clsattr().from_dict(dict_conf)
|
return cls_attr().from_dict(dict_conf)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_modern(cls, web_conf: dict):
|
def from_am_modern(cls, web_conf: dict):
|
||||||
if web_conf.get("bitmain-fan-ctrl") is not None:
|
if web_conf.get("bitmain-fan-ctrl") is not None:
|
||||||
fan_manual = web_conf["bitmain-fan-ctrl"]
|
fan_manual = web_conf["bitmain-fan-ctrl"]
|
||||||
if fan_manual:
|
if fan_manual:
|
||||||
return cls.manual(speed=web_conf["bitmain-fan-pwm"])
|
speed = int(web_conf["bitmain-fan-pwm"])
|
||||||
|
if speed == 0:
|
||||||
|
return cls.immersion()
|
||||||
|
return cls.manual(speed=speed)
|
||||||
|
else:
|
||||||
|
return cls.normal()
|
||||||
|
else:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hiveon_modern(cls, web_conf: dict):
|
||||||
|
if web_conf.get("bitmain-fan-ctrl") is not None:
|
||||||
|
fan_manual = web_conf["bitmain-fan-ctrl"]
|
||||||
|
if fan_manual:
|
||||||
|
speed = int(web_conf["bitmain-fan-pwm"])
|
||||||
|
if speed == 0:
|
||||||
|
return cls.immersion()
|
||||||
|
return cls.manual(speed=speed)
|
||||||
|
else:
|
||||||
|
return cls.normal()
|
||||||
|
else:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict):
|
||||||
|
if web_conf.get("fc-fan-ctrl") is not None:
|
||||||
|
fan_manual = web_conf["fc-fan-ctrl"]
|
||||||
|
if fan_manual:
|
||||||
|
speed = int(web_conf["fc-fan-pwm"])
|
||||||
|
if speed == 0:
|
||||||
|
return cls.immersion()
|
||||||
|
return cls.manual(speed=speed)
|
||||||
else:
|
else:
|
||||||
return cls.normal()
|
return cls.normal()
|
||||||
else:
|
else:
|
||||||
@@ -154,20 +298,28 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_conf: dict):
|
def from_bosminer(cls, toml_conf: dict):
|
||||||
if toml_conf.get("temp_control") is None:
|
try:
|
||||||
return cls.default()
|
mode = toml_conf["temp_control"]["mode"]
|
||||||
if toml_conf["temp_control"].get("mode") is None:
|
fan_config = toml_conf.get("fan_control", {})
|
||||||
|
if mode == "auto":
|
||||||
|
return cls.normal().from_bosminer(fan_config)
|
||||||
|
elif mode == "manual":
|
||||||
|
if toml_conf.get("fan_control"):
|
||||||
|
return cls.manual().from_bosminer(fan_config)
|
||||||
|
return cls.manual()
|
||||||
|
elif mode == "disabled":
|
||||||
|
return cls.immersion()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
min_fans = toml_conf["fan_control"]["min_fans"]
|
||||||
|
except KeyError:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
mode = toml_conf["temp_control"]["mode"]
|
if min_fans == 0:
|
||||||
if mode == "auto":
|
|
||||||
return cls.normal()
|
|
||||||
elif mode == "manual":
|
|
||||||
if toml_conf.get("fan_control"):
|
|
||||||
return cls.manual().from_bosminer(toml_conf["fan_control"])
|
|
||||||
return cls.manual()
|
|
||||||
elif mode == "disabled":
|
|
||||||
return cls.immersion()
|
return cls.immersion()
|
||||||
|
return cls.normal(minimum_fans=min_fans)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict):
|
def from_vnish(cls, web_settings: dict):
|
||||||
@@ -193,7 +345,7 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
keys = temperature_conf.keys()
|
keys = temperature_conf.keys()
|
||||||
if "auto" in keys:
|
if "auto" in keys:
|
||||||
if "minimumRequiredFans" in keys:
|
if "minimumRequiredFans" in keys:
|
||||||
return cls.normal(temperature_conf["minimumRequiredFans"])
|
return cls.normal(minimum_fans=temperature_conf["minimumRequiredFans"])
|
||||||
return cls.normal()
|
return cls.normal()
|
||||||
if "manual" in keys:
|
if "manual" in keys:
|
||||||
conf = {}
|
conf = {}
|
||||||
@@ -202,3 +354,68 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
if "minimumRequiredFans" in keys:
|
if "minimumRequiredFans" in keys:
|
||||||
conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"])
|
conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"])
|
||||||
return cls.manual(**conf)
|
return cls.manual(**conf)
|
||||||
|
if "disabled" in keys:
|
||||||
|
conf = {}
|
||||||
|
if "fanSpeedRatio" in temperature_conf["disabled"].keys():
|
||||||
|
conf["speed"] = int(temperature_conf["disabled"]["fanSpeedRatio"])
|
||||||
|
return cls.manual(**conf)
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_auradine(cls, web_fan: dict):
|
||||||
|
try:
|
||||||
|
fan_data = web_fan["Fan"][0]
|
||||||
|
fan_1_max = fan_data["Max"]
|
||||||
|
fan_1_target = fan_data["Target"]
|
||||||
|
return cls.manual(speed=round((fan_1_target / fan_1_max) * 100))
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_mara(cls, web_config: dict):
|
||||||
|
try:
|
||||||
|
mode = web_config["general-config"]["environment-profile"]
|
||||||
|
if mode == "AirCooling":
|
||||||
|
if web_config["advance-config"]["override-fan-control"]:
|
||||||
|
return cls.manual(
|
||||||
|
speed=web_config["advance-config"]["fan-fixed-percent"]
|
||||||
|
)
|
||||||
|
return cls.normal()
|
||||||
|
return cls.immersion()
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_espminer(cls, web_system_info: dict):
|
||||||
|
if web_system_info["autofanspeed"] == 1:
|
||||||
|
return cls.normal()
|
||||||
|
else:
|
||||||
|
return cls.manual(speed=web_system_info["fanspeed"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, rpc_fans: dict, rpc_tempctrl: dict):
|
||||||
|
try:
|
||||||
|
mode = rpc_tempctrl["TEMPCTRL"][0]["Mode"]
|
||||||
|
if mode == "Manual":
|
||||||
|
speed = rpc_fans["FANS"][0]["Speed"]
|
||||||
|
min_fans = rpc_fans["FANCTRL"][0]["MinFans"]
|
||||||
|
if min_fans == 0 and speed == 0:
|
||||||
|
return cls.immersion()
|
||||||
|
return cls.manual(
|
||||||
|
speed=speed,
|
||||||
|
minimum_fans=min_fans,
|
||||||
|
)
|
||||||
|
return cls.normal(
|
||||||
|
minimum_fans=rpc_fans["FANCTRL"][0]["MinFans"],
|
||||||
|
)
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
|
||||||
|
FanMode = TypeVar(
|
||||||
|
"FanMode",
|
||||||
|
bound=FanModeNormal | FanModeManual | FanModeImmersion,
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,332 +0,0 @@
|
|||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Copyright 2022 Upstream Data Inc -
|
|
||||||
# -
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
|
||||||
# you may not use this file except in compliance with the License. -
|
|
||||||
# You may obtain a copy of the License at -
|
|
||||||
# -
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
|
||||||
# -
|
|
||||||
# Unless required by applicable law or agreed to in writing, software -
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
|
||||||
# See the License for the specific language governing permissions and -
|
|
||||||
# limitations under the License. -
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Dict, Union
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
|
||||||
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
|
||||||
HashrateTargetMode,
|
|
||||||
PerformanceMode,
|
|
||||||
Power,
|
|
||||||
PowerTargetMode,
|
|
||||||
SaveAction,
|
|
||||||
SetPerformanceModeRequest,
|
|
||||||
TeraHashrate,
|
|
||||||
TunerPerformanceMode,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeNormal(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="normal")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeNormal":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeSleep(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="sleep")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeSleep":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "1"}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeLPM(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="low")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeLPM":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "3"}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeHPM(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="high")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeHPM":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_am_modern(self):
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
return {"mode": self.mode}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModePowerTune(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="power_tuning")
|
|
||||||
power: int = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModePowerTune":
|
|
||||||
return cls(dict_conf.get("power"))
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
|
||||||
if self.power is not None:
|
|
||||||
return {"mode": self.mode, self.mode: {"wattage": self.power}}
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
return {"autotuning": {"enabled": True, "psu_power_limit": self.power}}
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
return {
|
|
||||||
"set_performance_mode": SetPerformanceModeRequest(
|
|
||||||
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
|
||||||
mode=PerformanceMode(
|
|
||||||
tuner_mode=TunerPerformanceMode(
|
|
||||||
power_target=PowerTargetMode(
|
|
||||||
power_target=Power(watt=self.power)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeHashrateTune(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="hashrate_tuning")
|
|
||||||
hashrate: int = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeHashrateTune":
|
|
||||||
return cls(dict_conf.get("hashrate"))
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
return {
|
|
||||||
"set_performance_mode": SetPerformanceModeRequest(
|
|
||||||
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
|
|
||||||
mode=PerformanceMode(
|
|
||||||
tuner_mode=TunerPerformanceMode(
|
|
||||||
hashrate_target=HashrateTargetMode(
|
|
||||||
hashrate_target=TeraHashrate(
|
|
||||||
terahash_per_second=self.hashrate
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ManualBoardSettings(MinerConfigValue):
|
|
||||||
freq: float
|
|
||||||
volt: float
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "ManualBoardSettings":
|
|
||||||
return cls(freq=dict_conf["freq"], volt=dict_conf["volt"])
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MiningModeManual(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="manual")
|
|
||||||
|
|
||||||
global_freq: float
|
|
||||||
global_volt: float
|
|
||||||
boards: Dict[int, ManualBoardSettings] = field(default_factory=dict)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual":
|
|
||||||
return cls(
|
|
||||||
global_freq=dict_conf["global_freq"],
|
|
||||||
global_volt=dict_conf["global_volt"],
|
|
||||||
boards={i: ManualBoardSettings.from_dict(dict_conf[i]) for i in dict_conf},
|
|
||||||
)
|
|
||||||
|
|
||||||
def as_am_modern(self) -> dict:
|
|
||||||
return {"miner-mode": "0"}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
|
|
||||||
# will raise KeyError if it cant find the settings, values cannot be empty
|
|
||||||
voltage = web_overclock_settings["globals"]["volt"]
|
|
||||||
freq = web_overclock_settings["globals"]["freq"]
|
|
||||||
boards = {
|
|
||||||
idx: ManualBoardSettings(
|
|
||||||
freq=board["freq"],
|
|
||||||
volt=voltage if not board["freq"] == 0 else 0,
|
|
||||||
)
|
|
||||||
for idx, board in enumerate(web_overclock_settings["chains"])
|
|
||||||
}
|
|
||||||
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
|
||||||
|
|
||||||
|
|
||||||
class MiningModeConfig(MinerConfigOption):
|
|
||||||
normal = MiningModeNormal
|
|
||||||
low = MiningModeLPM
|
|
||||||
high = MiningModeHPM
|
|
||||||
sleep = MiningModeSleep
|
|
||||||
power_tuning = MiningModePowerTune
|
|
||||||
hashrate_tuning = MiningModeHashrateTune
|
|
||||||
manual = MiningModeManual
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def default(cls):
|
|
||||||
return cls.normal()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]):
|
|
||||||
if dict_conf is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
mode = dict_conf.get("mode")
|
|
||||||
if mode is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
clsattr = getattr(cls, mode)
|
|
||||||
if clsattr is not None:
|
|
||||||
return clsattr().from_dict(dict_conf)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_am_modern(cls, web_conf: dict):
|
|
||||||
if web_conf.get("bitmain-work-mode") is not None:
|
|
||||||
work_mode = web_conf["bitmain-work-mode"]
|
|
||||||
if work_mode == "":
|
|
||||||
return cls.default()
|
|
||||||
if int(work_mode) == 0:
|
|
||||||
return cls.normal()
|
|
||||||
elif int(work_mode) == 1:
|
|
||||||
return cls.sleep()
|
|
||||||
elif int(work_mode) == 3:
|
|
||||||
return cls.low()
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_epic(cls, web_conf: dict):
|
|
||||||
try:
|
|
||||||
work_mode = web_conf["PerpetualTune"]["Running"]
|
|
||||||
if work_mode:
|
|
||||||
if (
|
|
||||||
web_conf["PerpetualTune"]["Algorithm"].get("VoltageOptimizer")
|
|
||||||
is not None
|
|
||||||
):
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
web_conf["PerpetualTune"]["Algorithm"]["VoltageOptimizer"][
|
|
||||||
"Target"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
web_conf["PerpetualTune"]["Algorithm"]["ChipTune"]["Target"]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return cls.normal()
|
|
||||||
except KeyError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, toml_conf: dict):
|
|
||||||
if toml_conf.get("autotuning") is None:
|
|
||||||
return cls.default()
|
|
||||||
autotuning_conf = toml_conf["autotuning"]
|
|
||||||
|
|
||||||
if autotuning_conf.get("enabled") is None:
|
|
||||||
return cls.default()
|
|
||||||
if not autotuning_conf["enabled"]:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
if autotuning_conf.get("psu_power_limit") is not None:
|
|
||||||
# old autotuning conf
|
|
||||||
return cls.power_tuning(autotuning_conf["psu_power_limit"])
|
|
||||||
if autotuning_conf.get("mode") is not None:
|
|
||||||
# new autotuning conf
|
|
||||||
mode = autotuning_conf["mode"]
|
|
||||||
if mode == "power_target":
|
|
||||||
if autotuning_conf.get("power_target") is not None:
|
|
||||||
return cls.power_tuning(autotuning_conf["power_target"])
|
|
||||||
return cls.power_tuning()
|
|
||||||
if mode == "hashrate_target":
|
|
||||||
if autotuning_conf.get("hashrate_target") is not None:
|
|
||||||
return cls.hashrate_tuning(autotuning_conf["hashrate_target"])
|
|
||||||
return cls.hashrate_tuning()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vnish(cls, web_settings: dict):
|
|
||||||
try:
|
|
||||||
mode_settings = web_settings["miner"]["overclock"]
|
|
||||||
except KeyError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
if mode_settings["preset"] == "disabled":
|
|
||||||
return MiningModeManual.from_vnish(mode_settings)
|
|
||||||
else:
|
|
||||||
return cls.power_tuning(int(mode_settings["preset"]))
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_boser(cls, grpc_miner_conf: dict):
|
|
||||||
try:
|
|
||||||
tuner_conf = grpc_miner_conf["tuner"]
|
|
||||||
if not tuner_conf.get("enabled", False):
|
|
||||||
return cls.default()
|
|
||||||
except LookupError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
if tuner_conf.get("tunerMode") is not None:
|
|
||||||
if tuner_conf["tunerMode"] == 1:
|
|
||||||
if tuner_conf.get("powerTarget") is not None:
|
|
||||||
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
|
|
||||||
return cls.power_tuning()
|
|
||||||
|
|
||||||
if tuner_conf["tunerMode"] == 2:
|
|
||||||
if tuner_conf.get("hashrateTarget") is not None:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
|
|
||||||
)
|
|
||||||
return cls.hashrate_tuning()
|
|
||||||
|
|
||||||
if tuner_conf.get("powerTarget") is not None:
|
|
||||||
return cls.power_tuning(tuner_conf["powerTarget"]["watt"])
|
|
||||||
|
|
||||||
if tuner_conf.get("hashrateTarget") is not None:
|
|
||||||
return cls.hashrate_tuning(
|
|
||||||
int(tuner_conf["hashrateTarget"]["terahashPerSecond"])
|
|
||||||
)
|
|
||||||
907
pyasic/config/mining/__init__.py
Normal file
907
pyasic/config/mining/__init__.py
Normal file
@@ -0,0 +1,907 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import field
|
||||||
|
from typing import Any, TypeVar
|
||||||
|
|
||||||
|
from pyasic import settings
|
||||||
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
||||||
|
DpsHashrateTarget,
|
||||||
|
DpsPowerTarget,
|
||||||
|
DpsTarget,
|
||||||
|
HashrateTargetMode,
|
||||||
|
PerformanceMode,
|
||||||
|
Power,
|
||||||
|
PowerTargetMode,
|
||||||
|
SaveAction,
|
||||||
|
SetDpsRequest,
|
||||||
|
SetPerformanceModeRequest,
|
||||||
|
TeraHashrate,
|
||||||
|
TunerPerformanceMode,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .algo import (
|
||||||
|
BoardTuneAlgo,
|
||||||
|
ChipTuneAlgo,
|
||||||
|
StandardTuneAlgo,
|
||||||
|
TunerAlgo,
|
||||||
|
TunerAlgoType,
|
||||||
|
VOptAlgo,
|
||||||
|
)
|
||||||
|
from .presets import MiningPreset
|
||||||
|
from .scaling import ScalingConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeNormal(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="normal")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModeNormal:
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_btminer_v3(self) -> dict:
|
||||||
|
return {"set.miner.service": "start", "set.miner.power_mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": self.mode}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {"ptune": {"enabled": False}}
|
||||||
|
|
||||||
|
def as_goldshell(self) -> dict:
|
||||||
|
return {"settings": {"level": 0}}
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"mode": {
|
||||||
|
"work-mode-selector": "Stock",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"autotunerset": {"enabled": False}}
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
return {"autotuning": {"enabled": True}}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeSleep(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="sleep")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModeSleep:
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "1"}
|
||||||
|
return {"miner-mode": 1}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "1"}
|
||||||
|
return {"miner-mode": 1}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 1}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_btminer_v3(self) -> dict:
|
||||||
|
return {"set.miner.service": "stop"}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"sleep": "on"}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
return {"ptune": {"algo": "Sleep", "target": 0}}
|
||||||
|
|
||||||
|
def as_goldshell(self) -> dict:
|
||||||
|
return {"settings": {"level": 3}}
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"mode": {
|
||||||
|
"work-mode-selector": "Sleep",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeLPM(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="low")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModeLPM:
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "3"}
|
||||||
|
return {"miner-mode": 3}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "3"}
|
||||||
|
return {"miner-mode": 3}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 3}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_btminer_v3(self) -> dict:
|
||||||
|
return {"set.miner.service": "start", "set.miner.power_mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "eco"}}
|
||||||
|
|
||||||
|
def as_goldshell(self) -> dict:
|
||||||
|
return {"settings": {"level": 1}}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeHPM(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="high")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModeHPM:
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
return {"mode": self.mode}
|
||||||
|
|
||||||
|
def as_btminer_v3(self) -> dict:
|
||||||
|
return {"set.miner.service": "start", "set.miner.power_mode": self.mode}
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "turbo"}}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModePowerTune(MinerConfigValue):
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
mode: str = field(init=False, default="power_tuning")
|
||||||
|
power: int | None = None
|
||||||
|
algo: StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo = field(
|
||||||
|
default_factory=TunerAlgo.default
|
||||||
|
)
|
||||||
|
scaling: ScalingConfig | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModePowerTune:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls()
|
||||||
|
cls_conf = {}
|
||||||
|
if dict_conf.get("power"):
|
||||||
|
cls_conf["power"] = dict_conf["power"]
|
||||||
|
if dict_conf.get("algo"):
|
||||||
|
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
|
||||||
|
if dict_conf.get("scaling"):
|
||||||
|
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
|
||||||
|
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_wm(self) -> dict:
|
||||||
|
if self.power is not None:
|
||||||
|
return {"mode": self.mode, self.mode: {"wattage": self.power}}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def as_btminer_v3(self) -> dict:
|
||||||
|
return {"set.miner.service": "start", "set.miner.power_limit": self.power}
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
tuning_cfg = {"enabled": True, "mode": "power_target"}
|
||||||
|
if self.power is not None:
|
||||||
|
tuning_cfg["power_target"] = self.power
|
||||||
|
|
||||||
|
cfg = {"autotuning": tuning_cfg}
|
||||||
|
|
||||||
|
if self.scaling is not None:
|
||||||
|
scaling_cfg: dict[str, Any] = {"enabled": True}
|
||||||
|
if self.scaling.step is not None:
|
||||||
|
scaling_cfg["power_step"] = self.scaling.step
|
||||||
|
if self.scaling.minimum is not None:
|
||||||
|
scaling_cfg["min_power_target"] = self.scaling.minimum
|
||||||
|
if self.scaling.shutdown is not None:
|
||||||
|
scaling_cfg.update(self.scaling.shutdown.as_bosminer())
|
||||||
|
cfg["performance_scaling"] = scaling_cfg
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
cfg: dict[str, Any] = {
|
||||||
|
"set_performance_mode": SetPerformanceModeRequest(
|
||||||
|
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
|
||||||
|
mode=PerformanceMode(
|
||||||
|
tuner_mode=TunerPerformanceMode(
|
||||||
|
power_target=PowerTargetMode(
|
||||||
|
power_target=Power(watt=self.power)
|
||||||
|
if self.power is not None
|
||||||
|
else None # type: ignore[arg-type]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
if self.scaling is not None:
|
||||||
|
sd_cfg = {}
|
||||||
|
if self.scaling.shutdown is not None:
|
||||||
|
sd_cfg = self.scaling.shutdown.as_boser()
|
||||||
|
power_target_kwargs: dict[str, Any] = {}
|
||||||
|
if self.scaling.step is not None:
|
||||||
|
power_target_kwargs["power_step"] = Power(watt=self.scaling.step)
|
||||||
|
if self.scaling.minimum is not None:
|
||||||
|
power_target_kwargs["min_power_target"] = Power(
|
||||||
|
watt=self.scaling.minimum
|
||||||
|
)
|
||||||
|
cfg["set_dps"] = SetDpsRequest(
|
||||||
|
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
|
||||||
|
enable=True,
|
||||||
|
**sd_cfg,
|
||||||
|
target=DpsTarget(power_target=DpsPowerTarget(**power_target_kwargs)),
|
||||||
|
)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"mode": {
|
||||||
|
"work-mode-selector": "Auto",
|
||||||
|
"concorde": {
|
||||||
|
"mode-select": "PowerTarget",
|
||||||
|
"power-target": self.power,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"autotunerset": {"enabled": True}}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeHashrateTune(MinerConfigValue):
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
mode: str = field(init=False, default="hashrate_tuning")
|
||||||
|
hashrate: int | None = None
|
||||||
|
algo: StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo = field(
|
||||||
|
default_factory=TunerAlgo.default
|
||||||
|
)
|
||||||
|
scaling: ScalingConfig | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModeHashrateTune:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls()
|
||||||
|
cls_conf = {}
|
||||||
|
if dict_conf.get("hashrate"):
|
||||||
|
cls_conf["hashrate"] = dict_conf["hashrate"]
|
||||||
|
if dict_conf.get("algo"):
|
||||||
|
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
|
||||||
|
if dict_conf.get("scaling"):
|
||||||
|
cls_conf["scaling"] = ScalingConfig.from_dict(dict_conf["scaling"])
|
||||||
|
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
conf = {"enabled": True, "mode": "hashrate_target"}
|
||||||
|
if self.hashrate is not None:
|
||||||
|
conf["hashrate_target"] = self.hashrate
|
||||||
|
return {"autotuning": conf}
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
cfg: dict[str, Any] = {
|
||||||
|
"set_performance_mode": SetPerformanceModeRequest(
|
||||||
|
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
|
||||||
|
mode=PerformanceMode(
|
||||||
|
tuner_mode=TunerPerformanceMode(
|
||||||
|
hashrate_target=HashrateTargetMode(
|
||||||
|
hashrate_target=TeraHashrate(
|
||||||
|
terahash_per_second=float(self.hashrate)
|
||||||
|
if self.hashrate is not None
|
||||||
|
else None # type: ignore[arg-type]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if self.scaling is not None:
|
||||||
|
sd_cfg = {}
|
||||||
|
if self.scaling.shutdown is not None:
|
||||||
|
sd_cfg = self.scaling.shutdown.as_boser()
|
||||||
|
hashrate_target_kwargs: dict[str, Any] = {}
|
||||||
|
if self.scaling.step is not None:
|
||||||
|
hashrate_target_kwargs["hashrate_step"] = TeraHashrate(
|
||||||
|
terahash_per_second=float(self.scaling.step)
|
||||||
|
)
|
||||||
|
if self.scaling.minimum is not None:
|
||||||
|
hashrate_target_kwargs["min_hashrate_target"] = TeraHashrate(
|
||||||
|
terahash_per_second=float(self.scaling.minimum)
|
||||||
|
)
|
||||||
|
cfg["set_dps"] = SetDpsRequest(
|
||||||
|
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
|
||||||
|
enable=True,
|
||||||
|
**sd_cfg,
|
||||||
|
target=DpsTarget(
|
||||||
|
hashrate_target=DpsHashrateTarget(**hashrate_target_kwargs)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_auradine(self) -> dict:
|
||||||
|
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
mode = {
|
||||||
|
"ptune": {
|
||||||
|
"algo": (
|
||||||
|
self.algo.as_epic()
|
||||||
|
if hasattr(self.algo, "as_epic")
|
||||||
|
else TunerAlgo.default().as_epic()
|
||||||
|
),
|
||||||
|
"target": self.hashrate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.scaling is not None:
|
||||||
|
if self.scaling.minimum is not None:
|
||||||
|
mode["ptune"]["min_throttle"] = self.scaling.minimum
|
||||||
|
if self.scaling.step is not None:
|
||||||
|
mode["ptune"]["throttle_step"] = self.scaling.step
|
||||||
|
return mode
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"mode": {
|
||||||
|
"work-mode-selector": "Auto",
|
||||||
|
"concorde": {
|
||||||
|
"mode-select": "Hashrate",
|
||||||
|
"hash-target": self.hashrate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"autotunerset": {"enabled": True}}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModePreset(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="preset")
|
||||||
|
|
||||||
|
active_preset: MiningPreset
|
||||||
|
available_presets: list[MiningPreset] = field(default_factory=list)
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {"overclock": {**self.active_preset.as_vnish()}}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vnish(
|
||||||
|
cls,
|
||||||
|
web_overclock_settings: dict,
|
||||||
|
web_presets: list[dict],
|
||||||
|
web_perf_summary: dict,
|
||||||
|
) -> MiningModePreset:
|
||||||
|
active_preset = web_perf_summary.get("current_preset")
|
||||||
|
|
||||||
|
if active_preset is None:
|
||||||
|
for preset in web_presets:
|
||||||
|
if preset["name"] == web_overclock_settings["preset"]:
|
||||||
|
active_preset = preset
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
active_preset=MiningPreset.from_vnish(active_preset or {}),
|
||||||
|
available_presets=[MiningPreset.from_vnish(p) for p in web_presets],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModePreset:
|
||||||
|
active_preset = cls.get_active_preset_from_luxos(rpc_config, rpc_profiles)
|
||||||
|
return cls(
|
||||||
|
active_preset=active_preset,
|
||||||
|
available_presets=[
|
||||||
|
MiningPreset.from_luxos(p) for p in rpc_profiles["PROFILES"]
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_active_preset_from_luxos(
|
||||||
|
cls, rpc_config: dict, rpc_profiles: dict
|
||||||
|
) -> MiningPreset:
|
||||||
|
active_preset = None
|
||||||
|
active_profile = rpc_config["CONFIG"][0]["Profile"]
|
||||||
|
for profile in rpc_profiles["PROFILES"]:
|
||||||
|
if profile["Profile Name"] == active_profile:
|
||||||
|
active_preset = profile
|
||||||
|
return MiningPreset.from_luxos(active_preset or {})
|
||||||
|
|
||||||
|
|
||||||
|
class ManualBoardSettings(MinerConfigValue):
|
||||||
|
freq: float
|
||||||
|
volt: float
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict) -> ManualBoardSettings:
|
||||||
|
return cls(freq=dict_conf["freq"], volt=dict_conf["volt"])
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_hiveon_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {"freq": self.freq}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeManual(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="manual")
|
||||||
|
|
||||||
|
global_freq: float
|
||||||
|
global_volt: float
|
||||||
|
boards: dict[int, ManualBoardSettings] = field(default_factory=dict)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict) -> MiningModeManual:
|
||||||
|
return cls(
|
||||||
|
global_freq=dict_conf["global_freq"],
|
||||||
|
global_volt=dict_conf["global_volt"],
|
||||||
|
boards={
|
||||||
|
i: ManualBoardSettings.from_dict(dict_conf[i])
|
||||||
|
for i in dict_conf
|
||||||
|
if isinstance(i, int)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
def as_am_modern(self) -> dict:
|
||||||
|
if settings.get("antminer_mining_mode_as_str", False):
|
||||||
|
return {"miner-mode": "0"}
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
|
||||||
|
return {
|
||||||
|
"overclock": {
|
||||||
|
"chains": chains if chains != [] else None,
|
||||||
|
"globals": {
|
||||||
|
"freq": int(self.global_freq),
|
||||||
|
"volt": int(self.global_volt),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vnish(cls, web_overclock_settings: dict) -> MiningModeManual:
|
||||||
|
# will raise KeyError if it cant find the settings, values cannot be empty
|
||||||
|
voltage = web_overclock_settings["globals"]["volt"]
|
||||||
|
freq = web_overclock_settings["globals"]["freq"]
|
||||||
|
boards = {
|
||||||
|
idx: ManualBoardSettings(
|
||||||
|
freq=board["freq"],
|
||||||
|
volt=voltage if not board["freq"] == 0 else 0,
|
||||||
|
)
|
||||||
|
for idx, board in enumerate(web_overclock_settings["chains"])
|
||||||
|
}
|
||||||
|
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_epic(cls, epic_conf: dict) -> MiningModeManual:
|
||||||
|
voltage = 0
|
||||||
|
freq = 0
|
||||||
|
if epic_conf.get("HwConfig") is not None:
|
||||||
|
freq = epic_conf["HwConfig"]["Boards Target Clock"][0]["Data"]
|
||||||
|
if epic_conf.get("Power Supply Stats") is not None:
|
||||||
|
voltage = epic_conf["Power Supply Stats"]["Target Voltage"]
|
||||||
|
boards = {}
|
||||||
|
if epic_conf.get("HBs") is not None:
|
||||||
|
boards = {
|
||||||
|
board["Index"]: ManualBoardSettings(
|
||||||
|
freq=board["Core Clock Avg"], volt=board["Input Voltage"]
|
||||||
|
)
|
||||||
|
for board in epic_conf["HBs"]
|
||||||
|
}
|
||||||
|
return cls(global_freq=freq, global_volt=voltage, boards=boards)
|
||||||
|
|
||||||
|
def as_mara(self) -> dict:
|
||||||
|
return {
|
||||||
|
"mode": {
|
||||||
|
"work-mode-selector": "Fixed",
|
||||||
|
"fixed": {
|
||||||
|
"frequency": str(self.global_freq),
|
||||||
|
"voltage": self.global_volt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MiningModeConfig(MinerConfigOption):
|
||||||
|
normal = MiningModeNormal
|
||||||
|
low = MiningModeLPM
|
||||||
|
high = MiningModeHPM
|
||||||
|
sleep = MiningModeSleep
|
||||||
|
power_tuning = MiningModePowerTune
|
||||||
|
hashrate_tuning = MiningModeHashrateTune
|
||||||
|
preset = MiningModePreset
|
||||||
|
manual = MiningModeManual
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls) -> MiningModeConfig:
|
||||||
|
return cls.normal()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> MiningModeConfig:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
mode = dict_conf.get("mode")
|
||||||
|
if mode is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
cls_attr = getattr(cls, mode, None)
|
||||||
|
if cls_attr is not None:
|
||||||
|
return cls_attr().from_dict(dict_conf)
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_am_modern(cls, web_conf: dict) -> MiningModeConfig:
|
||||||
|
if web_conf.get("bitmain-work-mode") is not None:
|
||||||
|
work_mode = web_conf["bitmain-work-mode"]
|
||||||
|
if work_mode == "":
|
||||||
|
return cls.default()
|
||||||
|
if int(work_mode) == 0:
|
||||||
|
return cls.normal()
|
||||||
|
elif int(work_mode) == 1:
|
||||||
|
return cls.sleep()
|
||||||
|
elif int(work_mode) == 3:
|
||||||
|
return cls.low()
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hiveon_modern(cls, web_conf: dict) -> MiningModeConfig:
|
||||||
|
if web_conf.get("bitmain-work-mode") is not None:
|
||||||
|
work_mode = web_conf["bitmain-work-mode"]
|
||||||
|
if work_mode == "":
|
||||||
|
return cls.default()
|
||||||
|
if int(work_mode) == 0:
|
||||||
|
return cls.normal()
|
||||||
|
elif int(work_mode) == 1:
|
||||||
|
return cls.sleep()
|
||||||
|
elif int(work_mode) == 3:
|
||||||
|
return cls.low()
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict) -> MiningModeConfig:
|
||||||
|
if web_conf.get("fc-work-mode") is not None:
|
||||||
|
work_mode = web_conf["fc-work-mode"]
|
||||||
|
if work_mode == "":
|
||||||
|
return cls.default()
|
||||||
|
if int(work_mode) == 0:
|
||||||
|
return cls.normal()
|
||||||
|
elif int(work_mode) == 1:
|
||||||
|
return cls.sleep()
|
||||||
|
elif int(work_mode) == 3:
|
||||||
|
return cls.low()
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_epic(cls, web_conf: dict) -> MiningModeConfig:
|
||||||
|
try:
|
||||||
|
tuner_running = web_conf["PerpetualTune"]["Running"]
|
||||||
|
if tuner_running:
|
||||||
|
algo_info = web_conf["PerpetualTune"]["Algorithm"]
|
||||||
|
if algo_info.get("VoltageOptimizer") is not None:
|
||||||
|
scaling_cfg = None
|
||||||
|
if "Throttle Step" in algo_info["VoltageOptimizer"]:
|
||||||
|
scaling_cfg = ScalingConfig(
|
||||||
|
minimum=algo_info["VoltageOptimizer"].get(
|
||||||
|
"Min Throttle Target"
|
||||||
|
),
|
||||||
|
step=algo_info["VoltageOptimizer"].get("Throttle Step"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=algo_info["VoltageOptimizer"].get("Target"),
|
||||||
|
algo=TunerAlgo.voltage_optimizer(),
|
||||||
|
scaling=scaling_cfg,
|
||||||
|
)
|
||||||
|
elif algo_info.get("BoardTune") is not None:
|
||||||
|
scaling_cfg = None
|
||||||
|
if "Throttle Step" in algo_info["BoardTune"]:
|
||||||
|
scaling_cfg = ScalingConfig(
|
||||||
|
minimum=algo_info["BoardTune"].get("Min Throttle Target"),
|
||||||
|
step=algo_info["BoardTune"].get("Throttle Step"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=algo_info["BoardTune"].get("Target"),
|
||||||
|
algo=TunerAlgo.board_tune(),
|
||||||
|
scaling=scaling_cfg,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=algo_info["ChipTune"].get("Target"),
|
||||||
|
algo=TunerAlgo.chip_tune(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return cls.manual.from_epic(web_conf)
|
||||||
|
except KeyError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_conf: dict) -> MiningModeConfig:
|
||||||
|
if toml_conf.get("autotuning") is None:
|
||||||
|
return cls.default()
|
||||||
|
autotuning_conf = toml_conf["autotuning"]
|
||||||
|
|
||||||
|
if autotuning_conf.get("enabled") is None:
|
||||||
|
return cls.default()
|
||||||
|
if not autotuning_conf["enabled"]:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
if autotuning_conf.get("psu_power_limit") is not None:
|
||||||
|
# old autotuning conf
|
||||||
|
return cls.power_tuning(
|
||||||
|
power=autotuning_conf["psu_power_limit"],
|
||||||
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
||||||
|
)
|
||||||
|
if autotuning_conf.get("mode") is not None:
|
||||||
|
# new autotuning conf
|
||||||
|
mode = autotuning_conf["mode"]
|
||||||
|
if mode == "power_target":
|
||||||
|
if autotuning_conf.get("power_target") is not None:
|
||||||
|
return cls.power_tuning(
|
||||||
|
power=autotuning_conf["power_target"],
|
||||||
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
||||||
|
)
|
||||||
|
return cls.power_tuning(
|
||||||
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"),
|
||||||
|
)
|
||||||
|
if mode == "hashrate_target":
|
||||||
|
if autotuning_conf.get("hashrate_target") is not None:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=autotuning_conf["hashrate_target"],
|
||||||
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
|
||||||
|
)
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"),
|
||||||
|
)
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vnish(
|
||||||
|
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
|
||||||
|
) -> MiningModeConfig:
|
||||||
|
try:
|
||||||
|
mode_settings = web_settings["miner"]["overclock"]
|
||||||
|
except KeyError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
if mode_settings["preset"] == "disabled":
|
||||||
|
return cls.manual.from_vnish(mode_settings, web_presets, web_perf_summary)
|
||||||
|
else:
|
||||||
|
return cls.preset.from_vnish(mode_settings, web_presets, web_perf_summary)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, grpc_miner_conf: dict) -> MiningModeConfig:
|
||||||
|
try:
|
||||||
|
tuner_conf = grpc_miner_conf["tuner"]
|
||||||
|
if not tuner_conf.get("enabled", False):
|
||||||
|
return cls.default()
|
||||||
|
except LookupError:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
if tuner_conf.get("tunerMode") is not None:
|
||||||
|
if tuner_conf["tunerMode"] == 1:
|
||||||
|
if tuner_conf.get("powerTarget") is not None:
|
||||||
|
return cls.power_tuning(
|
||||||
|
power=tuner_conf["powerTarget"]["watt"],
|
||||||
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"),
|
||||||
|
)
|
||||||
|
return cls.power_tuning(
|
||||||
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power")
|
||||||
|
)
|
||||||
|
|
||||||
|
if tuner_conf["tunerMode"] == 2:
|
||||||
|
if tuner_conf.get("hashrateTarget") is not None:
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
||||||
|
scaling=ScalingConfig.from_boser(
|
||||||
|
grpc_miner_conf, mode="hashrate"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return cls.hashrate_tuning(
|
||||||
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if tuner_conf.get("powerTarget") is not None:
|
||||||
|
return cls.power_tuning(
|
||||||
|
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(
|
||||||
|
hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]),
|
||||||
|
scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_auradine(cls, web_mode: dict) -> MiningModeConfig:
|
||||||
|
try:
|
||||||
|
mode_data = web_mode["Mode"][0]
|
||||||
|
if mode_data.get("Sleep") == "on":
|
||||||
|
return cls.sleep()
|
||||||
|
if mode_data.get("Mode") == "normal":
|
||||||
|
return cls.normal()
|
||||||
|
if mode_data.get("Mode") == "eco":
|
||||||
|
return cls.low()
|
||||||
|
if mode_data.get("Mode") == "turbo":
|
||||||
|
return cls.high()
|
||||||
|
if mode_data.get("Ths") is not None:
|
||||||
|
return cls.hashrate_tuning(hashrate=mode_data["Ths"])
|
||||||
|
if mode_data.get("Power") is not None:
|
||||||
|
return cls.power_tuning(power=mode_data["Power"])
|
||||||
|
except LookupError:
|
||||||
|
return cls.default()
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_btminer_v3(
|
||||||
|
cls, rpc_device_info: dict, rpc_settings: dict
|
||||||
|
) -> MiningModeConfig:
|
||||||
|
try:
|
||||||
|
is_mining = rpc_device_info["msg"]["miner"]["working"] == "true"
|
||||||
|
if not is_mining:
|
||||||
|
return cls.sleep()
|
||||||
|
power_limit = rpc_settings["msg"]["power-limit"]
|
||||||
|
if not power_limit == 0:
|
||||||
|
return cls.power_tuning(power=power_limit)
|
||||||
|
power_mode = rpc_settings["msg"]["power-mode"]
|
||||||
|
if power_mode == "normal":
|
||||||
|
return cls.normal()
|
||||||
|
if power_mode == "high":
|
||||||
|
return cls.high()
|
||||||
|
if power_mode == "low":
|
||||||
|
return cls.low()
|
||||||
|
|
||||||
|
except LookupError:
|
||||||
|
return cls.default()
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_mara(cls, web_config: dict) -> MiningModeConfig:
|
||||||
|
try:
|
||||||
|
mode = web_config["mode"]["work-mode-selector"]
|
||||||
|
if mode == "Fixed":
|
||||||
|
fixed_conf = web_config["mode"]["fixed"]
|
||||||
|
return cls.manual(
|
||||||
|
global_freq=int(fixed_conf["frequency"]),
|
||||||
|
global_volt=fixed_conf["voltage"],
|
||||||
|
)
|
||||||
|
elif mode == "Stock":
|
||||||
|
return cls.normal()
|
||||||
|
elif mode == "Sleep":
|
||||||
|
return cls.sleep()
|
||||||
|
elif mode == "Auto":
|
||||||
|
auto_conf = web_config["mode"]["concorde"]
|
||||||
|
auto_mode = auto_conf["mode-select"]
|
||||||
|
if auto_mode == "Hashrate":
|
||||||
|
return cls.hashrate_tuning(hashrate=auto_conf["hash-target"])
|
||||||
|
elif auto_mode == "PowerTarget":
|
||||||
|
return cls.power_tuning(power=auto_conf["power-target"])
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModeConfig:
|
||||||
|
preset_info = MiningModePreset.from_luxos(rpc_config, rpc_profiles)
|
||||||
|
return cls.preset(
|
||||||
|
active_preset=preset_info.active_preset,
|
||||||
|
available_presets=preset_info.available_presets,
|
||||||
|
)
|
||||||
|
|
||||||
|
def as_btminer_v3(self) -> dict:
|
||||||
|
"""Delegate to the default instance for btminer v3 configuration."""
|
||||||
|
return self.default().as_btminer_v3()
|
||||||
|
|
||||||
|
|
||||||
|
MiningMode = TypeVar(
|
||||||
|
"MiningMode",
|
||||||
|
bound=MiningModeNormal
|
||||||
|
| MiningModeHPM
|
||||||
|
| MiningModeLPM
|
||||||
|
| MiningModeSleep
|
||||||
|
| MiningModeManual
|
||||||
|
| MiningModePowerTune
|
||||||
|
| MiningModeHashrateTune
|
||||||
|
| MiningModePreset,
|
||||||
|
)
|
||||||
66
pyasic/config/mining/algo.py
Normal file
66
pyasic/config/mining/algo.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import field
|
||||||
|
from typing import Any, TypeVar
|
||||||
|
|
||||||
|
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
|
class StandardTuneAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="standard")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return VOptAlgo().as_epic()
|
||||||
|
|
||||||
|
|
||||||
|
class VOptAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="voltage_optimizer")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return "VoltageOptimizer"
|
||||||
|
|
||||||
|
|
||||||
|
class BoardTuneAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="board_tune")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return "BoardTune"
|
||||||
|
|
||||||
|
|
||||||
|
class ChipTuneAlgo(MinerConfigValue):
|
||||||
|
mode: str = field(init=False, default="chip_tune")
|
||||||
|
|
||||||
|
def as_epic(self) -> str:
|
||||||
|
return "ChipTune"
|
||||||
|
|
||||||
|
|
||||||
|
class TunerAlgo(MinerConfigOption):
|
||||||
|
standard = StandardTuneAlgo
|
||||||
|
voltage_optimizer = VOptAlgo
|
||||||
|
board_tune = BoardTuneAlgo
|
||||||
|
chip_tune = ChipTuneAlgo
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls) -> StandardTuneAlgo:
|
||||||
|
return cls.standard()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(
|
||||||
|
cls, dict_conf: dict[Any, Any] | None
|
||||||
|
) -> StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls.default()
|
||||||
|
mode = dict_conf.get("mode")
|
||||||
|
if mode is None:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
cls_attr = getattr(cls, mode, None)
|
||||||
|
if cls_attr is not None:
|
||||||
|
return cls_attr().from_dict(dict_conf)
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
|
|
||||||
|
TunerAlgoType = TypeVar(
|
||||||
|
"TunerAlgoType",
|
||||||
|
bound=StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo,
|
||||||
|
)
|
||||||
54
pyasic/config/mining/presets.py
Normal file
54
pyasic/config/mining/presets.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from pyasic.config.base import MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
|
class MiningPreset(MinerConfigValue):
|
||||||
|
name: str | None = None
|
||||||
|
power: int | None = None
|
||||||
|
hashrate: int | None = None
|
||||||
|
tuned: bool | None = None
|
||||||
|
modded_psu: bool | None = None
|
||||||
|
frequency: int | None = None
|
||||||
|
voltage: float | None = None
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
if self.name is not None:
|
||||||
|
return {"preset": self.name}
|
||||||
|
return {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vnish(cls, web_preset: dict):
|
||||||
|
name = web_preset["name"]
|
||||||
|
hr_power_split = web_preset["pretty"].split("~")
|
||||||
|
if len(hr_power_split) == 1:
|
||||||
|
power = None
|
||||||
|
hashrate = None
|
||||||
|
else:
|
||||||
|
power = hr_power_split[0].replace("watt", "").strip()
|
||||||
|
hashrate = (
|
||||||
|
hr_power_split[1]
|
||||||
|
.replace("TH", "")
|
||||||
|
.replace("GH", "")
|
||||||
|
.replace("MH", "")
|
||||||
|
.replace(" LC", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
|
tuned = web_preset["status"] == "tuned"
|
||||||
|
modded_psu = web_preset["modded_psu_required"]
|
||||||
|
return cls(
|
||||||
|
name=name,
|
||||||
|
power=power,
|
||||||
|
hashrate=hashrate,
|
||||||
|
tuned=tuned,
|
||||||
|
modded_psu=modded_psu,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, profile: dict):
|
||||||
|
return cls(
|
||||||
|
name=profile["Profile Name"],
|
||||||
|
power=profile["Watts"],
|
||||||
|
hashrate=round(profile["Hashrate"]),
|
||||||
|
tuned=profile["IsTuned"],
|
||||||
|
frequency=profile["Frequency"],
|
||||||
|
voltage=profile["Voltage"],
|
||||||
|
)
|
||||||
133
pyasic/config/mining/scaling.py
Normal file
133
pyasic/config/mining/scaling.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pyasic.config.base import MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
|
class ScalingShutdown(MinerConfigValue):
|
||||||
|
enabled: bool = False
|
||||||
|
duration: int | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> ScalingShutdown:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls()
|
||||||
|
return cls(
|
||||||
|
enabled=dict_conf.get("enabled", False), duration=dict_conf.get("duration")
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, power_scaling_conf: dict):
|
||||||
|
sd_enabled = power_scaling_conf.get("shutdown_enabled")
|
||||||
|
if sd_enabled is not None:
|
||||||
|
return cls(
|
||||||
|
enabled=sd_enabled, duration=power_scaling_conf.get("shutdown_duration")
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, power_scaling_conf: dict):
|
||||||
|
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
||||||
|
if sd_enabled is not None:
|
||||||
|
try:
|
||||||
|
return cls(
|
||||||
|
enabled=sd_enabled,
|
||||||
|
duration=power_scaling_conf["shutdownDuration"]["hours"],
|
||||||
|
)
|
||||||
|
except KeyError:
|
||||||
|
return cls(enabled=sd_enabled)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def as_bosminer(self) -> dict:
|
||||||
|
cfg: dict[str, bool | int] = {"shutdown_enabled": self.enabled}
|
||||||
|
|
||||||
|
if self.duration is not None:
|
||||||
|
cfg["shutdown_duration"] = self.duration
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
|
||||||
|
def as_boser(self) -> dict:
|
||||||
|
return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration}
|
||||||
|
|
||||||
|
|
||||||
|
class ScalingConfig(MinerConfigValue):
|
||||||
|
step: int | None = None
|
||||||
|
minimum: int | None = None
|
||||||
|
shutdown: ScalingShutdown | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, dict_conf: dict | None) -> ScalingConfig:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls()
|
||||||
|
cls_conf = {
|
||||||
|
"step": dict_conf.get("step"),
|
||||||
|
"minimum": dict_conf.get("minimum"),
|
||||||
|
}
|
||||||
|
shutdown = dict_conf.get("shutdown")
|
||||||
|
if shutdown is not None:
|
||||||
|
cls_conf["shutdown"] = ScalingShutdown.from_dict(shutdown)
|
||||||
|
return cls(**cls_conf)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_conf: dict, mode: str | None = None):
|
||||||
|
if mode == "power":
|
||||||
|
return cls._from_bosminer_power(toml_conf)
|
||||||
|
if mode == "hashrate":
|
||||||
|
# not implemented yet
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _from_bosminer_power(cls, toml_conf: dict):
|
||||||
|
power_scaling = toml_conf.get("power_scaling")
|
||||||
|
if power_scaling is None:
|
||||||
|
power_scaling = toml_conf.get("performance_scaling")
|
||||||
|
if power_scaling is not None:
|
||||||
|
enabled = power_scaling.get("enabled")
|
||||||
|
if not enabled:
|
||||||
|
return None
|
||||||
|
power_step = power_scaling.get("power_step")
|
||||||
|
min_power = power_scaling.get("min_psu_power_limit")
|
||||||
|
if min_power is None:
|
||||||
|
min_power = power_scaling.get("min_power_target")
|
||||||
|
sd_mode = ScalingShutdown.from_bosminer(power_scaling)
|
||||||
|
|
||||||
|
return cls(step=power_step, minimum=min_power, shutdown=sd_mode)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, grpc_miner_conf: dict, mode: str | None = None):
|
||||||
|
if mode == "power":
|
||||||
|
return cls._from_boser_power(grpc_miner_conf)
|
||||||
|
if mode == "hashrate":
|
||||||
|
# not implemented yet
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _from_boser_power(cls, grpc_miner_conf: dict):
|
||||||
|
try:
|
||||||
|
dps_conf = grpc_miner_conf["dps"]
|
||||||
|
if not dps_conf.get("enabled", False):
|
||||||
|
return None
|
||||||
|
except LookupError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
conf = {"shutdown": ScalingShutdown.from_boser(dps_conf)}
|
||||||
|
|
||||||
|
if dps_conf.get("minPowerTarget") is not None:
|
||||||
|
conf["minimum"] = dps_conf["minPowerTarget"]["watt"]
|
||||||
|
if dps_conf.get("powerStep") is not None:
|
||||||
|
conf["step"] = dps_conf["powerStep"]["watt"]
|
||||||
|
return cls(**conf)
|
||||||
@@ -13,136 +13,223 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
from dataclasses import dataclass, field
|
from typing import Any
|
||||||
from typing import Dict, List, Union
|
|
||||||
|
from pydantic import Field
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigValue
|
from pyasic.config.base import MinerConfigValue
|
||||||
|
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
||||||
|
PoolConfiguration,
|
||||||
|
PoolGroupConfiguration,
|
||||||
|
Quota,
|
||||||
|
SaveAction,
|
||||||
|
SetPoolGroupsRequest,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Pool(MinerConfigValue):
|
class Pool(MinerConfigValue):
|
||||||
url: str
|
url: str
|
||||||
user: str
|
user: str
|
||||||
password: str
|
password: str
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str = None):
|
def as_am_modern(self, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
return {
|
||||||
return {
|
"url": self.url,
|
||||||
"url": self.url,
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
"user": f"{self.user}{user_suffix}",
|
"pass": self.password,
|
||||||
"pass": self.password,
|
}
|
||||||
}
|
|
||||||
return {"url": self.url, "user": self.user, "pass": self.password}
|
|
||||||
|
|
||||||
def as_wm(self, idx: int = 1, user_suffix: str = None):
|
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
return {
|
||||||
return {
|
"url": self.url,
|
||||||
f"pool_{idx}": self.url,
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
f"worker_{idx}": f"{self.user}{user_suffix}",
|
"pass": self.password,
|
||||||
f"passwd_{idx}": self.password,
|
}
|
||||||
}
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"url": self.url,
|
||||||
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
|
"pass": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
||||||
return {
|
return {
|
||||||
f"pool_{idx}": self.url,
|
f"pool_{idx}": self.url,
|
||||||
f"worker_{idx}": self.user,
|
f"worker_{idx}": f"{self.user}{user_suffix or ''}",
|
||||||
f"passwd_{idx}": self.password,
|
f"passwd_{idx}": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_am_old(self, idx: int = 1, user_suffix: str = None):
|
def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
return {
|
||||||
return {
|
"pool": self.url,
|
||||||
f"_ant_pool{idx}url": self.url,
|
"worker": f"{self.user}{user_suffix or ''}",
|
||||||
f"_ant_pool{idx}user": f"{self.user}{user_suffix}",
|
"passwd": self.password,
|
||||||
f"_ant_pool{idx}pw": self.password,
|
}
|
||||||
}
|
|
||||||
|
def as_am_old(
|
||||||
|
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
|
||||||
|
) -> dict:
|
||||||
|
idx = args[0] if args else kwargs.get("idx", 1)
|
||||||
return {
|
return {
|
||||||
f"_ant_pool{idx}url": self.url,
|
f"_ant_pool{idx}url": self.url,
|
||||||
f"_ant_pool{idx}user": self.user,
|
f"_ant_pool{idx}user": f"{self.user}{user_suffix or ''}",
|
||||||
f"_ant_pool{idx}pw": self.password,
|
f"_ant_pool{idx}pw": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str = None):
|
def as_goldshell(self, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
return {
|
||||||
return {
|
"url": self.url,
|
||||||
"url": self.url,
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
"user": f"{self.user}{user_suffix}",
|
"pass": self.password,
|
||||||
"pass": self.password,
|
}
|
||||||
}
|
|
||||||
return {"url": self.url, "user": self.user, "pass": self.password}
|
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str = None):
|
def as_avalon(self, user_suffix: str | None = None) -> str:
|
||||||
if user_suffix is not None:
|
return ",".join([self.url, f"{self.user}{user_suffix or ''}", self.password])
|
||||||
return ",".join([self.url, f"{self.user}{user_suffix}", self.password])
|
|
||||||
return ",".join([self.url, self.user, self.password])
|
|
||||||
|
|
||||||
def as_inno(self, idx: int = 1, user_suffix: str = None):
|
def as_inno(
|
||||||
if user_suffix is not None:
|
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
|
||||||
return {
|
) -> dict:
|
||||||
f"Pool{idx}": self.url,
|
idx = args[0] if args else kwargs.get("idx", 1)
|
||||||
f"UserName{idx}": f"{self.user}{user_suffix}",
|
|
||||||
f"Password{idx}": self.password,
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
f"Pool{idx}": self.url,
|
f"Pool{idx}": self.url,
|
||||||
f"UserName{idx}": self.user,
|
f"UserName{idx}": f"{self.user}{user_suffix or ''}",
|
||||||
f"Password{idx}": self.password,
|
f"Password{idx}": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str = None):
|
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
||||||
if user_suffix is not None:
|
return {
|
||||||
return {
|
"url": self.url,
|
||||||
"url": self.url,
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
"user": f"{self.user}{user_suffix}",
|
"password": self.password,
|
||||||
"password": self.password,
|
}
|
||||||
}
|
|
||||||
return {"url": self.url, "user": self.user, "password": self.password}
|
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"url": self.url,
|
||||||
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
|
"pass": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_epic(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"pool": self.url,
|
||||||
|
"login": f"{self.user}{user_suffix or ''}",
|
||||||
|
"password": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_mara(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"url": self.url,
|
||||||
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
|
"pass": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"stratumURL": self.url,
|
||||||
|
"stratumUser": f"{self.user}{user_suffix or ''}",
|
||||||
|
"stratumPassword": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_boser(self, user_suffix: str | None = None) -> PoolConfiguration:
|
||||||
|
return PoolConfiguration(
|
||||||
|
url=self.url,
|
||||||
|
user=f"{self.user}{user_suffix or ''}",
|
||||||
|
password=self.password,
|
||||||
|
enabled=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"url": self.url,
|
||||||
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
|
"pass": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "Pool":
|
def from_dict(cls, dict_conf: dict | None) -> Pool:
|
||||||
|
if dict_conf is None:
|
||||||
|
raise ValueError("dict_conf cannot be None")
|
||||||
return cls(
|
return cls(
|
||||||
url=dict_conf["url"], user=dict_conf["user"], password=dict_conf["password"]
|
url=dict_conf["url"], user=dict_conf["user"], password=dict_conf["password"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api(cls, api_pool: dict) -> "Pool":
|
def from_api(cls, api_pool: dict) -> Pool:
|
||||||
return cls(url=api_pool["URL"], user=api_pool["User"], password="x")
|
return cls(url=api_pool["URL"], user=api_pool["User"], password="x")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, api_pool: dict) -> "Pool":
|
def from_btminer_v3(cls, api_pool: dict) -> Pool:
|
||||||
|
return cls(url=api_pool["url"], user=api_pool["account"], password="x")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_epic(cls, api_pool: dict) -> Pool:
|
||||||
return cls(
|
return cls(
|
||||||
url=api_pool["pool"], user=api_pool["login"], password=api_pool["password"]
|
url=api_pool["pool"], user=api_pool["login"], password=api_pool["password"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_modern(cls, web_pool: dict) -> "Pool":
|
def from_am_modern(cls, web_pool: dict) -> Pool:
|
||||||
|
return cls(
|
||||||
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hiveon_modern(cls, web_pool: dict) -> Pool:
|
||||||
|
return cls(
|
||||||
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_pool: dict) -> Pool:
|
||||||
return cls(
|
return cls(
|
||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: check if this is accurate, user/username, pass/password
|
# TODO: check if this is accurate, user/username, pass/password
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pool: dict) -> "Pool":
|
def from_goldshell(cls, web_pool: dict) -> Pool:
|
||||||
return cls(
|
return cls(
|
||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_inno(cls, web_pool: dict) -> "Pool":
|
def from_inno(cls, web_pool: dict) -> Pool:
|
||||||
return cls(
|
return cls(
|
||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_pool_conf: dict) -> "Pool":
|
def from_bosminer(cls, toml_pool_conf: dict) -> Pool:
|
||||||
return cls(
|
return cls(
|
||||||
url=toml_pool_conf["url"],
|
url=toml_pool_conf["url"],
|
||||||
user=toml_pool_conf["user"],
|
user=toml_pool_conf["user"],
|
||||||
password=toml_pool_conf["password"],
|
password=toml_pool_conf.get("password", ""),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_pool: dict) -> "Pool":
|
def from_vnish(cls, web_pool: dict) -> Pool:
|
||||||
|
return cls(
|
||||||
|
url="stratum+tcp://" + web_pool["url"],
|
||||||
|
user=web_pool["user"],
|
||||||
|
password=web_pool["pass"],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_boser(cls, grpc_pool: dict) -> Pool:
|
||||||
|
return cls(
|
||||||
|
url=grpc_pool["url"],
|
||||||
|
user=grpc_pool["user"],
|
||||||
|
password=grpc_pool["password"],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_mara(cls, web_pool: dict) -> Pool:
|
||||||
return cls(
|
return cls(
|
||||||
url=web_pool["url"],
|
url=web_pool["url"],
|
||||||
user=web_pool["user"],
|
user=web_pool["user"],
|
||||||
@@ -150,19 +237,31 @@ class Pool(MinerConfigValue):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_pool: dict) -> "Pool":
|
def from_espminer(cls, web_system_info: dict) -> Pool:
|
||||||
|
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
|
||||||
return cls(
|
return cls(
|
||||||
url=grpc_pool["url"],
|
url=url,
|
||||||
user=grpc_pool["user"],
|
user=web_system_info["stratumUser"],
|
||||||
password=grpc_pool["password"],
|
password=web_system_info.get("stratumPassword", ""),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, rpc_pools: dict) -> Pool:
|
||||||
|
return cls.from_api(rpc_pools)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_iceriver(cls, web_pool: dict) -> Pool:
|
||||||
|
return cls(
|
||||||
|
url=web_pool["addr"],
|
||||||
|
user=web_pool["user"],
|
||||||
|
password=web_pool["pass"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PoolGroup(MinerConfigValue):
|
class PoolGroup(MinerConfigValue):
|
||||||
pools: List[Pool] = field(default_factory=list)
|
pools: list[Pool] = Field(default_factory=list)
|
||||||
quota: int = 1
|
quota: int = 1
|
||||||
name: str = None
|
name: str | None = None
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
if self.name is None:
|
if self.name is None:
|
||||||
@@ -170,67 +269,94 @@ class PoolGroup(MinerConfigValue):
|
|||||||
random.choice(string.ascii_uppercase + string.digits) for _ in range(6)
|
random.choice(string.ascii_uppercase + string.digits) for _ in range(6)
|
||||||
) # generate random pool group name in case it isn't set
|
) # generate random pool group name in case it isn't set
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str = None) -> list:
|
def as_am_modern(self, user_suffix: str | None = None) -> list:
|
||||||
pools = []
|
pools = []
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
if len(self.pools) > idx:
|
if len(self.pools) > idx:
|
||||||
pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix))
|
pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix))
|
||||||
else:
|
else:
|
||||||
pools.append(Pool("", "", "").as_am_modern())
|
pools.append(Pool(url="", user="", password="").as_am_modern())
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> dict:
|
def as_hiveon_modern(self, user_suffix: str | None = None) -> list:
|
||||||
pools = {}
|
pools = []
|
||||||
|
idx = 0
|
||||||
|
while idx < 3:
|
||||||
|
if len(self.pools) > idx:
|
||||||
|
pools.append(self.pools[idx].as_hiveon_modern(user_suffix=user_suffix))
|
||||||
|
else:
|
||||||
|
pools.append(Pool(url="", user="", password="").as_hiveon_modern())
|
||||||
|
idx += 1
|
||||||
|
return pools
|
||||||
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> list:
|
||||||
|
pools = []
|
||||||
|
idx = 0
|
||||||
|
while idx < 3:
|
||||||
|
if len(self.pools) > idx:
|
||||||
|
pools.append(self.pools[idx].as_elphapex(user_suffix=user_suffix))
|
||||||
|
else:
|
||||||
|
pools.append(Pool(url="", user="", password="").as_elphapex())
|
||||||
|
idx += 1
|
||||||
|
return pools
|
||||||
|
|
||||||
|
def as_wm(self, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> dict:
|
||||||
|
pools: dict[str, str] = {}
|
||||||
|
idx = 0
|
||||||
|
while idx < 3:
|
||||||
|
if len(self.pools) > idx:
|
||||||
|
pools.update(**self.pools[idx].as_wm(idx + 1, user_suffix=user_suffix))
|
||||||
|
else:
|
||||||
|
pools.update(**Pool(url="", user="", password="").as_wm(idx + 1))
|
||||||
|
idx += 1
|
||||||
|
return pools
|
||||||
|
|
||||||
|
def as_btminer_v3(self, user_suffix: str | None = None) -> list:
|
||||||
|
return [pool.as_btminer_v3(user_suffix) for pool in self.pools[:3]]
|
||||||
|
|
||||||
|
def as_am_old(
|
||||||
|
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
|
||||||
|
) -> dict:
|
||||||
|
pools: dict[str, str] = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
if len(self.pools) > idx:
|
if len(self.pools) > idx:
|
||||||
pools.update(
|
pools.update(
|
||||||
**self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix)
|
**self.pools[idx].as_am_old(idx + 1, user_suffix=user_suffix)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pools.update(**Pool("", "", "").as_wm(idx=idx + 1))
|
pools.update(**Pool(url="", user="", password="").as_am_old(idx + 1))
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str = None) -> dict:
|
def as_goldshell(self, user_suffix: str | None = None) -> list:
|
||||||
pools = {}
|
|
||||||
idx = 0
|
|
||||||
while idx < 3:
|
|
||||||
if len(self.pools) > idx:
|
|
||||||
pools.update(
|
|
||||||
**self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
pools.update(**Pool("", "", "").as_am_old(idx=idx + 1))
|
|
||||||
idx += 1
|
|
||||||
return pools
|
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str = None) -> list:
|
|
||||||
return [pool.as_goldshell(user_suffix) for pool in self.pools]
|
return [pool.as_goldshell(user_suffix) for pool in self.pools]
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str = None) -> dict:
|
def as_avalon(self, user_suffix: str | None = None) -> str:
|
||||||
if len(self.pools) > 0:
|
if len(self.pools) > 0:
|
||||||
return self.pools[0].as_avalon(user_suffix=user_suffix)
|
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:
|
def as_inno(
|
||||||
pools = {}
|
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
|
||||||
|
) -> dict:
|
||||||
|
pools: dict[str, str] = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
while idx < 3:
|
while idx < 3:
|
||||||
if len(self.pools) > idx:
|
if len(self.pools) > idx:
|
||||||
pools.update(
|
pools.update(
|
||||||
**self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix)
|
**self.pools[idx].as_inno(idx + 1, user_suffix=user_suffix)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
pools.update(**Pool("", "", "").as_inno(idx=idx + 1))
|
pools.update(**Pool(url="", user="", password="").as_inno(idx + 1))
|
||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str = None) -> dict:
|
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.pools) > 0:
|
if len(self.pools) > 0:
|
||||||
conf = {
|
conf: dict[str, Any] = {
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"pool": [
|
"pool": [
|
||||||
pool.as_bosminer(user_suffix=user_suffix) for pool in self.pools
|
pool.as_bosminer(user_suffix=user_suffix) for pool in self.pools
|
||||||
@@ -241,8 +367,33 @@ class PoolGroup(MinerConfigValue):
|
|||||||
return conf
|
return conf
|
||||||
return {"name": "Group", "pool": []}
|
return {"name": "Group", "pool": []}
|
||||||
|
|
||||||
|
def as_auradine(self, user_suffix: str | None = None) -> list:
|
||||||
|
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
|
||||||
|
|
||||||
|
def as_epic(self, user_suffix: str | None = None) -> list:
|
||||||
|
return [p.as_epic(user_suffix=user_suffix) for p in self.pools]
|
||||||
|
|
||||||
|
def as_mara(self, user_suffix: str | None = None) -> list:
|
||||||
|
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
|
||||||
|
|
||||||
|
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return self.pools[0].as_espminer(user_suffix=user_suffix)
|
||||||
|
|
||||||
|
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
|
||||||
|
return PoolGroupConfiguration(
|
||||||
|
name=self.name or "",
|
||||||
|
quota=Quota(value=self.quota),
|
||||||
|
pools=[p.as_boser() for p in self.pools],
|
||||||
|
)
|
||||||
|
|
||||||
|
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {"pools": [p.as_vnish(user_suffix=user_suffix) for p in self.pools]}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "PoolGroup":
|
def from_dict(cls, dict_conf: dict | None) -> PoolGroup:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls()
|
||||||
|
|
||||||
cls_conf = {}
|
cls_conf = {}
|
||||||
|
|
||||||
if dict_conf.get("quota") is not None:
|
if dict_conf.get("quota") is not None:
|
||||||
@@ -253,79 +404,120 @@ class PoolGroup(MinerConfigValue):
|
|||||||
return cls(**cls_conf)
|
return cls(**cls_conf)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api(cls, api_pool_list: list) -> "PoolGroup":
|
def from_api(cls, api_pool_list: list) -> PoolGroup:
|
||||||
pools = []
|
pools = []
|
||||||
for pool in api_pool_list:
|
for pool in api_pool_list:
|
||||||
pools.append(Pool.from_api(pool))
|
pools.append(Pool.from_api(pool))
|
||||||
return cls(pools=pools)
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, api_pool_list: list) -> "PoolGroup":
|
def from_btminer_v3(cls, api_pool_list: list) -> PoolGroup:
|
||||||
|
pools = []
|
||||||
|
for pool in api_pool_list:
|
||||||
|
pools.append(Pool.from_btminer_v3(pool))
|
||||||
|
return cls(pools=pools)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_epic(cls, api_pool_list: list) -> PoolGroup:
|
||||||
pools = []
|
pools = []
|
||||||
for pool in api_pool_list:
|
for pool in api_pool_list:
|
||||||
pools.append(Pool.from_epic(pool))
|
pools.append(Pool.from_epic(pool))
|
||||||
return cls(pools=pools)
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_modern(cls, web_pool_list: list) -> "PoolGroup":
|
def from_am_modern(cls, web_pool_list: list) -> PoolGroup:
|
||||||
pools = []
|
pools = []
|
||||||
for pool in web_pool_list:
|
for pool in web_pool_list:
|
||||||
pools.append(Pool.from_am_modern(pool))
|
pools.append(Pool.from_am_modern(pool))
|
||||||
return cls(pools=pools)
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
def from_hiveon_modern(cls, web_pool_list: list) -> PoolGroup:
|
||||||
return cls([Pool.from_goldshell(p) for p in web_pools])
|
pools = []
|
||||||
|
for pool in web_pool_list:
|
||||||
|
pools.append(Pool.from_hiveon_modern(pool))
|
||||||
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_inno(cls, web_pools: list) -> "PoolGroup":
|
def from_elphapex(cls, web_pool_list: list) -> PoolGroup:
|
||||||
return cls([Pool.from_inno(p) for p in web_pools])
|
pools = []
|
||||||
|
for pool in web_pool_list:
|
||||||
|
pools.append(Pool.from_elphapex(pool))
|
||||||
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup":
|
def from_goldshell(cls, web_pools: list) -> PoolGroup:
|
||||||
|
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_inno(cls, web_pools: list) -> PoolGroup:
|
||||||
|
return cls(pools=[Pool.from_inno(p) for p in web_pools])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_group_conf: dict) -> PoolGroup:
|
||||||
if toml_group_conf.get("pool") is not None:
|
if toml_group_conf.get("pool") is not None:
|
||||||
return cls(
|
return cls(
|
||||||
name=toml_group_conf["name"],
|
name=toml_group_conf["name"],
|
||||||
quota=toml_group_conf.get("quota"),
|
quota=toml_group_conf.get("quota", 1),
|
||||||
pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]],
|
pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]],
|
||||||
)
|
)
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings_pools: dict) -> "PoolGroup":
|
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 if p["url"] != ""]
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_pool_group: dict):
|
def from_boser(cls, grpc_pool_group: dict) -> PoolGroup:
|
||||||
try:
|
try:
|
||||||
return cls(
|
return cls(
|
||||||
pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]],
|
pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]],
|
||||||
name=grpc_pool_group["name"],
|
name=grpc_pool_group["name"],
|
||||||
quota=grpc_pool_group["quota"]["value"]
|
quota=(
|
||||||
if grpc_pool_group.get("quota") is not None
|
grpc_pool_group["quota"]["value"]
|
||||||
else 1,
|
if grpc_pool_group.get("quota") is not None
|
||||||
|
else 1
|
||||||
|
),
|
||||||
)
|
)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
@dataclass
|
def from_mara(cls, web_config_pools: dict) -> PoolGroup:
|
||||||
class PoolConfig(MinerConfigValue):
|
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
|
||||||
groups: List[PoolGroup] = field(default_factory=list)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls) -> "PoolConfig":
|
def from_espminer(cls, web_system_info: dict) -> PoolGroup:
|
||||||
|
return cls(pools=[Pool.from_espminer(web_system_info)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_iceriver(cls, web_userpanel: dict) -> PoolGroup:
|
||||||
|
return cls(
|
||||||
|
pools=[
|
||||||
|
Pool.from_iceriver(web_pool)
|
||||||
|
for web_pool in web_userpanel["data"]["pools"]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PoolConfig(MinerConfigValue):
|
||||||
|
groups: list[PoolGroup] = Field(default_factory=list)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls) -> PoolConfig:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "PoolConfig":
|
def from_dict(cls, dict_conf: dict | None) -> PoolConfig:
|
||||||
if dict_conf is None:
|
if dict_conf is None:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]])
|
return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def simple(cls, pools: List[Union[Pool, Dict[str, str]]]) -> "PoolConfig":
|
def simple(cls, pools: list[Pool | dict[str, str]]) -> PoolConfig:
|
||||||
group_pools = []
|
group_pools = []
|
||||||
for pool in pools:
|
for pool in pools:
|
||||||
if isinstance(pool, dict):
|
if isinstance(pool, dict):
|
||||||
@@ -333,91 +525,193 @@ class PoolConfig(MinerConfigValue):
|
|||||||
group_pools.append(pool)
|
group_pools.append(pool)
|
||||||
return cls(groups=[PoolGroup(pools=group_pools)])
|
return cls(groups=[PoolGroup(pools=group_pools)])
|
||||||
|
|
||||||
def as_am_modern(self, user_suffix: str = None) -> dict:
|
def as_am_modern(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_am_modern()}
|
return {"pools": PoolGroup().as_am_modern()}
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str = None) -> dict:
|
def as_hiveon_modern(self, user_suffix: str | None = None) -> dict:
|
||||||
|
if len(self.groups) > 0:
|
||||||
|
return {"pools": self.groups[0].as_hiveon_modern(user_suffix=user_suffix)}
|
||||||
|
return {"pools": PoolGroup().as_hiveon_modern()}
|
||||||
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
||||||
|
if len(self.groups) > 0:
|
||||||
|
return {"pools": self.groups[0].as_elphapex(user_suffix=user_suffix)}
|
||||||
|
return {"pools": PoolGroup().as_elphapex()}
|
||||||
|
|
||||||
|
def as_wm(self, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_wm()}
|
return {"pools": PoolGroup().as_wm()}
|
||||||
|
|
||||||
def as_am_old(self, user_suffix: str = None) -> dict:
|
def as_btminer_v3(self, user_suffix: str | None = None) -> dict:
|
||||||
|
if len(self.groups) > 0:
|
||||||
|
return {"pools": self.groups[0].as_btminer_v3(user_suffix=user_suffix)}
|
||||||
|
return {"pools": PoolGroup().as_btminer_v3()}
|
||||||
|
|
||||||
|
def as_am_old(
|
||||||
|
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
|
||||||
|
) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return self.groups[0].as_am_old(user_suffix=user_suffix)
|
return self.groups[0].as_am_old(user_suffix=user_suffix)
|
||||||
return PoolGroup().as_am_old()
|
return PoolGroup().as_am_old()
|
||||||
|
|
||||||
def as_goldshell(self, user_suffix: str = None) -> dict:
|
def as_goldshell(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_goldshell(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_goldshell(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_goldshell()}
|
return {"pools": PoolGroup().as_goldshell()}
|
||||||
|
|
||||||
def as_avalon(self, user_suffix: str = None) -> dict:
|
def as_avalon(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_avalon()}
|
return {"pools": PoolGroup().as_avalon()}
|
||||||
|
|
||||||
def as_inno(self, user_suffix: str = None) -> dict:
|
def as_inno(
|
||||||
|
self, *args: Any, user_suffix: str | None = None, **kwargs: Any
|
||||||
|
) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return self.groups[0].as_inno(user_suffix=user_suffix)
|
return self.groups[0].as_inno(user_suffix=user_suffix)
|
||||||
return PoolGroup().as_inno()
|
return PoolGroup().as_inno()
|
||||||
|
|
||||||
def as_bosminer(self, user_suffix: str = None) -> dict:
|
def as_bosminer(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {
|
return {
|
||||||
"group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups]
|
"group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups]
|
||||||
}
|
}
|
||||||
return {"group": [PoolGroup().as_bosminer()]}
|
return {"group": [PoolGroup().as_bosminer()]}
|
||||||
|
|
||||||
def as_boser(self, user_suffix: str = None) -> dict:
|
def as_boser(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"set_pool_groups": SetPoolGroupsRequest(
|
||||||
|
save_action=SaveAction(SaveAction.SAVE_AND_APPLY),
|
||||||
|
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_auradine(self, user_suffix: str | None = None) -> dict:
|
||||||
|
if len(self.groups) > 0:
|
||||||
|
return {
|
||||||
|
"updatepools": {
|
||||||
|
"pools": self.groups[0].as_auradine(user_suffix=user_suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {"updatepools": {"pools": PoolGroup().as_auradine()}}
|
||||||
|
|
||||||
|
def as_epic(self, user_suffix: str | None = None) -> dict:
|
||||||
|
if len(self.groups) > 0:
|
||||||
|
return {
|
||||||
|
"pools": {
|
||||||
|
"coin": "Btc",
|
||||||
|
"stratum_configs": self.groups[0].as_epic(user_suffix=user_suffix),
|
||||||
|
"unique_id": False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
"pools": {
|
||||||
|
"coin": "Btc",
|
||||||
|
"stratum_configs": [PoolGroup().as_epic()],
|
||||||
|
"unique_id": False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def as_mara(self, user_suffix: str | None = None) -> dict:
|
||||||
|
if len(self.groups) > 0:
|
||||||
|
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
|
||||||
|
return {"pools": []}
|
||||||
|
|
||||||
|
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return self.groups[0].as_espminer(user_suffix=user_suffix)
|
||||||
|
|
||||||
|
def as_luxos(self, user_suffix: str | None = None) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def as_vnish(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return self.groups[0].as_vnish(user_suffix=user_suffix)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api(cls, api_pools: dict) -> "PoolConfig":
|
def from_api(cls, api_pools: dict) -> PoolConfig:
|
||||||
try:
|
try:
|
||||||
pool_data = api_pools["POOLS"]
|
pool_data = api_pools["POOLS"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return PoolConfig.default()
|
return PoolConfig.default()
|
||||||
pool_data = sorted(pool_data, key=lambda x: int(x["POOL"]))
|
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
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict) -> "PoolConfig":
|
def from_btminer_v3(cls, rpc_pools: dict) -> PoolConfig:
|
||||||
|
try:
|
||||||
|
pool_data = rpc_pools["pools"]
|
||||||
|
except KeyError:
|
||||||
|
return PoolConfig.default()
|
||||||
|
pool_data = sorted(pool_data, key=lambda x: int(x["id"]))
|
||||||
|
|
||||||
|
return cls(groups=[PoolGroup.from_btminer_v3(pool_data)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_epic(cls, web_conf: dict) -> PoolConfig:
|
||||||
pool_data = web_conf["StratumConfigs"]
|
pool_data = web_conf["StratumConfigs"]
|
||||||
return cls([PoolGroup.from_epic(pool_data)])
|
return cls(groups=[PoolGroup.from_epic(pool_data)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_modern(cls, web_conf: dict) -> "PoolConfig":
|
def from_am_modern(cls, web_conf: dict) -> PoolConfig:
|
||||||
|
try:
|
||||||
|
pool_data = web_conf["pools"]
|
||||||
|
except KeyError:
|
||||||
|
return cls(groups=[])
|
||||||
|
|
||||||
|
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_hiveon_modern(cls, web_conf: dict) -> PoolConfig:
|
||||||
|
try:
|
||||||
|
pool_data = web_conf["pools"]
|
||||||
|
except KeyError:
|
||||||
|
return cls(groups=[])
|
||||||
|
|
||||||
|
return cls(groups=[PoolGroup.from_hiveon_modern(pool_data)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict) -> PoolConfig:
|
||||||
pool_data = web_conf["pools"]
|
pool_data = web_conf["pools"]
|
||||||
|
|
||||||
return cls([PoolGroup.from_am_modern(pool_data)])
|
return cls(groups=[PoolGroup.from_elphapex(pool_data)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
|
def from_goldshell(cls, web_pools: list) -> PoolConfig:
|
||||||
return cls([PoolGroup.from_goldshell(web_pools)])
|
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_inno(cls, web_pools: list) -> "PoolConfig":
|
def from_goldshell_byte(cls, web_pools: list) -> PoolConfig:
|
||||||
return cls([PoolGroup.from_inno(web_pools)])
|
return cls(
|
||||||
|
groups=[
|
||||||
|
PoolGroup.from_goldshell(g["pools"])
|
||||||
|
for g in web_pools
|
||||||
|
if len(g["pools"]) > 0
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_conf: dict) -> "PoolConfig":
|
def from_inno(cls, web_pools: list) -> PoolConfig:
|
||||||
|
return cls(groups=[PoolGroup.from_inno(web_pools)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_bosminer(cls, toml_conf: dict) -> PoolConfig:
|
||||||
if toml_conf.get("group") is None:
|
if toml_conf.get("group") is None:
|
||||||
return cls()
|
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
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict) -> "PoolConfig":
|
def from_vnish(cls, web_settings: dict) -> PoolConfig:
|
||||||
try:
|
try:
|
||||||
return cls([PoolGroup.from_vnish(web_settings["miner"]["pools"])])
|
return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])])
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_miner_conf: dict):
|
def from_boser(cls, grpc_miner_conf: dict) -> PoolConfig:
|
||||||
try:
|
try:
|
||||||
return cls(
|
return cls(
|
||||||
groups=[
|
groups=[
|
||||||
@@ -427,3 +721,32 @@ class PoolConfig(MinerConfigValue):
|
|||||||
)
|
)
|
||||||
except LookupError:
|
except LookupError:
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_mara(cls, web_config: dict) -> PoolConfig:
|
||||||
|
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_espminer(cls, web_system_info: dict) -> PoolConfig:
|
||||||
|
return cls(groups=[PoolGroup.from_espminer(web_system_info)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_iceriver(cls, web_userpanel: dict) -> PoolConfig:
|
||||||
|
return cls(groups=[PoolGroup.from_iceriver(web_userpanel)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> PoolConfig:
|
||||||
|
return cls(
|
||||||
|
groups=[
|
||||||
|
PoolGroup(
|
||||||
|
pools=[
|
||||||
|
Pool.from_luxos(pool)
|
||||||
|
for pool in rpc_pools["POOLS"]
|
||||||
|
if pool["GROUP"] == group["GROUP"]
|
||||||
|
],
|
||||||
|
name=group["Name"],
|
||||||
|
quota=group["Quota"],
|
||||||
|
)
|
||||||
|
for group in rpc_groups["GROUPS"]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,218 +0,0 @@
|
|||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Copyright 2022 Upstream Data Inc -
|
|
||||||
# -
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
|
||||||
# you may not use this file except in compliance with the License. -
|
|
||||||
# You may obtain a copy of the License at -
|
|
||||||
# -
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
|
||||||
# -
|
|
||||||
# Unless required by applicable law or agreed to in writing, software -
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
|
||||||
# See the License for the specific language governing permissions and -
|
|
||||||
# limitations under the License. -
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
from dataclasses import dataclass, field
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigOption, MinerConfigValue
|
|
||||||
from pyasic.web.braiins_os.proto.braiins.bos.v1 import (
|
|
||||||
DpsPowerTarget,
|
|
||||||
DpsTarget,
|
|
||||||
Power,
|
|
||||||
SetDpsRequest,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PowerScalingShutdownEnabled(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="enabled")
|
|
||||||
duration: int = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "PowerScalingShutdownEnabled":
|
|
||||||
return cls(duration=dict_conf.get("duration"))
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
cfg = {"shutdown_enabled": True}
|
|
||||||
|
|
||||||
if self.duration is not None:
|
|
||||||
cfg["shutdown_duration"] = self.duration
|
|
||||||
|
|
||||||
return cfg
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
return {"enable_shutdown": True, "shutdown_duration": self.duration}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PowerScalingShutdownDisabled(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="disabled")
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "PowerScalingShutdownDisabled":
|
|
||||||
return cls()
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
return {"shutdown_enabled": False}
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
return {"enable_shutdown ": False}
|
|
||||||
|
|
||||||
|
|
||||||
class PowerScalingShutdown(MinerConfigOption):
|
|
||||||
enabled = PowerScalingShutdownEnabled
|
|
||||||
disabled = PowerScalingShutdownDisabled
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]):
|
|
||||||
if dict_conf is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
mode = dict_conf.get("mode")
|
|
||||||
if mode is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
clsattr = getattr(cls, mode)
|
|
||||||
if clsattr is not None:
|
|
||||||
return clsattr().from_dict(dict_conf)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, power_scaling_conf: dict):
|
|
||||||
sd_enabled = power_scaling_conf.get("shutdown_enabled")
|
|
||||||
if sd_enabled is not None:
|
|
||||||
if sd_enabled:
|
|
||||||
return cls.enabled(power_scaling_conf.get("shutdown_duration"))
|
|
||||||
else:
|
|
||||||
return cls.disabled()
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_boser(cls, power_scaling_conf: dict):
|
|
||||||
sd_enabled = power_scaling_conf.get("shutdownEnabled")
|
|
||||||
if sd_enabled is not None:
|
|
||||||
if sd_enabled:
|
|
||||||
try:
|
|
||||||
return cls.enabled(power_scaling_conf["shutdownDuration"]["hours"])
|
|
||||||
except KeyError:
|
|
||||||
return cls.enabled()
|
|
||||||
else:
|
|
||||||
return cls.disabled()
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PowerScalingEnabled(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="enabled")
|
|
||||||
power_step: int = None
|
|
||||||
minimum_power: int = None
|
|
||||||
shutdown_enabled: Union[
|
|
||||||
PowerScalingShutdownEnabled, PowerScalingShutdownDisabled
|
|
||||||
] = None
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, power_scaling_conf: dict) -> "PowerScalingEnabled":
|
|
||||||
power_step = power_scaling_conf.get("power_step")
|
|
||||||
min_power = power_scaling_conf.get("min_psu_power_limit")
|
|
||||||
sd_mode = PowerScalingShutdown.from_bosminer(power_scaling_conf)
|
|
||||||
|
|
||||||
return cls(
|
|
||||||
power_step=power_step, minimum_power=min_power, shutdown_enabled=sd_mode
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "PowerScalingEnabled":
|
|
||||||
cls_conf = {
|
|
||||||
"power_step": dict_conf.get("power_step"),
|
|
||||||
"minimum_power": dict_conf.get("minimum_power"),
|
|
||||||
}
|
|
||||||
shutdown_enabled = dict_conf.get("shutdown_enabled")
|
|
||||||
if shutdown_enabled is not None:
|
|
||||||
cls_conf["shutdown_enabled"] = PowerScalingShutdown.from_dict(
|
|
||||||
shutdown_enabled
|
|
||||||
)
|
|
||||||
return cls(**cls_conf)
|
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
|
||||||
cfg = {"enabled": True}
|
|
||||||
if self.power_step is not None:
|
|
||||||
cfg["power_step"] = self.power_step
|
|
||||||
if self.minimum_power is not None:
|
|
||||||
cfg["min_psu_power_limit"] = self.minimum_power
|
|
||||||
|
|
||||||
if self.shutdown_enabled is not None:
|
|
||||||
cfg = {**cfg, **self.shutdown_enabled.as_bosminer()}
|
|
||||||
|
|
||||||
return {"power_scaling": cfg}
|
|
||||||
|
|
||||||
def as_boser(self) -> dict:
|
|
||||||
return {
|
|
||||||
"set_dps": SetDpsRequest(
|
|
||||||
enable=True,
|
|
||||||
**self.shutdown_enabled.as_boser(),
|
|
||||||
target=DpsTarget(
|
|
||||||
power_target=DpsPowerTarget(
|
|
||||||
power_step=Power(self.power_step),
|
|
||||||
min_power_target=Power(self.minimum_power),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PowerScalingDisabled(MinerConfigValue):
|
|
||||||
mode: str = field(init=False, default="disabled")
|
|
||||||
|
|
||||||
|
|
||||||
class PowerScalingConfig(MinerConfigOption):
|
|
||||||
enabled = PowerScalingEnabled
|
|
||||||
disabled = PowerScalingDisabled
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def default(cls):
|
|
||||||
return cls.disabled()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]):
|
|
||||||
if dict_conf is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
mode = dict_conf.get("mode")
|
|
||||||
if mode is None:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
clsattr = getattr(cls, mode)
|
|
||||||
if clsattr is not None:
|
|
||||||
return clsattr().from_dict(dict_conf)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_bosminer(cls, toml_conf: dict):
|
|
||||||
power_scaling = toml_conf.get("power_scaling")
|
|
||||||
if power_scaling is not None:
|
|
||||||
enabled = power_scaling.get("enabled")
|
|
||||||
if enabled is not None:
|
|
||||||
if enabled:
|
|
||||||
return cls.enabled().from_bosminer(power_scaling)
|
|
||||||
else:
|
|
||||||
return cls.disabled()
|
|
||||||
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_boser(cls, grpc_miner_conf: dict):
|
|
||||||
try:
|
|
||||||
dps_conf = grpc_miner_conf["dps"]
|
|
||||||
if not dps_conf.get("enabled", False):
|
|
||||||
return cls.disabled()
|
|
||||||
except LookupError:
|
|
||||||
return cls.default()
|
|
||||||
|
|
||||||
conf = {"shutdown_enabled": PowerScalingShutdown.from_boser(dps_conf)}
|
|
||||||
|
|
||||||
if dps_conf.get("minPowerTarget") is not None:
|
|
||||||
conf["minimum_power"] = dps_conf["minPowerTarget"]["watt"]
|
|
||||||
if dps_conf.get("powerStep") is not None:
|
|
||||||
conf["power_step"] = dps_conf["powerStep"]["watt"]
|
|
||||||
return cls.enabled(**conf)
|
|
||||||
@@ -13,17 +13,15 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from dataclasses import dataclass
|
from __future__ import annotations
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
from pyasic.config.base import MinerConfigValue
|
from pyasic.config.base import MinerConfigValue
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class TemperatureConfig(MinerConfigValue):
|
class TemperatureConfig(MinerConfigValue):
|
||||||
target: int = None
|
target: int | None = None
|
||||||
hot: int = None
|
hot: int | None = None
|
||||||
danger: int = None
|
danger: int | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default(cls):
|
def default(cls):
|
||||||
@@ -37,10 +35,32 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
temp_cfg["hot_temp"] = self.hot
|
temp_cfg["hot_temp"] = self.hot
|
||||||
if self.danger is not None:
|
if self.danger is not None:
|
||||||
temp_cfg["dangerous_temp"] = self.danger
|
temp_cfg["dangerous_temp"] = self.danger
|
||||||
|
if len(temp_cfg) == 0:
|
||||||
|
return {}
|
||||||
return {"temp_control": temp_cfg}
|
return {"temp_control": temp_cfg}
|
||||||
|
|
||||||
|
def as_epic(self) -> dict:
|
||||||
|
temps_config: dict = {"temps": {}, "fans": {"Auto": {}}}
|
||||||
|
if self.target is not None:
|
||||||
|
temps_config["fans"]["Auto"]["Target Temperature"] = self.target
|
||||||
|
else:
|
||||||
|
temps_config["fans"]["Auto"]["Target Temperature"] = 60
|
||||||
|
if self.danger is not None:
|
||||||
|
temps_config["temps"]["critical"] = self.danger
|
||||||
|
if self.hot is not None:
|
||||||
|
temps_config["temps"]["shutdown"] = self.hot
|
||||||
|
return temps_config
|
||||||
|
|
||||||
|
def as_luxos(self) -> dict:
|
||||||
|
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
|
||||||
|
|
||||||
|
def as_vnish(self) -> dict:
|
||||||
|
return {"misc": {"restart_temp": self.danger}}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, dict_conf: Union[dict, None]) -> "TemperatureConfig":
|
def from_dict(cls, dict_conf: dict | None) -> TemperatureConfig:
|
||||||
|
if dict_conf is None:
|
||||||
|
return cls()
|
||||||
return cls(
|
return cls(
|
||||||
target=dict_conf.get("target"),
|
target=dict_conf.get("target"),
|
||||||
hot=dict_conf.get("hot"),
|
hot=dict_conf.get("hot"),
|
||||||
@@ -48,7 +68,7 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_bosminer(cls, toml_conf: dict) -> "TemperatureConfig":
|
def from_bosminer(cls, toml_conf: dict) -> TemperatureConfig:
|
||||||
temp_control = toml_conf.get("temp_control")
|
temp_control = toml_conf.get("temp_control")
|
||||||
if temp_control is not None:
|
if temp_control is not None:
|
||||||
return cls(
|
return cls(
|
||||||
@@ -56,32 +76,44 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
hot=temp_control.get("hot_temp"),
|
hot=temp_control.get("hot_temp"),
|
||||||
danger=temp_control.get("dangerous_temp"),
|
danger=temp_control.get("dangerous_temp"),
|
||||||
)
|
)
|
||||||
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
|
def from_epic(cls, web_conf: dict) -> TemperatureConfig:
|
||||||
try:
|
try:
|
||||||
dangerous_temp = web_conf["Misc"]["Shutdown Temp"]
|
dangerous_temp = web_conf["Misc"]["Critical Temp"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
dangerous_temp = None
|
dangerous_temp = None
|
||||||
|
try:
|
||||||
|
hot_temp = web_conf["Misc"]["Shutdown Temp"]
|
||||||
|
except KeyError:
|
||||||
|
hot_temp = None
|
||||||
# Need to do this in two blocks to avoid KeyError if one is missing
|
# Need to do this in two blocks to avoid KeyError if one is missing
|
||||||
try:
|
try:
|
||||||
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
|
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
target_temp = None
|
target_temp = None
|
||||||
|
|
||||||
return cls(target=target_temp, danger=dangerous_temp)
|
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_vnish(cls, web_settings: dict):
|
def from_vnish(cls, web_settings: dict) -> TemperatureConfig:
|
||||||
|
try:
|
||||||
|
dangerous_temp = web_settings["misc"]["restart_temp"]
|
||||||
|
except KeyError:
|
||||||
|
dangerous_temp = None
|
||||||
try:
|
try:
|
||||||
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
|
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
|
||||||
return cls(target=web_settings["miner"]["cooling"]["mode"]["param"])
|
return cls(
|
||||||
|
target=web_settings["miner"]["cooling"]["mode"]["param"],
|
||||||
|
danger=dangerous_temp,
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
return cls()
|
return cls()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_boser(cls, grpc_miner_conf: dict):
|
def from_boser(cls, grpc_miner_conf: dict) -> TemperatureConfig:
|
||||||
try:
|
try:
|
||||||
temperature_conf = grpc_miner_conf["temperature"]
|
temperature_conf = grpc_miner_conf["temperature"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@@ -110,3 +142,16 @@ class TemperatureConfig(MinerConfigValue):
|
|||||||
|
|
||||||
return cls(**conf)
|
return cls(**conf)
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_luxos(cls, rpc_tempctrl: dict) -> TemperatureConfig:
|
||||||
|
try:
|
||||||
|
tempctrl_config = rpc_tempctrl["TEMPCTRL"][0]
|
||||||
|
return cls(
|
||||||
|
target=tempctrl_config.get("Target"),
|
||||||
|
hot=tempctrl_config.get("Hot"),
|
||||||
|
danger=tempctrl_config.get("Dangerous"),
|
||||||
|
)
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
return cls.default()
|
||||||
|
|||||||
@@ -13,88 +13,28 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
import json
|
|
||||||
import time
|
import time
|
||||||
from dataclasses import asdict, dataclass, field, fields
|
from collections.abc import Callable
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Any, List, Union
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field, computed_field
|
||||||
|
|
||||||
from pyasic.config import MinerConfig
|
from pyasic.config import MinerConfig
|
||||||
from pyasic.config.mining import MiningModePowerTune
|
from pyasic.config.mining import MiningModePowerTune
|
||||||
|
from pyasic.data.pools import PoolMetrics, Scheme
|
||||||
|
from pyasic.device.algorithm.hashrate import AlgoHashRateType
|
||||||
|
from pyasic.device.algorithm.hashrate.base import GenericHashrate
|
||||||
|
|
||||||
|
from .boards import HashBoard
|
||||||
|
from .device import DeviceInfo
|
||||||
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error
|
||||||
|
from .error_codes.base import BaseMinerError
|
||||||
|
from .fans import Fan
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class MinerData(BaseModel):
|
||||||
class HashBoard:
|
|
||||||
"""A Dataclass to standardize hashboard data.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
slot: The slot of the board as an int.
|
|
||||||
hashrate: The hashrate of the board in TH/s as a float.
|
|
||||||
temp: The temperature of the PCB as an int.
|
|
||||||
chip_temp: The temperature of the chips as an int.
|
|
||||||
chips: The chip count of the board as an int.
|
|
||||||
expected_chips: The expected chip count of the board as an int.
|
|
||||||
serial_number: The serial number of the board.
|
|
||||||
missing: Whether the board is returned from the miners data as a bool.
|
|
||||||
"""
|
|
||||||
|
|
||||||
slot: int = 0
|
|
||||||
hashrate: float = None
|
|
||||||
temp: int = None
|
|
||||||
chip_temp: int = None
|
|
||||||
chips: int = None
|
|
||||||
expected_chips: int = None
|
|
||||||
serial_number: str = None
|
|
||||||
missing: bool = True
|
|
||||||
|
|
||||||
def get(self, __key: str, default: Any = None):
|
|
||||||
try:
|
|
||||||
val = self.__getitem__(__key)
|
|
||||||
if val is None:
|
|
||||||
return default
|
|
||||||
return val
|
|
||||||
except KeyError:
|
|
||||||
return default
|
|
||||||
|
|
||||||
def __getitem__(self, item: str):
|
|
||||||
try:
|
|
||||||
return getattr(self, item)
|
|
||||||
except AttributeError:
|
|
||||||
raise KeyError(f"{item}")
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Fan:
|
|
||||||
"""A Dataclass to standardize fan data.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
speed: The speed of the fan.
|
|
||||||
"""
|
|
||||||
|
|
||||||
speed: int = None
|
|
||||||
|
|
||||||
def get(self, __key: str, default: Any = None):
|
|
||||||
try:
|
|
||||||
val = self.__getitem__(__key)
|
|
||||||
if val is None:
|
|
||||||
return default
|
|
||||||
return val
|
|
||||||
except KeyError:
|
|
||||||
return default
|
|
||||||
|
|
||||||
def __getitem__(self, item: str):
|
|
||||||
try:
|
|
||||||
return getattr(self, item)
|
|
||||||
except AttributeError:
|
|
||||||
raise KeyError(f"{item}")
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MinerData:
|
|
||||||
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
|
"""A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`)
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -102,20 +42,25 @@ class MinerData:
|
|||||||
datetime: The time and date this data was generated.
|
datetime: The time and date this data was generated.
|
||||||
uptime: The uptime of the miner in seconds.
|
uptime: The uptime of the miner in seconds.
|
||||||
mac: The MAC address of the miner as a str.
|
mac: The MAC address of the miner as a str.
|
||||||
|
device_info: Info about the device, such as model, make, and firmware.
|
||||||
model: The model of the miner as a str.
|
model: The model of the miner as a str.
|
||||||
make: The make of the miner as a str.
|
make: The make of the miner as a str.
|
||||||
|
firmware: The firmware on the miner as a str.
|
||||||
|
algo: The mining algorithm of the miner as a str.
|
||||||
api_ver: The current api version on the miner as a str.
|
api_ver: The current api version on the miner as a str.
|
||||||
fw_ver: The current firmware version on the miner as a str.
|
fw_ver: The current firmware version on the miner as a str.
|
||||||
hostname: The network hostname of 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: 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.
|
expected_hashrate: The factory nominal hashrate of the miner in TH/s as a float.
|
||||||
|
sticker_hashrate: The factory sticker hashrate of the miner as a float.
|
||||||
hashboards: A list of [`HashBoard`][pyasic.data.HashBoard]s on the miner with their statistics.
|
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.
|
temperature_avg: The average temperature across the boards. Calculated automatically.
|
||||||
env_temp: The environment temps as a float.
|
env_temp: The environment temps as a float.
|
||||||
wattage: Current power draw of the miner as an int.
|
wattage: Current power draw of the miner as an int.
|
||||||
|
voltage: Current output voltage of the PSU as an float.
|
||||||
wattage_limit: Power limit of the miner as an int.
|
wattage_limit: Power limit of the miner as an int.
|
||||||
fans: A list of fans on the miner with their speeds.
|
fans: A list of fans on the miner with their speeds.
|
||||||
|
expected_fans: The number of fans expected on a miner.
|
||||||
fan_psu: The speed of the PSU on the fan if the miner collects it.
|
fan_psu: The speed of the PSU on the fan if the miner collects it.
|
||||||
total_chips: The total number of chips on all boards. Calculated automatically.
|
total_chips: The total number of chips on all boards. Calculated automatically.
|
||||||
expected_chips: The expected number of chips in the miner as an int.
|
expected_chips: The expected number of chips in the miner as an int.
|
||||||
@@ -127,54 +72,73 @@ class MinerData:
|
|||||||
errors: A list of errors on the miner.
|
errors: A list of errors on the miner.
|
||||||
fault_light: Whether the fault light is on as a boolean.
|
fault_light: Whether the fault light is on as a boolean.
|
||||||
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
|
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
|
||||||
|
efficiency_fract: Same as efficiency, but is not rounded to integer. Calculated automatically.
|
||||||
is_mining: Whether the miner is mining.
|
is_mining: Whether the miner is mining.
|
||||||
|
pools: A list of PoolMetrics instances, each representing metrics for a pool.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# general
|
||||||
ip: str
|
ip: str
|
||||||
datetime: datetime = None
|
raw_datetime: datetime = Field(
|
||||||
uptime: int = None
|
exclude=True, default_factory=datetime.now(timezone.utc).astimezone, repr=False
|
||||||
mac: str = None
|
)
|
||||||
model: str = None
|
|
||||||
make: str = None
|
# about
|
||||||
api_ver: str = None
|
device_info: DeviceInfo | None = None
|
||||||
fw_ver: str = None
|
serial_number: str | None = None
|
||||||
hostname: str = None
|
mac: str | None = None
|
||||||
hashrate: float = field(init=False)
|
api_ver: str | None = None
|
||||||
_hashrate: float = field(repr=False, default=None)
|
fw_ver: str | None = None
|
||||||
expected_hashrate: float = None
|
hostname: str | None = None
|
||||||
hashboards: List[HashBoard] = field(default_factory=list)
|
|
||||||
expected_hashboards: int = None
|
# hashrate
|
||||||
temperature_avg: int = field(init=False)
|
raw_hashrate: AlgoHashRateType | None = Field(
|
||||||
env_temp: float = None
|
exclude=True, default=None, repr=False
|
||||||
wattage: int = None
|
)
|
||||||
wattage_limit: int = field(init=False)
|
|
||||||
_wattage_limit: int = field(repr=False, default=None)
|
# sticker
|
||||||
fans: List[Fan] = field(default_factory=list)
|
sticker_hashrate: AlgoHashRateType | None = None
|
||||||
fan_psu: int = None
|
|
||||||
total_chips: int = field(init=False)
|
# expected
|
||||||
expected_chips: int = None
|
expected_hashrate: AlgoHashRateType | None = None
|
||||||
percent_expected_chips: float = field(init=False)
|
expected_hashboards: int | None = None
|
||||||
percent_expected_hashrate: float = field(init=False)
|
expected_chips: int | None = None
|
||||||
percent_expected_wattage: float = field(init=False)
|
expected_fans: int | None = None
|
||||||
nominal: bool = field(init=False)
|
|
||||||
config: MinerConfig = None
|
# temperature
|
||||||
errors: List[
|
env_temp: float | None = None
|
||||||
Union[WhatsminerError, BraiinsOSError, X19Error, InnosiliconError]
|
|
||||||
] = field(default_factory=list)
|
# power
|
||||||
fault_light: Union[bool, None] = None
|
wattage: int | None = None
|
||||||
efficiency: int = field(init=False)
|
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 = None
|
||||||
|
|
||||||
|
# boards
|
||||||
|
hashboards: list[HashBoard] = Field(default_factory=list)
|
||||||
|
|
||||||
|
# config
|
||||||
|
config: MinerConfig | None = None
|
||||||
|
fault_light: bool | None = None
|
||||||
|
|
||||||
|
# errors
|
||||||
|
errors: list[BaseMinerError] = Field(default_factory=list)
|
||||||
|
|
||||||
|
# mining state
|
||||||
is_mining: bool = True
|
is_mining: bool = True
|
||||||
|
uptime: int | None = None
|
||||||
|
|
||||||
|
# pools
|
||||||
|
pools: list[PoolMetrics] = Field(default_factory=list)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fields(cls):
|
def fields(cls) -> set:
|
||||||
return [f.name for f in fields(cls) if not f.name.startswith("_")]
|
all_fields = set(cls.model_fields.keys())
|
||||||
|
all_fields.update(set(cls.model_computed_fields.keys()))
|
||||||
@staticmethod
|
return all_fields
|
||||||
def dict_factory(x):
|
|
||||||
return {k: v for (k, v) in x if not k.startswith("_")}
|
|
||||||
|
|
||||||
def __post_init__(self):
|
|
||||||
self.datetime = datetime.now(timezone.utc).astimezone()
|
|
||||||
|
|
||||||
def get(self, __key: str, default: Any = None):
|
def get(self, __key: str, default: Any = None):
|
||||||
try:
|
try:
|
||||||
@@ -202,19 +166,19 @@ class MinerData:
|
|||||||
|
|
||||||
def __floordiv__(self, other):
|
def __floordiv__(self, other):
|
||||||
cp = copy.deepcopy(self)
|
cp = copy.deepcopy(self)
|
||||||
for key in self:
|
for key in self.fields():
|
||||||
item = getattr(self, key)
|
item = getattr(self, key)
|
||||||
if isinstance(item, int):
|
if isinstance(item, int):
|
||||||
setattr(cp, key, item // other)
|
setattr(cp, key, item // other)
|
||||||
if isinstance(item, float):
|
if isinstance(item, float):
|
||||||
setattr(cp, key, round(item / other, 2))
|
setattr(cp, key, item / other)
|
||||||
return cp
|
return cp
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
if not isinstance(other, MinerData):
|
if not isinstance(other, MinerData):
|
||||||
raise TypeError("Cannot add MinerData to non MinerData type.")
|
raise TypeError("Cannot add MinerData to non MinerData type.")
|
||||||
cp = copy.deepcopy(self)
|
cp = copy.deepcopy(self)
|
||||||
for key in self:
|
for key in self.fields():
|
||||||
item = getattr(self, key)
|
item = getattr(self, key)
|
||||||
other_item = getattr(other, key)
|
other_item = getattr(other, key)
|
||||||
if item is None:
|
if item is None:
|
||||||
@@ -234,34 +198,47 @@ class MinerData:
|
|||||||
setattr(cp, key, item & other_item)
|
setattr(cp, key, item & other_item)
|
||||||
return cp
|
return cp
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def hashrate(self): # noqa - Skip PyCharm inspection
|
def hashrate(self) -> AlgoHashRateType | None:
|
||||||
if len(self.hashboards) > 0:
|
if len(self.hashboards) > 0:
|
||||||
hr_data = []
|
hr_data = []
|
||||||
for item in self.hashboards:
|
for item in self.hashboards:
|
||||||
if item.hashrate is not None:
|
if item.hashrate is not None:
|
||||||
hr_data.append(item.hashrate)
|
hr_data.append(item.hashrate)
|
||||||
if len(hr_data) > 0:
|
if len(hr_data) > 0:
|
||||||
return round(sum(hr_data), 2)
|
if self.device_info is not None and self.device_info.algo is not None:
|
||||||
return self._hashrate
|
from pyasic.device.algorithm.hashrate.unit.base import GenericUnit
|
||||||
|
|
||||||
|
return sum(
|
||||||
|
hr_data,
|
||||||
|
start=self.device_info.algo.hashrate(
|
||||||
|
rate=0, unit=GenericUnit.H
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return sum(hr_data, start=GenericHashrate(rate=0))
|
||||||
|
return self.raw_hashrate
|
||||||
|
|
||||||
@hashrate.setter
|
@hashrate.setter
|
||||||
def hashrate(self, val):
|
def hashrate(self, val):
|
||||||
self._hashrate = val
|
self.raw_hashrate = val
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def wattage_limit(self): # noqa - Skip PyCharm inspection
|
def wattage_limit(self) -> int | None:
|
||||||
if self.config is not None:
|
if self.config is not None:
|
||||||
if isinstance(self.config.mining_mode, MiningModePowerTune):
|
if isinstance(self.config.mining_mode, MiningModePowerTune):
|
||||||
return self.config.mining_mode.power
|
return self.config.mining_mode.power
|
||||||
return self._wattage_limit
|
return self.raw_wattage_limit
|
||||||
|
|
||||||
@wattage_limit.setter
|
@wattage_limit.setter
|
||||||
def wattage_limit(self, val: int):
|
def wattage_limit(self, val: int):
|
||||||
self._wattage_limit = val
|
self.raw_wattage_limit = val
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def total_chips(self): # noqa - Skip PyCharm inspection
|
def total_chips(self) -> int | None:
|
||||||
if len(self.hashboards) > 0:
|
if len(self.hashboards) > 0:
|
||||||
chip_data = []
|
chip_data = []
|
||||||
for item in self.hashboards:
|
for item in self.hashboards:
|
||||||
@@ -270,60 +247,48 @@ class MinerData:
|
|||||||
if len(chip_data) > 0:
|
if len(chip_data) > 0:
|
||||||
return sum(chip_data)
|
return sum(chip_data)
|
||||||
return None
|
return None
|
||||||
|
return 0
|
||||||
|
|
||||||
@total_chips.setter
|
@computed_field # type: ignore[prop-decorator]
|
||||||
def total_chips(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@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:
|
if self.total_chips is None or self.expected_chips is None:
|
||||||
return None
|
return None
|
||||||
return self.expected_chips == self.total_chips
|
return self.expected_chips == self.total_chips
|
||||||
|
|
||||||
@nominal.setter
|
@computed_field # type: ignore[prop-decorator]
|
||||||
def nominal(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@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:
|
if self.total_chips is None or self.expected_chips is None:
|
||||||
return None
|
return None
|
||||||
if self.total_chips == 0 or self.expected_chips == 0:
|
if self.total_chips == 0 or self.expected_chips == 0:
|
||||||
return 0
|
return 0
|
||||||
return round((self.total_chips / self.expected_chips) * 100)
|
return round((self.total_chips / self.expected_chips) * 100)
|
||||||
|
|
||||||
@percent_expected_chips.setter
|
@computed_field # type: ignore[prop-decorator]
|
||||||
def percent_expected_chips(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@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:
|
if self.hashrate is None or self.expected_hashrate is None:
|
||||||
return None
|
return None
|
||||||
if self.hashrate == 0 or self.expected_hashrate == 0:
|
try:
|
||||||
|
return round((self.hashrate / self.expected_hashrate) * 100)
|
||||||
|
except ZeroDivisionError:
|
||||||
return 0
|
return 0
|
||||||
return round((self.hashrate / self.expected_hashrate) * 100)
|
|
||||||
|
|
||||||
@percent_expected_hashrate.setter
|
|
||||||
def percent_expected_hashrate(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@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:
|
if self.wattage_limit is None or self.wattage is None:
|
||||||
return None
|
return None
|
||||||
if self.wattage_limit == 0 or self.wattage == 0:
|
try:
|
||||||
|
return round((self.wattage / self.wattage_limit) * 100)
|
||||||
|
except ZeroDivisionError:
|
||||||
return 0
|
return 0
|
||||||
return round((self.wattage / self.wattage_limit) * 100)
|
|
||||||
|
|
||||||
@percent_expected_wattage.setter
|
|
||||||
def percent_expected_wattage(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@property
|
||||||
def temperature_avg(self): # noqa - Skip PyCharm inspection
|
def temperature_avg(self) -> int | None:
|
||||||
total_temp = 0
|
total_temp: float = 0
|
||||||
temp_count = 0
|
temp_count = 0
|
||||||
for hb in self.hashboards:
|
for hb in self.hashboards:
|
||||||
if hb.temp is not None:
|
if hb.temp is not None:
|
||||||
@@ -333,24 +298,71 @@ class MinerData:
|
|||||||
return None
|
return None
|
||||||
return round(total_temp / temp_count)
|
return round(total_temp / temp_count)
|
||||||
|
|
||||||
@temperature_avg.setter
|
@computed_field # type: ignore[prop-decorator]
|
||||||
def temperature_avg(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def efficiency(self): # noqa - Skip PyCharm inspection
|
def efficiency(self) -> int | None:
|
||||||
|
efficiency = self._efficiency(0)
|
||||||
|
if efficiency is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return int(efficiency)
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def efficiency_fract(self) -> float | None:
|
||||||
|
return self._efficiency(2)
|
||||||
|
|
||||||
|
def _efficiency(self, ndigits: int) -> float | None:
|
||||||
if self.hashrate is None or self.wattage is None:
|
if self.hashrate is None or self.wattage is None:
|
||||||
return None
|
return None
|
||||||
if self.hashrate == 0 or self.wattage == 0:
|
try:
|
||||||
return 0
|
return round(self.wattage / float(self.hashrate), ndigits)
|
||||||
return round(self.wattage / self.hashrate)
|
except ZeroDivisionError:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
@efficiency.setter
|
@computed_field # type: ignore[prop-decorator]
|
||||||
def efficiency(self, val):
|
@property
|
||||||
pass
|
def datetime(self) -> str:
|
||||||
|
return self.raw_datetime.isoformat()
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def timestamp(self) -> int:
|
||||||
|
return int(time.mktime(self.raw_datetime.timetuple()))
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def make(self) -> str | None:
|
||||||
|
if self.device_info is not None and self.device_info.make is not None:
|
||||||
|
return str(self.device_info.make)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def model(self) -> str | None:
|
||||||
|
if self.device_info is not None and self.device_info.model is not None:
|
||||||
|
return str(self.device_info.model)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def firmware(self) -> str | None:
|
||||||
|
if self.device_info is not None and self.device_info.firmware is not None:
|
||||||
|
return str(self.device_info.firmware)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def algo(self) -> str | None:
|
||||||
|
if self.device_info is not None and self.device_info.algo is not None:
|
||||||
|
return str(self.device_info.algo)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def keys(self) -> list:
|
||||||
|
return list(self.model_fields.keys())
|
||||||
|
|
||||||
def asdict(self) -> dict:
|
def asdict(self) -> dict:
|
||||||
return asdict(self, dict_factory=self.dict_factory)
|
return self.model_dump()
|
||||||
|
|
||||||
def as_dict(self) -> dict:
|
def as_dict(self) -> dict:
|
||||||
"""Get this dataclass as a dictionary.
|
"""Get this dataclass as a dictionary.
|
||||||
@@ -366,9 +378,7 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A JSON version of this class.
|
A JSON version of this class.
|
||||||
"""
|
"""
|
||||||
data = self.asdict()
|
return self.model_dump_json()
|
||||||
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
|
||||||
return json.dumps(data)
|
|
||||||
|
|
||||||
def as_csv(self) -> str:
|
def as_csv(self) -> str:
|
||||||
"""Get this dataclass as CSV.
|
"""Get this dataclass as CSV.
|
||||||
@@ -377,7 +387,6 @@ class MinerData:
|
|||||||
A CSV version of this class with no headers.
|
A CSV version of this class with no headers.
|
||||||
"""
|
"""
|
||||||
data = self.asdict()
|
data = self.asdict()
|
||||||
data["datetime"] = str(int(time.mktime(data["datetime"].timetuple())))
|
|
||||||
errs = []
|
errs = []
|
||||||
for error in data["errors"]:
|
for error in data["errors"]:
|
||||||
errs.append(error["error_message"])
|
errs.append(error["error_message"])
|
||||||
@@ -385,7 +394,9 @@ class MinerData:
|
|||||||
data_list = [str(data[item]) for item in data]
|
data_list = [str(data[item]) for item in data]
|
||||||
return ",".join(data_list)
|
return ",".join(data_list)
|
||||||
|
|
||||||
def as_influxdb(self, measurement_name: str = "miner_data") -> str:
|
def as_influxdb(
|
||||||
|
self, measurement_name: str = "miner_data", level_delimiter: str = "."
|
||||||
|
) -> str:
|
||||||
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
|
"""Get this dataclass as [influxdb line protocol](https://docs.influxdata.com/influxdb/v2.4/reference/syntax/line-protocol/).
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
@@ -394,54 +405,127 @@ class MinerData:
|
|||||||
Returns:
|
Returns:
|
||||||
A influxdb line protocol version of this class.
|
A influxdb line protocol version of this class.
|
||||||
"""
|
"""
|
||||||
tag_data = [measurement_name]
|
|
||||||
|
def serialize_int(key: str, value: int) -> str:
|
||||||
|
return f"{key}={value}"
|
||||||
|
|
||||||
|
def serialize_float(key: str, value: float) -> str:
|
||||||
|
return f"{key}={value}"
|
||||||
|
|
||||||
|
def serialize_str(key: str, value: str) -> str:
|
||||||
|
return f'{key}="{value}"'
|
||||||
|
|
||||||
|
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
|
||||||
|
return f"{key}={round(float(value), 2)}"
|
||||||
|
|
||||||
|
def serialize_list(key: str, value: list[Any]) -> str | None:
|
||||||
|
if len(value) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
list_field_data = []
|
||||||
|
for idx, list_field_val in enumerate(value):
|
||||||
|
item_serialization_func = serialization_map.get(
|
||||||
|
type(list_field_val), lambda _k, _v: None
|
||||||
|
)
|
||||||
|
item_serialized = item_serialization_func(
|
||||||
|
f"{key}{level_delimiter}{idx}", list_field_val
|
||||||
|
)
|
||||||
|
if item_serialized is not None:
|
||||||
|
list_field_data.append(item_serialized)
|
||||||
|
continue
|
||||||
|
for dt in serialization_map_instance:
|
||||||
|
if item_serialized is None:
|
||||||
|
if isinstance(list_field_val, dt):
|
||||||
|
func = serialization_map_instance[dt]
|
||||||
|
item_serialized = func(
|
||||||
|
f"{key}{level_delimiter}{idx}", list_field_val
|
||||||
|
)
|
||||||
|
if item_serialized is not None:
|
||||||
|
list_field_data.append(item_serialized)
|
||||||
|
return ",".join(list_field_data)
|
||||||
|
|
||||||
|
def serialize_miner_error(key: str, value: BaseMinerError):
|
||||||
|
return value.as_influxdb(key, level_delimiter=level_delimiter)
|
||||||
|
|
||||||
|
def serialize_fan(key: str, value: Fan) -> str:
|
||||||
|
return f"{key}{level_delimiter}speed={value.speed}"
|
||||||
|
|
||||||
|
def serialize_hashboard(key: str, value: HashBoard) -> str:
|
||||||
|
return value.as_influxdb(key, level_delimiter=level_delimiter)
|
||||||
|
|
||||||
|
def serialize_bool(key: str, value: bool):
|
||||||
|
return f"{key}={str(value).lower()}"
|
||||||
|
|
||||||
|
def serialize_pool_metrics(key: str, value: PoolMetrics):
|
||||||
|
return value.as_influxdb(key, level_delimiter=level_delimiter)
|
||||||
|
|
||||||
|
include = [
|
||||||
|
"uptime",
|
||||||
|
"expected_hashrate",
|
||||||
|
"hashrate",
|
||||||
|
"hashboards",
|
||||||
|
"temperature_avg",
|
||||||
|
"env_temp",
|
||||||
|
"wattage",
|
||||||
|
"wattage_limit",
|
||||||
|
"voltage",
|
||||||
|
"fans",
|
||||||
|
"expected_fans",
|
||||||
|
"fan_psu",
|
||||||
|
"total_chips",
|
||||||
|
"expected_chips",
|
||||||
|
"efficiency",
|
||||||
|
"fault_light",
|
||||||
|
"is_mining",
|
||||||
|
"errors",
|
||||||
|
"pools",
|
||||||
|
]
|
||||||
|
|
||||||
|
serialization_map_instance: dict[type, Callable[[str, Any], str | None]] = {
|
||||||
|
AlgoHashRateType: serialize_algo_hash_rate,
|
||||||
|
BaseMinerError: serialize_miner_error,
|
||||||
|
}
|
||||||
|
serialization_map: dict[type, Callable[[str, Any], str | None]] = {
|
||||||
|
int: serialize_int,
|
||||||
|
float: serialize_float,
|
||||||
|
str: serialize_str,
|
||||||
|
bool: serialize_bool,
|
||||||
|
list: serialize_list,
|
||||||
|
Fan: serialize_fan,
|
||||||
|
HashBoard: serialize_hashboard,
|
||||||
|
PoolMetrics: serialize_pool_metrics,
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_data = [
|
||||||
|
measurement_name,
|
||||||
|
f"ip={str(self.ip)}",
|
||||||
|
f"mac={str(self.mac)}",
|
||||||
|
f"make={str(self.make)}",
|
||||||
|
f"model={str(self.model)}",
|
||||||
|
f"firmware={str(self.firmware)}",
|
||||||
|
f"algo={str(self.algo)}",
|
||||||
|
]
|
||||||
field_data = []
|
field_data = []
|
||||||
|
|
||||||
tags = ["ip", "mac", "model", "hostname"]
|
for field in include:
|
||||||
for attribute in self:
|
field_val = getattr(self, field)
|
||||||
if attribute in tags:
|
serialization_func = serialization_map.get(
|
||||||
escaped_data = self.get(attribute, "Unknown").replace(" ", "\\ ")
|
type(field_val), lambda _k, _v: None
|
||||||
tag_data.append(f"{attribute}={escaped_data}")
|
)
|
||||||
|
serialized = serialization_func(field, field_val)
|
||||||
|
if serialized is not None:
|
||||||
|
field_data.append(serialized)
|
||||||
continue
|
continue
|
||||||
elif str(attribute).startswith("_"):
|
for datatype in serialization_map_instance:
|
||||||
continue
|
if serialized is None:
|
||||||
elif isinstance(self[attribute], str):
|
if isinstance(field_val, datatype):
|
||||||
field_data.append(f'{attribute}="{self[attribute]}"')
|
func = serialization_map_instance[datatype]
|
||||||
continue
|
serialized = func(field, field_val)
|
||||||
elif isinstance(self[attribute], bool):
|
if serialized is not None:
|
||||||
field_data.append(f"{attribute}={str(self[attribute]).lower()}")
|
field_data.append(serialized)
|
||||||
continue
|
|
||||||
elif isinstance(self[attribute], int):
|
|
||||||
field_data.append(f"{attribute}={self[attribute]}")
|
|
||||||
continue
|
|
||||||
elif isinstance(self[attribute], float):
|
|
||||||
field_data.append(f"{attribute}={self[attribute]}")
|
|
||||||
continue
|
|
||||||
elif attribute == "errors":
|
|
||||||
for idx, item in enumerate(self[attribute]):
|
|
||||||
field_data.append(f'error_{idx+1}="{item.error_message}"')
|
|
||||||
elif attribute == "hashboards":
|
|
||||||
for idx, item in enumerate(self[attribute]):
|
|
||||||
field_data.append(
|
|
||||||
f"hashboard_{idx+1}_hashrate={item.get('hashrate', 0.0)}"
|
|
||||||
)
|
|
||||||
field_data.append(
|
|
||||||
f"hashboard_{idx+1}_temperature={item.get('temp', 0)}"
|
|
||||||
)
|
|
||||||
field_data.append(
|
|
||||||
f"hashboard_{idx+1}_chip_temperature={item.get('chip_temp', 0)}"
|
|
||||||
)
|
|
||||||
field_data.append(f"hashboard_{idx+1}_chips={item.get('chips', 0)}")
|
|
||||||
field_data.append(
|
|
||||||
f"hashboard_{idx+1}_expected_chips={item.get('expected_chips', 0)}"
|
|
||||||
)
|
|
||||||
elif attribute == "fans":
|
|
||||||
for idx, item in enumerate(self[attribute]):
|
|
||||||
if item.speed is not None:
|
|
||||||
field_data.append(f"fan_{idx+1}={item.speed}")
|
|
||||||
|
|
||||||
tags_str = ",".join(tag_data)
|
tags_str = ",".join(tag_data).replace(" ", "\\ ")
|
||||||
field_str = ",".join(field_data)
|
field_str = ",".join(field_data).replace(" ", "\\ ")
|
||||||
timestamp = str(int(time.mktime(self.datetime.timetuple()) * 1e9))
|
timestamp = str(self.timestamp * 10**9)
|
||||||
|
|
||||||
return " ".join([tags_str, field_str, timestamp])
|
return " ".join([tags_str, field_str, timestamp])
|
||||||
|
|||||||
140
pyasic/data/boards.py
Normal file
140
pyasic/data/boards.py
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from pyasic.device.algorithm.hashrate import AlgoHashRateType
|
||||||
|
|
||||||
|
|
||||||
|
class HashBoard(BaseModel):
|
||||||
|
"""A Dataclass to standardize hashboard data.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
slot: The slot of the board as an int.
|
||||||
|
hashrate: The hashrate of the board in TH/s as a float.
|
||||||
|
inlet_temp: Inlet temperature for hydro asics as an int
|
||||||
|
outlet_temp: Outlet temperature for hydro asics as an int
|
||||||
|
temp: The temperature of the PCB as an int.
|
||||||
|
chip_temp: The temperature of the chips as an int.
|
||||||
|
chips: The chip count of the board as an int.
|
||||||
|
expected_chips: The expected chip count of the board as an int.
|
||||||
|
serial_number: The serial number of the board.
|
||||||
|
missing: Whether the board is returned from the miners data as a bool.
|
||||||
|
tuned: Whether the board is tuned as a bool.
|
||||||
|
active: Whether the board is currently tuning as a bool.
|
||||||
|
voltage: Current input voltage of the board as a float.
|
||||||
|
"""
|
||||||
|
|
||||||
|
slot: int = 0
|
||||||
|
hashrate: AlgoHashRateType | None = None
|
||||||
|
inlet_temp: float | None = None
|
||||||
|
outlet_temp: float | None = None
|
||||||
|
temp: float | None = None
|
||||||
|
chip_temp: float | None = None
|
||||||
|
chips: int | None = None
|
||||||
|
expected_chips: int | None = None
|
||||||
|
serial_number: str | None = None
|
||||||
|
missing: bool = True
|
||||||
|
tuned: bool | None = None
|
||||||
|
active: bool | None = None
|
||||||
|
voltage: float | None = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fields(cls) -> set:
|
||||||
|
all_fields = set(cls.model_fields.keys())
|
||||||
|
all_fields.update(set(cls.model_computed_fields.keys()))
|
||||||
|
return all_fields
|
||||||
|
|
||||||
|
def get(self, __key: str, default: Any = None):
|
||||||
|
try:
|
||||||
|
val = self.__getitem__(__key)
|
||||||
|
if val is None:
|
||||||
|
return default
|
||||||
|
return val
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def __getitem__(self, item: str):
|
||||||
|
try:
|
||||||
|
return getattr(self, item)
|
||||||
|
except AttributeError:
|
||||||
|
raise KeyError(f"{item}")
|
||||||
|
|
||||||
|
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
|
||||||
|
def serialize_int(key: str, value: int) -> str:
|
||||||
|
return f"{key}={value}"
|
||||||
|
|
||||||
|
def serialize_float(key: str, value: float) -> str:
|
||||||
|
return f"{key}={value}"
|
||||||
|
|
||||||
|
def serialize_str(key: str, value: str) -> str:
|
||||||
|
return f'{key}="{value}"'
|
||||||
|
|
||||||
|
def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str:
|
||||||
|
return f"{key}={round(float(value), 2)}"
|
||||||
|
|
||||||
|
def serialize_bool(key: str, value: bool) -> str:
|
||||||
|
return f"{key}={str(value).lower()}"
|
||||||
|
|
||||||
|
serialization_map_instance = {
|
||||||
|
AlgoHashRateType: serialize_algo_hash_rate,
|
||||||
|
}
|
||||||
|
serialization_map = {
|
||||||
|
int: serialize_int,
|
||||||
|
float: serialize_float,
|
||||||
|
str: serialize_str,
|
||||||
|
bool: serialize_bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
include = [
|
||||||
|
"hashrate",
|
||||||
|
"temp",
|
||||||
|
"chip_temp",
|
||||||
|
"chips",
|
||||||
|
"expected_chips",
|
||||||
|
"tuned",
|
||||||
|
"active",
|
||||||
|
"voltage",
|
||||||
|
]
|
||||||
|
|
||||||
|
field_data = []
|
||||||
|
for field in include:
|
||||||
|
field_val = getattr(self, field)
|
||||||
|
serialization_func: Callable[[str, Any], str | None] = (
|
||||||
|
serialization_map.get(
|
||||||
|
type(field_val),
|
||||||
|
lambda _k, _v: None, # type: ignore
|
||||||
|
)
|
||||||
|
)
|
||||||
|
serialized = serialization_func(
|
||||||
|
f"{key_root}{level_delimiter}{field}", field_val
|
||||||
|
)
|
||||||
|
if serialized is not None:
|
||||||
|
field_data.append(serialized)
|
||||||
|
continue
|
||||||
|
for datatype in serialization_map_instance:
|
||||||
|
if serialized is None:
|
||||||
|
if isinstance(field_val, datatype):
|
||||||
|
serialized = serialization_map_instance[datatype](
|
||||||
|
f"{key_root}{level_delimiter}{field}", field_val
|
||||||
|
)
|
||||||
|
if serialized is not None:
|
||||||
|
field_data.append(serialized)
|
||||||
|
return ",".join(field_data)
|
||||||
31
pyasic/data/device.py
Normal file
31
pyasic/data/device.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from pydantic import BaseModel, ConfigDict, field_serializer
|
||||||
|
|
||||||
|
from pyasic.device.algorithm import MinerAlgoType
|
||||||
|
from pyasic.device.firmware import MinerFirmware
|
||||||
|
from pyasic.device.makes import MinerMake
|
||||||
|
from pyasic.device.models import MinerModelType
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceInfo(BaseModel):
|
||||||
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||||
|
|
||||||
|
make: MinerMake | None = None
|
||||||
|
model: MinerModelType | None = None
|
||||||
|
firmware: MinerFirmware | None = None
|
||||||
|
algo: type[MinerAlgoType] | None = None
|
||||||
|
|
||||||
|
@field_serializer("make")
|
||||||
|
def serialize_make(self, make: MinerMake, _info):
|
||||||
|
return str(make)
|
||||||
|
|
||||||
|
@field_serializer("model")
|
||||||
|
def serialize_model(self, model: MinerModelType, _info):
|
||||||
|
return str(model)
|
||||||
|
|
||||||
|
@field_serializer("firmware")
|
||||||
|
def serialize_firmware(self, firmware: MinerFirmware, _info):
|
||||||
|
return str(firmware)
|
||||||
|
|
||||||
|
@field_serializer("algo")
|
||||||
|
def serialize_algo(self, algo: MinerAlgoType, _info):
|
||||||
|
return str(algo)
|
||||||
@@ -13,12 +13,10 @@
|
|||||||
# See the License for the specific language governing permissions and -
|
# See the License for the specific language governing permissions and -
|
||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
from pyasic.data.error_codes.base import BaseMinerError
|
||||||
from dataclasses import asdict, dataclass, fields
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class X19Error(BaseMinerError):
|
||||||
class X19Error:
|
|
||||||
"""A Dataclass to handle error codes of X19 miners.
|
"""A Dataclass to handle error codes of X19 miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -28,10 +26,3 @@ class X19Error:
|
|||||||
|
|
||||||
error_message: str
|
error_message: str
|
||||||
error_code: int = 0
|
error_code: int = 0
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fields(cls):
|
|
||||||
return fields(cls)
|
|
||||||
|
|
||||||
def asdict(self):
|
|
||||||
return asdict(self)
|
|
||||||
|
|||||||
@@ -18,9 +18,15 @@ from typing import TypeVar
|
|||||||
|
|
||||||
from .bos import BraiinsOSError
|
from .bos import BraiinsOSError
|
||||||
from .innosilicon import InnosiliconError
|
from .innosilicon import InnosiliconError
|
||||||
|
from .vnish import VnishError
|
||||||
from .whatsminer import WhatsminerError
|
from .whatsminer import WhatsminerError
|
||||||
from .X19 import X19Error
|
from .X19 import X19Error
|
||||||
|
|
||||||
MinerErrorData = TypeVar(
|
MinerErrorData = TypeVar(
|
||||||
"MinerErrorData", WhatsminerError, BraiinsOSError, X19Error, InnosiliconError
|
"MinerErrorData",
|
||||||
|
WhatsminerError,
|
||||||
|
BraiinsOSError,
|
||||||
|
X19Error,
|
||||||
|
InnosiliconError,
|
||||||
|
VnishError,
|
||||||
)
|
)
|
||||||
|
|||||||
38
pyasic/data/error_codes/base.py
Normal file
38
pyasic/data/error_codes/base.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class BaseMinerError(BaseModel):
|
||||||
|
error_code: int | None = None
|
||||||
|
|
||||||
|
@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()
|
||||||
|
|
||||||
|
def as_influxdb(self, root_key: str, level_delimiter: str = ".") -> str:
|
||||||
|
field_data = []
|
||||||
|
|
||||||
|
if self.error_code is not None:
|
||||||
|
field_data.append(
|
||||||
|
f"{root_key}{level_delimiter}error_code={self.error_code}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if error_message exists as an attribute (either regular or computed field)
|
||||||
|
if hasattr(self, "error_message"):
|
||||||
|
error_message = getattr(self, "error_message")
|
||||||
|
if error_message is not None:
|
||||||
|
field_data.append(
|
||||||
|
f'{root_key}{level_delimiter}error_message="{error_message}"'
|
||||||
|
)
|
||||||
|
|
||||||
|
return ",".join(field_data)
|
||||||
@@ -14,11 +14,10 @@
|
|||||||
# limitations under the License. -
|
# limitations under the License. -
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from dataclasses import asdict, dataclass, fields
|
from pyasic.data.error_codes.base import BaseMinerError
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class BraiinsOSError(BaseMinerError):
|
||||||
class BraiinsOSError:
|
|
||||||
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
"""A Dataclass to handle error codes of BraiinsOS+ miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -28,10 +27,3 @@ class BraiinsOSError:
|
|||||||
|
|
||||||
error_message: str
|
error_message: str
|
||||||
error_code: int = 0
|
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. -
|
# 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(BaseMinerError):
|
||||||
class InnosiliconError:
|
|
||||||
"""A Dataclass to handle error codes of Innosilicon miners.
|
"""A Dataclass to handle error codes of Innosilicon miners.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -27,25 +29,14 @@ class InnosiliconError:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
error_code: int
|
error_code: int
|
||||||
error_message: str = field(init=False)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fields(cls):
|
|
||||||
return fields(cls)
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@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:
|
if self.error_code in ERROR_CODES:
|
||||||
return ERROR_CODES[self.error_code]
|
return ERROR_CODES[self.error_code]
|
||||||
return "Unknown error type."
|
return "Unknown error type."
|
||||||
|
|
||||||
@error_message.setter
|
|
||||||
def error_message(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def asdict(self):
|
|
||||||
return asdict(self)
|
|
||||||
|
|
||||||
|
|
||||||
ERROR_CODES = {
|
ERROR_CODES = {
|
||||||
21: "The PLUG signal of the hash board is not detected.",
|
21: "The PLUG signal of the hash board is not detected.",
|
||||||
|
|||||||
28
pyasic/data/error_codes/vnish.py
Normal file
28
pyasic/data/error_codes/vnish.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from pyasic.data.error_codes.base import BaseMinerError
|
||||||
|
|
||||||
|
|
||||||
|
class VnishError(BaseMinerError):
|
||||||
|
"""A Dataclass to handle error codes of Vnish miners.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
error_message: The error message as a string.
|
||||||
|
error_code: The error code as an int. 0 if the message is not assigned a code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
error_message: str
|
||||||
|
error_code: int = 0
|
||||||
@@ -14,11 +14,12 @@
|
|||||||
# limitations under the License. -
|
# 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 WhatsminerError(BaseMinerError):
|
||||||
class WhatsminerError:
|
|
||||||
"""A Dataclass to handle error codes of Whatsminers.
|
"""A Dataclass to handle error codes of Whatsminers.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@@ -27,62 +28,70 @@ class WhatsminerError:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
error_code: int
|
error_code: int
|
||||||
error_message: str = field(init=False)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fields(cls):
|
|
||||||
return fields(cls)
|
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
@property
|
@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":
|
error_str = str(self.error_code)
|
||||||
err_type = int(str(self.error_code)[:2])
|
|
||||||
err_subtype = int(str(self.error_code)[2:3])
|
# Handle edge cases for short error codes
|
||||||
err_value = int(str(self.error_code)[3:])
|
if len(error_str) < 3:
|
||||||
|
return "Unknown error type."
|
||||||
|
|
||||||
|
if len(error_str) == 6 and not error_str[:1] == "1":
|
||||||
|
err_type = int(error_str[:2])
|
||||||
|
err_subtype = int(error_str[2:3])
|
||||||
|
err_value = int(error_str[3:])
|
||||||
else:
|
else:
|
||||||
err_type = int(str(self.error_code)[:-2])
|
err_type = int(error_str[:-2])
|
||||||
err_subtype = int(str(self.error_code)[-2:-1])
|
err_subtype = int(error_str[-2:-1])
|
||||||
err_value = int(str(self.error_code)[-1:])
|
err_value = int(error_str[-1:])
|
||||||
try:
|
try:
|
||||||
select_err_type = ERROR_CODES[err_type]
|
select_err_type = ERROR_CODES.get(err_type)
|
||||||
|
if select_err_type is None:
|
||||||
|
return "Unknown error type."
|
||||||
|
|
||||||
if err_subtype in select_err_type:
|
if err_subtype in select_err_type:
|
||||||
select_err_subtype = select_err_type[err_subtype]
|
select_err_subtype = select_err_type[err_subtype]
|
||||||
if err_value in select_err_subtype:
|
if isinstance(select_err_subtype, dict):
|
||||||
return select_err_subtype[err_value]
|
if err_value in select_err_subtype:
|
||||||
elif "n" in select_err_subtype:
|
result = select_err_subtype[err_value]
|
||||||
return select_err_subtype[
|
return str(result) if not isinstance(result, str) else result
|
||||||
"n" # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
elif "n" in select_err_subtype:
|
||||||
].replace("{n}", str(err_value))
|
template = select_err_subtype["n"]
|
||||||
|
if isinstance(template, str):
|
||||||
|
return template.replace("{n}", str(err_value))
|
||||||
|
else:
|
||||||
|
return "Unknown error type."
|
||||||
|
else:
|
||||||
|
return "Unknown error type."
|
||||||
else:
|
else:
|
||||||
return "Unknown error type."
|
return "Unknown error type."
|
||||||
elif "n" in select_err_type:
|
elif "n" in select_err_type:
|
||||||
select_err_subtype = select_err_type[
|
select_err_subtype = select_err_type["n"]
|
||||||
"n" # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
if isinstance(select_err_subtype, dict):
|
||||||
]
|
if err_value in select_err_subtype:
|
||||||
if err_value in select_err_subtype:
|
result = select_err_subtype[err_value]
|
||||||
return select_err_subtype[err_value]
|
return str(result) if not isinstance(result, str) else result
|
||||||
elif "c" in select_err_subtype:
|
elif "c" in select_err_subtype:
|
||||||
return (
|
template = select_err_subtype["c"]
|
||||||
select_err_subtype["c"]
|
if isinstance(template, str):
|
||||||
.replace( # noqa: picks up `select_err_subtype["n"]` as not being numeric?
|
return template.replace("{n}", str(err_subtype)).replace(
|
||||||
"{n}", str(err_subtype)
|
"{c}", str(err_value)
|
||||||
)
|
)
|
||||||
.replace("{c}", str(err_value))
|
else:
|
||||||
)
|
return "Unknown error type."
|
||||||
|
else:
|
||||||
|
return "Unknown error type."
|
||||||
|
else:
|
||||||
|
return "Unknown error type."
|
||||||
else:
|
else:
|
||||||
return "Unknown error type."
|
return "Unknown error type."
|
||||||
except KeyError:
|
except (KeyError, TypeError):
|
||||||
return "Unknown error type."
|
return "Unknown error type."
|
||||||
|
|
||||||
@error_message.setter
|
|
||||||
def error_message(self, val):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def asdict(self):
|
ERROR_CODES: dict[int, dict[int | str, str | dict[int | str, str]]] = {
|
||||||
return asdict(self)
|
|
||||||
|
|
||||||
|
|
||||||
ERROR_CODES = {
|
|
||||||
1: { # Fan error
|
1: { # Fan error
|
||||||
0: {
|
0: {
|
||||||
0: "Fan unknown.",
|
0: "Fan unknown.",
|
||||||
|
|||||||
44
pyasic/data/fans.py
Normal file
44
pyasic/data/fans.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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 Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class Fan(BaseModel):
|
||||||
|
"""A Dataclass to standardize fan data.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
speed: The speed of the fan.
|
||||||
|
"""
|
||||||
|
|
||||||
|
speed: int | None = None
|
||||||
|
|
||||||
|
def get(self, __key: str, default: Any = None):
|
||||||
|
try:
|
||||||
|
val = self.__getitem__(__key)
|
||||||
|
if val is None:
|
||||||
|
return default
|
||||||
|
return val
|
||||||
|
except KeyError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
def __getitem__(self, item: str):
|
||||||
|
try:
|
||||||
|
return getattr(self, item)
|
||||||
|
except AttributeError:
|
||||||
|
raise KeyError(f"{item}")
|
||||||
147
pyasic/data/pools.py
Normal file
147
pyasic/data/pools.py
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Any
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from pydantic import BaseModel, computed_field, model_serializer
|
||||||
|
from typing_extensions import Self
|
||||||
|
|
||||||
|
|
||||||
|
class Scheme(Enum):
|
||||||
|
STRATUM_V1 = "stratum+tcp"
|
||||||
|
STRATUM_V2 = "stratum2+tcp"
|
||||||
|
STRATUM_V1_SSL = "stratum+ssl"
|
||||||
|
|
||||||
|
|
||||||
|
class PoolUrl(BaseModel):
|
||||||
|
scheme: Scheme
|
||||||
|
host: str
|
||||||
|
port: int
|
||||||
|
pubkey: str | None = 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}"
|
||||||
|
else:
|
||||||
|
return f"{self.scheme.value}://{self.host}:{self.port}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_str(cls, url: str) -> Self | None:
|
||||||
|
parsed_url = urlparse(url)
|
||||||
|
if not parsed_url.hostname:
|
||||||
|
return None
|
||||||
|
if not parsed_url.scheme.strip() == "":
|
||||||
|
scheme = Scheme(parsed_url.scheme)
|
||||||
|
else:
|
||||||
|
scheme = Scheme.STRATUM_V1
|
||||||
|
host = parsed_url.hostname
|
||||||
|
port = parsed_url.port
|
||||||
|
if port is None:
|
||||||
|
return None
|
||||||
|
pubkey = parsed_url.path.lstrip("/") if scheme == Scheme.STRATUM_V2 else None
|
||||||
|
return cls(scheme=scheme, host=host, port=port, pubkey=pubkey)
|
||||||
|
|
||||||
|
|
||||||
|
class PoolMetrics(BaseModel):
|
||||||
|
"""A dataclass to standardize pool metrics returned from miners.
|
||||||
|
Attributes:
|
||||||
|
|
||||||
|
accepted: Number of accepted shares.
|
||||||
|
rejected: Number of rejected shares.
|
||||||
|
get_failures: Number of failures in obtaining work from the pool.
|
||||||
|
remote_failures: Number of failures communicating with the pool server.
|
||||||
|
active: Indicates if the miner is connected to the stratum server.
|
||||||
|
Alive : Indicates if a pool is alive.
|
||||||
|
url: URL of the pool.
|
||||||
|
index: Index of the pool.
|
||||||
|
user: Username for the pool.
|
||||||
|
pool_rejected_percent: Percentage of rejected shares by the pool.
|
||||||
|
pool_stale_percent: Percentage of stale shares by the pool.
|
||||||
|
"""
|
||||||
|
|
||||||
|
url: PoolUrl | None
|
||||||
|
accepted: int | None = None
|
||||||
|
rejected: int | None = None
|
||||||
|
get_failures: int | None = None
|
||||||
|
remote_failures: int | None = None
|
||||||
|
active: bool | None = None
|
||||||
|
alive: bool | None = None
|
||||||
|
index: int | None = None
|
||||||
|
user: str | None = None
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
|
||||||
|
"""Calculate and return the percentage of rejected shares"""
|
||||||
|
if self.rejected is None or self.accepted is None:
|
||||||
|
return 0.0
|
||||||
|
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
|
||||||
|
|
||||||
|
@computed_field # type: ignore[prop-decorator]
|
||||||
|
@property
|
||||||
|
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
|
||||||
|
"""Calculate and return the percentage of stale shares."""
|
||||||
|
if self.get_failures is None or self.accepted is None or self.rejected is None:
|
||||||
|
return 0.0
|
||||||
|
return self._calculate_percentage(
|
||||||
|
self.get_failures, self.accepted + self.rejected
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _calculate_percentage(value: int, total: int) -> float:
|
||||||
|
"""Calculate the percentage."""
|
||||||
|
if total == 0:
|
||||||
|
return 0.0
|
||||||
|
return (value / total) * 100
|
||||||
|
|
||||||
|
def as_influxdb(self, key_root: str, level_delimiter: str = ".") -> str:
|
||||||
|
def serialize_int(key: str, value: int) -> str:
|
||||||
|
return f"{key}={value}"
|
||||||
|
|
||||||
|
def serialize_float(key: str, value: float) -> str:
|
||||||
|
return f"{key}={value}"
|
||||||
|
|
||||||
|
def serialize_str(key: str, value: str) -> str:
|
||||||
|
return f'{key}="{value}"'
|
||||||
|
|
||||||
|
def serialize_pool_url(key: str, value: PoolUrl) -> str:
|
||||||
|
return f'{key}="{str(value)}"'
|
||||||
|
|
||||||
|
def serialize_bool(key: str, value: bool) -> str:
|
||||||
|
return f"{key}={str(value).lower()}"
|
||||||
|
|
||||||
|
serialization_map: dict[type, Callable[[str, Any], str]] = {
|
||||||
|
int: serialize_int,
|
||||||
|
float: serialize_float,
|
||||||
|
str: serialize_str,
|
||||||
|
bool: serialize_bool,
|
||||||
|
PoolUrl: serialize_pool_url,
|
||||||
|
}
|
||||||
|
|
||||||
|
include = [
|
||||||
|
"url",
|
||||||
|
"accepted",
|
||||||
|
"rejected",
|
||||||
|
"active",
|
||||||
|
"alive",
|
||||||
|
"user",
|
||||||
|
]
|
||||||
|
|
||||||
|
field_data = []
|
||||||
|
for field in include:
|
||||||
|
field_val = getattr(self, field)
|
||||||
|
if field_val is None:
|
||||||
|
continue
|
||||||
|
serialization_func = serialization_map.get(type(field_val))
|
||||||
|
if serialization_func is not None:
|
||||||
|
serialized = serialization_func(
|
||||||
|
f"{key_root}{level_delimiter}{field}", field_val
|
||||||
|
)
|
||||||
|
if serialized is not None:
|
||||||
|
field_data.append(serialized)
|
||||||
|
|
||||||
|
return ",".join(field_data)
|
||||||
4
pyasic/device/__init__.py
Normal file
4
pyasic/device/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
from .algorithm import MinerAlgo
|
||||||
|
from .firmware import MinerFirmware
|
||||||
|
from .makes import MinerMake
|
||||||
|
from .models import MinerModel
|
||||||
30
pyasic/device/algorithm/__init__.py
Normal file
30
pyasic/device/algorithm/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from .base import MinerAlgoType
|
||||||
|
from .blake256 import Blake256Algo
|
||||||
|
from .blockflow import BlockFlowAlgo
|
||||||
|
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
|
||||||
|
from .zksnark import ZkSnarkAlgo
|
||||||
|
|
||||||
|
|
||||||
|
class MinerAlgo:
|
||||||
|
SHA256 = SHA256Algo
|
||||||
|
SCRYPT = ScryptAlgo
|
||||||
|
KHEAVYHASH = KHeavyHashAlgo
|
||||||
|
KADENA = KadenaAlgo
|
||||||
|
HANDSHAKE = HandshakeAlgo
|
||||||
|
X11 = X11Algo
|
||||||
|
BLAKE256 = Blake256Algo
|
||||||
|
EAGLESONG = EaglesongAlgo
|
||||||
|
ETHASH = EtHashAlgo
|
||||||
|
EQUIHASH = EquihashAlgo
|
||||||
|
BLOCKFLOW = BlockFlowAlgo
|
||||||
|
ZKSNARK = ZkSnarkAlgo
|
||||||
23
pyasic/device/algorithm/base.py
Normal file
23
pyasic/device/algorithm/base.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .hashrate.base import AlgoHashRateType, GenericHashrate
|
||||||
|
from .hashrate.unit.base import AlgoHashRateUnitType, GenericUnit
|
||||||
|
|
||||||
|
|
||||||
|
class MinerAlgoMeta(type):
|
||||||
|
name: str
|
||||||
|
|
||||||
|
def __str__(cls):
|
||||||
|
return cls.name
|
||||||
|
|
||||||
|
|
||||||
|
class MinerAlgoType(metaclass=MinerAlgoMeta):
|
||||||
|
hashrate: type[AlgoHashRateType]
|
||||||
|
unit: type[AlgoHashRateUnitType]
|
||||||
|
|
||||||
|
|
||||||
|
class GenericAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[GenericHashrate] = GenericHashrate
|
||||||
|
unit: type[GenericUnit] = GenericUnit
|
||||||
|
|
||||||
|
name = "Generic (Unknown)"
|
||||||
13
pyasic/device/algorithm/blake256.py
Normal file
13
pyasic/device/algorithm/blake256.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import Blake256HashRate
|
||||||
|
from .hashrate.unit import Blake256Unit
|
||||||
|
|
||||||
|
|
||||||
|
# make this json serializable
|
||||||
|
class Blake256Algo(MinerAlgoType):
|
||||||
|
hashrate: type[Blake256HashRate] = Blake256HashRate
|
||||||
|
unit: type[Blake256Unit] = Blake256Unit
|
||||||
|
|
||||||
|
name = "Blake256"
|
||||||
12
pyasic/device/algorithm/blockflow.py
Normal file
12
pyasic/device/algorithm/blockflow.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import BlockFlowHashRate
|
||||||
|
from .hashrate.unit import BlockFlowUnit
|
||||||
|
|
||||||
|
|
||||||
|
class BlockFlowAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[BlockFlowHashRate] = BlockFlowHashRate
|
||||||
|
unit: type[BlockFlowUnit] = BlockFlowUnit
|
||||||
|
|
||||||
|
name = "BlockFlow"
|
||||||
13
pyasic/device/algorithm/eaglesong.py
Normal file
13
pyasic/device/algorithm/eaglesong.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import EaglesongHashRate
|
||||||
|
from .hashrate.unit import EaglesongUnit
|
||||||
|
|
||||||
|
|
||||||
|
# make this json serializable
|
||||||
|
class EaglesongAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[EaglesongHashRate] = EaglesongHashRate
|
||||||
|
unit: type[EaglesongUnit] = EaglesongUnit
|
||||||
|
|
||||||
|
name = "Eaglesong"
|
||||||
13
pyasic/device/algorithm/equihash.py
Normal file
13
pyasic/device/algorithm/equihash.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import EquihashHashRate
|
||||||
|
from .hashrate.unit import EquihashUnit
|
||||||
|
|
||||||
|
|
||||||
|
# make this json serializable
|
||||||
|
class EquihashAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[EquihashHashRate] = EquihashHashRate
|
||||||
|
unit: type[EquihashUnit] = EquihashUnit
|
||||||
|
|
||||||
|
name = "Equihash"
|
||||||
12
pyasic/device/algorithm/ethash.py
Normal file
12
pyasic/device/algorithm/ethash.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .base import MinerAlgoType
|
||||||
|
from .hashrate import EtHashHashRate
|
||||||
|
from .hashrate.unit import EtHashUnit
|
||||||
|
|
||||||
|
|
||||||
|
class EtHashAlgo(MinerAlgoType):
|
||||||
|
hashrate: type[EtHashHashRate] = EtHashHashRate
|
||||||
|
unit: type[EtHashUnit] = EtHashUnit
|
||||||
|
|
||||||
|
name = "EtHash"
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user