From 48a249326c5da21b74a5e036c873ffc7e232ddbf Mon Sep 17 00:00:00 2001 From: James Hilliard Date: Tue, 30 Sep 2025 00:33:16 -0600 Subject: [PATCH] Enable mypy strict mode --- docs/generate_miners.py | 14 +- pyasic/__init__.py | 9 + pyasic/config/__init__.py | 126 +- pyasic/config/base.py | 120 +- pyasic/config/fans.py | 195 +-- pyasic/config/mining/__init__.py | 366 +++--- pyasic/config/mining/algo.py | 53 +- pyasic/config/mining/presets.py | 8 +- pyasic/config/mining/scaling.py | 41 +- pyasic/config/pools.py | 248 ++-- pyasic/config/temperature.py | 26 +- pyasic/data/__init__.py | 78 +- pyasic/data/boards.py | 14 +- pyasic/data/device.py | 12 +- pyasic/data/error_codes/__init__.py | 11 + pyasic/data/error_codes/base.py | 8 +- pyasic/data/fans.py | 4 +- pyasic/data/pools.py | 4 +- pyasic/device/__init__.py | 7 + pyasic/device/algorithm/__init__.py | 19 + pyasic/device/algorithm/base.py | 2 +- pyasic/device/algorithm/blake256.py | 6 +- pyasic/device/algorithm/blockflow.py | 6 +- pyasic/device/algorithm/eaglesong.py | 6 +- pyasic/device/algorithm/equihash.py | 6 +- pyasic/device/algorithm/ethash.py | 6 +- pyasic/device/algorithm/handshake.py | 6 +- pyasic/device/algorithm/hashrate/__init__.py | 17 + pyasic/device/algorithm/hashrate/base.py | 51 +- pyasic/device/algorithm/hashrate/blake256.py | 2 +- pyasic/device/algorithm/hashrate/blockflow.py | 2 +- pyasic/device/algorithm/hashrate/eaglesong.py | 2 +- pyasic/device/algorithm/hashrate/equihash.py | 2 +- pyasic/device/algorithm/hashrate/ethash.py | 2 +- pyasic/device/algorithm/hashrate/handshake.py | 2 +- pyasic/device/algorithm/hashrate/kadena.py | 2 +- .../device/algorithm/hashrate/kheavyhash.py | 2 +- pyasic/device/algorithm/hashrate/scrypt.py | 2 +- pyasic/device/algorithm/hashrate/sha256.py | 2 +- pyasic/device/algorithm/hashrate/unit/base.py | 29 +- .../algorithm/hashrate/unit/equihash.py | 3 +- pyasic/device/algorithm/hashrate/x11.py | 2 +- pyasic/device/algorithm/hashrate/zksnark.py | 2 +- pyasic/device/algorithm/kadena.py | 6 +- pyasic/device/algorithm/kheavyhash.py | 6 +- pyasic/device/algorithm/scrypt.py | 6 +- pyasic/device/algorithm/sha256.py | 6 +- pyasic/device/algorithm/x11.py | 6 +- pyasic/device/algorithm/zksnark.py | 6 +- pyasic/device/firmware.py | 2 +- pyasic/device/makes.py | 2 +- pyasic/device/models.py | 28 +- pyasic/errors/__init__.py | 18 +- pyasic/logger/__init__.py | 2 +- pyasic/miners/__init__.py | 8 + .../miners/antminer/bmminer/X15/__init__.py | 4 + .../miners/antminer/bmminer/X17/__init__.py | 10 + .../miners/antminer/bmminer/X19/__init__.py | 23 + .../miners/antminer/bmminer/X21/__init__.py | 9 + pyasic/miners/antminer/bmminer/X3/KS3.py | 2 +- pyasic/miners/antminer/bmminer/X3/__init__.py | 7 + pyasic/miners/antminer/bmminer/X5/KS5.py | 2 +- pyasic/miners/antminer/bmminer/X5/__init__.py | 5 + pyasic/miners/antminer/bmminer/X7/__init__.py | 6 + pyasic/miners/antminer/bmminer/X9/__init__.py | 10 + .../miners/antminer/bosminer/X17/__init__.py | 10 + .../miners/antminer/bosminer/X19/__init__.py | 19 + .../miners/antminer/bosminer/X21/__init__.py | 9 + .../miners/antminer/bosminer/X9/__init__.py | 4 + .../miners/antminer/cgminer/X15/__init__.py | 2 + pyasic/miners/antminer/cgminer/X3/__init__.py | 2 + pyasic/miners/antminer/cgminer/X5/__init__.py | 2 + pyasic/miners/antminer/epic/X19/__init__.py | 12 + pyasic/miners/antminer/epic/X21/__init__.py | 6 + pyasic/miners/antminer/hiveon/X19/__init__.py | 21 + pyasic/miners/antminer/hiveon/X9/T9.py | 10 +- pyasic/miners/antminer/hiveon/X9/__init__.py | 4 + pyasic/miners/antminer/luxos/X19/__init__.py | 10 + pyasic/miners/antminer/luxos/X21/__init__.py | 5 + pyasic/miners/antminer/luxos/X9/__init__.py | 4 + .../miners/antminer/marathon/X19/__init__.py | 10 + .../miners/antminer/marathon/X21/__init__.py | 5 + pyasic/miners/antminer/mskminer/X19/S19.py | 2 +- .../miners/antminer/mskminer/X19/__init__.py | 4 + pyasic/miners/antminer/vnish/X17/S17.py | 2 +- pyasic/miners/antminer/vnish/X17/__init__.py | 2 + pyasic/miners/antminer/vnish/X19/S19.py | 2 +- pyasic/miners/antminer/vnish/X19/T19.py | 2 +- pyasic/miners/antminer/vnish/X19/__init__.py | 18 + pyasic/miners/antminer/vnish/X21/S21.py | 8 +- pyasic/miners/antminer/vnish/X21/T21.py | 2 +- pyasic/miners/antminer/vnish/X21/__init__.py | 9 + pyasic/miners/antminer/vnish/X3/L3.py | 2 +- pyasic/miners/antminer/vnish/X3/__init__.py | 2 + pyasic/miners/antminer/vnish/X7/L7.py | 2 +- pyasic/miners/antminer/vnish/X7/__init__.py | 2 + pyasic/miners/antminer/vnish/X9/L9.py | 2 +- pyasic/miners/antminer/vnish/X9/__init__.py | 2 + pyasic/miners/auradine/__init__.py | 20 +- pyasic/miners/auradine/flux/AD/AT1.py | 2 +- pyasic/miners/auradine/flux/AD/AT2.py | 2 +- pyasic/miners/auradine/flux/AD/__init__.py | 2 + pyasic/miners/auradine/flux/AI/AI2.py | 2 +- pyasic/miners/auradine/flux/AI/AI3.py | 2 +- pyasic/miners/auradine/flux/AI/__init__.py | 2 + pyasic/miners/auradine/flux/AT/AD2.py | 2 +- pyasic/miners/auradine/flux/AT/AD3.py | 2 +- pyasic/miners/auradine/flux/AT/__init__.py | 2 + pyasic/miners/auradine/flux/__init__.py | 16 +- pyasic/miners/avalonminer/__init__.py | 40 +- .../avalonminer/cgminer/A10X/__init__.py | 2 + .../avalonminer/cgminer/A11X/__init__.py | 2 + .../avalonminer/cgminer/A12X/__init__.py | 2 + .../avalonminer/cgminer/A15X/__init__.py | 2 + .../avalonminer/cgminer/A7X/__init__.py | 2 + .../avalonminer/cgminer/A8X/__init__.py | 2 + .../avalonminer/cgminer/A9X/__init__.py | 2 + .../miners/avalonminer/cgminer/Q/__init__.py | 2 + pyasic/miners/avalonminer/cgminer/__init__.py | 38 +- .../avalonminer/cgminer/nano/__init__.py | 2 + .../miners/avalonminer/cgminer/nano/nano3.py | 31 +- pyasic/miners/backends/__init__.py | 35 + pyasic/miners/backends/antminer.py | 69 +- pyasic/miners/backends/auradine.py | 62 +- pyasic/miners/backends/avalonminer.py | 42 +- pyasic/miners/backends/bfgminer.py | 29 +- pyasic/miners/backends/bmminer.py | 31 +- pyasic/miners/backends/braiins_os.py | 1041 +++++++++++++---- pyasic/miners/backends/btminer.py | 680 +++++++---- pyasic/miners/backends/cgminer.py | 20 +- pyasic/miners/backends/elphapex.py | 55 +- pyasic/miners/backends/epic.py | 163 ++- pyasic/miners/backends/espminer.py | 179 ++- pyasic/miners/backends/goldshell.py | 221 +++- pyasic/miners/backends/hammer.py | 401 +++++-- pyasic/miners/backends/hiveon.py | 29 +- pyasic/miners/backends/iceriver.py | 181 ++- pyasic/miners/backends/innosilicon.py | 45 +- pyasic/miners/backends/luxminer.py | 390 ++++-- pyasic/miners/backends/marathon.py | 318 +++-- pyasic/miners/backends/mskminer.py | 19 +- pyasic/miners/backends/unknown.py | 15 +- pyasic/miners/backends/vnish.py | 246 +++- pyasic/miners/base.py | 51 +- pyasic/miners/bitaxe/__init__.py | 9 +- pyasic/miners/bitaxe/espminer/BM/BM1366.py | 2 +- pyasic/miners/bitaxe/espminer/BM/BM1368.py | 2 +- pyasic/miners/bitaxe/espminer/BM/BM1370.py | 2 +- pyasic/miners/bitaxe/espminer/BM/BM1397.py | 2 +- pyasic/miners/bitaxe/espminer/BM/__init__.py | 2 + pyasic/miners/bitaxe/espminer/__init__.py | 4 +- pyasic/miners/braiins/__init__.py | 7 +- pyasic/miners/braiins/braiins/BMM/__init__.py | 2 + pyasic/miners/braiins/braiins/__init__.py | 4 +- pyasic/miners/data.py | 6 +- .../device/models/antminer/X15/__init__.py | 5 + .../device/models/antminer/X17/__init__.py | 10 + .../device/models/antminer/X19/__init__.py | 30 + .../device/models/antminer/X21/__init__.py | 9 + .../device/models/antminer/X3/__init__.py | 8 + .../device/models/antminer/X5/__init__.py | 6 + .../device/models/antminer/X7/__init__.py | 6 + .../device/models/antminer/X9/__init__.py | 10 + .../device/models/auradine/AD/__init__.py | 5 + .../device/models/auradine/AI/__init__.py | 5 + .../device/models/auradine/AT/__init__.py | 6 + .../models/avalonminer/A10X/__init__.py | 2 + .../models/avalonminer/A11X/__init__.py | 2 + .../models/avalonminer/A12X/__init__.py | 2 + .../models/avalonminer/A15X/__init__.py | 2 + .../device/models/avalonminer/A7X/__init__.py | 2 + .../device/models/avalonminer/A8X/__init__.py | 2 + .../device/models/avalonminer/A9X/__init__.py | 2 + .../device/models/avalonminer/Q/__init__.py | 2 + .../models/avalonminer/nano/__init__.py | 2 + .../device/models/bitaxe/BM/__init__.py | 2 + .../device/models/elphapex/DGX/__init__.py | 2 + .../device/models/goldshell/X5/__init__.py | 2 + .../device/models/goldshell/XBox/__init__.py | 2 + .../device/models/goldshell/XMax/__init__.py | 2 + .../device/models/goldshell/byte/__init__.py | 2 + .../models/goldshell/mini_doge/__init__.py | 2 + .../device/models/hammer/DX/__init__.py | 4 + .../device/models/iceriver/ALX/__init__.py | 4 + .../device/models/iceriver/KSX/__init__.py | 12 + .../miners/device/models/iceriver/__init__.py | 4 +- .../device/models/innosilicon/T3X/__init__.py | 4 + .../device/models/luckyminer/LV/__init__.py | 2 + .../device/models/volcminer/DX/__init__.py | 4 + .../device/models/whatsminer/M2X/__init__.py | 16 + .../device/models/whatsminer/M3X/__init__.py | 161 +++ .../device/models/whatsminer/M5X/__init__.py | 101 ++ .../device/models/whatsminer/M6X/__init__.py | 96 ++ .../device/models/whatsminer/M7X/__init__.py | 4 + pyasic/miners/elphapex/__init__.py | 8 +- pyasic/miners/elphapex/daoge/DGX/DG1.py | 2 +- pyasic/miners/elphapex/daoge/DGX/__init__.py | 2 + pyasic/miners/elphapex/daoge/__init__.py | 4 +- pyasic/miners/factory.py | 779 +++++++++++- pyasic/miners/goldshell/__init__.py | 22 +- .../miners/goldshell/bfgminer/X5/__init__.py | 2 + .../goldshell/bfgminer/XBox/__init__.py | 2 + .../goldshell/bfgminer/XMax/__init__.py | 2 + pyasic/miners/goldshell/bfgminer/__init__.py | 21 +- .../goldshell/bfgminer/byte/__init__.py | 2 + pyasic/miners/goldshell/bfgminer/byte/byte.py | 26 +- .../goldshell/bfgminer/mini_doge/__init__.py | 2 + .../goldshell/bfgminer/mini_doge/mini_doge.py | 10 +- pyasic/miners/hammer/__init__.py | 6 +- pyasic/miners/hammer/blackminer/DX/D10.py | 2 +- .../miners/hammer/blackminer/DX/__init__.py | 2 + pyasic/miners/hammer/blackminer/__init__.py | 4 +- pyasic/miners/iceriver/iceminer/ALX/AL3.py | 2 +- .../miners/iceriver/iceminer/ALX/__init__.py | 4 + pyasic/miners/iceriver/iceminer/KSX/KS0.py | 2 +- pyasic/miners/iceriver/iceminer/KSX/KS1.py | 2 +- pyasic/miners/iceriver/iceminer/KSX/KS2.py | 2 +- pyasic/miners/iceriver/iceminer/KSX/KS3.py | 2 +- pyasic/miners/iceriver/iceminer/KSX/KS5.py | 2 +- .../miners/iceriver/iceminer/KSX/__init__.py | 12 + pyasic/miners/innosilicon/__init__.py | 14 +- .../innosilicon/cgminer/A10X/__init__.py | 2 + .../innosilicon/cgminer/A11X/__init__.py | 2 + pyasic/miners/innosilicon/cgminer/T3X/T3H.py | 2 +- .../innosilicon/cgminer/T3X/__init__.py | 2 + pyasic/miners/innosilicon/cgminer/__init__.py | 13 +- pyasic/miners/listener.py | 32 +- pyasic/miners/luckyminer/__init__.py | 4 +- pyasic/miners/luckyminer/espminer/LV/LV07.py | 2 +- pyasic/miners/luckyminer/espminer/LV/LV08.py | 2 +- .../miners/luckyminer/espminer/LV/__init__.py | 2 + pyasic/miners/luckyminer/espminer/__init__.py | 4 +- pyasic/miners/volcminer/__init__.py | 4 +- pyasic/miners/volcminer/blackminer/DX/D1.py | 2 +- .../volcminer/blackminer/DX/__init__.py | 2 + .../miners/volcminer/blackminer/__init__.py | 4 +- pyasic/miners/whatsminer/btminer/M2X/M20.py | 2 +- pyasic/miners/whatsminer/btminer/M2X/M20P.py | 4 +- pyasic/miners/whatsminer/btminer/M2X/M20S.py | 6 +- .../whatsminer/btminer/M2X/M20S_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M2X/M21.py | 2 +- pyasic/miners/whatsminer/btminer/M2X/M21S.py | 6 +- .../whatsminer/btminer/M2X/M21S_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M2X/M29.py | 2 +- .../miners/whatsminer/btminer/M2X/__init__.py | 16 + pyasic/miners/whatsminer/btminer/M3X/M30.py | 4 +- pyasic/miners/whatsminer/btminer/M3X/M30K.py | 2 +- pyasic/miners/whatsminer/btminer/M3X/M30L.py | 2 +- pyasic/miners/whatsminer/btminer/M3X/M30S.py | 60 +- .../whatsminer/btminer/M3X/M30S_Plus.py | 70 +- .../whatsminer/btminer/M3X/M30S_Plus_Plus.py | 56 +- pyasic/miners/whatsminer/btminer/M3X/M31.py | 4 +- pyasic/miners/whatsminer/btminer/M3X/M31H.py | 4 +- pyasic/miners/whatsminer/btminer/M3X/M31L.py | 2 +- pyasic/miners/whatsminer/btminer/M3X/M31S.py | 24 +- pyasic/miners/whatsminer/btminer/M3X/M31SE.py | 6 +- .../whatsminer/btminer/M3X/M31S_Plus.py | 40 +- pyasic/miners/whatsminer/btminer/M3X/M32.py | 4 +- pyasic/miners/whatsminer/btminer/M3X/M32S.py | 2 +- pyasic/miners/whatsminer/btminer/M3X/M33.py | 6 +- pyasic/miners/whatsminer/btminer/M3X/M33S.py | 2 +- .../whatsminer/btminer/M3X/M33S_Plus.py | 8 +- .../whatsminer/btminer/M3X/M33S_Plus_Plus.py | 6 +- .../whatsminer/btminer/M3X/M34S_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M3X/M36S.py | 2 +- .../whatsminer/btminer/M3X/M36S_Plus.py | 2 +- .../whatsminer/btminer/M3X/M36S_Plus_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M3X/M39.py | 6 +- .../miners/whatsminer/btminer/M3X/__init__.py | 160 +++ pyasic/miners/whatsminer/btminer/M5X/M50.py | 36 +- pyasic/miners/whatsminer/btminer/M5X/M50S.py | 38 +- .../whatsminer/btminer/M5X/M50S_Plus.py | 22 +- .../whatsminer/btminer/M5X/M50S_Plus_Plus.py | 22 +- pyasic/miners/whatsminer/btminer/M5X/M52S.py | 2 +- .../whatsminer/btminer/M5X/M52S_Plus_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M5X/M53.py | 10 +- pyasic/miners/whatsminer/btminer/M5X/M53H.py | 2 +- pyasic/miners/whatsminer/btminer/M5X/M53S.py | 10 +- .../whatsminer/btminer/M5X/M53S_Plus.py | 8 +- .../whatsminer/btminer/M5X/M53S_Plus_Plus.py | 12 +- .../whatsminer/btminer/M5X/M54S_Plus_Plus.py | 6 +- pyasic/miners/whatsminer/btminer/M5X/M56.py | 2 +- pyasic/miners/whatsminer/btminer/M5X/M56S.py | 6 +- .../whatsminer/btminer/M5X/M56S_Plus.py | 8 +- .../whatsminer/btminer/M5X/M56S_Plus_Plus.py | 8 +- pyasic/miners/whatsminer/btminer/M5X/M59.py | 2 +- .../miners/whatsminer/btminer/M5X/__init__.py | 101 ++ pyasic/miners/whatsminer/btminer/M6X/M60.py | 20 +- pyasic/miners/whatsminer/btminer/M6X/M60S.py | 22 +- .../whatsminer/btminer/M6X/M60S_Plus.py | 20 +- .../whatsminer/btminer/M6X/M60S_Plus_Plus.py | 4 +- pyasic/miners/whatsminer/btminer/M6X/M61.py | 18 +- pyasic/miners/whatsminer/btminer/M6X/M61S.py | 6 +- .../whatsminer/btminer/M6X/M61S_Plus.py | 2 +- .../whatsminer/btminer/M6X/M62S_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M6X/M63.py | 10 +- pyasic/miners/whatsminer/btminer/M6X/M63S.py | 14 +- .../whatsminer/btminer/M6X/M63S_Plus.py | 10 +- .../whatsminer/btminer/M6X/M63S_Plus_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M6X/M64.py | 4 +- pyasic/miners/whatsminer/btminer/M6X/M64S.py | 2 +- pyasic/miners/whatsminer/btminer/M6X/M65S.py | 4 +- .../whatsminer/btminer/M6X/M65S_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M6X/M66.py | 8 +- pyasic/miners/whatsminer/btminer/M6X/M66S.py | 20 +- .../whatsminer/btminer/M6X/M66S_Plus.py | 12 +- .../whatsminer/btminer/M6X/M66S_Plus_Plus.py | 2 +- pyasic/miners/whatsminer/btminer/M6X/M67S.py | 2 +- .../miners/whatsminer/btminer/M6X/__init__.py | 96 ++ pyasic/miners/whatsminer/btminer/M7X/M70.py | 2 +- .../miners/whatsminer/btminer/M7X/__init__.py | 4 + pyasic/misc/__init__.py | 14 +- pyasic/network/__init__.py | 5 +- pyasic/rpc/__init__.py | 12 + pyasic/rpc/antminer.py | 20 +- pyasic/rpc/avalonminer.py | 4 +- pyasic/rpc/base.py | 36 +- pyasic/rpc/bfgminer.py | 22 +- pyasic/rpc/bosminer.py | 40 +- pyasic/rpc/btminer.py | 691 ++++++++--- pyasic/rpc/ccminer.py | 4 +- pyasic/rpc/cgminer.py | 90 +- pyasic/rpc/gcminer.py | 28 +- pyasic/rpc/luxminer.py | 134 ++- pyasic/rpc/marathon.py | 12 +- pyasic/rpc/unknown.py | 12 +- pyasic/settings/__init__.py | 8 +- pyasic/ssh/__init__.py | 5 + pyasic/ssh/antminer.py | 2 +- pyasic/ssh/braiins_os.py | 20 +- pyasic/web/__init__.py | 29 + pyasic/web/antminer.py | 56 +- pyasic/web/auradine.py | 68 +- pyasic/web/avalonminer.py | 16 +- pyasic/web/base.py | 4 +- pyasic/web/braiins_os/__init__.py | 5 + pyasic/web/braiins_os/better_monkey.py | 10 +- pyasic/web/braiins_os/boser.py | 162 ++- pyasic/web/braiins_os/bosminer.py | 19 +- pyasic/web/elphapex.py | 33 +- pyasic/web/epic.py | 44 +- pyasic/web/espminer.py | 20 +- pyasic/web/goldshell.py | 42 +- pyasic/web/hammer.py | 29 +- pyasic/web/hiveon.py | 38 +- pyasic/web/iceriver.py | 11 +- pyasic/web/innosilicon.py | 30 +- pyasic/web/luckyminer.py | 2 +- pyasic/web/marathon.py | 54 +- pyasic/web/mskminer.py | 9 +- pyasic/web/vnish.py | 40 +- pyproject.toml | 4 +- tests/__init__.py | 28 +- tests/config_tests/__init__.py | 22 +- tests/config_tests/fans.py | 14 +- tests/miners_tests/__init__.py | 14 +- .../avalonminer_tests/__init__.py | 2 + .../version_24102401_25462b2_9ddf522.py | 12 +- .../backends_tests/elphapex_tests/__init__.py | 2 + .../elphapex_tests/version_1_0_2.py | 10 +- .../backends_tests/hammer_tests/__init__.py | 2 + .../hammer_tests/version_2023_05_28.py | 10 +- .../backends_tests/mskminer_tests/__init__.py | 2 + .../mskminer_tests/version_2_6_0_39.py | 10 +- tests/network_tests/__init__.py | 6 +- tests/rpc_tests/__init__.py | 72 +- 366 files changed, 8014 insertions(+), 3184 deletions(-) diff --git a/docs/generate_miners.py b/docs/generate_miners.py index f2de9e327..0e9565ed1 100644 --- a/docs/generate_miners.py +++ b/docs/generate_miners.py @@ -9,17 +9,17 @@ warnings.filterwarnings("ignore") -def path(cls): +def path(cls: type[Any]) -> str: module = importlib.import_module(cls.__module__) return module.__name__ + "." + cls.__name__ -def make(cls): +def make(cls: type[Any]) -> str: p = path(cls) return p.split(".")[2] -def model_type(cls): +def model_type(cls: type[Any]) -> str: p = path(cls) return p.split(".")[4] @@ -67,7 +67,7 @@ def backend_str(backend: MinerTypes) -> str: raise TypeError("Unknown miner backend, cannot generate docs") -def create_url_str(mtype: str): +def create_url_str(mtype: str) -> str: return ( mtype.lower() .replace(" ", "-") @@ -143,7 +143,7 @@ def create_url_str(mtype: str): done.append(miner) -def create_directory_structure(directory, data): +def create_directory_structure(directory: str | Path, data: dict[str, Any]) -> None: if not os.path.exists(directory): os.makedirs(directory) @@ -171,11 +171,11 @@ def create_directory_structure(directory, data): ) -def create_supported_types(directory): +def create_supported_types(directory: str | Path) -> None: with open(os.path.join(directory, "supported_types.md"), "w") as file: file.write(SUPPORTED_TYPES_HEADER) for mback in MINER_CLASSES: - backend_types = {} + backend_types: dict[str, list[type[Any]]] = {} file.write(BACKEND_TYPE_HEADER.format(backend_str(mback))) for mtype in MINER_CLASSES[mback]: if mtype is None: diff --git a/pyasic/__init__.py b/pyasic/__init__.py index cb5449721..e558feac1 100644 --- a/pyasic/__init__.py +++ b/pyasic/__init__.py @@ -26,3 +26,12 @@ from pyasic.web import * __version__ = importlib.metadata.version("pyasic") + +__all__ = [ + "settings", + "MinerConfig", + "MinerData", + "APIError", + "APIWarning", + "MinerNetwork", +] diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 4910e2ad0..0d47a1eaa 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from __future__ import annotations + from typing import Any from pydantic import BaseModel, Field @@ -54,6 +56,28 @@ from pyasic.config.temperature import TemperatureConfig from pyasic.misc import merge_dicts +__all__ = [ + "MinerConfig", + "MiningModeConfig", + "FanModeConfig", + "PoolConfig", + "TemperatureConfig", + "ScalingConfig", + "FanModeType", + "MiningModeType", + "FanModeNormal", + "FanModeManual", + "FanModeImmersion", + "MiningModeNormal", + "MiningModeHPM", + "MiningModeLPM", + "MiningModeSleep", + "MiningModeManual", + "MiningModePowerTune", + "MiningModeHashrateTune", + "MiningModePreset", +] + class MinerConfig(BaseModel): """Represents the configuration for a miner including pool configuration, @@ -73,11 +97,11 @@ def __getitem__(self, item: str) -> Any: except AttributeError: raise KeyError - def as_dict(self) -> dict: + def as_dict(self) -> dict[str, Any]: """Converts the MinerConfig object to a dictionary.""" return self.model_dump() - def as_am_modern(self, user_suffix: str | None = None) -> dict: + def as_am_modern(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for modern Antminers.""" return { **self.fan_mode.as_am_modern(), @@ -87,7 +111,7 @@ def as_am_modern(self, user_suffix: str | None = None) -> dict: **self.temperature.as_am_modern(), } - def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for modern Hiveon.""" return { **self.fan_mode.as_hiveon_modern(), @@ -97,7 +121,7 @@ def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: **self.temperature.as_hiveon_modern(), } - def as_elphapex(self, user_suffix: str | None = None) -> dict: + def as_elphapex(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for modern Elphapex.""" return { **self.fan_mode.as_elphapex(), @@ -107,7 +131,7 @@ def as_elphapex(self, user_suffix: str | None = None) -> dict: **self.temperature.as_elphapex(), } - def as_wm(self, user_suffix: str | None = None) -> dict: + def as_wm(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for Whatsminers.""" return { **self.fan_mode.as_wm(), @@ -116,14 +140,14 @@ def as_wm(self, user_suffix: str | None = None) -> dict: **self.temperature.as_wm(), } - def as_btminer_v3(self, user_suffix: str | None = None) -> dict: + def as_btminer_v3(self, user_suffix: str | None = None) -> dict[str, Any]: """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: + def as_am_old(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for old versions of Antminers.""" return { **self.fan_mode.as_am_old(), @@ -132,7 +156,7 @@ def as_am_old(self, user_suffix: str | None = None) -> dict: **self.temperature.as_am_old(), } - def as_goldshell(self, user_suffix: str | None = None) -> dict: + def as_goldshell(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for Goldshell miners.""" return { **self.fan_mode.as_goldshell(), @@ -141,7 +165,7 @@ def as_goldshell(self, user_suffix: str | None = None) -> dict: **self.temperature.as_goldshell(), } - def as_avalon(self, user_suffix: str | None = None) -> dict: + def as_avalon(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for Avalonminers.""" return { **self.fan_mode.as_avalon(), @@ -150,7 +174,7 @@ def as_avalon(self, user_suffix: str | None = None) -> dict: **self.temperature.as_avalon(), } - def as_inno(self, user_suffix: str | None = None) -> dict: + def as_inno(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for Innosilicon miners.""" return { **self.fan_mode.as_inno(), @@ -159,7 +183,7 @@ def as_inno(self, user_suffix: str | None = None) -> dict: **self.temperature.as_inno(), } - def as_bosminer(self, user_suffix: str | None = None) -> dict: + def as_bosminer(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the bosminer.toml format.""" return { **merge_dicts(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()), @@ -167,7 +191,7 @@ def as_bosminer(self, user_suffix: str | None = None) -> dict: **self.pools.as_bosminer(user_suffix=user_suffix), } - def as_boser(self, user_suffix: str | None = None) -> dict: + def as_boser(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for BOSer.""" return { **self.fan_mode.as_boser(), @@ -176,7 +200,7 @@ def as_boser(self, user_suffix: str | None = None) -> dict: **self.pools.as_boser(user_suffix=user_suffix), } - def as_epic(self, user_suffix: str | None = None) -> dict: + def as_epic(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for ePIC miners.""" return { **merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()), @@ -184,7 +208,7 @@ def as_epic(self, user_suffix: str | None = None) -> dict: **self.pools.as_epic(user_suffix=user_suffix), } - def as_auradine(self, user_suffix: str | None = None) -> dict: + def as_auradine(self, user_suffix: str | None = None) -> dict[str, Any]: """Generates the configuration in the format suitable for Auradine miners.""" return { **self.fan_mode.as_auradine(), @@ -193,7 +217,7 @@ def as_auradine(self, user_suffix: str | None = None) -> dict: **self.pools.as_auradine(user_suffix=user_suffix), } - def as_mara(self, user_suffix: str | None = None) -> dict: + def as_mara(self, user_suffix: str | None = None) -> dict[str, Any]: return { **self.fan_mode.as_mara(), **self.temperature.as_mara(), @@ -201,7 +225,7 @@ def as_mara(self, user_suffix: str | None = None) -> dict: **self.pools.as_mara(user_suffix=user_suffix), } - def as_espminer(self, user_suffix: str | None = None) -> dict: + def as_espminer(self, user_suffix: str | None = None) -> dict[str, Any]: return { **self.fan_mode.as_espminer(), **self.temperature.as_espminer(), @@ -209,7 +233,7 @@ def as_espminer(self, user_suffix: str | None = None) -> dict: **self.pools.as_espminer(user_suffix=user_suffix), } - def as_luxos(self, user_suffix: str | None = None) -> dict: + def as_luxos(self, user_suffix: str | None = None) -> dict[str, Any]: return { **self.fan_mode.as_luxos(), **self.temperature.as_luxos(), @@ -217,7 +241,7 @@ def as_luxos(self, user_suffix: str | None = None) -> dict: **self.pools.as_luxos(user_suffix=user_suffix), } - def as_vnish(self, user_suffix: str | None = None) -> dict: + def as_vnish(self, user_suffix: str | None = None) -> dict[str, Any]: main_cfg = { "miner": { **self.fan_mode.as_vnish(), @@ -230,11 +254,11 @@ def as_vnish(self, user_suffix: str | None = None) -> dict: main_cfg["miner"]["cooling"]["mode"]["param"] = self.temperature.target return main_cfg - def as_hammer(self, *args, **kwargs) -> dict: + def as_hammer(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return self.as_am_modern(*args, **kwargs) @classmethod - def from_dict(cls, dict_conf: dict) -> "MinerConfig": + def from_dict(cls, dict_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from a dictionary.""" return cls( pools=PoolConfig.from_dict(dict_conf.get("pools")), @@ -244,12 +268,12 @@ def from_dict(cls, dict_conf: dict) -> "MinerConfig": ) @classmethod - def from_api(cls, api_pools: dict) -> "MinerConfig": + def from_api(cls, api_pools: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from API pool data.""" return cls(pools=PoolConfig.from_api(api_pools)) @classmethod - def from_am_modern(cls, web_conf: dict) -> "MinerConfig": + def from_am_modern(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for modern Antminers.""" return cls( pools=PoolConfig.from_am_modern(web_conf), @@ -258,7 +282,7 @@ def from_am_modern(cls, web_conf: dict) -> "MinerConfig": ) @classmethod - def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig": + def from_hiveon_modern(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for Hiveon.""" return cls( pools=PoolConfig.from_hiveon_modern(web_conf), @@ -267,7 +291,7 @@ def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig": ) @classmethod - def from_elphapex(cls, web_conf: dict) -> "MinerConfig": + def from_elphapex(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for modern Antminers.""" return cls( pools=PoolConfig.from_elphapex(web_conf), @@ -276,32 +300,32 @@ def from_elphapex(cls, web_conf: dict) -> "MinerConfig": ) @classmethod - def from_am_old(cls, web_conf: dict) -> "MinerConfig": + def from_am_old(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for old versions of Antminers.""" return cls.from_am_modern(web_conf) @classmethod - def from_goldshell(cls, web_conf: dict) -> "MinerConfig": + def from_goldshell(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for Goldshell miners.""" return cls(pools=PoolConfig.from_am_modern(web_conf)) @classmethod - def from_goldshell_list(cls, web_conf: list) -> "MinerConfig": + def from_goldshell_list(cls, web_conf: list[dict[str, Any]]) -> 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": + def from_goldshell_byte(cls, web_conf: list[dict[str, Any]]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for Goldshell Byte miners.""" return cls(pools=PoolConfig.from_goldshell_byte(web_conf)) @classmethod - def from_inno(cls, web_pools: list) -> "MinerConfig": + def from_inno(cls, web_pools: list[dict[str, Any]]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for Innosilicon miners.""" return cls(pools=PoolConfig.from_inno(web_pools)) @classmethod - def from_bosminer(cls, toml_conf: dict) -> "MinerConfig": + def from_bosminer(cls, toml_conf: dict[str, Any]) -> 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( pools=PoolConfig.from_bosminer(toml_conf), @@ -311,7 +335,7 @@ def from_bosminer(cls, toml_conf: dict) -> "MinerConfig": ) @classmethod - def from_boser(cls, grpc_miner_conf: dict) -> "MinerConfig": + def from_boser(cls, grpc_miner_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from gRPC configuration for BOSer.""" return cls( pools=PoolConfig.from_boser(grpc_miner_conf), @@ -321,7 +345,7 @@ def from_boser(cls, grpc_miner_conf: dict) -> "MinerConfig": ) @classmethod - def from_epic(cls, web_conf: dict) -> "MinerConfig": + def from_epic(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for ePIC miners.""" return cls( pools=PoolConfig.from_epic(web_conf), @@ -332,8 +356,11 @@ def from_epic(cls, web_conf: dict) -> "MinerConfig": @classmethod def from_vnish( - cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict - ) -> "MinerConfig": + cls, + web_settings: dict[str, Any], + web_presets: list[dict[str, Any]], + web_perf_summary: dict[str, Any], + ) -> MinerConfig: """Constructs a MinerConfig object from web settings for VNish miners.""" return cls( pools=PoolConfig.from_vnish(web_settings), @@ -345,7 +372,7 @@ def from_vnish( ) @classmethod - def from_auradine(cls, web_conf: dict) -> "MinerConfig": + def from_auradine(cls, web_conf: dict[str, Any]) -> MinerConfig: """Constructs a MinerConfig object from web configuration for Auradine miners.""" return cls( pools=PoolConfig.from_api(web_conf["pools"]), @@ -354,7 +381,7 @@ def from_auradine(cls, web_conf: dict) -> "MinerConfig": ) @classmethod - def from_mara(cls, web_miner_config: dict) -> "MinerConfig": + def from_mara(cls, web_miner_config: dict[str, Any]) -> MinerConfig: return cls( pools=PoolConfig.from_mara(web_miner_config), fan_mode=FanModeConfig.from_mara(web_miner_config), @@ -362,14 +389,14 @@ def from_mara(cls, web_miner_config: dict) -> "MinerConfig": ) @classmethod - def from_espminer(cls, web_system_info: dict) -> "MinerConfig": + def from_espminer(cls, web_system_info: dict[str, Any]) -> 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": + def from_iceriver(cls, web_userpanel: dict[str, Any]) -> MinerConfig: return cls( pools=PoolConfig.from_iceriver(web_userpanel), ) @@ -377,13 +404,13 @@ def from_iceriver(cls, web_userpanel: dict) -> "MinerConfig": @classmethod def from_luxos( cls, - rpc_tempctrl: dict, - rpc_fans: dict, - rpc_pools: dict, - rpc_groups: dict, - rpc_config: dict, - rpc_profiles: dict, - ) -> "MinerConfig": + rpc_tempctrl: dict[str, Any], + rpc_fans: dict[str, Any], + rpc_pools: dict[str, Any], + rpc_groups: dict[str, Any], + rpc_config: dict[str, Any], + rpc_profiles: dict[str, Any], + ) -> MinerConfig: return cls( temperature=TemperatureConfig.from_luxos(rpc_tempctrl=rpc_tempctrl), fan_mode=FanModeConfig.from_luxos( @@ -396,13 +423,16 @@ def from_luxos( ) @classmethod - def from_hammer(cls, *args, **kwargs) -> "MinerConfig": + def from_hammer(cls, *args: Any, **kwargs: Any) -> 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": + cls, + rpc_pools: dict[str, Any], + rpc_settings: dict[str, Any], + rpc_device_info: dict[str, Any], + ) -> MinerConfig: return cls( pools=PoolConfig.from_btminer_v3(rpc_pools=rpc_pools["msg"]), mining_mode=MiningModeConfig.from_btminer_v3( diff --git a/pyasic/config/base.py b/pyasic/config/base.py index ec2dfd07c..a3af6e0c1 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -16,72 +16,76 @@ from __future__ import annotations from enum import Enum -from typing import Any +from typing import Any, TypeVar from pydantic import BaseModel +_T = TypeVar("_T", bound="MinerConfigValue") + class MinerConfigOption(Enum): + value: type[MinerConfigValue] + @classmethod - def from_dict(cls, dict_conf: dict | None): + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MinerConfigOption: return cls.default() - def as_am_modern(self) -> dict: - return self.value.as_am_modern() + def as_am_modern(self) -> dict[str, Any]: + return self.value().as_am_modern() - def as_hiveon_modern(self) -> dict: - return self.value.as_hiveon_modern() + def as_hiveon_modern(self) -> dict[str, Any]: + return self.value().as_hiveon_modern() - def as_am_old(self) -> dict: - return self.value.as_am_old() + def as_am_old(self) -> dict[str, Any]: + return self.value().as_am_old() - def as_wm(self) -> dict: - return self.value.as_wm() + def as_wm(self) -> dict[str, Any]: + return self.value().as_wm() - def as_inno(self) -> dict: - return self.value.as_inno() + def as_inno(self) -> dict[str, Any]: + return self.value().as_inno() - def as_goldshell(self) -> dict: - return self.value.as_goldshell() + def as_goldshell(self) -> dict[str, Any]: + return self.value().as_goldshell() - def as_avalon(self) -> dict: - return self.value.as_avalon() + def as_avalon(self) -> dict[str, Any]: + return self.value().as_avalon() - def as_bosminer(self) -> dict: - return self.value.as_bosminer() + def as_bosminer(self) -> dict[str, Any]: + return self.value().as_bosminer() - def as_boser(self) -> dict: - return self.value.as_boser + def as_boser(self) -> dict[str, Any]: + return self.value().as_boser() - def as_epic(self) -> dict: - return self.value.as_epic() + def as_epic(self) -> dict[str, Any]: + return self.value().as_epic() - def as_vnish(self) -> dict: - return self.value.as_vnish() + def as_vnish(self) -> dict[str, Any]: + return self.value().as_vnish() - def as_auradine(self) -> dict: - return self.value.as_auradine() + def as_auradine(self) -> dict[str, Any]: + return self.value().as_auradine() - def as_mara(self) -> dict: - return self.value.as_mara() + def as_mara(self) -> dict[str, Any]: + return self.value().as_mara() - def as_espminer(self) -> dict: - return self.value.as_espminer() + def as_espminer(self) -> dict[str, Any]: + return self.value().as_espminer() - def as_luxos(self) -> dict: - return self.value.as_luxos() + def as_luxos(self) -> dict[str, Any]: + return self.value().as_luxos() - def as_elphapex(self) -> dict: - return self.value.as_elphapex() + def as_elphapex(self) -> dict[str, Any]: + return self.value().as_elphapex() - def __call__(self, *args, **kwargs): + def __call__(self, *args: Any, **kwargs: Any) -> Any: return self.value(*args, **kwargs) @classmethod - def default(cls): - pass + def default(cls) -> MinerConfigOption: + raise NotImplementedError - def __getitem__(self, item): + def __getitem__(self, item: str) -> Any: try: return getattr(self, item) except AttributeError: @@ -90,64 +94,64 @@ def __getitem__(self, item): class MinerConfigValue(BaseModel): @classmethod - def from_dict(cls, dict_conf: dict): + def from_dict(cls, dict_conf: dict[str, Any]) -> MinerConfigValue: return cls() - def as_dict(self) -> dict: + def as_dict(self) -> dict[str, Any]: return self.model_dump() - def as_am_modern(self, *args: Any, **kwargs: Any) -> Any: + def as_am_modern(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_hiveon_modern(self, *args: Any, **kwargs: Any) -> Any: + def as_hiveon_modern(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_am_old(self, *args: Any, **kwargs: Any) -> Any: + def as_am_old(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_wm(self, *args: Any, **kwargs: Any) -> Any: + def as_wm(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_btminer_v3(self, *args: Any, **kwargs: Any) -> Any: + def as_btminer_v3(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_inno(self, *args: Any, **kwargs: Any) -> Any: + def as_inno(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_goldshell(self, *args: Any, **kwargs: Any) -> Any: + def as_goldshell(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_avalon(self, *args: Any, **kwargs: Any) -> Any: + def as_avalon(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_bosminer(self, *args: Any, **kwargs: Any) -> Any: + def as_bosminer(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_boser(self, *args: Any, **kwargs: Any) -> Any: + def as_boser(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_epic(self, *args: Any, **kwargs: Any) -> Any: + def as_epic(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_vnish(self, *args: Any, **kwargs: Any) -> Any: + def as_vnish(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_auradine(self, *args: Any, **kwargs: Any) -> Any: + def as_auradine(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_mara(self, *args: Any, **kwargs: Any) -> Any: + def as_mara(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_espminer(self, *args: Any, **kwargs: Any) -> Any: + def as_espminer(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_luxos(self, *args: Any, **kwargs: Any) -> Any: + def as_luxos(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def as_elphapex(self, *args: Any, **kwargs: Any) -> Any: + def as_elphapex(self, *args: Any, **kwargs: Any) -> dict[str, Any]: return {} - def __getitem__(self, item): + def __getitem__(self, item: str) -> Any: try: return getattr(self, item) except AttributeError: diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index dc0ecee25..d114832a8 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from __future__ import annotations -from typing import TypeVar +from typing import Any, TypeVar from pydantic import Field @@ -28,7 +28,7 @@ class FanModeNormal(MinerConfigValue): minimum_speed: int = 0 @classmethod - def from_dict(cls, dict_conf: dict) -> FanModeNormal: + def from_dict(cls, dict_conf: dict[str, Any]) -> FanModeNormal: cls_conf = {} if dict_conf.get("minimum_fans") is not None: cls_conf["minimum_fans"] = dict_conf["minimum_fans"] @@ -37,7 +37,7 @@ def from_dict(cls, dict_conf: dict) -> FanModeNormal: return cls(**cls_conf) @classmethod - def from_vnish(cls, web_cooling_settings: dict) -> FanModeNormal: + def from_vnish(cls, web_cooling_settings: dict[str, Any]) -> FanModeNormal: cls_conf = {} if web_cooling_settings.get("fan_min_count") is not None: cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"] @@ -46,28 +46,28 @@ def from_vnish(cls, web_cooling_settings: dict) -> FanModeNormal: return cls(**cls_conf) @classmethod - def from_bosminer(cls, toml_fan_conf: dict): + def from_bosminer(cls, toml_fan_conf: dict[str, Any]) -> FanModeNormal: 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[str, Any]: return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"} - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: return { "temp_control": {"mode": "auto"}, "fan_control": {"min_fans": self.minimum_fans}, } - def as_epic(self) -> dict: + def as_epic(self) -> dict[str, Any]: return { "fans": { "Auto": { @@ -78,7 +78,7 @@ def as_epic(self) -> dict: } } - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "general-config": {"environment-profile": "AirCooling"}, "advance-config": { @@ -87,13 +87,13 @@ def as_mara(self) -> dict: }, } - def as_espminer(self) -> dict: + def as_espminer(self) -> dict[str, Any]: return {"autoFanspeed": 1} - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}} - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: return { "cooling": { "fan_min_count": self.minimum_fans, @@ -112,7 +112,7 @@ class FanModeManual(MinerConfigValue): minimum_fans: int = 1 @classmethod - def from_dict(cls, dict_conf: dict) -> FanModeManual: + def from_dict(cls, dict_conf: dict[str, Any]) -> FanModeManual: cls_conf = {} if dict_conf.get("speed") is not None: cls_conf["speed"] = dict_conf["speed"] @@ -121,7 +121,7 @@ def from_dict(cls, dict_conf: dict) -> FanModeManual: return cls(**cls_conf) @classmethod - def from_bosminer(cls, toml_fan_conf: dict) -> FanModeManual: + def from_bosminer(cls, toml_fan_conf: dict[str, Any]) -> FanModeManual: cls_conf = {} if toml_fan_conf.get("min_fans") is not None: cls_conf["minimum_fans"] = toml_fan_conf["min_fans"] @@ -130,7 +130,7 @@ def from_bosminer(cls, toml_fan_conf: dict) -> FanModeManual: return cls(**cls_conf) @classmethod - def from_vnish(cls, web_cooling_settings: dict) -> FanModeManual: + def from_vnish(cls, web_cooling_settings: dict[str, Any]) -> FanModeManual: cls_conf = {} if web_cooling_settings.get("fan_min_count") is not None: cls_conf["minimum_fans"] = web_cooling_settings["fan_min_count"] @@ -138,28 +138,28 @@ def from_vnish(cls, web_cooling_settings: dict) -> FanModeManual: cls_conf["speed"] = web_cooling_settings["mode"]["param"] return cls(**cls_conf) - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"fc-fan-ctrl": True, "fc-fan-pwm": str(self.speed)} - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: return { "temp_control": {"mode": "manual"}, "fan_control": {"min_fans": self.minimum_fans, "speed": self.speed}, } - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"fan": {"percentage": self.speed}} - def as_epic(self) -> dict: + def as_epic(self) -> dict[str, Any]: return {"fans": {"Manual": {"speed": self.speed}}} - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "general-config": {"environment-profile": "AirCooling"}, "advance-config": { @@ -168,13 +168,13 @@ def as_mara(self) -> dict: }, } - def as_espminer(self) -> dict: + def as_espminer(self) -> dict[str, Any]: return {"autoFanspeed": 0, "fanspeed": self.speed} - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}} - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: return { "cooling": { "fan_min_count": self.minimum_fans, @@ -191,47 +191,50 @@ class FanModeImmersion(MinerConfigValue): mode: str = Field(init=False, default="immersion") @classmethod - def from_dict(cls, dict_conf: dict | None) -> FanModeImmersion: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> FanModeImmersion: return cls() - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"} - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: return { "fan_control": {"min_fans": 0}, } - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"fan": {"percentage": 0}} - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return {"general-config": {"environment-profile": "OilImmersionCooling"}} - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"fanset": {"speed": 0, "min_fans": 0}} - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: return {"cooling": {"mode": {"name": "immers"}}} +FanModeInstance = FanModeNormal | FanModeManual | FanModeImmersion + + class FanModeConfig(MinerConfigOption): normal = FanModeNormal manual = FanModeManual immersion = FanModeImmersion @classmethod - def default(cls): - return cls.normal() + def default(cls) -> FanModeInstance: # type: ignore[override] + return FanModeNormal() @classmethod - def from_dict(cls, dict_conf: dict | None): + def from_dict(cls, dict_conf: dict[str, Any] | None) -> FanModeInstance: # type: ignore[override] if dict_conf is None: return cls.default() @@ -239,76 +242,81 @@ def from_dict(cls, dict_conf: dict | None): if mode is None: return cls.default() - cls_attr = getattr(cls, mode) - if cls_attr is not None: - return cls_attr().from_dict(dict_conf) + if mode == "normal": + return FanModeNormal().from_dict(dict_conf) + elif mode == "manual": + return FanModeManual().from_dict(dict_conf) + elif mode == "immersion": + return FanModeImmersion().from_dict(dict_conf) + + return cls.default() @classmethod - def from_am_modern(cls, web_conf: dict): + def from_am_modern(cls, web_conf: dict[str, Any]) -> FanModeInstance: 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) + return FanModeImmersion() + return FanModeManual(speed=speed) else: - return cls.normal() + return FanModeNormal() else: return cls.default() @classmethod - def from_hiveon_modern(cls, web_conf: dict): + def from_hiveon_modern(cls, web_conf: dict[str, Any]) -> FanModeInstance: 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) + return FanModeImmersion() + return FanModeManual(speed=speed) else: - return cls.normal() + return FanModeNormal() else: return cls.default() @classmethod - def from_elphapex(cls, web_conf: dict): + def from_elphapex(cls, web_conf: dict[str, Any]) -> FanModeInstance: 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) + return FanModeImmersion() + return FanModeManual(speed=speed) else: - return cls.normal() + return FanModeNormal() else: return cls.default() @classmethod - def from_epic(cls, web_conf: dict): + def from_epic(cls, web_conf: dict[str, Any]) -> FanModeInstance: try: fan_mode = web_conf["Fans"]["Fan Mode"] if fan_mode.get("Manual") is not None: - return cls.manual(speed=fan_mode.get("Manual")) + return FanModeManual(speed=fan_mode.get("Manual")) else: - return cls.normal() + return FanModeNormal() except KeyError: return cls.default() @classmethod - def from_bosminer(cls, toml_conf: dict): + def from_bosminer(cls, toml_conf: dict[str, Any]) -> FanModeInstance: try: mode = toml_conf["temp_control"]["mode"] fan_config = toml_conf.get("fan_control", {}) if mode == "auto": - return cls.normal().from_bosminer(fan_config) + return FanModeNormal().from_bosminer(fan_config) elif mode == "manual": if toml_conf.get("fan_control"): - return cls.manual().from_bosminer(fan_config) - return cls.manual() + return FanModeManual().from_bosminer(fan_config) + return FanModeManual() elif mode == "disabled": - return cls.immersion() + return FanModeImmersion() except KeyError: pass @@ -318,25 +326,27 @@ def from_bosminer(cls, toml_conf: dict): return cls.default() if min_fans == 0: - return cls.immersion() - return cls.normal(minimum_fans=min_fans) + return FanModeImmersion() + return FanModeNormal(minimum_fans=min_fans) @classmethod - def from_vnish(cls, web_settings: dict): + def from_vnish(cls, web_settings: dict[str, Any]) -> FanModeInstance: try: mode = web_settings["miner"]["cooling"]["mode"]["name"] except LookupError: return cls.default() if mode == "auto": - return cls.normal().from_vnish(web_settings["miner"]["cooling"]) + return FanModeNormal().from_vnish(web_settings["miner"]["cooling"]) elif mode == "manual": - return cls.manual().from_vnish(web_settings["miner"]["cooling"]) + return FanModeManual().from_vnish(web_settings["miner"]["cooling"]) elif mode == "immers": - return cls.immersion() + return FanModeImmersion() + + return cls.default() @classmethod - def from_boser(cls, grpc_miner_conf: dict): + def from_boser(cls, grpc_miner_conf: dict[str, Any]) -> FanModeInstance: try: temperature_conf = grpc_miner_conf["temperature"] except LookupError: @@ -345,69 +355,74 @@ def from_boser(cls, grpc_miner_conf: dict): keys = temperature_conf.keys() if "auto" in keys: if "minimumRequiredFans" in keys: - return cls.normal(minimum_fans=temperature_conf["minimumRequiredFans"]) - return cls.normal() + return FanModeNormal( + minimum_fans=temperature_conf["minimumRequiredFans"] + ) + return FanModeNormal() if "manual" in keys: - conf = {} + speed = 100 + minimum_fans = 1 if "fanSpeedRatio" in temperature_conf["manual"].keys(): - conf["speed"] = int(temperature_conf["manual"]["fanSpeedRatio"]) + speed = int(temperature_conf["manual"]["fanSpeedRatio"]) if "minimumRequiredFans" in keys: - conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"]) - return cls.manual(**conf) + minimum_fans = int(temperature_conf["minimumRequiredFans"]) + return FanModeManual(speed=speed, minimum_fans=minimum_fans) if "disabled" in keys: - conf = {} + speed = 0 if "fanSpeedRatio" in temperature_conf["disabled"].keys(): - conf["speed"] = int(temperature_conf["disabled"]["fanSpeedRatio"]) - return cls.manual(**conf) + speed = int(temperature_conf["disabled"]["fanSpeedRatio"]) + return FanModeManual(speed=speed) return cls.default() @classmethod - def from_auradine(cls, web_fan: dict): + def from_auradine(cls, web_fan: dict[str, Any]) -> FanModeInstance: 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)) + return FanModeManual(speed=round((fan_1_target / fan_1_max) * 100)) except LookupError: pass return cls.default() @classmethod - def from_mara(cls, web_config: dict): + def from_mara(cls, web_config: dict[str, Any]) -> FanModeInstance: try: mode = web_config["general-config"]["environment-profile"] if mode == "AirCooling": if web_config["advance-config"]["override-fan-control"]: - return cls.manual( + return FanModeManual( speed=web_config["advance-config"]["fan-fixed-percent"] ) - return cls.normal() - return cls.immersion() + return FanModeNormal() + return FanModeImmersion() except LookupError: pass return cls.default() @classmethod - def from_espminer(cls, web_system_info: dict): + def from_espminer(cls, web_system_info: dict[str, Any]) -> FanModeInstance: if web_system_info["autofanspeed"] == 1: - return cls.normal() + return FanModeNormal() else: - return cls.manual(speed=web_system_info["fanspeed"]) + return FanModeManual(speed=web_system_info["fanspeed"]) @classmethod - def from_luxos(cls, rpc_fans: dict, rpc_tempctrl: dict): + def from_luxos( + cls, rpc_fans: dict[str, Any], rpc_tempctrl: dict[str, Any] + ) -> FanModeInstance: 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( + return FanModeImmersion() + return FanModeManual( speed=speed, minimum_fans=min_fans, ) - return cls.normal( + return FanModeNormal( minimum_fans=rpc_fans["FANCTRL"][0]["MinFans"], ) except LookupError: diff --git a/pyasic/config/mining/__init__.py b/pyasic/config/mining/__init__.py index c2899ed8e..fb7608a30 100644 --- a/pyasic/config/mining/__init__.py +++ b/pyasic/config/mining/__init__.py @@ -15,8 +15,12 @@ # ------------------------------------------------------------------------------ from __future__ import annotations -from dataclasses import field -from typing import Any, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar, Union + +if TYPE_CHECKING: + from typing import TypeAlias + +from pydantic import Field, field_serializer from pyasic import settings from pyasic.config.base import MinerConfigOption, MinerConfigValue @@ -40,98 +44,118 @@ ChipTuneAlgo, StandardTuneAlgo, TunerAlgo, - TunerAlgoType, VOptAlgo, ) from .presets import MiningPreset from .scaling import ScalingConfig +__all__ = [ + "BoardTuneAlgo", + "ChipTuneAlgo", + "StandardTuneAlgo", + "TunerAlgo", + "VOptAlgo", + "MiningPreset", + "ScalingConfig", + "MiningModeNormal", + "MiningModeSleep", + "MiningModeLPM", + "MiningModeHPM", + "MiningModePowerTune", + "MiningModeHashrateTune", + "MiningModePreset", + "ManualBoardSettings", + "MiningModeManual", + "MiningModeConfig", + "MiningMode", +] + class MiningModeNormal(MinerConfigValue): - mode: str = field(init=False, default="normal") + mode: str = Field(init=False, default="normal") @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModeNormal: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModeNormal: return cls() - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 0} - def as_wm(self) -> dict: + def as_wm(self) -> dict[str, Any]: return {"mode": self.mode} - def as_btminer_v3(self) -> dict: + def as_btminer_v3(self) -> dict[str, Any]: return {"set.miner.service": "start", "set.miner.power_mode": self.mode} - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"mode": {"mode": self.mode}} - def as_epic(self) -> dict: + def as_epic(self) -> dict[str, Any]: return {"ptune": {"enabled": False}} - def as_goldshell(self) -> dict: + def as_goldshell(self) -> dict[str, Any]: return {"settings": {"level": 0}} - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "mode": { "work-mode-selector": "Stock", } } - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"autotunerset": {"enabled": False}} - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: return {"autotuning": {"enabled": True}} class MiningModeSleep(MinerConfigValue): - mode: str = field(init=False, default="sleep") + mode: str = Field(init=False, default="sleep") @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModeSleep: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModeSleep: return cls() - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "1"} return {"miner-mode": 1} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "1"} return {"miner-mode": 1} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 1} - def as_wm(self) -> dict: + def as_wm(self) -> dict[str, Any]: return {"mode": self.mode} - def as_btminer_v3(self) -> dict: + def as_btminer_v3(self) -> dict[str, Any]: return {"set.miner.service": "stop"} - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"mode": {"sleep": "on"}} - def as_epic(self) -> dict: + def as_epic(self) -> dict[str, Any]: return {"ptune": {"algo": "Sleep", "target": 0}} - def as_goldshell(self) -> dict: + def as_goldshell(self) -> dict[str, Any]: return {"settings": {"level": 3}} - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "mode": { "work-mode-selector": "Sleep", @@ -140,65 +164,65 @@ def as_mara(self) -> dict: class MiningModeLPM(MinerConfigValue): - mode: str = field(init=False, default="low") + mode: str = Field(init=False, default="low") @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModeLPM: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModeLPM: return cls() - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "3"} return {"miner-mode": 3} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "3"} return {"miner-mode": 3} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 3} - def as_wm(self) -> dict: + def as_wm(self) -> dict[str, Any]: return {"mode": self.mode} - def as_btminer_v3(self) -> dict: + def as_btminer_v3(self) -> dict[str, Any]: return {"set.miner.service": "start", "set.miner.power_mode": self.mode} - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"mode": {"mode": "eco"}} - def as_goldshell(self) -> dict: + def as_goldshell(self) -> dict[str, Any]: return {"settings": {"level": 1}} class MiningModeHPM(MinerConfigValue): - mode: str = field(init=False, default="high") + mode: str = Field(init=False, default="high") @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModeHPM: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModeHPM: return cls() - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 0} - def as_wm(self) -> dict: + def as_wm(self) -> dict[str, Any]: return {"mode": self.mode} - def as_btminer_v3(self) -> dict: + def as_btminer_v3(self) -> dict[str, Any]: return {"set.miner.service": "start", "set.miner.power_mode": self.mode} - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"mode": {"mode": "turbo"}} @@ -206,15 +230,19 @@ class MiningModePowerTune(MinerConfigValue): class Config: arbitrary_types_allowed = True - mode: str = field(init=False, default="power_tuning") + mode: str = Field(init=False, default="power_tuning") power: int | None = None - algo: StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo = field( - default_factory=TunerAlgo.default - ) + algo: TunerAlgo | None = Field(default_factory=TunerAlgo.default) scaling: ScalingConfig | None = None + @field_serializer("algo") + def serialize_algo(self, value: TunerAlgo | None) -> dict[str, Any] | None: + if value is None: + return None + return value.value().as_dict() + @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModePowerTune: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModePowerTune: if dict_conf is None: return cls() cls_conf = {} @@ -227,28 +255,28 @@ def from_dict(cls, dict_conf: dict | None) -> MiningModePowerTune: return cls(**cls_conf) - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 0} - def as_wm(self) -> dict: + def as_wm(self) -> dict[str, Any]: if self.power is not None: return {"mode": self.mode, self.mode: {"wattage": self.power}} return {} - def as_btminer_v3(self) -> dict: + def as_btminer_v3(self) -> dict[str, Any]: return {"set.miner.service": "start", "set.miner.power_limit": self.power} - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: tuning_cfg = {"enabled": True, "mode": "power_target"} if self.power is not None: tuning_cfg["power_target"] = self.power @@ -267,7 +295,7 @@ def as_bosminer(self) -> dict: return cfg - def as_boser(self) -> dict: + def as_boser(self) -> dict[str, Any]: cfg: dict[str, Any] = { "set_performance_mode": SetPerformanceModeRequest( save_action=SaveAction(SaveAction.SAVE_AND_APPLY), @@ -302,10 +330,10 @@ def as_boser(self) -> dict: return cfg - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"mode": {"mode": "custom", "tune": "power", "power": self.power}} - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "mode": { "work-mode-selector": "Auto", @@ -316,7 +344,7 @@ def as_mara(self) -> dict: } } - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"autotunerset": {"enabled": True}} @@ -324,15 +352,19 @@ class MiningModeHashrateTune(MinerConfigValue): class Config: arbitrary_types_allowed = True - mode: str = field(init=False, default="hashrate_tuning") + mode: str = Field(init=False, default="hashrate_tuning") hashrate: int | None = None - algo: StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo = field( - default_factory=TunerAlgo.default - ) + algo: TunerAlgo | None = Field(default_factory=TunerAlgo.default) scaling: ScalingConfig | None = None + @field_serializer("algo") + def serialize_algo(self, value: TunerAlgo | None) -> dict[str, Any] | None: + if value is None: + return None + return value.value().as_dict() + @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModeHashrateTune: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModeHashrateTune: if dict_conf is None: return cls() cls_conf = {} @@ -345,26 +377,26 @@ def from_dict(cls, dict_conf: dict | None) -> MiningModeHashrateTune: return cls(**cls_conf) - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 0} - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: 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: + def as_boser(self) -> dict[str, Any]: cfg: dict[str, Any] = { "set_performance_mode": SetPerformanceModeRequest( save_action=SaveAction(SaveAction.SAVE_AND_APPLY), @@ -405,16 +437,16 @@ def as_boser(self) -> dict: return cfg - def as_auradine(self) -> dict: + def as_auradine(self) -> dict[str, Any]: return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}} - def as_epic(self) -> dict: + def as_epic(self) -> dict[str, Any]: mode = { "ptune": { "algo": ( - self.algo.as_epic() - if hasattr(self.algo, "as_epic") - else TunerAlgo.default().as_epic() + self.algo().as_epic() + if self.algo is not None and hasattr(self.algo(), "as_epic") + else TunerAlgo.default()().as_epic() ), "target": self.hashrate, } @@ -426,7 +458,7 @@ def as_epic(self) -> dict: mode["ptune"]["throttle_step"] = self.scaling.step return mode - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "mode": { "work-mode-selector": "Auto", @@ -437,25 +469,25 @@ def as_mara(self) -> dict: } } - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"autotunerset": {"enabled": True}} class MiningModePreset(MinerConfigValue): - mode: str = field(init=False, default="preset") + mode: str = Field(init=False, default="preset") active_preset: MiningPreset - available_presets: list[MiningPreset] = field(default_factory=list) + available_presets: list[MiningPreset] = Field(default_factory=list) - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: return {"overclock": {**self.active_preset.as_vnish()}} @classmethod def from_vnish( cls, - web_overclock_settings: dict, - web_presets: list[dict], - web_perf_summary: dict, + web_overclock_settings: dict[str, Any], + web_presets: list[dict[str, Any]], + web_perf_summary: dict[str, Any], ) -> MiningModePreset: active_preset = web_perf_summary.get("current_preset") @@ -470,7 +502,9 @@ def from_vnish( ) @classmethod - def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModePreset: + def from_luxos( + cls, rpc_config: dict[str, Any], rpc_profiles: dict[str, Any] + ) -> MiningModePreset: active_preset = cls.get_active_preset_from_luxos(rpc_config, rpc_profiles) return cls( active_preset=active_preset, @@ -481,7 +515,7 @@ def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModePreset: @classmethod def get_active_preset_from_luxos( - cls, rpc_config: dict, rpc_profiles: dict + cls, rpc_config: dict[str, Any], rpc_profiles: dict[str, Any] ) -> MiningPreset: active_preset = None active_profile = rpc_config["CONFIG"][0]["Profile"] @@ -496,54 +530,54 @@ class ManualBoardSettings(MinerConfigValue): volt: float @classmethod - def from_dict(cls, dict_conf: dict) -> ManualBoardSettings: + def from_dict(cls, dict_conf: dict[str, Any]) -> ManualBoardSettings: return cls(freq=dict_conf["freq"], volt=dict_conf["volt"]) - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_hiveon_modern(self) -> dict: + def as_hiveon_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 0} - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: return {"freq": self.freq} class MiningModeManual(MinerConfigValue): - mode: str = field(init=False, default="manual") + mode: str = Field(init=False, default="manual") global_freq: float global_volt: float - boards: dict[int, ManualBoardSettings] = field(default_factory=dict) + boards: dict[int, ManualBoardSettings] = Field(default_factory=dict) @classmethod - def from_dict(cls, dict_conf: dict) -> MiningModeManual: + def from_dict(cls, dict_conf: dict[str, Any]) -> 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) + int(k): ManualBoardSettings.from_dict(v) + for k, v in dict_conf.items() + if k.isdigit() }, ) - def as_am_modern(self) -> dict: + def as_am_modern(self) -> dict[str, Any]: if settings.get("antminer_mining_mode_as_str", False): return {"miner-mode": "0"} return {"miner-mode": 0} - def as_elphapex(self) -> dict: + def as_elphapex(self) -> dict[str, Any]: return {"miner-mode": 0} - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0] return { "overclock": { @@ -556,7 +590,7 @@ def as_vnish(self) -> dict: } @classmethod - def from_vnish(cls, web_overclock_settings: dict) -> MiningModeManual: + def from_vnish(cls, web_overclock_settings: dict[str, Any]) -> 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"] @@ -570,7 +604,7 @@ def from_vnish(cls, web_overclock_settings: dict) -> MiningModeManual: return cls(global_freq=freq, global_volt=voltage, boards=boards) @classmethod - def from_epic(cls, epic_conf: dict) -> MiningModeManual: + def from_epic(cls, epic_conf: dict[str, Any]) -> MiningModeManual: voltage = 0 freq = 0 if epic_conf.get("HwConfig") is not None: @@ -587,7 +621,7 @@ def from_epic(cls, epic_conf: dict) -> MiningModeManual: } return cls(global_freq=freq, global_volt=voltage, boards=boards) - def as_mara(self) -> dict: + def as_mara(self) -> dict[str, Any]: return { "mode": { "work-mode-selector": "Fixed", @@ -599,6 +633,19 @@ def as_mara(self) -> dict: } +MiningModeResult: TypeAlias = Union[ + "MiningModeNormal", + "MiningModeLPM", + "MiningModeHPM", + "MiningModeSleep", + "MiningModePowerTune", + "MiningModeHashrateTune", + "MiningModePreset", + "MiningModeManual", + "MiningModeConfig", +] + + class MiningModeConfig(MinerConfigOption): normal = MiningModeNormal low = MiningModeLPM @@ -611,10 +658,10 @@ class MiningModeConfig(MinerConfigOption): @classmethod def default(cls) -> MiningModeConfig: - return cls.normal() + return cls.normal() # type: ignore[no-any-return] @classmethod - def from_dict(cls, dict_conf: dict | None) -> MiningModeConfig: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> MiningModeConfig: if dict_conf is None: return cls.default() @@ -624,53 +671,53 @@ def from_dict(cls, dict_conf: dict | None) -> MiningModeConfig: cls_attr = getattr(cls, mode, None) if cls_attr is not None: - return cls_attr().from_dict(dict_conf) + return cls_attr().from_dict(dict_conf) # type: ignore[no-any-return] return cls.default() @classmethod - def from_am_modern(cls, web_conf: dict) -> MiningModeConfig: + def from_am_modern(cls, web_conf: dict[str, Any]) -> 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() + return cls.normal() # type: ignore[no-any-return] elif int(work_mode) == 1: - return cls.sleep() + return cls.sleep() # type: ignore[no-any-return] elif int(work_mode) == 3: - return cls.low() + return cls.low() # type: ignore[no-any-return] return cls.default() @classmethod - def from_hiveon_modern(cls, web_conf: dict) -> MiningModeConfig: + def from_hiveon_modern(cls, web_conf: dict[str, Any]) -> 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() + return cls.normal() # type: ignore[no-any-return] elif int(work_mode) == 1: - return cls.sleep() + return cls.sleep() # type: ignore[no-any-return] elif int(work_mode) == 3: - return cls.low() + return cls.low() # type: ignore[no-any-return] return cls.default() @classmethod - def from_elphapex(cls, web_conf: dict) -> MiningModeConfig: + def from_elphapex(cls, web_conf: dict[str, Any]) -> 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() + return cls.normal() # type: ignore[no-any-return] elif int(work_mode) == 1: - return cls.sleep() + return cls.sleep() # type: ignore[no-any-return] elif int(work_mode) == 3: - return cls.low() + return cls.low() # type: ignore[no-any-return] return cls.default() @classmethod - def from_epic(cls, web_conf: dict) -> MiningModeConfig: + def from_epic(cls, web_conf: dict[str, Any]) -> MiningModeConfig: try: tuner_running = web_conf["PerpetualTune"]["Running"] if tuner_running: @@ -685,7 +732,7 @@ def from_epic(cls, web_conf: dict) -> MiningModeConfig: step=algo_info["VoltageOptimizer"].get("Throttle Step"), ) - return cls.hashrate_tuning( + return cls.hashrate_tuning( # type: ignore[no-any-return] hashrate=algo_info["VoltageOptimizer"].get("Target"), algo=TunerAlgo.voltage_optimizer(), scaling=scaling_cfg, @@ -698,23 +745,23 @@ def from_epic(cls, web_conf: dict) -> MiningModeConfig: step=algo_info["BoardTune"].get("Throttle Step"), ) - return cls.hashrate_tuning( + return cls.hashrate_tuning( # type: ignore[no-any-return] hashrate=algo_info["BoardTune"].get("Target"), algo=TunerAlgo.board_tune(), scaling=scaling_cfg, ) else: - return cls.hashrate_tuning( + return cls.hashrate_tuning( # type: ignore[no-any-return] hashrate=algo_info["ChipTune"].get("Target"), algo=TunerAlgo.chip_tune(), ) else: - return cls.manual.from_epic(web_conf) + return cls.manual().from_epic(web_conf) # type: ignore[no-any-return] except KeyError: return cls.default() @classmethod - def from_bosminer(cls, toml_conf: dict) -> MiningModeConfig: + def from_bosminer(cls, toml_conf: dict[str, Any]) -> MiningModeConfig: if toml_conf.get("autotuning") is None: return cls.default() autotuning_conf = toml_conf["autotuning"] @@ -726,7 +773,7 @@ def from_bosminer(cls, toml_conf: dict) -> MiningModeConfig: if autotuning_conf.get("psu_power_limit") is not None: # old autotuning conf - return cls.power_tuning( + return cls.power_tuning( # type: ignore[no-any-return] power=autotuning_conf["psu_power_limit"], scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"), ) @@ -735,27 +782,30 @@ def from_bosminer(cls, toml_conf: dict) -> MiningModeConfig: mode = autotuning_conf["mode"] if mode == "power_target": if autotuning_conf.get("power_target") is not None: - return cls.power_tuning( + return cls.power_tuning( # type: ignore[no-any-return] power=autotuning_conf["power_target"], scaling=ScalingConfig.from_bosminer(toml_conf, mode="power"), ) - return cls.power_tuning( + return cls.power_tuning( # type: ignore[no-any-return] 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( + return cls.hashrate_tuning( # type: ignore[no-any-return] hashrate=autotuning_conf["hashrate_target"], scaling=ScalingConfig.from_bosminer(toml_conf, mode="hashrate"), ) - return cls.hashrate_tuning( + return cls.hashrate_tuning( # type: ignore[no-any-return] 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 + cls, + web_settings: dict[str, Any], + web_presets: list[dict[str, Any]], + web_perf_summary: dict[str, Any], ) -> MiningModeConfig: try: mode_settings = web_settings["miner"]["overclock"] @@ -768,7 +818,7 @@ def from_vnish( return cls.preset.from_vnish(mode_settings, web_presets, web_perf_summary) @classmethod - def from_boser(cls, grpc_miner_conf: dict) -> MiningModeConfig: + def from_boser(cls, grpc_miner_conf: dict[str, Any]) -> MiningModeConfig: try: tuner_conf = grpc_miner_conf["tuner"] if not tuner_conf.get("enabled", False): @@ -779,34 +829,34 @@ def from_boser(cls, grpc_miner_conf: dict) -> MiningModeConfig: 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( + return cls.power_tuning( # type: ignore[no-any-return] power=tuner_conf["powerTarget"]["watt"], scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="power"), ) - return cls.power_tuning( + return cls.power_tuning( # type: ignore[no-any-return] 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( + return cls.hashrate_tuning( # type: ignore[no-any-return] hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]), scaling=ScalingConfig.from_boser( grpc_miner_conf, mode="hashrate" ), ) - return cls.hashrate_tuning( + return cls.hashrate_tuning( # type: ignore[no-any-return] scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"), ) if tuner_conf.get("powerTarget") is not None: - return cls.power_tuning( + return cls.power_tuning( # type: ignore[no-any-return] 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( + return cls.hashrate_tuning( # type: ignore[no-any-return] hashrate=int(tuner_conf["hashrateTarget"]["terahashPerSecond"]), scaling=ScalingConfig.from_boser(grpc_miner_conf, mode="hashrate"), ) @@ -814,82 +864,84 @@ def from_boser(cls, grpc_miner_conf: dict) -> MiningModeConfig: return cls.default() @classmethod - def from_auradine(cls, web_mode: dict) -> MiningModeConfig: + def from_auradine(cls, web_mode: dict[str, Any]) -> MiningModeConfig: try: mode_data = web_mode["Mode"][0] if mode_data.get("Sleep") == "on": - return cls.sleep() + return cls.sleep() # type: ignore[no-any-return] if mode_data.get("Mode") == "normal": - return cls.normal() + return cls.normal() # type: ignore[no-any-return] if mode_data.get("Mode") == "eco": - return cls.low() + return cls.low() # type: ignore[no-any-return] if mode_data.get("Mode") == "turbo": - return cls.high() + return cls.high() # type: ignore[no-any-return] if mode_data.get("Ths") is not None: - return cls.hashrate_tuning(hashrate=mode_data["Ths"]) + return cls.hashrate_tuning(hashrate=mode_data["Ths"]) # type: ignore[no-any-return] if mode_data.get("Power") is not None: - return cls.power_tuning(power=mode_data["Power"]) + return cls.power_tuning(power=mode_data["Power"]) # type: ignore[no-any-return] except LookupError: return cls.default() return cls.default() @classmethod def from_btminer_v3( - cls, rpc_device_info: dict, rpc_settings: dict + cls, rpc_device_info: dict[str, Any], rpc_settings: dict[str, Any] ) -> MiningModeConfig: try: is_mining = rpc_device_info["msg"]["miner"]["working"] == "true" if not is_mining: - return cls.sleep() + return cls.sleep() # type: ignore[no-any-return] power_limit = rpc_settings["msg"]["power-limit"] if not power_limit == 0: - return cls.power_tuning(power=power_limit) + return cls.power_tuning(power=power_limit) # type: ignore[no-any-return] power_mode = rpc_settings["msg"]["power-mode"] if power_mode == "normal": - return cls.normal() + return cls.normal() # type: ignore[no-any-return] if power_mode == "high": - return cls.high() + return cls.high() # type: ignore[no-any-return] if power_mode == "low": - return cls.low() + return cls.low() # type: ignore[no-any-return] except LookupError: return cls.default() return cls.default() @classmethod - def from_mara(cls, web_config: dict) -> MiningModeConfig: + def from_mara(cls, web_config: dict[str, Any]) -> MiningModeConfig: try: mode = web_config["mode"]["work-mode-selector"] if mode == "Fixed": fixed_conf = web_config["mode"]["fixed"] - return cls.manual( + return cls.manual( # type: ignore[no-any-return] global_freq=int(fixed_conf["frequency"]), global_volt=fixed_conf["voltage"], ) elif mode == "Stock": - return cls.normal() + return cls.normal() # type: ignore[no-any-return] elif mode == "Sleep": - return cls.sleep() + return cls.sleep() # type: ignore[no-any-return] 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"]) + return cls.hashrate_tuning(hashrate=auto_conf["hash-target"]) # type: ignore[no-any-return] elif auto_mode == "PowerTarget": - return cls.power_tuning(power=auto_conf["power-target"]) + return cls.power_tuning(power=auto_conf["power-target"]) # type: ignore[no-any-return] except LookupError: pass return cls.default() @classmethod - def from_luxos(cls, rpc_config: dict, rpc_profiles: dict) -> MiningModeConfig: + def from_luxos( + cls, rpc_config: dict[str, Any], rpc_profiles: dict[str, Any] + ) -> MiningModeConfig: preset_info = MiningModePreset.from_luxos(rpc_config, rpc_profiles) - return cls.preset( + return cls.preset( # type: ignore[no-any-return] active_preset=preset_info.active_preset, available_presets=preset_info.available_presets, ) - def as_btminer_v3(self) -> dict: + def as_btminer_v3(self) -> dict[str, Any]: """Delegate to the default instance for btminer v3 configuration.""" return self.default().as_btminer_v3() diff --git a/pyasic/config/mining/algo.py b/pyasic/config/mining/algo.py index 5e7d35c90..6653a7f15 100644 --- a/pyasic/config/mining/algo.py +++ b/pyasic/config/mining/algo.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import field -from typing import Any, TypeVar +from typing import Any from pyasic.config.base import MinerConfigOption, MinerConfigValue @@ -9,29 +9,33 @@ class StandardTuneAlgo(MinerConfigValue): mode: str = field(init=False, default="standard") - def as_epic(self) -> str: - return VOptAlgo().as_epic() + def as_epic(self, *args: Any, **kwargs: Any) -> dict[str, Any]: + return VOptAlgo().as_epic(*args, **kwargs) class VOptAlgo(MinerConfigValue): mode: str = field(init=False, default="voltage_optimizer") - def as_epic(self) -> str: - return "VoltageOptimizer" + def as_epic(self, *args: Any, **kwargs: Any) -> dict[str, Any]: + return {"algorithm": "VoltageOptimizer"} class BoardTuneAlgo(MinerConfigValue): mode: str = field(init=False, default="board_tune") - def as_epic(self) -> str: - return "BoardTune" + def as_epic(self, *args: Any, **kwargs: Any) -> dict[str, Any]: + return {"algorithm": "BoardTune"} class ChipTuneAlgo(MinerConfigValue): mode: str = field(init=False, default="chip_tune") - def as_epic(self) -> str: - return "ChipTune" + def as_epic(self, *args: Any, **kwargs: Any) -> dict[str, Any]: + return {"algorithm": "ChipTune"} + + +# Type alias for all possible tuner algorithm instances +TunerAlgoInstance = StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo class TunerAlgo(MinerConfigOption): @@ -41,26 +45,31 @@ class TunerAlgo(MinerConfigOption): chip_tune = ChipTuneAlgo @classmethod - def default(cls) -> StandardTuneAlgo: - return cls.standard() + def default(cls) -> TunerAlgo: + return cls.standard @classmethod - def from_dict( - cls, dict_conf: dict[Any, Any] | None - ) -> StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo: + def from_dict(cls, dict_conf: dict[str, Any] | TunerAlgo | None) -> TunerAlgo: if dict_conf is None: return cls.default() + + if isinstance(dict_conf, cls): + return dict_conf + + if not isinstance(dict_conf, dict): + 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) + for member in cls: + if member.name == mode: + return member return cls.default() - -TunerAlgoType = TypeVar( - "TunerAlgoType", - bound=StandardTuneAlgo | VOptAlgo | BoardTuneAlgo | ChipTuneAlgo, -) + def as_dict(self) -> dict[str, Any]: + algo_class = self.value + if isinstance(algo_class, type) and issubclass(algo_class, MinerConfigValue): + return algo_class().as_dict() + return {} diff --git a/pyasic/config/mining/presets.py b/pyasic/config/mining/presets.py index e59f42394..2b62207dd 100644 --- a/pyasic/config/mining/presets.py +++ b/pyasic/config/mining/presets.py @@ -1,3 +1,5 @@ +from typing import Any + from pyasic.config.base import MinerConfigValue @@ -10,13 +12,13 @@ class MiningPreset(MinerConfigValue): frequency: int | None = None voltage: float | None = None - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: if self.name is not None: return {"preset": self.name} return {} @classmethod - def from_vnish(cls, web_preset: dict): + def from_vnish(cls, web_preset: dict[str, Any]) -> "MiningPreset": name = web_preset["name"] hr_power_split = web_preset["pretty"].split("~") if len(hr_power_split) == 1: @@ -43,7 +45,7 @@ def from_vnish(cls, web_preset: dict): ) @classmethod - def from_luxos(cls, profile: dict): + def from_luxos(cls, profile: dict[str, Any]) -> "MiningPreset": return cls( name=profile["Profile Name"], power=profile["Watts"], diff --git a/pyasic/config/mining/scaling.py b/pyasic/config/mining/scaling.py index 2023bf65e..98b41f79b 100644 --- a/pyasic/config/mining/scaling.py +++ b/pyasic/config/mining/scaling.py @@ -15,6 +15,8 @@ # ------------------------------------------------------------------------------ from __future__ import annotations +from typing import Any + from pyasic.config.base import MinerConfigValue @@ -23,7 +25,7 @@ class ScalingShutdown(MinerConfigValue): duration: int | None = None @classmethod - def from_dict(cls, dict_conf: dict | None) -> ScalingShutdown: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> ScalingShutdown: if dict_conf is None: return cls() return cls( @@ -31,7 +33,9 @@ def from_dict(cls, dict_conf: dict | None) -> ScalingShutdown: ) @classmethod - def from_bosminer(cls, power_scaling_conf: dict): + def from_bosminer( + cls, power_scaling_conf: dict[str, Any] + ) -> ScalingShutdown | None: sd_enabled = power_scaling_conf.get("shutdown_enabled") if sd_enabled is not None: return cls( @@ -40,7 +44,7 @@ def from_bosminer(cls, power_scaling_conf: dict): return None @classmethod - def from_boser(cls, power_scaling_conf: dict): + def from_boser(cls, power_scaling_conf: dict[str, Any]) -> ScalingShutdown | None: sd_enabled = power_scaling_conf.get("shutdownEnabled") if sd_enabled is not None: try: @@ -52,7 +56,7 @@ def from_boser(cls, power_scaling_conf: dict): return cls(enabled=sd_enabled) return None - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: cfg: dict[str, bool | int] = {"shutdown_enabled": self.enabled} if self.duration is not None: @@ -60,7 +64,7 @@ def as_bosminer(self) -> dict: return cfg - def as_boser(self) -> dict: + def as_boser(self) -> dict[str, Any]: return {"enable_shutdown": self.enabled, "shutdown_duration": self.duration} @@ -70,7 +74,7 @@ class ScalingConfig(MinerConfigValue): shutdown: ScalingShutdown | None = None @classmethod - def from_dict(cls, dict_conf: dict | None) -> ScalingConfig: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> ScalingConfig: if dict_conf is None: return cls() cls_conf = { @@ -83,15 +87,18 @@ def from_dict(cls, dict_conf: dict | None) -> ScalingConfig: return cls(**cls_conf) @classmethod - def from_bosminer(cls, toml_conf: dict, mode: str | None = None): + def from_bosminer( + cls, toml_conf: dict[str, Any], mode: str | None = None + ) -> ScalingConfig | None: if mode == "power": return cls._from_bosminer_power(toml_conf) if mode == "hashrate": # not implemented yet pass + return None @classmethod - def _from_bosminer_power(cls, toml_conf: dict): + def _from_bosminer_power(cls, toml_conf: dict[str, Any]) -> ScalingConfig | None: power_scaling = toml_conf.get("power_scaling") if power_scaling is None: power_scaling = toml_conf.get("performance_scaling") @@ -106,17 +113,21 @@ def _from_bosminer_power(cls, toml_conf: dict): sd_mode = ScalingShutdown.from_bosminer(power_scaling) return cls(step=power_step, minimum=min_power, shutdown=sd_mode) + return None @classmethod - def from_boser(cls, grpc_miner_conf: dict, mode: str | None = None): + def from_boser( + cls, grpc_miner_conf: dict[str, Any], mode: str | None = None + ) -> ScalingConfig | None: if mode == "power": return cls._from_boser_power(grpc_miner_conf) if mode == "hashrate": # not implemented yet pass + return None @classmethod - def _from_boser_power(cls, grpc_miner_conf: dict): + def _from_boser_power(cls, grpc_miner_conf: dict[str, Any]) -> ScalingConfig | None: try: dps_conf = grpc_miner_conf["dps"] if not dps_conf.get("enabled", False): @@ -124,10 +135,12 @@ def _from_boser_power(cls, grpc_miner_conf: dict): except LookupError: return None - conf = {"shutdown": ScalingShutdown.from_boser(dps_conf)} + step: int | None = None + minimum: int | None = None + shutdown = ScalingShutdown.from_boser(dps_conf) if dps_conf.get("minPowerTarget") is not None: - conf["minimum"] = dps_conf["minPowerTarget"]["watt"] + minimum = dps_conf["minPowerTarget"]["watt"] if dps_conf.get("powerStep") is not None: - conf["step"] = dps_conf["powerStep"]["watt"] - return cls(**conf) + step = dps_conf["powerStep"]["watt"] + return cls(step=step, minimum=minimum, shutdown=shutdown) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index e4388256f..05db9675a 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -36,35 +36,35 @@ class Pool(MinerConfigValue): user: str password: str - def as_am_modern(self, user_suffix: str | None = None) -> dict: + def as_am_modern(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", "pass": self.password, } - def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", "pass": self.password, } - def as_elphapex(self, user_suffix: str | None = None) -> dict: + def as_elphapex(self, user_suffix: str | None = None) -> dict[str, Any]: 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: + def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict[str, Any]: return { f"pool_{idx}": self.url, f"worker_{idx}": f"{self.user}{user_suffix or ''}", f"passwd_{idx}": self.password, } - def as_btminer_v3(self, user_suffix: str | None = None) -> dict: + def as_btminer_v3(self, user_suffix: str | None = None) -> dict[str, Any]: return { "pool": self.url, "worker": f"{self.user}{user_suffix or ''}", @@ -73,7 +73,7 @@ def as_btminer_v3(self, user_suffix: str | None = None) -> dict: def as_am_old( self, *args: Any, user_suffix: str | None = None, **kwargs: Any - ) -> dict: + ) -> dict[str, Any]: idx = args[0] if args else kwargs.get("idx", 1) return { f"_ant_pool{idx}url": self.url, @@ -81,19 +81,19 @@ def as_am_old( f"_ant_pool{idx}pw": self.password, } - def as_goldshell(self, user_suffix: str | None = None) -> dict: + def as_goldshell(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", "pass": self.password, } - def as_avalon(self, user_suffix: str | None = None) -> str: + def as_avalon(self, user_suffix: str | None = None) -> str: # type: ignore[override] return ",".join([self.url, f"{self.user}{user_suffix or ''}", self.password]) def as_inno( self, *args: Any, user_suffix: str | None = None, **kwargs: Any - ) -> dict: + ) -> dict[str, Any]: idx = args[0] if args else kwargs.get("idx", 1) return { f"Pool{idx}": self.url, @@ -101,42 +101,42 @@ def as_inno( f"Password{idx}": self.password, } - def as_bosminer(self, user_suffix: str | None = None) -> dict: + def as_bosminer(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", "password": self.password, } - def as_auradine(self, user_suffix: str | None = None) -> dict: + def as_auradine(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", "pass": self.password, } - def as_epic(self, user_suffix: str | None = None) -> dict: + def as_epic(self, user_suffix: str | None = None) -> dict[str, Any]: return { "pool": self.url, "login": f"{self.user}{user_suffix or ''}", "password": self.password, } - def as_mara(self, user_suffix: str | None = None) -> dict: + def as_mara(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", "pass": self.password, } - def as_espminer(self, user_suffix: str | None = None) -> dict: + def as_espminer(self, user_suffix: str | None = None) -> dict[str, Any]: return { "stratumURL": self.url, "stratumUser": f"{self.user}{user_suffix or ''}", "stratumPassword": self.password, } - def as_boser(self, user_suffix: str | None = None) -> PoolConfiguration: + def as_boser(self, user_suffix: str | None = None) -> PoolConfiguration: # type: ignore[override] return PoolConfiguration( url=self.url, user=f"{self.user}{user_suffix or ''}", @@ -144,7 +144,7 @@ def as_boser(self, user_suffix: str | None = None) -> PoolConfiguration: enabled=True, ) - def as_vnish(self, user_suffix: str | None = None) -> dict: + def as_vnish(self, user_suffix: str | None = None) -> dict[str, Any]: return { "url": self.url, "user": f"{self.user}{user_suffix or ''}", @@ -152,7 +152,7 @@ def as_vnish(self, user_suffix: str | None = None) -> dict: } @classmethod - def from_dict(cls, dict_conf: dict | None) -> Pool: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> Pool: if dict_conf is None: raise ValueError("dict_conf cannot be None") return cls( @@ -160,52 +160,52 @@ def from_dict(cls, dict_conf: dict | None) -> Pool: ) @classmethod - def from_api(cls, api_pool: dict) -> Pool: + def from_api(cls, api_pool: dict[str, Any]) -> Pool: return cls(url=api_pool["URL"], user=api_pool["User"], password="x") @classmethod - def from_btminer_v3(cls, api_pool: dict) -> Pool: + def from_btminer_v3(cls, api_pool: dict[str, Any]) -> Pool: return cls(url=api_pool["url"], user=api_pool["account"], password="x") @classmethod - def from_epic(cls, api_pool: dict) -> Pool: + def from_epic(cls, api_pool: dict[str, Any]) -> Pool: return cls( url=api_pool["pool"], user=api_pool["login"], password=api_pool["password"] ) @classmethod - def from_am_modern(cls, web_pool: dict) -> Pool: + def from_am_modern(cls, web_pool: dict[str, Any]) -> 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: + def from_hiveon_modern(cls, web_pool: dict[str, Any]) -> Pool: return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) @classmethod - def from_elphapex(cls, web_pool: dict) -> Pool: + def from_elphapex(cls, web_pool: dict[str, Any]) -> Pool: return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) # TODO: check if this is accurate, user/username, pass/password @classmethod - def from_goldshell(cls, web_pool: dict) -> Pool: + def from_goldshell(cls, web_pool: dict[str, Any]) -> Pool: return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) @classmethod - def from_inno(cls, web_pool: dict) -> Pool: + def from_inno(cls, web_pool: dict[str, Any]) -> Pool: return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) @classmethod - def from_bosminer(cls, toml_pool_conf: dict) -> Pool: + def from_bosminer(cls, toml_pool_conf: dict[str, Any]) -> Pool: return cls( url=toml_pool_conf["url"], user=toml_pool_conf["user"], @@ -213,7 +213,7 @@ def from_bosminer(cls, toml_pool_conf: dict) -> Pool: ) @classmethod - def from_vnish(cls, web_pool: dict) -> Pool: + def from_vnish(cls, web_pool: dict[str, Any]) -> Pool: return cls( url="stratum+tcp://" + web_pool["url"], user=web_pool["user"], @@ -221,7 +221,7 @@ def from_vnish(cls, web_pool: dict) -> Pool: ) @classmethod - def from_boser(cls, grpc_pool: dict) -> Pool: + def from_boser(cls, grpc_pool: dict[str, Any]) -> Pool: return cls( url=grpc_pool["url"], user=grpc_pool["user"], @@ -229,7 +229,7 @@ def from_boser(cls, grpc_pool: dict) -> Pool: ) @classmethod - def from_mara(cls, web_pool: dict) -> Pool: + def from_mara(cls, web_pool: dict[str, Any]) -> Pool: return cls( url=web_pool["url"], user=web_pool["user"], @@ -237,7 +237,7 @@ def from_mara(cls, web_pool: dict) -> Pool: ) @classmethod - def from_espminer(cls, web_system_info: dict) -> Pool: + def from_espminer(cls, web_system_info: dict[str, Any]) -> Pool: url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}" return cls( url=url, @@ -246,11 +246,11 @@ def from_espminer(cls, web_system_info: dict) -> Pool: ) @classmethod - def from_luxos(cls, rpc_pools: dict) -> Pool: + def from_luxos(cls, rpc_pools: dict[str, Any]) -> Pool: return cls.from_api(rpc_pools) @classmethod - def from_iceriver(cls, web_pool: dict) -> Pool: + def from_iceriver(cls, web_pool: dict[str, Any]) -> Pool: return cls( url=web_pool["addr"], user=web_pool["user"], @@ -263,13 +263,13 @@ class PoolGroup(MinerConfigValue): quota: int = 1 name: str | None = None - def __post_init__(self): + def __post_init__(self) -> None: if self.name is None: self.name = "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(6) ) # generate random pool group name in case it isn't set - def as_am_modern(self, user_suffix: str | None = None) -> list: + def as_am_modern(self, user_suffix: str | None = None) -> dict[str, Any]: pools = [] idx = 0 while idx < 3: @@ -278,9 +278,9 @@ def as_am_modern(self, user_suffix: str | None = None) -> list: else: pools.append(Pool(url="", user="", password="").as_am_modern()) idx += 1 - return pools + return {"pools": pools} - def as_hiveon_modern(self, user_suffix: str | None = None) -> list: + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict[str, Any]: pools = [] idx = 0 while idx < 3: @@ -289,9 +289,9 @@ def as_hiveon_modern(self, user_suffix: str | None = None) -> list: else: pools.append(Pool(url="", user="", password="").as_hiveon_modern()) idx += 1 - return pools + return {"pools": pools} - def as_elphapex(self, user_suffix: str | None = None) -> list: + def as_elphapex(self, user_suffix: str | None = None) -> dict[str, Any]: pools = [] idx = 0 while idx < 3: @@ -300,9 +300,11 @@ def as_elphapex(self, user_suffix: str | None = None) -> list: else: pools.append(Pool(url="", user="", password="").as_elphapex()) idx += 1 - return pools + return {"pools": pools} - def as_wm(self, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> dict: + def as_wm( + self, *args: Any, user_suffix: str | None = None, **kwargs: Any + ) -> dict[str, Any]: pools: dict[str, str] = {} idx = 0 while idx < 3: @@ -313,12 +315,12 @@ def as_wm(self, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> di 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_btminer_v3(self, user_suffix: str | None = None) -> dict[str, Any]: + return {"pools": [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: + ) -> dict[str, Any]: pools: dict[str, str] = {} idx = 0 while idx < 3: @@ -331,17 +333,17 @@ def as_am_old( idx += 1 return pools - def as_goldshell(self, user_suffix: str | None = None) -> list: + def as_goldshell(self, user_suffix: str | None = None) -> list[dict[str, Any]]: # type: ignore[override] return [pool.as_goldshell(user_suffix) for pool in self.pools] - def as_avalon(self, user_suffix: str | None = None) -> str: + def as_avalon(self, user_suffix: str | None = None) -> str: # type: ignore[override] if len(self.pools) > 0: return self.pools[0].as_avalon(user_suffix=user_suffix) return Pool(url="", user="", password="").as_avalon() def as_inno( self, *args: Any, user_suffix: str | None = None, **kwargs: Any - ) -> dict: + ) -> dict[str, Any]: pools: dict[str, str] = {} idx = 0 while idx < 3: @@ -354,7 +356,7 @@ def as_inno( idx += 1 return pools - def as_bosminer(self, user_suffix: str | None = None) -> dict: + def as_bosminer(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.pools) > 0: conf: dict[str, Any] = { "name": self.name, @@ -367,30 +369,30 @@ def as_bosminer(self, user_suffix: str | None = None) -> dict: return conf 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_auradine(self, user_suffix: str | None = None) -> dict[str, Any]: + return {"pools": [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_epic(self, user_suffix: str | None = None) -> dict[str, Any]: + return {"pools": [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_mara(self, user_suffix: str | None = None) -> dict[str, Any]: + return {"pools": [p.as_mara(user_suffix=user_suffix) for p in self.pools]} - def as_espminer(self, user_suffix: str | None = None) -> dict: + def as_espminer(self, user_suffix: str | None = None) -> dict[str, Any]: return self.pools[0].as_espminer(user_suffix=user_suffix) - def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration: + def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration: # type: ignore[override] 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: + def as_vnish(self, user_suffix: str | None = None) -> dict[str, Any]: return {"pools": [p.as_vnish(user_suffix=user_suffix) for p in self.pools]} @classmethod - def from_dict(cls, dict_conf: dict | None) -> PoolGroup: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> PoolGroup: if dict_conf is None: return cls() @@ -404,57 +406,57 @@ def from_dict(cls, dict_conf: dict | None) -> PoolGroup: return cls(**cls_conf) @classmethod - def from_api(cls, api_pool_list: list) -> PoolGroup: + def from_api(cls, api_pool_list: list[Any]) -> PoolGroup: pools = [] for pool in api_pool_list: pools.append(Pool.from_api(pool)) return cls(pools=pools) @classmethod - def from_btminer_v3(cls, api_pool_list: list) -> PoolGroup: + def from_btminer_v3(cls, api_pool_list: list[Any]) -> 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: + def from_epic(cls, api_pool_list: list[Any]) -> PoolGroup: pools = [] for pool in api_pool_list: pools.append(Pool.from_epic(pool)) return cls(pools=pools) @classmethod - def from_am_modern(cls, web_pool_list: list) -> PoolGroup: + def from_am_modern(cls, web_pool_list: list[Any]) -> PoolGroup: pools = [] for pool in web_pool_list: pools.append(Pool.from_am_modern(pool)) return cls(pools=pools) @classmethod - def from_hiveon_modern(cls, web_pool_list: list) -> PoolGroup: + def from_hiveon_modern(cls, web_pool_list: list[Any]) -> PoolGroup: pools = [] for pool in web_pool_list: pools.append(Pool.from_hiveon_modern(pool)) return cls(pools=pools) @classmethod - def from_elphapex(cls, web_pool_list: list) -> PoolGroup: + def from_elphapex(cls, web_pool_list: list[Any]) -> PoolGroup: pools = [] for pool in web_pool_list: pools.append(Pool.from_elphapex(pool)) return cls(pools=pools) @classmethod - def from_goldshell(cls, web_pools: list) -> PoolGroup: + def from_goldshell(cls, web_pools: list[Any]) -> PoolGroup: return cls(pools=[Pool.from_goldshell(p) for p in web_pools]) @classmethod - def from_inno(cls, web_pools: list) -> PoolGroup: + def from_inno(cls, web_pools: list[Any]) -> PoolGroup: return cls(pools=[Pool.from_inno(p) for p in web_pools]) @classmethod - def from_bosminer(cls, toml_group_conf: dict) -> PoolGroup: + def from_bosminer(cls, toml_group_conf: dict[str, Any]) -> PoolGroup: if toml_group_conf.get("pool") is not None: return cls( name=toml_group_conf["name"], @@ -464,13 +466,13 @@ def from_bosminer(cls, toml_group_conf: dict) -> PoolGroup: return cls() @classmethod - def from_vnish(cls, web_settings_pools: dict) -> PoolGroup: + def from_vnish(cls, web_settings_pools: list[dict[str, Any]]) -> PoolGroup: return cls( pools=[Pool.from_vnish(p) for p in web_settings_pools if p["url"] != ""] ) @classmethod - def from_boser(cls, grpc_pool_group: dict) -> PoolGroup: + def from_boser(cls, grpc_pool_group: dict[str, Any]) -> PoolGroup: try: return cls( pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]], @@ -485,15 +487,15 @@ def from_boser(cls, grpc_pool_group: dict) -> PoolGroup: return cls() @classmethod - def from_mara(cls, web_config_pools: dict) -> PoolGroup: + def from_mara(cls, web_config_pools: list[dict[str, Any]]) -> PoolGroup: return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools]) @classmethod - def from_espminer(cls, web_system_info: dict) -> PoolGroup: + def from_espminer(cls, web_system_info: dict[str, Any]) -> PoolGroup: return cls(pools=[Pool.from_espminer(web_system_info)]) @classmethod - def from_iceriver(cls, web_userpanel: dict) -> PoolGroup: + def from_iceriver(cls, web_userpanel: dict[str, Any]) -> PoolGroup: return cls( pools=[ Pool.from_iceriver(web_pool) @@ -510,7 +512,7 @@ def default(cls) -> PoolConfig: return cls() @classmethod - def from_dict(cls, dict_conf: dict | None) -> PoolConfig: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> PoolConfig: if dict_conf is None: return cls.default() @@ -525,63 +527,65 @@ def simple(cls, pools: list[Pool | dict[str, str]]) -> PoolConfig: group_pools.append(pool) return cls(groups=[PoolGroup(pools=group_pools)]) - def as_am_modern(self, user_suffix: str | None = None) -> dict: + def as_am_modern(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: - return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)} - return {"pools": PoolGroup().as_am_modern()} + return self.groups[0].as_am_modern(user_suffix=user_suffix) + return PoolGroup().as_am_modern() - def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: - return {"pools": self.groups[0].as_hiveon_modern(user_suffix=user_suffix)} - return {"pools": PoolGroup().as_hiveon_modern()} + return self.groups[0].as_hiveon_modern(user_suffix=user_suffix) + return PoolGroup().as_hiveon_modern() - def as_elphapex(self, user_suffix: str | None = None) -> dict: + def as_elphapex(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: - return {"pools": self.groups[0].as_elphapex(user_suffix=user_suffix)} - return {"pools": PoolGroup().as_elphapex()} + return self.groups[0].as_elphapex(user_suffix=user_suffix) + return PoolGroup().as_elphapex() - def as_wm(self, *args: Any, user_suffix: str | None = None, **kwargs: Any) -> dict: + def as_wm( + self, *args: Any, user_suffix: str | None = None, **kwargs: Any + ) -> dict[str, Any]: if len(self.groups) > 0: return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)} return {"pools": PoolGroup().as_wm()} - def as_btminer_v3(self, user_suffix: str | None = None) -> dict: + def as_btminer_v3(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: - return {"pools": self.groups[0].as_btminer_v3(user_suffix=user_suffix)} - return {"pools": PoolGroup().as_btminer_v3()} + return self.groups[0].as_btminer_v3(user_suffix=user_suffix) + return PoolGroup().as_btminer_v3() def as_am_old( self, *args: Any, user_suffix: str | None = None, **kwargs: Any - ) -> dict: + ) -> dict[str, Any]: if len(self.groups) > 0: return self.groups[0].as_am_old(user_suffix=user_suffix) return PoolGroup().as_am_old() - def as_goldshell(self, user_suffix: str | None = None) -> dict: + def as_goldshell(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: return {"pools": self.groups[0].as_goldshell(user_suffix=user_suffix)} return {"pools": PoolGroup().as_goldshell()} - def as_avalon(self, user_suffix: str | None = None) -> dict: + def as_avalon(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)} return {"pools": PoolGroup().as_avalon()} def as_inno( self, *args: Any, user_suffix: str | None = None, **kwargs: Any - ) -> dict: + ) -> dict[str, Any]: if len(self.groups) > 0: return self.groups[0].as_inno(user_suffix=user_suffix) return PoolGroup().as_inno() - def as_bosminer(self, user_suffix: str | None = None) -> dict: + def as_bosminer(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: return { "group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups] } return {"group": [PoolGroup().as_bosminer()]} - def as_boser(self, user_suffix: str | None = None) -> dict: + def as_boser(self, user_suffix: str | None = None) -> dict[str, Any]: return { "set_pool_groups": SetPoolGroupsRequest( save_action=SaveAction(SaveAction.SAVE_AND_APPLY), @@ -589,48 +593,46 @@ def as_boser(self, user_suffix: str | None = None) -> dict: ) } - def as_auradine(self, user_suffix: str | None = None) -> dict: + def as_auradine(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: - return { - "updatepools": { - "pools": self.groups[0].as_auradine(user_suffix=user_suffix) - } - } - return {"updatepools": {"pools": PoolGroup().as_auradine()}} + return {"updatepools": self.groups[0].as_auradine(user_suffix=user_suffix)} + return {"updatepools": PoolGroup().as_auradine()} - def as_epic(self, user_suffix: str | None = None) -> dict: + def as_epic(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: + group_result = self.groups[0].as_epic(user_suffix=user_suffix) return { "pools": { "coin": "Btc", - "stratum_configs": self.groups[0].as_epic(user_suffix=user_suffix), + "stratum_configs": group_result["pools"], "unique_id": False, } } + empty_result = PoolGroup().as_epic() return { "pools": { "coin": "Btc", - "stratum_configs": [PoolGroup().as_epic()], + "stratum_configs": empty_result["pools"], "unique_id": False, } } - def as_mara(self, user_suffix: str | None = None) -> dict: + def as_mara(self, user_suffix: str | None = None) -> dict[str, Any]: if len(self.groups) > 0: - return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)} + return self.groups[0].as_mara(user_suffix=user_suffix) return {"pools": []} - def as_espminer(self, user_suffix: str | None = None) -> dict: + def as_espminer(self, user_suffix: str | None = None) -> dict[str, Any]: return self.groups[0].as_espminer(user_suffix=user_suffix) - def as_luxos(self, user_suffix: str | None = None) -> dict: + def as_luxos(self, user_suffix: str | None = None) -> dict[str, Any]: return {} - def as_vnish(self, user_suffix: str | None = None) -> dict: + def as_vnish(self, user_suffix: str | None = None) -> dict[str, Any]: return self.groups[0].as_vnish(user_suffix=user_suffix) @classmethod - def from_api(cls, api_pools: dict) -> PoolConfig: + def from_api(cls, api_pools: dict[str, Any]) -> PoolConfig: try: pool_data = api_pools["POOLS"] except KeyError: @@ -640,7 +642,7 @@ def from_api(cls, api_pools: dict) -> PoolConfig: return cls(groups=[PoolGroup.from_api(pool_data)]) @classmethod - def from_btminer_v3(cls, rpc_pools: dict) -> PoolConfig: + def from_btminer_v3(cls, rpc_pools: dict[str, Any]) -> PoolConfig: try: pool_data = rpc_pools["pools"] except KeyError: @@ -650,12 +652,12 @@ def from_btminer_v3(cls, rpc_pools: dict) -> PoolConfig: return cls(groups=[PoolGroup.from_btminer_v3(pool_data)]) @classmethod - def from_epic(cls, web_conf: dict) -> PoolConfig: + def from_epic(cls, web_conf: dict[str, Any]) -> PoolConfig: pool_data = web_conf["StratumConfigs"] return cls(groups=[PoolGroup.from_epic(pool_data)]) @classmethod - def from_am_modern(cls, web_conf: dict) -> PoolConfig: + def from_am_modern(cls, web_conf: dict[str, Any]) -> PoolConfig: try: pool_data = web_conf["pools"] except KeyError: @@ -664,7 +666,7 @@ def from_am_modern(cls, web_conf: dict) -> PoolConfig: return cls(groups=[PoolGroup.from_am_modern(pool_data)]) @classmethod - def from_hiveon_modern(cls, web_conf: dict) -> PoolConfig: + def from_hiveon_modern(cls, web_conf: dict[str, Any]) -> PoolConfig: try: pool_data = web_conf["pools"] except KeyError: @@ -673,17 +675,17 @@ def from_hiveon_modern(cls, web_conf: dict) -> PoolConfig: return cls(groups=[PoolGroup.from_hiveon_modern(pool_data)]) @classmethod - def from_elphapex(cls, web_conf: dict) -> PoolConfig: + def from_elphapex(cls, web_conf: dict[str, Any]) -> PoolConfig: pool_data = web_conf["pools"] return cls(groups=[PoolGroup.from_elphapex(pool_data)]) @classmethod - def from_goldshell(cls, web_pools: list) -> PoolConfig: + def from_goldshell(cls, web_pools: list[Any]) -> PoolConfig: return cls(groups=[PoolGroup.from_goldshell(web_pools)]) @classmethod - def from_goldshell_byte(cls, web_pools: list) -> PoolConfig: + def from_goldshell_byte(cls, web_pools: list[Any]) -> PoolConfig: return cls( groups=[ PoolGroup.from_goldshell(g["pools"]) @@ -693,25 +695,25 @@ def from_goldshell_byte(cls, web_pools: list) -> PoolConfig: ) @classmethod - def from_inno(cls, web_pools: list) -> PoolConfig: + def from_inno(cls, web_pools: list[Any]) -> PoolConfig: return cls(groups=[PoolGroup.from_inno(web_pools)]) @classmethod - def from_bosminer(cls, toml_conf: dict) -> PoolConfig: + def from_bosminer(cls, toml_conf: dict[str, Any]) -> PoolConfig: if toml_conf.get("group") is None: return cls() return cls(groups=[PoolGroup.from_bosminer(g) for g in toml_conf["group"]]) @classmethod - def from_vnish(cls, web_settings: dict) -> PoolConfig: + def from_vnish(cls, web_settings: dict[str, Any]) -> PoolConfig: try: return cls(groups=[PoolGroup.from_vnish(web_settings["miner"]["pools"])]) except LookupError: return cls() @classmethod - def from_boser(cls, grpc_miner_conf: dict) -> PoolConfig: + def from_boser(cls, grpc_miner_conf: dict[str, Any]) -> PoolConfig: try: return cls( groups=[ @@ -723,19 +725,21 @@ def from_boser(cls, grpc_miner_conf: dict) -> PoolConfig: return cls() @classmethod - def from_mara(cls, web_config: dict) -> PoolConfig: + def from_mara(cls, web_config: dict[str, Any]) -> PoolConfig: return cls(groups=[PoolGroup.from_mara(web_config["pools"])]) @classmethod - def from_espminer(cls, web_system_info: dict) -> PoolConfig: + def from_espminer(cls, web_system_info: dict[str, Any]) -> PoolConfig: return cls(groups=[PoolGroup.from_espminer(web_system_info)]) @classmethod - def from_iceriver(cls, web_userpanel: dict) -> PoolConfig: + def from_iceriver(cls, web_userpanel: dict[str, Any]) -> PoolConfig: return cls(groups=[PoolGroup.from_iceriver(web_userpanel)]) @classmethod - def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> PoolConfig: + def from_luxos( + cls, rpc_groups: dict[str, Any], rpc_pools: dict[str, Any] + ) -> PoolConfig: return cls( groups=[ PoolGroup( diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py index e16cc00c4..b0bccbf84 100644 --- a/pyasic/config/temperature.py +++ b/pyasic/config/temperature.py @@ -15,6 +15,8 @@ # ------------------------------------------------------------------------------ from __future__ import annotations +from typing import Any + from pyasic.config.base import MinerConfigValue @@ -24,10 +26,10 @@ class TemperatureConfig(MinerConfigValue): danger: int | None = None @classmethod - def default(cls): + def default(cls) -> TemperatureConfig: return cls() - def as_bosminer(self) -> dict: + def as_bosminer(self) -> dict[str, Any]: temp_cfg = {} if self.target is not None: temp_cfg["target_temp"] = self.target @@ -39,8 +41,8 @@ def as_bosminer(self) -> dict: return {} return {"temp_control": temp_cfg} - def as_epic(self) -> dict: - temps_config: dict = {"temps": {}, "fans": {"Auto": {}}} + def as_epic(self) -> dict[str, Any]: + temps_config: dict[str, Any] = {"temps": {}, "fans": {"Auto": {}}} if self.target is not None: temps_config["fans"]["Auto"]["Target Temperature"] = self.target else: @@ -51,14 +53,14 @@ def as_epic(self) -> dict: temps_config["temps"]["shutdown"] = self.hot return temps_config - def as_luxos(self) -> dict: + def as_luxos(self) -> dict[str, Any]: return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]} - def as_vnish(self) -> dict: + def as_vnish(self) -> dict[str, Any]: return {"misc": {"restart_temp": self.danger}} @classmethod - def from_dict(cls, dict_conf: dict | None) -> TemperatureConfig: + def from_dict(cls, dict_conf: dict[str, Any] | None) -> TemperatureConfig: if dict_conf is None: return cls() return cls( @@ -68,7 +70,7 @@ def from_dict(cls, dict_conf: dict | None) -> TemperatureConfig: ) @classmethod - def from_bosminer(cls, toml_conf: dict) -> TemperatureConfig: + def from_bosminer(cls, toml_conf: dict[str, Any]) -> TemperatureConfig: temp_control = toml_conf.get("temp_control") if temp_control is not None: return cls( @@ -79,7 +81,7 @@ def from_bosminer(cls, toml_conf: dict) -> TemperatureConfig: return cls() @classmethod - def from_epic(cls, web_conf: dict) -> TemperatureConfig: + def from_epic(cls, web_conf: dict[str, Any]) -> TemperatureConfig: try: dangerous_temp = web_conf["Misc"]["Critical Temp"] except KeyError: @@ -97,7 +99,7 @@ def from_epic(cls, web_conf: dict) -> TemperatureConfig: return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp) @classmethod - def from_vnish(cls, web_settings: dict) -> TemperatureConfig: + def from_vnish(cls, web_settings: dict[str, Any]) -> TemperatureConfig: try: dangerous_temp = web_settings["misc"]["restart_temp"] except KeyError: @@ -113,7 +115,7 @@ def from_vnish(cls, web_settings: dict) -> TemperatureConfig: return cls() @classmethod - def from_boser(cls, grpc_miner_conf: dict) -> TemperatureConfig: + def from_boser(cls, grpc_miner_conf: dict[str, Any]) -> TemperatureConfig: try: temperature_conf = grpc_miner_conf["temperature"] except KeyError: @@ -144,7 +146,7 @@ def from_boser(cls, grpc_miner_conf: dict) -> TemperatureConfig: return cls.default() @classmethod - def from_luxos(cls, rpc_tempctrl: dict) -> TemperatureConfig: + def from_luxos(cls, rpc_tempctrl: dict[str, Any]) -> TemperatureConfig: try: tempctrl_config = rpc_tempctrl["TEMPCTRL"][0] return cls( diff --git a/pyasic/data/__init__.py b/pyasic/data/__init__.py index 37a07dbda..2f0427ede 100644 --- a/pyasic/data/__init__.py +++ b/pyasic/data/__init__.py @@ -24,15 +24,31 @@ from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePowerTune from pyasic.data.pools import PoolMetrics, Scheme -from pyasic.device.algorithm.hashrate import AlgoHashRateType -from pyasic.device.algorithm.hashrate.base import GenericHashrate +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType, GenericHashrate from .boards import HashBoard from .device import DeviceInfo -from .error_codes import BraiinsOSError, InnosiliconError, WhatsminerError, X19Error from .error_codes.base import BaseMinerError +from .error_codes.bos import BraiinsOSError +from .error_codes.innosilicon import InnosiliconError +from .error_codes.whatsminer import WhatsminerError +from .error_codes.X19 import X19Error from .fans import Fan +__all__ = [ + "MinerData", + "Fan", + "HashBoard", + "DeviceInfo", + "PoolMetrics", + "Scheme", + "BaseMinerError", + "BraiinsOSError", + "InnosiliconError", + "WhatsminerError", + "X19Error", +] + class MinerData(BaseModel): """A Dataclass to standardize data returned from miners (specifically `AnyMiner().get_data()`) @@ -135,12 +151,12 @@ class MinerData(BaseModel): pools: list[PoolMetrics] = Field(default_factory=list) @classmethod - def fields(cls) -> set: + def fields(cls) -> set[str]: 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): + def get(self, __key: str, default: Any = None) -> Any: try: val = self.__getitem__(__key) if val is None: @@ -149,22 +165,22 @@ def get(self, __key: str, default: Any = None): except KeyError: return default - def __getitem__(self, item: str): + def __getitem__(self, item: str) -> Any: try: return getattr(self, item) except AttributeError: raise KeyError(f"{item}") - def __setitem__(self, key, value): + def __setitem__(self, key: str, value: Any) -> None: return setattr(self, key, value) - def __iter__(self): + def __iter__(self) -> Any: return iter([item for item in self.asdict()]) - def __truediv__(self, other): + def __truediv__(self, other: int | float) -> "MinerData": return self // other - def __floordiv__(self, other): + def __floordiv__(self, other: int | float) -> "MinerData": cp = copy.deepcopy(self) for key in self.fields(): item = getattr(self, key) @@ -174,7 +190,7 @@ def __floordiv__(self, other): setattr(cp, key, item / other) return cp - def __add__(self, other): + def __add__(self, other: "MinerData") -> "MinerData": if not isinstance(other, MinerData): raise TypeError("Cannot add MinerData to non MinerData type.") cp = copy.deepcopy(self) @@ -221,7 +237,7 @@ def hashrate(self) -> AlgoHashRateType | None: return self.raw_hashrate @hashrate.setter - def hashrate(self, val): + def hashrate(self, val: AlgoHashRateType | None) -> None: self.raw_hashrate = val @computed_field # type: ignore[prop-decorator] @@ -233,7 +249,7 @@ def wattage_limit(self) -> int | None: return self.raw_wattage_limit @wattage_limit.setter - def wattage_limit(self, val: int): + def wattage_limit(self, val: int) -> None: self.raw_wattage_limit = val @computed_field # type: ignore[prop-decorator] @@ -271,7 +287,7 @@ def percent_expected_hashrate(self) -> int | None: if self.hashrate is None or self.expected_hashrate is None: return None try: - return round((self.hashrate / self.expected_hashrate) * 100) + return round((float(self.hashrate) / float(self.expected_hashrate)) * 100) except ZeroDivisionError: return 0 @@ -358,13 +374,13 @@ def algo(self) -> str | None: return str(self.device_info.algo) return "" - def keys(self) -> list: + def keys(self) -> list[str]: return list(self.model_fields.keys()) - def asdict(self) -> dict: + def asdict(self) -> dict[str, Any]: return self.model_dump() - def as_dict(self) -> dict: + def as_dict(self) -> dict[str, Any]: """Get this dataclass as a dictionary. Returns: @@ -424,8 +440,8 @@ def serialize_list(key: str, value: list[Any]) -> str | 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_serialization_func: Callable[[str, Any], str | None] = ( + serialization_map.get(type(list_field_val), lambda _k, _v: None) ) item_serialized = item_serialization_func( f"{key}{level_delimiter}{idx}", list_field_val @@ -436,7 +452,9 @@ def serialize_list(key: str, value: list[Any]) -> str | None: for dt in serialization_map_instance: if item_serialized is None: if isinstance(list_field_val, dt): - func = serialization_map_instance[dt] + func: Callable[[str, Any], str | None] = ( + serialization_map_instance[dt] + ) item_serialized = func( f"{key}{level_delimiter}{idx}", list_field_val ) @@ -444,7 +462,7 @@ def serialize_list(key: str, value: list[Any]) -> str | None: list_field_data.append(item_serialized) return ",".join(list_field_data) - def serialize_miner_error(key: str, value: BaseMinerError): + def serialize_miner_error(key: str, value: BaseMinerError) -> str: return value.as_influxdb(key, level_delimiter=level_delimiter) def serialize_fan(key: str, value: Fan) -> str: @@ -453,10 +471,10 @@ def serialize_fan(key: str, value: Fan) -> str: def serialize_hashboard(key: str, value: HashBoard) -> str: return value.as_influxdb(key, level_delimiter=level_delimiter) - def serialize_bool(key: str, value: bool): + def serialize_bool(key: str, value: bool) -> str: return f"{key}={str(value).lower()}" - def serialize_pool_metrics(key: str, value: PoolMetrics): + def serialize_pool_metrics(key: str, value: PoolMetrics) -> str: return value.as_influxdb(key, level_delimiter=level_delimiter) include = [ @@ -481,11 +499,13 @@ def serialize_pool_metrics(key: str, value: PoolMetrics): "pools", ] - serialization_map_instance: dict[type, Callable[[str, Any], str | None]] = { + serialization_map_instance: dict[ + type[Any], Callable[[str, Any], str | None] + ] = { AlgoHashRateType: serialize_algo_hash_rate, BaseMinerError: serialize_miner_error, } - serialization_map: dict[type, Callable[[str, Any], str | None]] = { + serialization_map: dict[type[Any], Callable[[str, Any], str | None]] = { int: serialize_int, float: serialize_float, str: serialize_str, @@ -509,8 +529,8 @@ def serialize_pool_metrics(key: str, value: PoolMetrics): for field in include: field_val = getattr(self, field) - serialization_func = serialization_map.get( - type(field_val), lambda _k, _v: None + serialization_func: Callable[[str, Any], str | None] = ( + serialization_map.get(type(field_val), lambda _k, _v: None) ) serialized = serialization_func(field, field_val) if serialized is not None: @@ -519,7 +539,9 @@ def serialize_pool_metrics(key: str, value: PoolMetrics): for datatype in serialization_map_instance: if serialized is None: if isinstance(field_val, datatype): - func = serialization_map_instance[datatype] + func: Callable[[str, Any], str | None] = ( + serialization_map_instance[datatype] + ) serialized = func(field, field_val) if serialized is not None: field_data.append(serialized) diff --git a/pyasic/data/boards.py b/pyasic/data/boards.py index a261a14c5..3524abc23 100644 --- a/pyasic/data/boards.py +++ b/pyasic/data/boards.py @@ -20,7 +20,7 @@ from pydantic import BaseModel -from pyasic.device.algorithm.hashrate import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType class HashBoard(BaseModel): @@ -57,12 +57,12 @@ class HashBoard(BaseModel): voltage: float | None = None @classmethod - def fields(cls) -> set: + def fields(cls) -> set[str]: 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): + def get(self, __key: str, default: Any = None) -> Any: try: val = self.__getitem__(__key) if val is None: @@ -71,7 +71,7 @@ def get(self, __key: str, default: Any = None): except KeyError: return default - def __getitem__(self, item: str): + def __getitem__(self, item: str) -> Any: try: return getattr(self, item) except AttributeError: @@ -93,10 +93,10 @@ def serialize_algo_hash_rate(key: str, value: AlgoHashRateType) -> str: def serialize_bool(key: str, value: bool) -> str: return f"{key}={str(value).lower()}" - serialization_map_instance = { + serialization_map_instance: dict[type[Any], Callable[[str, Any], str]] = { AlgoHashRateType: serialize_algo_hash_rate, } - serialization_map = { + serialization_map: dict[type[Any], Callable[[str, Any], str]] = { int: serialize_int, float: serialize_float, str: serialize_str, @@ -120,7 +120,7 @@ def serialize_bool(key: str, value: bool) -> str: serialization_func: Callable[[str, Any], str | None] = ( serialization_map.get( type(field_val), - lambda _k, _v: None, # type: ignore + lambda _k, _v: None, ) ) serialized = serialization_func( diff --git a/pyasic/data/device.py b/pyasic/data/device.py index 5f53b4930..c37450716 100644 --- a/pyasic/data/device.py +++ b/pyasic/data/device.py @@ -1,6 +1,8 @@ +from typing import Any + from pydantic import BaseModel, ConfigDict, field_serializer -from pyasic.device.algorithm import MinerAlgoType +from pyasic.device.algorithm.base import MinerAlgoType from pyasic.device.firmware import MinerFirmware from pyasic.device.makes import MinerMake from pyasic.device.models import MinerModelType @@ -15,17 +17,17 @@ class DeviceInfo(BaseModel): algo: type[MinerAlgoType] | None = None @field_serializer("make") - def serialize_make(self, make: MinerMake, _info): + def serialize_make(self, make: MinerMake, _info: Any) -> str: return str(make) @field_serializer("model") - def serialize_model(self, model: MinerModelType, _info): + def serialize_model(self, model: MinerModelType, _info: Any) -> str: return str(model) @field_serializer("firmware") - def serialize_firmware(self, firmware: MinerFirmware, _info): + def serialize_firmware(self, firmware: MinerFirmware, _info: Any) -> str: return str(firmware) @field_serializer("algo") - def serialize_algo(self, algo: MinerAlgoType, _info): + def serialize_algo(self, algo: type[MinerAlgoType], _info: Any) -> str: return str(algo) diff --git a/pyasic/data/error_codes/__init__.py b/pyasic/data/error_codes/__init__.py index f94168b5c..216aa2d51 100644 --- a/pyasic/data/error_codes/__init__.py +++ b/pyasic/data/error_codes/__init__.py @@ -16,12 +16,23 @@ from typing import TypeVar +from .base import BaseMinerError from .bos import BraiinsOSError from .innosilicon import InnosiliconError from .vnish import VnishError from .whatsminer import WhatsminerError from .X19 import X19Error +__all__ = [ + "BaseMinerError", + "BraiinsOSError", + "InnosiliconError", + "VnishError", + "WhatsminerError", + "X19Error", + "MinerErrorData", +] + MinerErrorData = TypeVar( "MinerErrorData", WhatsminerError, diff --git a/pyasic/data/error_codes/base.py b/pyasic/data/error_codes/base.py index 4f293b9b3..407b46e3b 100644 --- a/pyasic/data/error_codes/base.py +++ b/pyasic/data/error_codes/base.py @@ -1,3 +1,5 @@ +from typing import Any + from pydantic import BaseModel @@ -5,13 +7,13 @@ class BaseMinerError(BaseModel): error_code: int | None = None @classmethod - def fields(cls): + def fields(cls) -> list[str]: return list(cls.model_fields.keys()) - def asdict(self) -> dict: + def asdict(self) -> dict[str, Any]: return self.model_dump() - def as_dict(self) -> dict: + def as_dict(self) -> dict[str, Any]: """Get this dataclass as a dictionary. Returns: diff --git a/pyasic/data/fans.py b/pyasic/data/fans.py index ace0631bd..91c0e5788 100644 --- a/pyasic/data/fans.py +++ b/pyasic/data/fans.py @@ -28,7 +28,7 @@ class Fan(BaseModel): speed: int | None = None - def get(self, __key: str, default: Any = None): + def get(self, __key: str, default: Any = None) -> Any: try: val = self.__getitem__(__key) if val is None: @@ -37,7 +37,7 @@ def get(self, __key: str, default: Any = None): except KeyError: return default - def __getitem__(self, item: str): + def __getitem__(self, item: str) -> Any: try: return getattr(self, item) except AttributeError: diff --git a/pyasic/data/pools.py b/pyasic/data/pools.py index dbc1c6a8a..1e705ee72 100644 --- a/pyasic/data/pools.py +++ b/pyasic/data/pools.py @@ -20,7 +20,7 @@ class PoolUrl(BaseModel): pubkey: str | None = None @model_serializer - def serialize(self): + def serialize(self) -> str: return str(self) def __str__(self) -> str: @@ -114,7 +114,7 @@ def serialize_pool_url(key: str, value: PoolUrl) -> str: def serialize_bool(key: str, value: bool) -> str: return f"{key}={str(value).lower()}" - serialization_map: dict[type, Callable[[str, Any], str]] = { + serialization_map: dict[type[Any], Callable[[str, Any], str]] = { int: serialize_int, float: serialize_float, str: serialize_str, diff --git a/pyasic/device/__init__.py b/pyasic/device/__init__.py index 86ba4f320..e07a7552a 100644 --- a/pyasic/device/__init__.py +++ b/pyasic/device/__init__.py @@ -2,3 +2,10 @@ from .firmware import MinerFirmware from .makes import MinerMake from .models import MinerModel + +__all__ = [ + "MinerAlgo", + "MinerFirmware", + "MinerMake", + "MinerModel", +] diff --git a/pyasic/device/algorithm/__init__.py b/pyasic/device/algorithm/__init__.py index a4aa7c02e..7500259be 100644 --- a/pyasic/device/algorithm/__init__.py +++ b/pyasic/device/algorithm/__init__.py @@ -6,6 +6,7 @@ from .ethash import EtHashAlgo from .handshake import HandshakeAlgo from .hashrate import * +from .hashrate.base import AlgoHashRateType from .hashrate.unit import * from .kadena import KadenaAlgo from .kheavyhash import KHeavyHashAlgo @@ -14,6 +15,24 @@ from .x11 import X11Algo from .zksnark import ZkSnarkAlgo +__all__ = [ + "MinerAlgoType", + "AlgoHashRateType", + "Blake256Algo", + "BlockFlowAlgo", + "EaglesongAlgo", + "EquihashAlgo", + "EtHashAlgo", + "HandshakeAlgo", + "KadenaAlgo", + "KHeavyHashAlgo", + "ScryptAlgo", + "SHA256Algo", + "X11Algo", + "ZkSnarkAlgo", + "MinerAlgo", +] + class MinerAlgo: SHA256 = SHA256Algo diff --git a/pyasic/device/algorithm/base.py b/pyasic/device/algorithm/base.py index e2f867aec..53d72c6db 100644 --- a/pyasic/device/algorithm/base.py +++ b/pyasic/device/algorithm/base.py @@ -7,7 +7,7 @@ class MinerAlgoMeta(type): name: str - def __str__(cls): + def __str__(cls) -> str: return cls.name diff --git a/pyasic/device/algorithm/blake256.py b/pyasic/device/algorithm/blake256.py index 462c5ef80..71fe87ecd 100644 --- a/pyasic/device/algorithm/blake256.py +++ b/pyasic/device/algorithm/blake256.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import Blake256HashRate -from .hashrate.unit import Blake256Unit +from .hashrate.blake256 import Blake256HashRate +from .hashrate.unit.blake256 import Blake256Unit + +__all__ = ["Blake256Algo", "Blake256HashRate", "Blake256Unit"] # make this json serializable diff --git a/pyasic/device/algorithm/blockflow.py b/pyasic/device/algorithm/blockflow.py index fa7b61786..4cfdf53a6 100644 --- a/pyasic/device/algorithm/blockflow.py +++ b/pyasic/device/algorithm/blockflow.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import BlockFlowHashRate -from .hashrate.unit import BlockFlowUnit +from .hashrate.blockflow import BlockFlowHashRate +from .hashrate.unit.blockflow import BlockFlowUnit + +__all__ = ["BlockFlowAlgo", "BlockFlowHashRate", "BlockFlowUnit"] class BlockFlowAlgo(MinerAlgoType): diff --git a/pyasic/device/algorithm/eaglesong.py b/pyasic/device/algorithm/eaglesong.py index c2ba02ab3..3fc488284 100644 --- a/pyasic/device/algorithm/eaglesong.py +++ b/pyasic/device/algorithm/eaglesong.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import EaglesongHashRate -from .hashrate.unit import EaglesongUnit +from .hashrate.eaglesong import EaglesongHashRate +from .hashrate.unit.eaglesong import EaglesongUnit + +__all__ = ["EaglesongAlgo", "EaglesongHashRate", "EaglesongUnit"] # make this json serializable diff --git a/pyasic/device/algorithm/equihash.py b/pyasic/device/algorithm/equihash.py index cb76cff6c..233f209bc 100644 --- a/pyasic/device/algorithm/equihash.py +++ b/pyasic/device/algorithm/equihash.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import EquihashHashRate -from .hashrate.unit import EquihashUnit +from .hashrate.equihash import EquihashHashRate +from .hashrate.unit.equihash import EquihashUnit + +__all__ = ["EquihashAlgo", "EquihashHashRate", "EquihashUnit"] # make this json serializable diff --git a/pyasic/device/algorithm/ethash.py b/pyasic/device/algorithm/ethash.py index 93cdb65f2..3b030396e 100644 --- a/pyasic/device/algorithm/ethash.py +++ b/pyasic/device/algorithm/ethash.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import EtHashHashRate -from .hashrate.unit import EtHashUnit +from .hashrate.ethash import EtHashHashRate +from .hashrate.unit.ethash import EtHashUnit + +__all__ = ["EtHashAlgo", "EtHashHashRate", "EtHashUnit"] class EtHashAlgo(MinerAlgoType): diff --git a/pyasic/device/algorithm/handshake.py b/pyasic/device/algorithm/handshake.py index d7e28b136..f7882759e 100644 --- a/pyasic/device/algorithm/handshake.py +++ b/pyasic/device/algorithm/handshake.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import HandshakeHashRate -from .hashrate.unit import HandshakeUnit +from .hashrate.handshake import HandshakeHashRate +from .hashrate.unit.handshake import HandshakeUnit + +__all__ = ["HandshakeAlgo", "HandshakeHashRate", "HandshakeUnit"] # make this json serializable diff --git a/pyasic/device/algorithm/hashrate/__init__.py b/pyasic/device/algorithm/hashrate/__init__.py index c6c359c63..6d0cdab37 100644 --- a/pyasic/device/algorithm/hashrate/__init__.py +++ b/pyasic/device/algorithm/hashrate/__init__.py @@ -12,6 +12,23 @@ from .x11 import X11HashRate from .zksnark import ZkSnarkHashRate +__all__ = [ + "AlgoHashRateType", + "Blake256HashRate", + "BlockFlowHashRate", + "EaglesongHashRate", + "EquihashHashRate", + "EtHashHashRate", + "HandshakeHashRate", + "KadenaHashRate", + "KHeavyHashHashRate", + "ScryptHashRate", + "SHA256HashRate", + "X11HashRate", + "ZkSnarkHashRate", + "AlgoHashRate", +] + class AlgoHashRate: SHA256 = SHA256HashRate diff --git a/pyasic/device/algorithm/hashrate/base.py b/pyasic/device/algorithm/hashrate/base.py index 55a72bb51..b6518a627 100644 --- a/pyasic/device/algorithm/hashrate/base.py +++ b/pyasic/device/algorithm/hashrate/base.py @@ -1,55 +1,56 @@ from __future__ import annotations -from abc import ABC, abstractmethod -from typing import Generic, TypeVar +from typing import Any from pydantic import BaseModel, field_serializer from typing_extensions import Self -from .unit.base import AlgoHashRateUnitType, GenericUnit +from .unit.base import GenericUnit -UnitType = TypeVar("UnitType", bound=AlgoHashRateUnitType) - -class AlgoHashRateType(BaseModel, ABC, Generic[UnitType]): - unit: UnitType +class AlgoHashRateType(BaseModel): + unit: Any rate: float @field_serializer("unit") - def serialize_unit(self, unit: UnitType): - return unit.model_dump() + def serialize_unit(self, unit: Any) -> dict[str, object]: + result: dict[str, object] = unit.model_dump() + return result - @abstractmethod - def into(self, other: UnitType) -> Self: - pass + def into(self, other: Any) -> Self: + if hasattr(self.unit, "value") and hasattr(other, "value"): + return self.__class__( + rate=self.rate / (other.value / self.unit.value), unit=other + ) + return self - def auto_unit(self): - if 1 < self.rate // int(self.unit.H) < 1000: + def auto_unit(self) -> Self: + if hasattr(self.unit, "H") and 1 < self.rate / int(self.unit.H) < 1000: return self.into(self.unit.H) - if 1 < self.rate // int(self.unit.MH) < 1000: + if hasattr(self.unit, "MH") and 1 < self.rate / int(self.unit.MH) < 1000: return self.into(self.unit.MH) - if 1 < self.rate // int(self.unit.GH) < 1000: + if hasattr(self.unit, "GH") and 1 < self.rate / int(self.unit.GH) < 1000: return self.into(self.unit.GH) - if 1 < self.rate // int(self.unit.TH) < 1000: + if hasattr(self.unit, "TH") and 1 < self.rate / int(self.unit.TH) < 1000: return self.into(self.unit.TH) - if 1 < self.rate // int(self.unit.PH) < 1000: + if hasattr(self.unit, "PH") and 1 < self.rate / int(self.unit.PH) < 1000: return self.into(self.unit.PH) - if 1 < self.rate // int(self.unit.EH) < 1000: + if hasattr(self.unit, "EH") and 1 < self.rate / int(self.unit.EH) < 1000: return self.into(self.unit.EH) - if 1 < self.rate // int(self.unit.ZH) < 1000: + if hasattr(self.unit, "ZH") and 1 < self.rate / int(self.unit.ZH) < 1000: return self.into(self.unit.ZH) return self - def __float__(self): + def __float__(self) -> float: return float(self.rate) - def __int__(self): + def __int__(self) -> int: return int(self.rate) - def __repr__(self): + def __repr__(self) -> str: return f"{self.rate} {str(self.unit)}" - def __round__(self, n: int | None = None): + def __round__(self, n: int | None = None) -> float: return round(self.rate, n) def __add__(self, other: Self | int | float) -> Self: @@ -88,7 +89,7 @@ def __mul__(self, other: Self | int | float) -> Self: return self.__class__(rate=self.rate * other, unit=self.unit) -class GenericHashrate(AlgoHashRateType[GenericUnit]): +class GenericHashrate(AlgoHashRateType): rate: float = 0 unit: GenericUnit = GenericUnit.H diff --git a/pyasic/device/algorithm/hashrate/blake256.py b/pyasic/device/algorithm/hashrate/blake256.py index f9296455a..75324c6a8 100644 --- a/pyasic/device/algorithm/hashrate/blake256.py +++ b/pyasic/device/algorithm/hashrate/blake256.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class Blake256HashRate(AlgoHashRateType[Blake256Unit]): +class Blake256HashRate(AlgoHashRateType): rate: float unit: Blake256Unit = HashUnit.BLAKE256.default diff --git a/pyasic/device/algorithm/hashrate/blockflow.py b/pyasic/device/algorithm/hashrate/blockflow.py index ad02ea914..89c13d466 100644 --- a/pyasic/device/algorithm/hashrate/blockflow.py +++ b/pyasic/device/algorithm/hashrate/blockflow.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class BlockFlowHashRate(AlgoHashRateType[BlockFlowUnit]): +class BlockFlowHashRate(AlgoHashRateType): rate: float unit: BlockFlowUnit = HashUnit.BLOCKFLOW.default diff --git a/pyasic/device/algorithm/hashrate/eaglesong.py b/pyasic/device/algorithm/hashrate/eaglesong.py index bdf6d0db6..0d3048e66 100644 --- a/pyasic/device/algorithm/hashrate/eaglesong.py +++ b/pyasic/device/algorithm/hashrate/eaglesong.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class EaglesongHashRate(AlgoHashRateType[EaglesongUnit]): +class EaglesongHashRate(AlgoHashRateType): rate: float unit: EaglesongUnit = HashUnit.EAGLESONG.default diff --git a/pyasic/device/algorithm/hashrate/equihash.py b/pyasic/device/algorithm/hashrate/equihash.py index 08503a937..56c281812 100644 --- a/pyasic/device/algorithm/hashrate/equihash.py +++ b/pyasic/device/algorithm/hashrate/equihash.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class EquihashHashRate(AlgoHashRateType[EquihashUnit]): +class EquihashHashRate(AlgoHashRateType): rate: float unit: EquihashUnit = HashUnit.EQUIHASH.default diff --git a/pyasic/device/algorithm/hashrate/ethash.py b/pyasic/device/algorithm/hashrate/ethash.py index ccff11ddb..9b1ab4356 100644 --- a/pyasic/device/algorithm/hashrate/ethash.py +++ b/pyasic/device/algorithm/hashrate/ethash.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class EtHashHashRate(AlgoHashRateType[EtHashUnit]): +class EtHashHashRate(AlgoHashRateType): rate: float unit: EtHashUnit = HashUnit.ETHASH.default diff --git a/pyasic/device/algorithm/hashrate/handshake.py b/pyasic/device/algorithm/hashrate/handshake.py index 6cef14852..e7ae2e5b3 100644 --- a/pyasic/device/algorithm/hashrate/handshake.py +++ b/pyasic/device/algorithm/hashrate/handshake.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class HandshakeHashRate(AlgoHashRateType[HandshakeUnit]): +class HandshakeHashRate(AlgoHashRateType): rate: float unit: HandshakeUnit = HashUnit.HANDSHAKE.default diff --git a/pyasic/device/algorithm/hashrate/kadena.py b/pyasic/device/algorithm/hashrate/kadena.py index f37fc8e4e..8d99be834 100644 --- a/pyasic/device/algorithm/hashrate/kadena.py +++ b/pyasic/device/algorithm/hashrate/kadena.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class KadenaHashRate(AlgoHashRateType[KadenaUnit]): +class KadenaHashRate(AlgoHashRateType): rate: float unit: KadenaUnit = HashUnit.KADENA.default diff --git a/pyasic/device/algorithm/hashrate/kheavyhash.py b/pyasic/device/algorithm/hashrate/kheavyhash.py index d07ed9dea..75953e0ec 100644 --- a/pyasic/device/algorithm/hashrate/kheavyhash.py +++ b/pyasic/device/algorithm/hashrate/kheavyhash.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class KHeavyHashHashRate(AlgoHashRateType[KHeavyHashUnit]): +class KHeavyHashHashRate(AlgoHashRateType): rate: float unit: KHeavyHashUnit = HashUnit.KHEAVYHASH.default diff --git a/pyasic/device/algorithm/hashrate/scrypt.py b/pyasic/device/algorithm/hashrate/scrypt.py index a92306750..77f0bbfcc 100644 --- a/pyasic/device/algorithm/hashrate/scrypt.py +++ b/pyasic/device/algorithm/hashrate/scrypt.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class ScryptHashRate(AlgoHashRateType[ScryptUnit]): +class ScryptHashRate(AlgoHashRateType): rate: float unit: ScryptUnit = HashUnit.SCRYPT.default diff --git a/pyasic/device/algorithm/hashrate/sha256.py b/pyasic/device/algorithm/hashrate/sha256.py index ba1f83fab..7b28343d7 100644 --- a/pyasic/device/algorithm/hashrate/sha256.py +++ b/pyasic/device/algorithm/hashrate/sha256.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class SHA256HashRate(AlgoHashRateType[SHA256Unit]): +class SHA256HashRate(AlgoHashRateType): rate: float unit: SHA256Unit = HashUnit.SHA256.default diff --git a/pyasic/device/algorithm/hashrate/unit/base.py b/pyasic/device/algorithm/hashrate/unit/base.py index a144397b2..50ab8f5cb 100644 --- a/pyasic/device/algorithm/hashrate/unit/base.py +++ b/pyasic/device/algorithm/hashrate/unit/base.py @@ -1,8 +1,11 @@ +from __future__ import annotations + from enum import IntEnum +from typing import Any class AlgoHashRateUnitType(IntEnum): - def __str__(self): + def __str__(self) -> str: if hasattr(self.__class__, "H") and self.value == self.__class__.H: return "H/s" if hasattr(self.__class__, "KH") and self.value == self.__class__.KH: @@ -22,31 +25,31 @@ def __str__(self): return "" @classmethod - def from_str(cls, value: str): + def from_str(cls, value: str) -> AlgoHashRateUnitType | None: if value == "H" and hasattr(cls, "H"): - return cls.H + return cls.H # type: ignore[no-any-return] elif value == "KH" and hasattr(cls, "KH"): - return cls.KH + return cls.KH # type: ignore[no-any-return] elif value == "MH" and hasattr(cls, "MH"): - return cls.MH + return cls.MH # type: ignore[no-any-return] elif value == "GH" and hasattr(cls, "GH"): - return cls.GH + return cls.GH # type: ignore[no-any-return] elif value == "TH" and hasattr(cls, "TH"): - return cls.TH + return cls.TH # type: ignore[no-any-return] elif value == "PH" and hasattr(cls, "PH"): - return cls.PH + return cls.PH # type: ignore[no-any-return] elif value == "EH" and hasattr(cls, "EH"): - return cls.EH + return cls.EH # type: ignore[no-any-return] elif value == "ZH" and hasattr(cls, "ZH"): - return cls.ZH + return cls.ZH # type: ignore[no-any-return] if hasattr(cls, "default"): - return cls.default + return cls.default # type: ignore[no-any-return] return None - def __repr__(self): + def __repr__(self) -> str: return str(self) - def model_dump(self): + def model_dump(self) -> dict[str, Any]: return {"value": self.value, "suffix": str(self)} diff --git a/pyasic/device/algorithm/hashrate/unit/equihash.py b/pyasic/device/algorithm/hashrate/unit/equihash.py index b002249b3..95c7b1b88 100644 --- a/pyasic/device/algorithm/hashrate/unit/equihash.py +++ b/pyasic/device/algorithm/hashrate/unit/equihash.py @@ -15,7 +15,7 @@ class EquihashUnit(AlgoHashRateUnitType): default = KH - def __str__(self): + def __str__(self) -> str: if self.value == self.H: return "Sol/s" if self.value == self.KH: @@ -32,3 +32,4 @@ def __str__(self): return "ESol/s" if self.value == self.ZH: return "ZSol/s" + return "" diff --git a/pyasic/device/algorithm/hashrate/x11.py b/pyasic/device/algorithm/hashrate/x11.py index 50ed80c79..7c1e6fb6a 100644 --- a/pyasic/device/algorithm/hashrate/x11.py +++ b/pyasic/device/algorithm/hashrate/x11.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class X11HashRate(AlgoHashRateType[X11Unit]): +class X11HashRate(AlgoHashRateType): rate: float unit: X11Unit = HashUnit.X11.default diff --git a/pyasic/device/algorithm/hashrate/zksnark.py b/pyasic/device/algorithm/hashrate/zksnark.py index b08ff2ce1..32d30b829 100644 --- a/pyasic/device/algorithm/hashrate/zksnark.py +++ b/pyasic/device/algorithm/hashrate/zksnark.py @@ -8,7 +8,7 @@ from .unit import HashUnit -class ZkSnarkHashRate(AlgoHashRateType[ZkSnarkUnit]): +class ZkSnarkHashRate(AlgoHashRateType): rate: float unit: ZkSnarkUnit = HashUnit.ZKSNARK.default diff --git a/pyasic/device/algorithm/kadena.py b/pyasic/device/algorithm/kadena.py index 7a00a3941..853629f5b 100644 --- a/pyasic/device/algorithm/kadena.py +++ b/pyasic/device/algorithm/kadena.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import KadenaHashRate -from .hashrate.unit import KadenaUnit +from .hashrate.kadena import KadenaHashRate +from .hashrate.unit.kadena import KadenaUnit + +__all__ = ["KadenaAlgo", "KadenaHashRate", "KadenaUnit"] # make this json serializable diff --git a/pyasic/device/algorithm/kheavyhash.py b/pyasic/device/algorithm/kheavyhash.py index 9489bd429..93ac791d6 100644 --- a/pyasic/device/algorithm/kheavyhash.py +++ b/pyasic/device/algorithm/kheavyhash.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import KHeavyHashHashRate -from .hashrate.unit import KHeavyHashUnit +from .hashrate.kheavyhash import KHeavyHashHashRate +from .hashrate.unit.kheavyhash import KHeavyHashUnit + +__all__ = ["KHeavyHashAlgo", "KHeavyHashHashRate", "KHeavyHashUnit"] # make this json serializable diff --git a/pyasic/device/algorithm/scrypt.py b/pyasic/device/algorithm/scrypt.py index ff9422a26..5e6a8642a 100644 --- a/pyasic/device/algorithm/scrypt.py +++ b/pyasic/device/algorithm/scrypt.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import ScryptHashRate -from .hashrate.unit import ScryptUnit +from .hashrate.scrypt import ScryptHashRate +from .hashrate.unit.scrypt import ScryptUnit + +__all__ = ["ScryptAlgo", "ScryptHashRate", "ScryptUnit"] class ScryptAlgo(MinerAlgoType): diff --git a/pyasic/device/algorithm/sha256.py b/pyasic/device/algorithm/sha256.py index 28dbe89b6..c6f02a657 100644 --- a/pyasic/device/algorithm/sha256.py +++ b/pyasic/device/algorithm/sha256.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import SHA256HashRate -from .hashrate.unit import SHA256Unit +from .hashrate.sha256 import SHA256HashRate +from .hashrate.unit.sha256 import SHA256Unit + +__all__ = ["SHA256Algo", "SHA256HashRate", "SHA256Unit"] # make this json serializable diff --git a/pyasic/device/algorithm/x11.py b/pyasic/device/algorithm/x11.py index 10669e879..2365167a7 100644 --- a/pyasic/device/algorithm/x11.py +++ b/pyasic/device/algorithm/x11.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import X11HashRate -from .hashrate.unit import X11Unit +from .hashrate.unit.x11 import X11Unit +from .hashrate.x11 import X11HashRate + +__all__ = ["X11Algo", "X11HashRate", "X11Unit"] # make this json serializable diff --git a/pyasic/device/algorithm/zksnark.py b/pyasic/device/algorithm/zksnark.py index 3c1c2a09b..312c5b6eb 100644 --- a/pyasic/device/algorithm/zksnark.py +++ b/pyasic/device/algorithm/zksnark.py @@ -1,8 +1,10 @@ from __future__ import annotations from .base import MinerAlgoType -from .hashrate import ZkSnarkHashRate -from .hashrate.unit import ZkSnarkUnit +from .hashrate.unit.zksnark import ZkSnarkUnit +from .hashrate.zksnark import ZkSnarkHashRate + +__all__ = ["ZkSnarkAlgo", "ZkSnarkHashRate", "ZkSnarkUnit"] class ZkSnarkAlgo(MinerAlgoType): diff --git a/pyasic/device/firmware.py b/pyasic/device/firmware.py index 87ae79f0e..468660540 100644 --- a/pyasic/device/firmware.py +++ b/pyasic/device/firmware.py @@ -26,5 +26,5 @@ class MinerFirmware(str, Enum): LUXOS = "LuxOS" MARATHON = "MaraFW" - def __str__(self): + def __str__(self) -> str: return self.value diff --git a/pyasic/device/makes.py b/pyasic/device/makes.py index 476480921..9696b865b 100644 --- a/pyasic/device/makes.py +++ b/pyasic/device/makes.py @@ -33,5 +33,5 @@ class MinerMake(str, Enum): ELPHAPEX = "Elphapex" BRAIINS = "Braiins" - def __str__(self): + def __str__(self) -> str: return self.value diff --git a/pyasic/device/models.py b/pyasic/device/models.py index 0ddf39e71..fb2c31c2a 100644 --- a/pyasic/device/models.py +++ b/pyasic/device/models.py @@ -67,7 +67,7 @@ class AntminerModels(MinerModelType): T21 = "T21" S19XPHydro = "S19 XP Hydro" - def __str__(self): + def __str__(self) -> str: return self.value @@ -436,7 +436,7 @@ class WhatsminerModels(MinerModelType): M67SVK30 = "M67S VK30" M70VM30 = "M70 VM30" - def __str__(self): + def __str__(self) -> str: return self.value @@ -459,7 +459,7 @@ class AvalonminerModels(MinerModelType): AvalonNano3s = "Avalon Nano 3s" AvalonQHome = "Avalon Q Home" - def __str__(self): + def __str__(self) -> str: return self.value @@ -469,7 +469,7 @@ class InnosiliconModels(MinerModelType): A11 = "A11" A11MX = "A11MX" - def __str__(self): + def __str__(self) -> str: return self.value @@ -483,7 +483,7 @@ class GoldshellModels(MinerModelType): Byte = "Byte" MiniDoge = "Mini Doge" - def __str__(self): + def __str__(self) -> str: return self.value @@ -494,7 +494,7 @@ class ePICModels(MinerModelType): S19jProDual = "S19j Pro Dual" S19kProDual = "S19k Pro Dual" - def __str__(self): + def __str__(self) -> str: return self.value @@ -507,7 +507,7 @@ class AuradineModels(MinerModelType): AD2500 = "AD2500" AD3500 = "AD3500" - def __str__(self): + def __str__(self) -> str: return self.value @@ -517,7 +517,7 @@ class BitAxeModels(MinerModelType): BM1397 = "Max" BM1370 = "Gamma" - def __str__(self): + def __str__(self) -> str: return self.value @@ -525,7 +525,7 @@ class LuckyMinerModels(MinerModelType): LV07 = "LV07" LV08 = "LV08" - def __str__(self): + def __str__(self) -> str: return self.value @@ -541,21 +541,21 @@ class IceRiverModels(MinerModelType): KS5M = "KS5M" AL3 = "AL3" - def __str__(self): + def __str__(self) -> str: return self.value class HammerModels(MinerModelType): D10 = "D10" - def __str__(self): + def __str__(self) -> str: return self.value class VolcMinerModels(MinerModelType): D1 = "D1" - def __str__(self): + def __str__(self) -> str: return self.value @@ -563,7 +563,7 @@ class BraiinsModels(MinerModelType): BMM100 = "BMM100" BMM101 = "BMM101" - def __str__(self): + def __str__(self) -> str: return self.value @@ -572,7 +572,7 @@ class ElphapexModels(MinerModelType): DG1Plus = "DG1+" DG1Home = "DG1Home" - def __str__(self): + def __str__(self) -> str: return self.value diff --git a/pyasic/errors/__init__.py b/pyasic/errors/__init__.py index 590d92f74..4bfdf0f04 100644 --- a/pyasic/errors/__init__.py +++ b/pyasic/errors/__init__.py @@ -16,13 +16,15 @@ class APIError(Exception): - def __init__(self, *args): + message: object | None + + def __init__(self, *args: object) -> None: if args: self.message = args[0] else: self.message = None - def __str__(self): + def __str__(self) -> str: if self.message: if self.message == "can't access write cmd": return f"{self.message}, please make sure your miner has been unlocked." @@ -32,13 +34,15 @@ def __str__(self): class PhaseBalancingError(Exception): - def __init__(self, *args): + message: object | None + + def __init__(self, *args: object) -> None: if args: self.message = args[0] else: self.message = None - def __str__(self): + def __str__(self) -> str: if self.message: return f"{self.message}" else: @@ -46,13 +50,15 @@ def __str__(self): class APIWarning(Warning): - def __init__(self, *args): + message: object | None + + def __init__(self, *args: object) -> None: if args: self.message = args[0] else: self.message = None - def __str__(self): + def __str__(self) -> str: if self.message: return f"{self.message}" else: diff --git a/pyasic/logger/__init__.py b/pyasic/logger/__init__.py index 51e17c04e..415b2f1a9 100644 --- a/pyasic/logger/__init__.py +++ b/pyasic/logger/__init__.py @@ -17,7 +17,7 @@ import logging -def init_logger(): +def init_logger() -> logging.Logger: # if PyasicSettings().logfile: # logging.basicConfig( # filename="logfile.txt", diff --git a/pyasic/miners/__init__.py b/pyasic/miners/__init__.py index 95b0c4c86..166627c4e 100644 --- a/pyasic/miners/__init__.py +++ b/pyasic/miners/__init__.py @@ -18,3 +18,11 @@ from .data import DataOptions from .factory import get_miner, miner_factory from .listener import MinerListener + +__all__ = [ + "AnyMiner", + "DataOptions", + "get_miner", + "miner_factory", + "MinerListener", +] diff --git a/pyasic/miners/antminer/bmminer/X15/__init__.py b/pyasic/miners/antminer/bmminer/X15/__init__.py index b69124207..7260a0132 100644 --- a/pyasic/miners/antminer/bmminer/X15/__init__.py +++ b/pyasic/miners/antminer/bmminer/X15/__init__.py @@ -14,3 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .Z15 import BMMinerZ15Pro + +__all__ = [ + "BMMinerZ15Pro", +] diff --git a/pyasic/miners/antminer/bmminer/X17/__init__.py b/pyasic/miners/antminer/bmminer/X17/__init__.py index 27d1f0211..c0e6b7912 100644 --- a/pyasic/miners/antminer/bmminer/X17/__init__.py +++ b/pyasic/miners/antminer/bmminer/X17/__init__.py @@ -16,3 +16,13 @@ from .S17 import BMMinerS17, BMMinerS17e, BMMinerS17Plus, BMMinerS17Pro from .T17 import BMMinerT17, BMMinerT17e, BMMinerT17Plus + +__all__ = [ + "BMMinerS17", + "BMMinerS17e", + "BMMinerS17Plus", + "BMMinerS17Pro", + "BMMinerT17", + "BMMinerT17e", + "BMMinerT17Plus", +] diff --git a/pyasic/miners/antminer/bmminer/X19/__init__.py b/pyasic/miners/antminer/bmminer/X19/__init__.py index d1f4798fb..8fa1617aa 100644 --- a/pyasic/miners/antminer/bmminer/X19/__init__.py +++ b/pyasic/miners/antminer/bmminer/X19/__init__.py @@ -36,3 +36,26 @@ BMMinerS19XP, ) from .T19 import BMMinerT19 + +__all__ = [ + "BMMinerS19", + "BMMinerS19a", + "BMMinerS19aPro", + "BMMinerS19Hydro", + "BMMinerS19i", + "BMMinerS19j", + "BMMinerS19jNoPIC", + "BMMinerS19jPlus", + "BMMinerS19jPro", + "BMMinerS19jProPlus", + "BMMinerS19jXP", + "BMMinerS19KPro", + "BMMinerS19L", + "BMMinerS19Plus", + "BMMinerS19Pro", + "BMMinerS19ProHydro", + "BMMinerS19ProPlus", + "BMMinerS19ProPlusHydro", + "BMMinerS19XP", + "BMMinerT19", +] diff --git a/pyasic/miners/antminer/bmminer/X21/__init__.py b/pyasic/miners/antminer/bmminer/X21/__init__.py index bfbbf5ed0..6eb50ecda 100644 --- a/pyasic/miners/antminer/bmminer/X21/__init__.py +++ b/pyasic/miners/antminer/bmminer/X21/__init__.py @@ -21,3 +21,12 @@ BMMinerS21Pro, ) from .T21 import BMMinerT21 + +__all__ = [ + "BMMinerS21", + "BMMinerS21Hydro", + "BMMinerS21Plus", + "BMMinerS21PlusHydro", + "BMMinerS21Pro", + "BMMinerT21", +] diff --git a/pyasic/miners/antminer/bmminer/X3/KS3.py b/pyasic/miners/antminer/bmminer/X3/KS3.py index f83e54206..175ed78ff 100644 --- a/pyasic/miners/antminer/bmminer/X3/KS3.py +++ b/pyasic/miners/antminer/bmminer/X3/KS3.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import AntminerModern -from pyasic.miners.device.models.antminer import KS3 +from pyasic.miners.device.models.antminer.X3.KS3 import KS3 class BMMinerKS3(AntminerModern, KS3): diff --git a/pyasic/miners/antminer/bmminer/X3/__init__.py b/pyasic/miners/antminer/bmminer/X3/__init__.py index 7cdc4b051..e928099f2 100644 --- a/pyasic/miners/antminer/bmminer/X3/__init__.py +++ b/pyasic/miners/antminer/bmminer/X3/__init__.py @@ -17,3 +17,10 @@ from .KA3 import BMMinerKA3 from .KS3 import BMMinerKS3 from .L3 import BMMinerL3Plus + +__all__ = [ + "BMMinerHS3", + "BMMinerKA3", + "BMMinerKS3", + "BMMinerL3Plus", +] diff --git a/pyasic/miners/antminer/bmminer/X5/KS5.py b/pyasic/miners/antminer/bmminer/X5/KS5.py index e061a9d8c..fe9519081 100644 --- a/pyasic/miners/antminer/bmminer/X5/KS5.py +++ b/pyasic/miners/antminer/bmminer/X5/KS5.py @@ -14,7 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from pyasic.miners.backends import AntminerModern -from pyasic.miners.device.models.antminer import KS5, KS5Pro +from pyasic.miners.device.models.antminer.X5.KS5 import KS5, KS5Pro class BMMinerKS5(AntminerModern, KS5): diff --git a/pyasic/miners/antminer/bmminer/X5/__init__.py b/pyasic/miners/antminer/bmminer/X5/__init__.py index e63b63663..f48381b1c 100644 --- a/pyasic/miners/antminer/bmminer/X5/__init__.py +++ b/pyasic/miners/antminer/bmminer/X5/__init__.py @@ -14,3 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .KS5 import BMMinerKS5, BMMinerKS5Pro + +__all__ = [ + "BMMinerKS5", + "BMMinerKS5Pro", +] diff --git a/pyasic/miners/antminer/bmminer/X7/__init__.py b/pyasic/miners/antminer/bmminer/X7/__init__.py index f4abbb86c..43fdd5d84 100644 --- a/pyasic/miners/antminer/bmminer/X7/__init__.py +++ b/pyasic/miners/antminer/bmminer/X7/__init__.py @@ -16,3 +16,9 @@ from .D7 import BMMinerD7 from .K7 import BMMinerK7 from .L7 import BMMinerL7 + +__all__ = [ + "BMMinerD7", + "BMMinerK7", + "BMMinerL7", +] diff --git a/pyasic/miners/antminer/bmminer/X9/__init__.py b/pyasic/miners/antminer/bmminer/X9/__init__.py index 4e2bf2366..fcd79c300 100644 --- a/pyasic/miners/antminer/bmminer/X9/__init__.py +++ b/pyasic/miners/antminer/bmminer/X9/__init__.py @@ -19,3 +19,13 @@ from .L9 import BMMinerL9 from .S9 import BMMinerS9, BMMinerS9i, BMMinerS9j from .T9 import BMMinerT9 + +__all__ = [ + "BMMinerD9", + "BMMinerE9Pro", + "BMMinerL9", + "BMMinerS9", + "BMMinerS9i", + "BMMinerS9j", + "BMMinerT9", +] diff --git a/pyasic/miners/antminer/bosminer/X17/__init__.py b/pyasic/miners/antminer/bosminer/X17/__init__.py index 166dd4751..334766a55 100644 --- a/pyasic/miners/antminer/bosminer/X17/__init__.py +++ b/pyasic/miners/antminer/bosminer/X17/__init__.py @@ -16,3 +16,13 @@ from .S17 import BOSMinerS17, BOSMinerS17e, BOSMinerS17Plus, BOSMinerS17Pro from .T17 import BOSMinerT17, BOSMinerT17e, BOSMinerT17Plus + +__all__ = [ + "BOSMinerS17", + "BOSMinerS17e", + "BOSMinerS17Plus", + "BOSMinerS17Pro", + "BOSMinerT17", + "BOSMinerT17e", + "BOSMinerT17Plus", +] diff --git a/pyasic/miners/antminer/bosminer/X19/__init__.py b/pyasic/miners/antminer/bosminer/X19/__init__.py index 3d50ad64b..8c98d1b1a 100644 --- a/pyasic/miners/antminer/bosminer/X19/__init__.py +++ b/pyasic/miners/antminer/bosminer/X19/__init__.py @@ -32,3 +32,22 @@ BOSMinerS19XPHydro, ) from .T19 import BOSMinerT19 + +__all__ = [ + "BOSMinerS19", + "BOSMinerS19a", + "BOSMinerS19aPro", + "BOSMinerS19j", + "BOSMinerS19jNoPIC", + "BOSMinerS19jPro", + "BOSMinerS19jProNoPIC", + "BOSMinerS19jProPlus", + "BOSMinerS19jProPlusNoPIC", + "BOSMinerS19kProNoPIC", + "BOSMinerS19Plus", + "BOSMinerS19Pro", + "BOSMinerS19ProPlusHydro", + "BOSMinerS19XP", + "BOSMinerS19XPHydro", + "BOSMinerT19", +] diff --git a/pyasic/miners/antminer/bosminer/X21/__init__.py b/pyasic/miners/antminer/bosminer/X21/__init__.py index 8a45680f1..f8843e859 100644 --- a/pyasic/miners/antminer/bosminer/X21/__init__.py +++ b/pyasic/miners/antminer/bosminer/X21/__init__.py @@ -22,3 +22,12 @@ BOSMinerS21Pro, ) from .T21 import BOSMinerT21 + +__all__ = [ + "BOSMinerS21", + "BOSMinerS21Hydro", + "BOSMinerS21Plus", + "BOSMinerS21PlusHydro", + "BOSMinerS21Pro", + "BOSMinerT21", +] diff --git a/pyasic/miners/antminer/bosminer/X9/__init__.py b/pyasic/miners/antminer/bosminer/X9/__init__.py index 95f5d4f72..daf9ba587 100644 --- a/pyasic/miners/antminer/bosminer/X9/__init__.py +++ b/pyasic/miners/antminer/bosminer/X9/__init__.py @@ -15,3 +15,7 @@ # ------------------------------------------------------------------------------ from .S9 import BOSMinerS9 + +__all__ = [ + "BOSMinerS9", +] diff --git a/pyasic/miners/antminer/cgminer/X15/__init__.py b/pyasic/miners/antminer/cgminer/X15/__init__.py index a9ec958dd..9f4b79f8f 100644 --- a/pyasic/miners/antminer/cgminer/X15/__init__.py +++ b/pyasic/miners/antminer/cgminer/X15/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .Z15 import CGMinerZ15 + +__all__ = ["CGMinerZ15"] diff --git a/pyasic/miners/antminer/cgminer/X3/__init__.py b/pyasic/miners/antminer/cgminer/X3/__init__.py index 23db702ee..46e2d1627 100644 --- a/pyasic/miners/antminer/cgminer/X3/__init__.py +++ b/pyasic/miners/antminer/cgminer/X3/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .D3 import CGMinerD3 + +__all__ = ["CGMinerD3"] diff --git a/pyasic/miners/antminer/cgminer/X5/__init__.py b/pyasic/miners/antminer/cgminer/X5/__init__.py index 4379571c0..446d03371 100644 --- a/pyasic/miners/antminer/cgminer/X5/__init__.py +++ b/pyasic/miners/antminer/cgminer/X5/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .DR5 import CGMinerDR5 + +__all__ = ["CGMinerDR5"] diff --git a/pyasic/miners/antminer/epic/X19/__init__.py b/pyasic/miners/antminer/epic/X19/__init__.py index 5f422fb2e..9318f7fc0 100644 --- a/pyasic/miners/antminer/epic/X19/__init__.py +++ b/pyasic/miners/antminer/epic/X19/__init__.py @@ -25,3 +25,15 @@ ePICS19Pro, ePICS19XP, ) + +__all__ = [ + "ePICS19", + "ePICS19j", + "ePICS19jPro", + "ePICS19jProDual", + "ePICS19jProPlus", + "ePICS19kPro", + "ePICS19kProDual", + "ePICS19Pro", + "ePICS19XP", +] diff --git a/pyasic/miners/antminer/epic/X21/__init__.py b/pyasic/miners/antminer/epic/X21/__init__.py index a924de5bd..09bf41c9e 100644 --- a/pyasic/miners/antminer/epic/X21/__init__.py +++ b/pyasic/miners/antminer/epic/X21/__init__.py @@ -16,3 +16,9 @@ from .S21 import ePICS21, ePICS21Pro from .T21 import ePICT21 + +__all__ = [ + "ePICS21", + "ePICS21Pro", + "ePICT21", +] diff --git a/pyasic/miners/antminer/hiveon/X19/__init__.py b/pyasic/miners/antminer/hiveon/X19/__init__.py index 7cbf4595b..241791b24 100644 --- a/pyasic/miners/antminer/hiveon/X19/__init__.py +++ b/pyasic/miners/antminer/hiveon/X19/__init__.py @@ -34,3 +34,24 @@ HiveonS19XP, ) from .T19 import HiveonT19 + +__all__ = [ + "HiveonS19", + "HiveonS19a", + "HiveonS19aPro", + "HiveonS19Hydro", + "HiveonS19i", + "HiveonS19j", + "HiveonS19jNoPIC", + "HiveonS19jPro", + "HiveonS19kPro", + "HiveonS19L", + "HiveonS19NoPIC", + "HiveonS19Plus", + "HiveonS19Pro", + "HiveonS19ProHydro", + "HiveonS19ProPlus", + "HiveonS19ProPlusHydro", + "HiveonS19XP", + "HiveonT19", +] diff --git a/pyasic/miners/antminer/hiveon/X9/T9.py b/pyasic/miners/antminer/hiveon/X9/T9.py index 7bedc2493..6b0cdb9f6 100644 --- a/pyasic/miners/antminer/hiveon/X9/T9.py +++ b/pyasic/miners/antminer/hiveon/X9/T9.py @@ -15,6 +15,8 @@ # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.data import HashBoard from pyasic.device.algorithm import AlgoHashRate, HashUnit from pyasic.errors import APIError @@ -75,7 +77,9 @@ class HiveonT9(HiveonOld, T9): ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: hashboards = [ HashBoard(slot=board, expected_chips=self.expected_chips) for board in range(self.expected_hashboards) @@ -120,7 +124,9 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard return hashboards - async def _get_env_temp(self, rpc_stats: dict | None = None) -> float | None: + async def _get_env_temp( + self, rpc_stats: dict[str, Any] | None = None + ) -> float | None: env_temp_list: list[int] = [] board_map = { 0: [2, 9, 10], diff --git a/pyasic/miners/antminer/hiveon/X9/__init__.py b/pyasic/miners/antminer/hiveon/X9/__init__.py index d0e15a52c..be80214f6 100644 --- a/pyasic/miners/antminer/hiveon/X9/__init__.py +++ b/pyasic/miners/antminer/hiveon/X9/__init__.py @@ -15,3 +15,7 @@ # ------------------------------------------------------------------------------ from .T9 import HiveonT9 + +__all__ = [ + "HiveonT9", +] diff --git a/pyasic/miners/antminer/luxos/X19/__init__.py b/pyasic/miners/antminer/luxos/X19/__init__.py index 74f53d798..36e8dc7eb 100644 --- a/pyasic/miners/antminer/luxos/X19/__init__.py +++ b/pyasic/miners/antminer/luxos/X19/__init__.py @@ -23,3 +23,13 @@ LUXMinerS19XP, ) from .T19 import LUXMinerT19 + +__all__ = [ + "LUXMinerS19", + "LUXMinerS19jPro", + "LUXMinerS19jProPlus", + "LUXMinerS19kPro", + "LUXMinerS19Pro", + "LUXMinerS19XP", + "LUXMinerT19", +] diff --git a/pyasic/miners/antminer/luxos/X21/__init__.py b/pyasic/miners/antminer/luxos/X21/__init__.py index 68410fd89..c2e20c3de 100644 --- a/pyasic/miners/antminer/luxos/X21/__init__.py +++ b/pyasic/miners/antminer/luxos/X21/__init__.py @@ -16,3 +16,8 @@ from .S21 import LUXMinerS21 from .T21 import LUXMinerT21 + +__all__ = [ + "LUXMinerS21", + "LUXMinerT21", +] diff --git a/pyasic/miners/antminer/luxos/X9/__init__.py b/pyasic/miners/antminer/luxos/X9/__init__.py index 6ece38a2b..b5bcbb85d 100644 --- a/pyasic/miners/antminer/luxos/X9/__init__.py +++ b/pyasic/miners/antminer/luxos/X9/__init__.py @@ -15,3 +15,7 @@ # ------------------------------------------------------------------------------ from .S9 import LUXMinerS9 + +__all__ = [ + "LUXMinerS9", +] diff --git a/pyasic/miners/antminer/marathon/X19/__init__.py b/pyasic/miners/antminer/marathon/X19/__init__.py index b4ae21016..60c0a0d3b 100644 --- a/pyasic/miners/antminer/marathon/X19/__init__.py +++ b/pyasic/miners/antminer/marathon/X19/__init__.py @@ -7,3 +7,13 @@ MaraS19Pro, MaraS19XP, ) + +__all__ = [ + "MaraS19", + "MaraS19j", + "MaraS19jNoPIC", + "MaraS19jPro", + "MaraS19KPro", + "MaraS19Pro", + "MaraS19XP", +] diff --git a/pyasic/miners/antminer/marathon/X21/__init__.py b/pyasic/miners/antminer/marathon/X21/__init__.py index ce2c1f97b..48596d365 100644 --- a/pyasic/miners/antminer/marathon/X21/__init__.py +++ b/pyasic/miners/antminer/marathon/X21/__init__.py @@ -1,2 +1,7 @@ from .S21 import MaraS21 from .T21 import MaraT21 + +__all__ = [ + "MaraS21", + "MaraT21", +] diff --git a/pyasic/miners/antminer/mskminer/X19/S19.py b/pyasic/miners/antminer/mskminer/X19/S19.py index 71a776952..b0fd42d80 100644 --- a/pyasic/miners/antminer/mskminer/X19/S19.py +++ b/pyasic/miners/antminer/mskminer/X19/S19.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends.mskminer import MSKMiner -from pyasic.miners.device.models import ( +from pyasic.miners.device.models.antminer.X19.S19 import ( S19NoPIC, ) diff --git a/pyasic/miners/antminer/mskminer/X19/__init__.py b/pyasic/miners/antminer/mskminer/X19/__init__.py index 06104eafa..4e360b605 100644 --- a/pyasic/miners/antminer/mskminer/X19/__init__.py +++ b/pyasic/miners/antminer/mskminer/X19/__init__.py @@ -15,3 +15,7 @@ # ------------------------------------------------------------------------------ from .S19 import MSKMinerS19NoPIC + +__all__ = [ + "MSKMinerS19NoPIC", +] diff --git a/pyasic/miners/antminer/vnish/X17/S17.py b/pyasic/miners/antminer/vnish/X17/S17.py index 07c135c5d..cf9e295f2 100644 --- a/pyasic/miners/antminer/vnish/X17/S17.py +++ b/pyasic/miners/antminer/vnish/X17/S17.py @@ -14,7 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import S17Plus, S17Pro +from pyasic.miners.device.models.antminer.X17.S17 import S17Plus, S17Pro class VNishS17Plus(VNish, S17Plus): diff --git a/pyasic/miners/antminer/vnish/X17/__init__.py b/pyasic/miners/antminer/vnish/X17/__init__.py index 68430b816..ab5ea495c 100644 --- a/pyasic/miners/antminer/vnish/X17/__init__.py +++ b/pyasic/miners/antminer/vnish/X17/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .S17 import VNishS17Plus, VNishS17Pro + +__all__ = ["VNishS17Plus", "VNishS17Pro"] diff --git a/pyasic/miners/antminer/vnish/X19/S19.py b/pyasic/miners/antminer/vnish/X19/S19.py index d79797e36..2383a6ab0 100644 --- a/pyasic/miners/antminer/vnish/X19/S19.py +++ b/pyasic/miners/antminer/vnish/X19/S19.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import ( +from pyasic.miners.device.models.antminer.X19.S19 import ( S19, S19XP, S19a, diff --git a/pyasic/miners/antminer/vnish/X19/T19.py b/pyasic/miners/antminer/vnish/X19/T19.py index a5c781a6e..fab9ca112 100644 --- a/pyasic/miners/antminer/vnish/X19/T19.py +++ b/pyasic/miners/antminer/vnish/X19/T19.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import T19 +from pyasic.miners.device.models.antminer.X19.T19 import T19 class VNishT19(VNish, T19): diff --git a/pyasic/miners/antminer/vnish/X19/__init__.py b/pyasic/miners/antminer/vnish/X19/__init__.py index 60e67259a..37992474b 100644 --- a/pyasic/miners/antminer/vnish/X19/__init__.py +++ b/pyasic/miners/antminer/vnish/X19/__init__.py @@ -31,3 +31,21 @@ VNishS19XPHydro, ) from .T19 import VNishT19 + +__all__ = [ + "VNishS19", + "VNishS19a", + "VNishS19aPro", + "VNishS19Hydro", + "VNishS19i", + "VNishS19j", + "VNishS19jPro", + "VNishS19kPro", + "VNishS19NoPIC", + "VNishS19Pro", + "VNishS19ProA", + "VNishS19ProHydro", + "VNishS19XP", + "VNishS19XPHydro", + "VNishT19", +] diff --git a/pyasic/miners/antminer/vnish/X21/S21.py b/pyasic/miners/antminer/vnish/X21/S21.py index 9c2fc543a..1d50c193f 100644 --- a/pyasic/miners/antminer/vnish/X21/S21.py +++ b/pyasic/miners/antminer/vnish/X21/S21.py @@ -15,7 +15,13 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro +from pyasic.miners.device.models.antminer.X21.S21 import ( + S21, + S21Hydro, + S21Plus, + S21PlusHydro, + S21Pro, +) class VNishS21(VNish, S21): diff --git a/pyasic/miners/antminer/vnish/X21/T21.py b/pyasic/miners/antminer/vnish/X21/T21.py index c94bb37a4..531a352de 100644 --- a/pyasic/miners/antminer/vnish/X21/T21.py +++ b/pyasic/miners/antminer/vnish/X21/T21.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import T21 +from pyasic.miners.device.models.antminer.X21.T21 import T21 class VNishT21(VNish, T21): diff --git a/pyasic/miners/antminer/vnish/X21/__init__.py b/pyasic/miners/antminer/vnish/X21/__init__.py index a1dbea14b..0a3d2fd5f 100644 --- a/pyasic/miners/antminer/vnish/X21/__init__.py +++ b/pyasic/miners/antminer/vnish/X21/__init__.py @@ -22,3 +22,12 @@ VNishS21Pro, ) from .T21 import VNishT21 + +__all__ = [ + "VNishS21", + "VNishS21Hydro", + "VNishS21Plus", + "VNishS21PlusHydro", + "VNishS21Pro", + "VNishT21", +] diff --git a/pyasic/miners/antminer/vnish/X3/L3.py b/pyasic/miners/antminer/vnish/X3/L3.py index b1854f1dc..e49043b50 100644 --- a/pyasic/miners/antminer/vnish/X3/L3.py +++ b/pyasic/miners/antminer/vnish/X3/L3.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import L3Plus +from pyasic.miners.device.models.antminer.X3.L3 import L3Plus class VNishL3Plus(VNish, L3Plus): diff --git a/pyasic/miners/antminer/vnish/X3/__init__.py b/pyasic/miners/antminer/vnish/X3/__init__.py index 40b546003..1c31c97e3 100644 --- a/pyasic/miners/antminer/vnish/X3/__init__.py +++ b/pyasic/miners/antminer/vnish/X3/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .L3 import VNishL3Plus + +__all__ = ["VNishL3Plus"] diff --git a/pyasic/miners/antminer/vnish/X7/L7.py b/pyasic/miners/antminer/vnish/X7/L7.py index e65d9199c..07dc83591 100644 --- a/pyasic/miners/antminer/vnish/X7/L7.py +++ b/pyasic/miners/antminer/vnish/X7/L7.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import L7 +from pyasic.miners.device.models.antminer.X7.L7 import L7 class VNishL7(VNish, L7): diff --git a/pyasic/miners/antminer/vnish/X7/__init__.py b/pyasic/miners/antminer/vnish/X7/__init__.py index 61a18dbe3..a45ee4ea3 100644 --- a/pyasic/miners/antminer/vnish/X7/__init__.py +++ b/pyasic/miners/antminer/vnish/X7/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .L7 import VNishL7 + +__all__ = ["VNishL7"] diff --git a/pyasic/miners/antminer/vnish/X9/L9.py b/pyasic/miners/antminer/vnish/X9/L9.py index 188aa5750..e4106a147 100644 --- a/pyasic/miners/antminer/vnish/X9/L9.py +++ b/pyasic/miners/antminer/vnish/X9/L9.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import VNish -from pyasic.miners.device.models import L9 +from pyasic.miners.device.models.antminer.X9.L9 import L9 class VNishL9(VNish, L9): diff --git a/pyasic/miners/antminer/vnish/X9/__init__.py b/pyasic/miners/antminer/vnish/X9/__init__.py index 7c50538bd..1afeb989e 100644 --- a/pyasic/miners/antminer/vnish/X9/__init__.py +++ b/pyasic/miners/antminer/vnish/X9/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .L9 import VNishL9 + +__all__ = ["VNishL9"] diff --git a/pyasic/miners/auradine/__init__.py b/pyasic/miners/auradine/__init__.py index b6cc0d732..23ede7621 100644 --- a/pyasic/miners/auradine/__init__.py +++ b/pyasic/miners/auradine/__init__.py @@ -1 +1,19 @@ -from .flux import * +from .flux import ( + AuradineFluxAD2500, + AuradineFluxAD3500, + AuradineFluxAI2500, + AuradineFluxAI3680, + AuradineFluxAT1500, + AuradineFluxAT2860, + AuradineFluxAT2880, +) + +__all__ = [ + "AuradineFluxAD2500", + "AuradineFluxAD3500", + "AuradineFluxAI2500", + "AuradineFluxAI3680", + "AuradineFluxAT1500", + "AuradineFluxAT2860", + "AuradineFluxAT2880", +] diff --git a/pyasic/miners/auradine/flux/AD/AT1.py b/pyasic/miners/auradine/flux/AD/AT1.py index e69aa6671..71ede659b 100644 --- a/pyasic/miners/auradine/flux/AD/AT1.py +++ b/pyasic/miners/auradine/flux/AD/AT1.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import Auradine -from pyasic.miners.device.models import AuradineAT1500 +from pyasic.miners.device.models.auradine.AT.AT1 import AuradineAT1500 class AuradineFluxAT1500(AuradineAT1500, Auradine): diff --git a/pyasic/miners/auradine/flux/AD/AT2.py b/pyasic/miners/auradine/flux/AD/AT2.py index fc0010de8..5b51f3248 100644 --- a/pyasic/miners/auradine/flux/AD/AT2.py +++ b/pyasic/miners/auradine/flux/AD/AT2.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import Auradine -from pyasic.miners.device.models import AuradineAT2860, AuradineAT2880 +from pyasic.miners.device.models.auradine.AT.AT2 import AuradineAT2860, AuradineAT2880 class AuradineFluxAT2860(AuradineAT2860, Auradine): diff --git a/pyasic/miners/auradine/flux/AD/__init__.py b/pyasic/miners/auradine/flux/AD/__init__.py index 0be7a6fe2..54461c00c 100644 --- a/pyasic/miners/auradine/flux/AD/__init__.py +++ b/pyasic/miners/auradine/flux/AD/__init__.py @@ -1,2 +1,4 @@ from .AT1 import AuradineFluxAT1500 from .AT2 import AuradineFluxAT2860, AuradineFluxAT2880 + +__all__ = ["AuradineFluxAT1500", "AuradineFluxAT2860", "AuradineFluxAT2880"] diff --git a/pyasic/miners/auradine/flux/AI/AI2.py b/pyasic/miners/auradine/flux/AI/AI2.py index 303fe4052..c7e72866e 100644 --- a/pyasic/miners/auradine/flux/AI/AI2.py +++ b/pyasic/miners/auradine/flux/AI/AI2.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import Auradine -from pyasic.miners.device.models import AuradineAI2500 +from pyasic.miners.device.models.auradine.AI.AI2 import AuradineAI2500 class AuradineFluxAI2500(AuradineAI2500, Auradine): diff --git a/pyasic/miners/auradine/flux/AI/AI3.py b/pyasic/miners/auradine/flux/AI/AI3.py index 1ee2a08b4..a19673505 100644 --- a/pyasic/miners/auradine/flux/AI/AI3.py +++ b/pyasic/miners/auradine/flux/AI/AI3.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import Auradine -from pyasic.miners.device.models import AuradineAI3680 +from pyasic.miners.device.models.auradine.AI.AI3 import AuradineAI3680 class AuradineFluxAI3680(AuradineAI3680, Auradine): diff --git a/pyasic/miners/auradine/flux/AI/__init__.py b/pyasic/miners/auradine/flux/AI/__init__.py index ebed7183e..5b5daf220 100644 --- a/pyasic/miners/auradine/flux/AI/__init__.py +++ b/pyasic/miners/auradine/flux/AI/__init__.py @@ -1,2 +1,4 @@ from .AI2 import AuradineFluxAI2500 from .AI3 import AuradineFluxAI3680 + +__all__ = ["AuradineFluxAI2500", "AuradineFluxAI3680"] diff --git a/pyasic/miners/auradine/flux/AT/AD2.py b/pyasic/miners/auradine/flux/AT/AD2.py index 31ef88c68..8c1a7ad6f 100644 --- a/pyasic/miners/auradine/flux/AT/AD2.py +++ b/pyasic/miners/auradine/flux/AT/AD2.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import Auradine -from pyasic.miners.device.models import AuradineAD2500 +from pyasic.miners.device.models.auradine.AD.AD2 import AuradineAD2500 class AuradineFluxAD2500(AuradineAD2500, Auradine): diff --git a/pyasic/miners/auradine/flux/AT/AD3.py b/pyasic/miners/auradine/flux/AT/AD3.py index 80bd1aca7..6ef7635da 100644 --- a/pyasic/miners/auradine/flux/AT/AD3.py +++ b/pyasic/miners/auradine/flux/AT/AD3.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import Auradine -from pyasic.miners.device.models import AuradineAD3500 +from pyasic.miners.device.models.auradine.AD.AD3 import AuradineAD3500 class AuradineFluxAD3500(AuradineAD3500, Auradine): diff --git a/pyasic/miners/auradine/flux/AT/__init__.py b/pyasic/miners/auradine/flux/AT/__init__.py index 3b29e4f2a..2d7bad76a 100644 --- a/pyasic/miners/auradine/flux/AT/__init__.py +++ b/pyasic/miners/auradine/flux/AT/__init__.py @@ -1,2 +1,4 @@ from .AD2 import AuradineFluxAD2500 from .AD3 import AuradineFluxAD3500 + +__all__ = ["AuradineFluxAD2500", "AuradineFluxAD3500"] diff --git a/pyasic/miners/auradine/flux/__init__.py b/pyasic/miners/auradine/flux/__init__.py index 9a346701b..e65d2a61b 100644 --- a/pyasic/miners/auradine/flux/__init__.py +++ b/pyasic/miners/auradine/flux/__init__.py @@ -1,3 +1,13 @@ -from .AD import * -from .AI import * -from .AT import * +from .AD import AuradineFluxAT1500, AuradineFluxAT2860, AuradineFluxAT2880 +from .AI import AuradineFluxAI2500, AuradineFluxAI3680 +from .AT import AuradineFluxAD2500, AuradineFluxAD3500 + +__all__ = [ + "AuradineFluxAD2500", + "AuradineFluxAD3500", + "AuradineFluxAI2500", + "AuradineFluxAI3680", + "AuradineFluxAT1500", + "AuradineFluxAT2860", + "AuradineFluxAT2880", +] diff --git a/pyasic/miners/avalonminer/__init__.py b/pyasic/miners/avalonminer/__init__.py index 5d1891a7a..9b99f146c 100644 --- a/pyasic/miners/avalonminer/__init__.py +++ b/pyasic/miners/avalonminer/__init__.py @@ -14,4 +14,42 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .cgminer import * +from .cgminer import ( + CGMinerAvalon721, + CGMinerAvalon741, + CGMinerAvalon761, + CGMinerAvalon821, + CGMinerAvalon841, + CGMinerAvalon851, + CGMinerAvalon921, + CGMinerAvalon1026, + CGMinerAvalon1047, + CGMinerAvalon1066, + CGMinerAvalon1126Pro, + CGMinerAvalon1166Pro, + CGMinerAvalon1246, + CGMinerAvalon1566, + CGMinerAvalonNano3, + CGMinerAvalonNano3s, + CGMinerAvalonQHome, +) + +__all__ = [ + "CGMinerAvalon721", + "CGMinerAvalon741", + "CGMinerAvalon761", + "CGMinerAvalon821", + "CGMinerAvalon841", + "CGMinerAvalon851", + "CGMinerAvalon921", + "CGMinerAvalon1026", + "CGMinerAvalon1047", + "CGMinerAvalon1066", + "CGMinerAvalon1126Pro", + "CGMinerAvalon1166Pro", + "CGMinerAvalon1246", + "CGMinerAvalon1566", + "CGMinerAvalonNano3", + "CGMinerAvalonNano3s", + "CGMinerAvalonQHome", +] diff --git a/pyasic/miners/avalonminer/cgminer/A10X/__init__.py b/pyasic/miners/avalonminer/cgminer/A10X/__init__.py index 87eb30233..54831d55b 100644 --- a/pyasic/miners/avalonminer/cgminer/A10X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A10X/__init__.py @@ -17,3 +17,5 @@ from .A1026 import CGMinerAvalon1026 from .A1047 import CGMinerAvalon1047 from .A1066 import CGMinerAvalon1066 + +__all__ = ["CGMinerAvalon1026", "CGMinerAvalon1047", "CGMinerAvalon1066"] diff --git a/pyasic/miners/avalonminer/cgminer/A11X/__init__.py b/pyasic/miners/avalonminer/cgminer/A11X/__init__.py index 1eab02ee3..1365dc179 100644 --- a/pyasic/miners/avalonminer/cgminer/A11X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A11X/__init__.py @@ -16,3 +16,5 @@ from .A1126 import CGMinerAvalon1126Pro from .A1166 import CGMinerAvalon1166Pro + +__all__ = ["CGMinerAvalon1126Pro", "CGMinerAvalon1166Pro"] diff --git a/pyasic/miners/avalonminer/cgminer/A12X/__init__.py b/pyasic/miners/avalonminer/cgminer/A12X/__init__.py index e590883d4..2e302f6f5 100644 --- a/pyasic/miners/avalonminer/cgminer/A12X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A12X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A1246 import CGMinerAvalon1246 + +__all__ = ["CGMinerAvalon1246"] diff --git a/pyasic/miners/avalonminer/cgminer/A15X/__init__.py b/pyasic/miners/avalonminer/cgminer/A15X/__init__.py index 83c79ef4f..3deb01f6f 100644 --- a/pyasic/miners/avalonminer/cgminer/A15X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A15X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A1566 import CGMinerAvalon1566 + +__all__ = ["CGMinerAvalon1566"] diff --git a/pyasic/miners/avalonminer/cgminer/A7X/__init__.py b/pyasic/miners/avalonminer/cgminer/A7X/__init__.py index 487995068..a7298997f 100644 --- a/pyasic/miners/avalonminer/cgminer/A7X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A7X/__init__.py @@ -17,3 +17,5 @@ from .A721 import CGMinerAvalon721 from .A741 import CGMinerAvalon741 from .A761 import CGMinerAvalon761 + +__all__ = ["CGMinerAvalon721", "CGMinerAvalon741", "CGMinerAvalon761"] diff --git a/pyasic/miners/avalonminer/cgminer/A8X/__init__.py b/pyasic/miners/avalonminer/cgminer/A8X/__init__.py index 794260173..73499f890 100644 --- a/pyasic/miners/avalonminer/cgminer/A8X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A8X/__init__.py @@ -17,3 +17,5 @@ from .A821 import CGMinerAvalon821 from .A841 import CGMinerAvalon841 from .A851 import CGMinerAvalon851 + +__all__ = ["CGMinerAvalon821", "CGMinerAvalon841", "CGMinerAvalon851"] diff --git a/pyasic/miners/avalonminer/cgminer/A9X/__init__.py b/pyasic/miners/avalonminer/cgminer/A9X/__init__.py index 239cbed20..737b6edb2 100644 --- a/pyasic/miners/avalonminer/cgminer/A9X/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/A9X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A921 import CGMinerAvalon921 + +__all__ = ["CGMinerAvalon921"] diff --git a/pyasic/miners/avalonminer/cgminer/Q/__init__.py b/pyasic/miners/avalonminer/cgminer/Q/__init__.py index 59176da92..1ec3456b2 100644 --- a/pyasic/miners/avalonminer/cgminer/Q/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/Q/__init__.py @@ -1 +1,3 @@ from .Q import CGMinerAvalonQHome + +__all__ = ["CGMinerAvalonQHome"] diff --git a/pyasic/miners/avalonminer/cgminer/__init__.py b/pyasic/miners/avalonminer/cgminer/__init__.py index 9b6f020f8..072648861 100644 --- a/pyasic/miners/avalonminer/cgminer/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/__init__.py @@ -14,12 +14,32 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .A7X import * -from .A8X import * -from .A9X import * -from .A10X import * -from .A11X import * -from .A12X import * -from .A15X import * -from .nano import * -from .Q import * +from .A7X import CGMinerAvalon721, CGMinerAvalon741, CGMinerAvalon761 +from .A8X import CGMinerAvalon821, CGMinerAvalon841, CGMinerAvalon851 +from .A9X import CGMinerAvalon921 +from .A10X import CGMinerAvalon1026, CGMinerAvalon1047, CGMinerAvalon1066 +from .A11X import CGMinerAvalon1126Pro, CGMinerAvalon1166Pro +from .A12X import CGMinerAvalon1246 +from .A15X import CGMinerAvalon1566 +from .nano import CGMinerAvalonNano3, CGMinerAvalonNano3s +from .Q import CGMinerAvalonQHome + +__all__ = [ + "CGMinerAvalon721", + "CGMinerAvalon741", + "CGMinerAvalon761", + "CGMinerAvalon821", + "CGMinerAvalon841", + "CGMinerAvalon851", + "CGMinerAvalon921", + "CGMinerAvalon1026", + "CGMinerAvalon1047", + "CGMinerAvalon1066", + "CGMinerAvalon1126Pro", + "CGMinerAvalon1166Pro", + "CGMinerAvalon1246", + "CGMinerAvalon1566", + "CGMinerAvalonNano3", + "CGMinerAvalonNano3s", + "CGMinerAvalonQHome", +] diff --git a/pyasic/miners/avalonminer/cgminer/nano/__init__.py b/pyasic/miners/avalonminer/cgminer/nano/__init__.py index e4432bfbc..2fbb45b42 100644 --- a/pyasic/miners/avalonminer/cgminer/nano/__init__.py +++ b/pyasic/miners/avalonminer/cgminer/nano/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .nano3 import CGMinerAvalonNano3, CGMinerAvalonNano3s + +__all__ = ["CGMinerAvalonNano3", "CGMinerAvalonNano3s"] diff --git a/pyasic/miners/avalonminer/cgminer/nano/nano3.py b/pyasic/miners/avalonminer/cgminer/nano/nano3.py index 74fe0b95c..596d5a263 100644 --- a/pyasic/miners/avalonminer/cgminer/nano/nano3.py +++ b/pyasic/miners/avalonminer/cgminer/nano/nano3.py @@ -15,9 +15,12 @@ # ------------------------------------------------------------------------------ from typing import Any +from pydantic import BaseModel, ValidationError + from pyasic import APIError from pyasic.data.boards import HashBoard from pyasic.device.algorithm import AlgoHashRateType +from pyasic.logger import logger from pyasic.miners.backends import AvalonMiner from pyasic.miners.data import ( DataFunction, @@ -29,6 +32,14 @@ from pyasic.miners.device.models import AvalonNano3, AvalonNano3s from pyasic.web.avalonminer import AvalonMinerWebAPI + +class AvalonMinerInfo(BaseModel): + mac: str | None = None + + class Config: + extra = "allow" + + AVALON_NANO_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -159,18 +170,20 @@ async def _get_mac(self, web_minerinfo: dict[Any, Any] | None = None) -> str | N if web_minerinfo is not None: try: - mac = web_minerinfo.get("mac") - if mac is not None: - return mac.upper() - except (KeyError, ValueError): - pass + response = AvalonMinerInfo.model_validate(web_minerinfo) + if response.mac: + return response.mac.upper() + except ValidationError as e: + logger.warning(f"{self} - Failed to parse miner info for MAC: {e}") return None class CGMinerAvalonNano3s(AvalonMiner, AvalonNano3s): data_locations = AVALON_NANO3S_DATA_LOC - async def _get_wattage(self, rpc_estats: dict | None = None) -> int | None: + async def _get_wattage( + self, rpc_estats: dict[str, Any] | None = None + ) -> int | None: if rpc_estats is None: try: rpc_estats = await self.rpc.estats() @@ -186,7 +199,7 @@ async def _get_wattage(self, rpc_estats: dict | None = None) -> int | None: return None async def _get_hashrate( - self, rpc_estats: dict | None = None + self, rpc_estats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_estats is None: try: @@ -204,7 +217,9 @@ async def _get_hashrate( pass return None - async def _get_hashboards(self, rpc_estats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_estats: dict[str, Any] | None = None + ) -> list[HashBoard]: hashboards = await AvalonMiner._get_hashboards(self, rpc_estats) if rpc_estats is None: diff --git a/pyasic/miners/backends/__init__.py b/pyasic/miners/backends/__init__.py index 4d9fa152e..d06327f94 100644 --- a/pyasic/miners/backends/__init__.py +++ b/pyasic/miners/backends/__init__.py @@ -36,3 +36,38 @@ from .unknown import UnknownMiner from .vnish import VNish from .whatsminer import M2X, M3X, M5X, M6X, M7X + +__all__ = [ + "AntminerModern", + "AntminerOld", + "Auradine", + "AvalonMiner", + "BFGMiner", + "BitAxe", + "BMMiner", + "BOSer", + "BOSMiner", + "BTMiner", + "BTMinerV2", + "BTMinerV3", + "CGMiner", + "ElphapexMiner", + "ePIC", + "GoldshellMiner", + "BlackMiner", + "HiveonModern", + "HiveonOld", + "IceRiver", + "Innosilicon", + "LuckyMiner", + "LUXMiner", + "MaraMiner", + "MSKMiner", + "UnknownMiner", + "VNish", + "M2X", + "M3X", + "M5X", + "M6X", + "M7X", +] diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index bdce3ddd9..67c223e87 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -16,12 +16,15 @@ import logging from pathlib import Path +from typing import Any -from pyasic.config import MinerConfig, MiningModeConfig -from pyasic.data import Fan, HashBoard -from pyasic.data.error_codes import X19Error +from pyasic.config import MinerConfig +from pyasic.config.mining import MiningModeConfig +from pyasic.data.boards import HashBoard +from pyasic.data.error_codes.X19 import X19Error +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.backends.cgminer import CGMiner @@ -227,7 +230,7 @@ async def resume_mining(self) -> bool: return True async def _get_hostname( - self, web_get_system_info: dict | None = None + self, web_get_system_info: dict[str, Any] | None = None ) -> str | None: if web_get_system_info is None: try: @@ -237,12 +240,14 @@ async def _get_hostname( if web_get_system_info is not None: try: - return web_get_system_info["hostname"] + return str(web_get_system_info["hostname"]) except KeyError: pass return None - async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: + async def _get_mac( + self, web_get_system_info: dict[str, Any] | None = None + ) -> str | None: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -251,20 +256,20 @@ async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: if web_get_system_info is not None: try: - return web_get_system_info["macaddr"] + return str(web_get_system_info["macaddr"]) except KeyError: pass try: data = await self.web.get_network_info() if data: - return data["macaddr"] + return str(data["macaddr"]) except KeyError: pass return None async def _get_errors( # type: ignore[override] - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> list[X19Error]: if web_summary is None: try: @@ -357,7 +362,7 @@ async def _get_hashboards(self) -> list[HashBoard]: # type: ignore[override] return hashboards async def _get_fault_light( - self, web_get_blink_status: dict | None = None + self, web_get_blink_status: dict[str, Any] | None = None ) -> bool | None: if self.light: return self.light @@ -376,7 +381,7 @@ async def _get_fault_light( return self.light async def _get_expected_hashrate( - self, rpc_stats: dict | None = None + self, rpc_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_stats is None: try: @@ -399,7 +404,7 @@ async def _get_expected_hashrate( return None async def _get_serial_number( - self, web_get_system_info: dict | None = None + self, web_get_system_info: dict[str, Any] | None = None ) -> str | None: if web_get_system_info is None: try: @@ -409,7 +414,7 @@ async def _get_serial_number( if web_get_system_info is not None: try: - return web_get_system_info["serinum"] + return str(web_get_system_info["serinum"]) except LookupError: pass return None @@ -421,7 +426,7 @@ async def set_static_ip( gateway: str, subnet_mask: str = "255.255.255.0", hostname: str | None = None, - ): + ) -> None: if not hostname: hostname = await self.get_hostname() or "" await self.web.set_network_conf( @@ -433,14 +438,14 @@ async def set_static_ip( protocol=2, ) - async def set_dhcp(self, hostname: str | None = None): + async def set_dhcp(self, hostname: str | None = None) -> None: if not hostname: hostname = await self.get_hostname() or "" await self.web.set_network_conf( ip="", dns="", gateway="", subnet_mask="", hostname=hostname, protocol=1 ) - async def set_hostname(self, hostname: str): + async def set_hostname(self, hostname: str) -> None: cfg = await self.web.get_network_info() dns = cfg["conf_dnsservers"] gateway = cfg["conf_gateway"] @@ -456,7 +461,9 @@ async def set_hostname(self, hostname: str): protocol=protocol, ) - async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: + async def _is_mining( + self, web_get_conf: dict[str, Any] | None = None + ) -> bool | None: if web_get_conf is None: try: web_get_conf = await self.web.get_miner_conf() @@ -474,7 +481,7 @@ async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: pass return None - async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: + async def _get_uptime(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -488,7 +495,9 @@ async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: pass return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -589,7 +598,7 @@ async def _get_mac(self) -> str | None: try: data = await self.web.get_system_info() if data: - return data["macaddr"] + return str(data["macaddr"]) except KeyError: pass return None @@ -624,7 +633,7 @@ async def reboot(self) -> bool: return False async def _get_fault_light( - self, web_get_blink_status: dict | None = None + self, web_get_blink_status: dict[str, Any] | None = None ) -> bool | None: if self.light: return self.light @@ -643,7 +652,7 @@ async def _get_fault_light( return self.light async def _get_hostname( - self, web_get_system_info: dict | None = None + self, web_get_system_info: dict[str, Any] | None = None ) -> str | None: if web_get_system_info is None: try: @@ -653,12 +662,12 @@ async def _get_hostname( if web_get_system_info is not None: try: - return web_get_system_info["hostname"] + return str(web_get_system_info["hostname"]) except KeyError: pass return None - async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_stats: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -689,7 +698,9 @@ async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: pass return fans_data - async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] hashboards: list[HashBoard] = [] @@ -753,7 +764,9 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard return hashboards - async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: + async def _is_mining( + self, web_get_conf: dict[str, Any] | None = None + ) -> bool | None: if web_get_conf is None: try: web_get_conf = await self.web.get_miner_conf() @@ -780,7 +793,7 @@ async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: return None - async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: + async def _get_uptime(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 22e883a6b..21d935f24 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -15,10 +15,12 @@ # ------------------------------------------------------------------------------ import logging from enum import Enum +from typing import Any from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import ( DataFunction, @@ -93,7 +95,7 @@ class AuradineLEDColors(Enum): RED_FLASHING = 5 YELLOW_FLASHING = 6 - def __int__(self): + def __int__(self) -> int: return self.value @@ -109,7 +111,7 @@ class AuradineLEDCodes(Enum): CUSTOM1 = 101 CUSTOM2 = 102 - def __int__(self): + def __int__(self) -> int: return self.value @@ -201,7 +203,7 @@ async def upgrade_firmware( url: str | None = None, version: str | None = "latest", keep_settings: bool = False, - **kwargs, + **kwargs: Any, ) -> bool: """ Upgrade the firmware of the Auradine device. @@ -250,7 +252,7 @@ async def upgrade_firmware( ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_mac(self, web_ipreport: dict | None = None) -> str | None: + async def _get_mac(self, web_ipreport: dict[str, Any] | None = None) -> str | None: if web_ipreport is None: try: web_ipreport = await self.web.ipreport() @@ -259,12 +261,14 @@ async def _get_mac(self, web_ipreport: dict | None = None) -> str | None: if web_ipreport is not None: try: - return web_ipreport["IPReport"][0]["mac"].upper() + return str(web_ipreport["IPReport"][0]["mac"]).upper() except (LookupError, AttributeError): pass return None - async def _get_fw_ver(self, web_ipreport: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_ipreport: dict[str, Any] | None = None + ) -> str | None: if web_ipreport is None: try: web_ipreport = await self.web.ipreport() @@ -273,12 +277,14 @@ async def _get_fw_ver(self, web_ipreport: dict | None = None) -> str | None: if web_ipreport is not None: try: - return web_ipreport["IPReport"][0]["version"] + return str(web_ipreport["IPReport"][0]["version"]) except LookupError: pass return None - async def _get_hostname(self, web_ipreport: dict | None = None) -> str | None: + async def _get_hostname( + self, web_ipreport: dict[str, Any] | None = None + ) -> str | None: if web_ipreport is None: try: web_ipreport = await self.web.ipreport() @@ -287,13 +293,13 @@ async def _get_hostname(self, web_ipreport: dict | None = None) -> str | None: if web_ipreport is not None: try: - return web_ipreport["IPReport"][0]["hostname"] + return str(web_ipreport["IPReport"][0]["hostname"]) except LookupError: pass return None async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: @@ -312,7 +318,9 @@ async def _get_hashrate( return None async def _get_hashboards( - self, rpc_devs: dict | None = None, web_ipreport: dict | None = None + self, + rpc_devs: dict[str, Any] | None = None, + web_ipreport: dict[str, Any] | None = None, ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -358,7 +366,7 @@ async def _get_hashboards( return hashboards - async def _get_wattage(self, web_psu: dict | None = None) -> int | None: + async def _get_wattage(self, web_psu: dict[str, Any] | None = None) -> int | None: if web_psu is None: try: web_psu = await self.web.get_psu() @@ -373,7 +381,9 @@ async def _get_wattage(self, web_psu: dict | None = None) -> int | None: return None async def _get_wattage_limit( - self, web_mode: dict | None = None, web_psu: dict | None = None + self, + web_mode: dict[str, Any] | None = None, + web_psu: dict[str, Any] | None = None, ) -> int | None: if web_mode is None: try: @@ -383,7 +393,7 @@ async def _get_wattage_limit( if web_mode is not None: try: - return web_mode["Mode"][0]["Power"] + return int(web_mode["Mode"][0]["Power"]) except (LookupError, TypeError, ValueError): pass @@ -400,7 +410,7 @@ async def _get_wattage_limit( pass return None - async def _get_fans(self, web_fan: dict | None = None) -> list[Fan]: + async def _get_fans(self, web_fan: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -419,7 +429,9 @@ async def _get_fans(self, web_fan: dict | None = None) -> list[Fan]: pass return fans - async def _get_fault_light(self, web_led: dict | None = None) -> bool | None: + async def _get_fault_light( + self, web_led: dict[str, Any] | None = None + ) -> bool | None: if web_led is None: try: web_led = await self.web.get_led() @@ -428,12 +440,14 @@ async def _get_fault_light(self, web_led: dict | None = None) -> bool | None: if web_led is not None: try: - return web_led["LED"][0]["Code"] == int(AuradineLEDCodes.LOCATE_MINER) + return bool( + web_led["LED"][0]["Code"] == int(AuradineLEDCodes.LOCATE_MINER) + ) except LookupError: pass return None - async def _is_mining(self, web_mode: dict | None = None) -> bool | None: + async def _is_mining(self, web_mode: dict[str, Any] | None = None) -> bool | None: if web_mode is None: try: web_mode = await self.web.get_mode() @@ -442,12 +456,14 @@ async def _is_mining(self, web_mode: dict | None = None) -> bool | None: if web_mode is not None: try: - return web_mode["Mode"][0]["Sleep"] == "off" + return bool(web_mode["Mode"][0]["Sleep"] == "off") except (LookupError, TypeError, ValueError): pass return None - async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: + async def _get_uptime( + self, rpc_summary: dict[str, Any] | None = None + ) -> int | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -456,7 +472,7 @@ async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: if rpc_summary is not None: try: - return rpc_summary["SUMMARY"][0]["Elapsed"] + return int(rpc_summary["SUMMARY"][0]["Elapsed"]) except LookupError: pass return None diff --git a/pyasic/miners/backends/avalonminer.py b/pyasic/miners/backends/avalonminer.py index 1574dd809..c4bfc384c 100644 --- a/pyasic/miners/backends/avalonminer.py +++ b/pyasic/miners/backends/avalonminer.py @@ -16,9 +16,11 @@ import copy import re import time +from typing import Any -from pyasic.data import Fan, HashBoard -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.backends.cgminer import CGMiner from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand @@ -161,11 +163,11 @@ async def resume_mining(self) -> bool: return False @staticmethod - def parse_estats(data): + def parse_estats(data: dict[str, Any]) -> dict[str, Any]: # Deep copy to preserve original structure new_data = copy.deepcopy(data) - def convert_value(val, key): + def convert_value(val: str, key: str) -> Any: val = val.strip() if key == "SYSTEMSTATU": @@ -173,7 +175,7 @@ def convert_value(val, key): if " " in val: parts = val.split() - result = [] + result: list[Any] = [] for part in parts: if part.isdigit(): result.append(int(part)) @@ -191,7 +193,7 @@ def convert_value(val, key): except ValueError: return val - def parse_info_block(info_str): + def parse_info_block(info_str: str) -> dict[str, Any]: pattern = re.compile(r"(\w+)\[([^\]]*)\]") return { key: convert_value(val, key) for key, val in pattern.findall(info_str) @@ -231,7 +233,7 @@ def parse_info_block(info_str): ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_mac(self, rpc_version: dict | None = None) -> str | None: + async def _get_mac(self, rpc_version: dict[str, Any] | None = None) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -251,7 +253,7 @@ async def _get_mac(self, rpc_version: dict | None = None) -> str | None: return None async def _get_hashrate( - self, rpc_devs: dict | None = None + self, rpc_devs: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_devs is None: try: @@ -269,7 +271,9 @@ async def _get_hashrate( pass return None - async def _get_hashboards(self, rpc_estats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_estats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -386,7 +390,7 @@ async def _get_hashboards(self, rpc_estats: dict | None = None) -> list[HashBoar return hashboards async def _get_expected_hashrate( - self, rpc_estats: dict | None = None + self, rpc_estats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_estats is None: try: @@ -405,7 +409,9 @@ async def _get_expected_hashrate( pass return None - async def _get_env_temp(self, rpc_estats: dict | None = None) -> float | None: + async def _get_env_temp( + self, rpc_estats: dict[str, Any] | None = None + ) -> float | None: if rpc_estats is None: try: rpc_estats = await self.rpc.estats() @@ -420,7 +426,9 @@ async def _get_env_temp(self, rpc_estats: dict | None = None) -> float | None: pass return None - async def _get_wattage_limit(self, rpc_estats: dict | None = None) -> int | None: + async def _get_wattage_limit( + self, rpc_estats: dict[str, Any] | None = None + ) -> int | None: if rpc_estats is None: try: rpc_estats = await self.rpc.estats() @@ -435,7 +443,9 @@ async def _get_wattage_limit(self, rpc_estats: dict | None = None) -> int | None pass return None - async def _get_wattage(self, rpc_estats: dict | None = None) -> int | None: + async def _get_wattage( + self, rpc_estats: dict[str, Any] | None = None + ) -> int | None: if rpc_estats is None: try: rpc_estats = await self.rpc.estats() @@ -450,7 +460,7 @@ async def _get_wattage(self, rpc_estats: dict | None = None) -> int | None: pass return None - async def _get_fans(self, rpc_estats: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_estats: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -474,7 +484,9 @@ async def _get_fans(self, rpc_estats: dict | None = None) -> list[Fan]: pass return fans_data - async def _get_fault_light(self, rpc_estats: dict | None = None) -> bool | None: + async def _get_fault_light( + self, rpc_estats: dict[str, Any] | None = None + ) -> bool | None: if self.light: return self.light if rpc_estats is None: diff --git a/pyasic/miners/backends/bfgminer.py b/pyasic/miners/backends/bfgminer.py index e93a68e39..1c99038fc 100644 --- a/pyasic/miners/backends/bfgminer.py +++ b/pyasic/miners/backends/bfgminer.py @@ -14,10 +14,13 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware @@ -80,7 +83,9 @@ async def get_config(self) -> MinerConfig: ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -95,7 +100,9 @@ async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: return self.api_ver - async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_fw_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -111,7 +118,7 @@ async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: return self.fw_ver async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # get hr from API if rpc_summary is None: @@ -132,7 +139,9 @@ async def _get_hashrate( pass return None - async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -194,7 +203,7 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard return hashboards - async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_stats: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -227,7 +236,9 @@ async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: return fans - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -258,7 +269,7 @@ async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: return pools_data async def _get_expected_hashrate( - self, rpc_stats: dict | None = None + self, rpc_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # X19 method, not sure compatibility if rpc_stats is None: diff --git a/pyasic/miners/backends/bmminer.py b/pyasic/miners/backends/bmminer.py index 5190957c1..4b44b17fa 100644 --- a/pyasic/miners/backends/bmminer.py +++ b/pyasic/miners/backends/bmminer.py @@ -15,10 +15,13 @@ # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware @@ -85,7 +88,9 @@ async def get_config(self) -> MinerConfig: ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -100,7 +105,9 @@ async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: return self.api_ver - async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_fw_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -116,7 +123,7 @@ async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: return self.fw_ver async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # get hr from API if rpc_summary is None: @@ -137,7 +144,9 @@ async def _get_hashrate( pass return None - async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -212,7 +221,7 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard return hashboards - async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_stats: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -245,7 +254,7 @@ async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: return fans async def _get_expected_hashrate( - self, rpc_stats: dict | None = None + self, rpc_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # X19 method, not sure compatibility if rpc_stats is None: @@ -270,7 +279,7 @@ async def _get_expected_hashrate( pass return None - async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: + async def _get_uptime(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -284,7 +293,9 @@ async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: pass return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() diff --git a/pyasic/miners/backends/braiins_os.py b/pyasic/miners/backends/braiins_os.py index 7c3a6b53e..3a754c077 100644 --- a/pyasic/miners/backends/braiins_os.py +++ b/pyasic/miners/backends/braiins_os.py @@ -16,9 +16,11 @@ import base64 import logging import time +from typing import Any import aiofiles import tomli_w +from pydantic import BaseModel, Field, ValidationError try: import tomllib @@ -27,11 +29,13 @@ from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePowerTune -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard from pyasic.data.error_codes import BraiinsOSError, MinerErrorData +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError +from pyasic.logger import logger from pyasic.miners.data import ( DataFunction, DataLocations, @@ -45,6 +49,360 @@ from pyasic.web.braiins_os import BOSerWebAPI, BOSMinerWebAPI from pyasic.web.braiins_os.proto.braiins.bos.v1 import SaveAction + +class BOSNetworkInterfaceStatus(BaseModel): + macaddr: str | None = None + + class Config: + extra = "allow" + + +class BOSInfoResponse(BaseModel): + version: str | None = None + + class Config: + extra = "allow" + + +class BOSVersionResponse(BaseModel): + api: str | None = Field(None, alias="API") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSVersionWrapper(BaseModel): + version: list[BOSVersionResponse] = Field([], alias="VERSION") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSSummaryItem(BaseModel): + mhs_1m: float | None = Field(None, alias="MHS 1m") + elapsed: int | None = Field(None, alias="Elapsed") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSSummaryWrapper(BaseModel): + summary: list[BOSSummaryItem] = Field([], alias="SUMMARY") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSTempItem(BaseModel): + id: int = Field(alias="ID") + chip: float | None = Field(None, alias="Chip") + board: float | None = Field(None, alias="Board") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSTempsWrapper(BaseModel): + temps: list[BOSTempItem] = Field([], alias="TEMPS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSDevDetailsItem(BaseModel): + id: int = Field(alias="ID") + chips: int | None = Field(None, alias="Chips") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSDevDetailsWrapper(BaseModel): + devdetails: list[BOSDevDetailsItem] = Field([], alias="DEVDETAILS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSDevsItem(BaseModel): + id: int = Field(alias="ID") + mhs_1m: float | None = Field(None, alias="MHS 1m") + nominal_mhs: float | None = Field(None, alias="Nominal MHS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSDevsWrapper(BaseModel): + devs: list[BOSDevsItem] = Field([], alias="DEVS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSTunerChainStatus(BaseModel): + hashchain_index: int = Field(alias="HashchainIndex") + status: str | None = Field(None, alias="Status") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSTunerStatusItem(BaseModel): + approximate_miner_power_consumption: float | None = Field( + None, alias="ApproximateMinerPowerConsumption" + ) + power_limit: int | None = Field(None, alias="PowerLimit") + tuner_chain_status: list[BOSTunerChainStatus] | None = Field( + None, alias="TunerChainStatus" + ) + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSTunerStatusWrapper(BaseModel): + tunerstatus: list[BOSTunerStatusItem] = Field([], alias="TUNERSTATUS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSFanItem(BaseModel): + rpm: int | None = Field(None, alias="RPM") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSFansWrapper(BaseModel): + fans: list[BOSFanItem] = Field([], alias="FANS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSStatusItem(BaseModel): + msg: str | None = Field(None, alias="Msg") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSStatusWrapper(BaseModel): + status: list[BOSStatusItem] = Field([], alias="STATUS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSPoolItem(BaseModel): + pool: int | None = Field(None, alias="POOL") + url: str | None = Field(None, alias="URL") + user: str | None = Field(None, alias="User") + accepted: int | None = Field(None, alias="Accepted") + rejected: int | None = Field(None, alias="Rejected") + get_failures: int | None = Field(None, alias="Get Failures") + remote_failures: int | None = Field(None, alias="Remote Failures") + stratum_active: bool | None = Field(None, alias="Stratum Active") + status: str | None = Field(None, alias="Status") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSPoolsWrapper(BaseModel): + pools: list[BOSPoolItem] = Field([], alias="POOLS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerBosVersion(BaseModel): + current: str | None = None + + class Config: + extra = "allow" + + +class BOSerHashrateInfo(BaseModel): + gigahashPerSecond: float | None = None + + class Config: + extra = "allow" + + +class BOSerMinerDetails(BaseModel): + mac_address: str | None = Field(None, alias="macAddress") + hostname: str | None = None + bos_version: BOSerBosVersion | None = Field(None, alias="bosVersion") + sticker_hashrate: BOSerHashrateInfo | None = Field(None, alias="stickerHashrate") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerHashBoardStats(BaseModel): + real_hashrate: dict[str, BOSerHashrateInfo] | None = Field( + None, alias="realHashrate" + ) + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerTemperature(BaseModel): + degree_c: float | None = Field(None, alias="degreeC") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerChipTemp(BaseModel): + temperature: BOSerTemperature | None = None + + class Config: + extra = "allow" + + +class BOSerHashBoard(BaseModel): + id: str | None = None + chips_count: int | None = Field(None, alias="chipsCount") + board_temp: BOSerTemperature | None = Field(None, alias="boardTemp") + highest_chip_temp: BOSerChipTemp | None = Field(None, alias="highestChipTemp") + stats: BOSerHashBoardStats | None = None + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerHashBoards(BaseModel): + hashboards: list[BOSerHashBoard] = [] + + class Config: + extra = "allow" + + +class BOSerPowerTarget(BaseModel): + watt: int | None = None + + class Config: + extra = "allow" + + +class BOSerPowerStats(BaseModel): + approximated_consumption: BOSerPowerTarget | None = Field( + None, alias="approximatedConsumption" + ) + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerMinerStats(BaseModel): + power_stats: BOSerPowerStats | None = Field(None, alias="powerStats") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerTunerMode(BaseModel): + power_target: dict[str, BOSerPowerTarget] | None = Field(None, alias="powerTarget") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerActivePerformanceMode(BaseModel): + tuner_mode: BOSerTunerMode | None = Field(None, alias="tunerMode") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerFan(BaseModel): + rpm: int | None = None + + class Config: + extra = "allow" + + +class BOSerCoolingState(BaseModel): + fans: list[BOSerFan] = [] + + class Config: + extra = "allow" + + +class BOSerPoolStats(BaseModel): + accepted_shares: int = Field(0, alias="acceptedShares") + rejected_shares: int = Field(0, alias="rejectedShares") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerPool(BaseModel): + url: str | None = None + user: str | None = None + active: bool = False + alive: bool | None = None + stats: BOSerPoolStats | None = None + + class Config: + extra = "allow" + + +class BOSerPoolGroup(BaseModel): + pools: list[BOSerPool] = [] + + class Config: + extra = "allow" + + +class BOSerPoolGroups(BaseModel): + pool_groups: list[BOSerPoolGroup] = Field([], alias="poolGroups") + + class Config: + populate_by_name = True + extra = "allow" + + +class BOSerLocateDeviceStatus(BaseModel): + enabled: bool | None = None + + class Config: + extra = "allow" + + BOSMINER_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -180,6 +538,9 @@ async def reboot(self) -> bool: async def get_config(self) -> MinerConfig: raw_data = await self.ssh.get_config_file() + if raw_data is None: + raise APIError("Failed to get config file.") + try: toml_data = tomllib.loads(raw_data) cfg = MinerConfig.from_bosminer(toml_data) @@ -236,7 +597,7 @@ async def set_static_ip( dns: str, gateway: str, subnet_mask: str = "255.255.255.0", - ): + ) -> None: cfg_data_lan = "\n\t".join( [ "config interface 'lan'", @@ -251,6 +612,9 @@ async def set_static_ip( ) data = await self.ssh.get_network_config() + if data is None: + raise APIError("Failed to get network config.") + split_data = data.split("\n\n") for idx, val in enumerate(split_data): if "config interface 'lan'" in val: @@ -259,7 +623,7 @@ async def set_static_ip( await self.ssh.send_command("echo '" + config + "' > /etc/config/network") - async def set_dhcp(self): + async def set_dhcp(self) -> None: cfg_data_lan = "\n\t".join( [ "config interface 'lan'", @@ -270,6 +634,9 @@ async def set_dhcp(self): ) data = await self.ssh.get_network_config() + if data is None: + raise APIError("Failed to get network config.") + split_data = data.split("\n\n") for idx, val in enumerate(split_data): if "config interface 'lan'" in val: @@ -282,7 +649,9 @@ async def set_dhcp(self): ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_mac(self, web_net_conf: dict | list | None = None) -> str | None: + async def _get_mac( + self, web_net_conf: dict[str, Any] | list[Any] | None = None + ) -> str | None: if web_net_conf is None: try: web_net_conf = await self.web.get_net_conf() @@ -295,34 +664,47 @@ async def _get_mac(self, web_net_conf: dict | list | None = None) -> str | None: if web_net_conf is not None: try: - return web_net_conf[0]["macaddr"] - except LookupError: - pass + if isinstance(web_net_conf, list) and len(web_net_conf) > 0: + response = BOSNetworkInterfaceStatus.model_validate(web_net_conf[0]) + elif isinstance(web_net_conf, dict): + response = BOSNetworkInterfaceStatus.model_validate(web_net_conf) + else: + return None + + if response.macaddr: + return response.macaddr + except ValidationError as e: + logging.warning(f"{self} - Failed to parse network config for MAC: {e}") return None # could use ssh, but its slow and buggy # result = await self.send_ssh_command("cat /sys/class/net/eth0/address") # if result: # return result.upper().strip() - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() except APIError: return None - # Now get the API version if rpc_version is not None: try: - rpc_ver = rpc_version["VERSION"][0]["API"] - except LookupError: - rpc_ver = None - self.api_ver = rpc_ver - self.rpc.rpc_ver = self.api_ver # type: ignore + response = BOSVersionWrapper.model_validate(rpc_version) + if response.version and response.version[0].api: + self.api_ver = response.version[0].api + self.rpc.rpc_ver = self.api_ver # type: ignore + return self.api_ver + except ValidationError as e: + logging.warning(f"{self} - Failed to parse version for API: {e}") return self.api_ver - async def _get_fw_ver(self, web_bos_info: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_bos_info: dict[str, Any] | None = None + ) -> str | None: if web_bos_info is None: try: web_bos_info = await self.web.get_bos_info() @@ -337,17 +719,26 @@ async def _get_fw_ver(self, web_bos_info: dict | None = None) -> str | None: web_bos_info = web_bos_info["bos/info"] try: - ver = web_bos_info["version"].split("-")[5] - if "." in ver: - self.fw_ver = ver - except (LookupError, AttributeError): + response = BOSInfoResponse.model_validate(web_bos_info) + if response.version: + ver = response.version.split("-")[5] + if "." in ver: + self.fw_ver = ver + return self.fw_ver + except (ValidationError, AttributeError, IndexError) as e: + if isinstance(e, ValidationError): + logging.warning(f"{self} - Failed to parse BOS info for firmware: {e}") return None return self.fw_ver async def _get_hostname(self) -> str | None: try: - hostname = (await self.ssh.get_hostname()).strip() + hostname_result = await self.ssh.get_hostname() + if hostname_result is not None: + hostname = hostname_result.strip() + else: + return None except AttributeError: return None except Exception as e: @@ -356,7 +747,7 @@ async def _get_hostname(self) -> str | None: return hostname async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: @@ -366,19 +757,21 @@ async def _get_hashrate( if rpc_summary is not None: try: - return self.algo.hashrate( - rate=float(rpc_summary["SUMMARY"][0]["MHS 1m"]), - unit=self.algo.unit.MH, # type: ignore[attr-defined] - ).into(self.algo.unit.default) # type: ignore[attr-defined] - except (KeyError, IndexError, ValueError, TypeError): - pass + response = BOSSummaryWrapper.model_validate(rpc_summary) + if response.summary and response.summary[0].mhs_1m is not None: + return self.algo.hashrate( + rate=float(response.summary[0].mhs_1m), + unit=self.algo.unit.MH, # type: ignore[attr-defined] + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except ValidationError as e: + logging.warning(f"{self} - Failed to parse summary for hashrate: {e}") return None async def _get_hashboards( self, - rpc_temps: dict | None = None, - rpc_devdetails: dict | None = None, - rpc_devs: dict | None = None, + rpc_temps: dict[str, Any] | None = None, + rpc_devdetails: dict[str, Any] | None = None, + rpc_devs: dict[str, Any] | None = None, ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -414,47 +807,64 @@ async def _get_hashboards( rpc_devs = None if rpc_temps is not None: try: - offset = 6 if rpc_temps["TEMPS"][0]["ID"] in [6, 7, 8] else 1 - - for board in rpc_temps["TEMPS"]: - _id = board["ID"] - offset - chip_temp = round(board["Chip"]) - board_temp = round(board["Board"]) - hashboards[_id].chip_temp = chip_temp - hashboards[_id].temp = board_temp - except (IndexError, KeyError, ValueError, TypeError): - pass + temps_response = BOSTempsWrapper.model_validate(rpc_temps) + if temps_response.temps: + offset = 6 if temps_response.temps[0].id in [6, 7, 8] else 1 + for board in temps_response.temps: + if board.id is not None: + _id = board.id - offset + if 0 <= _id < len(hashboards): + if board.chip is not None: + hashboards[_id].chip_temp = round(board.chip) + if board.board is not None: + hashboards[_id].temp = round(board.board) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse temps for hashboards: {e}") if rpc_devdetails is not None: try: - offset = 6 if rpc_devdetails["DEVDETAILS"][0]["ID"] in [6, 7, 8] else 1 - - for board in rpc_devdetails["DEVDETAILS"]: - _id = board["ID"] - offset - chips = board["Chips"] - hashboards[_id].chips = chips - hashboards[_id].missing = False - except (IndexError, KeyError): - pass + devdetails_response = BOSDevDetailsWrapper.model_validate( + rpc_devdetails + ) + if devdetails_response.devdetails: + offset = ( + 6 if devdetails_response.devdetails[0].id in [6, 7, 8] else 1 + ) + for dev_detail in devdetails_response.devdetails: + if dev_detail.id is not None: + _id = dev_detail.id - offset + if 0 <= _id < len(hashboards): + if dev_detail.chips is not None: + hashboards[_id].chips = dev_detail.chips + hashboards[_id].missing = False + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse devdetails for hashboards: {e}" + ) if rpc_devs is not None: try: - offset = 6 if rpc_devs["DEVS"][0]["ID"] in [6, 7, 8] else 1 - - for board in rpc_devs["DEVS"]: - _id = board["ID"] - offset - hashboards[_id].hashrate = self.algo.hashrate( - rate=float(board["MHS 1m"]), - unit=self.algo.unit.MH, # type: ignore[attr-defined] - ).into( - self.algo.unit.default # type: ignore[attr-defined] - ) - except (IndexError, KeyError): - pass + devs_response = BOSDevsWrapper.model_validate(rpc_devs) + if devs_response.devs: + offset = 6 if devs_response.devs[0].id in [6, 7, 8] else 1 + for dev_item in devs_response.devs: + if dev_item.id is not None and dev_item.mhs_1m is not None: + _id = dev_item.id - offset + if 0 <= _id < len(hashboards): + hashboards[_id].hashrate = self.algo.hashrate( + rate=float(dev_item.mhs_1m), + unit=self.algo.unit.MH, # type: ignore[attr-defined] + ).into( + self.algo.unit.default # type: ignore[attr-defined] + ) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse devs for hashboards: {e}") return hashboards - async def _get_wattage(self, rpc_tunerstatus: dict | None = None) -> int | None: + async def _get_wattage( + self, rpc_tunerstatus: dict[str, Any] | None = None + ) -> int | None: if rpc_tunerstatus is None: try: rpc_tunerstatus = await self.rpc.tunerstatus() @@ -463,15 +873,21 @@ async def _get_wattage(self, rpc_tunerstatus: dict | None = None) -> int | None: if rpc_tunerstatus is not None: try: - return rpc_tunerstatus["TUNERSTATUS"][0][ - "ApproximateMinerPowerConsumption" - ] - except LookupError: - pass + response = BOSTunerStatusWrapper.model_validate(rpc_tunerstatus) + if ( + response.tunerstatus + and response.tunerstatus[0].approximate_miner_power_consumption + is not None + ): + return int( + response.tunerstatus[0].approximate_miner_power_consumption + ) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse tunerstatus for wattage: {e}") return None async def _get_wattage_limit( - self, rpc_tunerstatus: dict | None = None + self, rpc_tunerstatus: dict[str, Any] | None = None ) -> int | None: if rpc_tunerstatus is None: try: @@ -481,12 +897,19 @@ async def _get_wattage_limit( if rpc_tunerstatus is not None: try: - return rpc_tunerstatus["TUNERSTATUS"][0]["PowerLimit"] - except LookupError: - pass + response = BOSTunerStatusWrapper.model_validate(rpc_tunerstatus) + if ( + response.tunerstatus + and response.tunerstatus[0].power_limit is not None + ): + return int(response.tunerstatus[0].power_limit) + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse tunerstatus for wattage limit: {e}" + ) return None - async def _get_fans(self, rpc_fans: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_fans: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -497,17 +920,22 @@ async def _get_fans(self, rpc_fans: dict | None = None) -> list[Fan]: return [Fan() for _ in range(self.expected_fans)] if rpc_fans is not None: - fans = [] - for n in range(self.expected_fans): - try: - fans.append(Fan(speed=rpc_fans["FANS"][n]["RPM"])) - except (IndexError, KeyError): - pass - return fans + try: + response = BOSFansWrapper.model_validate(rpc_fans) + fans = [] + for n in range(self.expected_fans): + if n < len(response.fans) and response.fans[n].rpm is not None: + fans.append(Fan(speed=response.fans[n].rpm)) + else: + fans.append(Fan()) + return fans + except ValidationError as e: + logger.warning(f"{self} - Failed to parse fans: {e}") + return [Fan() for _ in range(self.expected_fans)] return [Fan() for _ in range(self.expected_fans)] async def _get_errors( - self, rpc_tunerstatus: dict | None = None + self, rpc_tunerstatus: dict[str, Any] | None = None ) -> list[MinerErrorData]: if rpc_tunerstatus is None: try: @@ -518,43 +946,52 @@ async def _get_errors( if rpc_tunerstatus is not None: errors = [] try: - chain_status = rpc_tunerstatus["TUNERSTATUS"][0]["TunerChainStatus"] - if chain_status and len(chain_status) > 0: - offset = ( - 6 if int(chain_status[0]["HashchainIndex"]) in [6, 7, 8] else 0 - ) + response = BOSTunerStatusWrapper.model_validate(rpc_tunerstatus) + if response.tunerstatus and response.tunerstatus[0].tuner_chain_status: + chain_status = response.tunerstatus[0].tuner_chain_status + if chain_status: + offset = ( + 6 if chain_status[0].hashchain_index in [6, 7, 8] else 0 + ) - for board in chain_status: - _id = board["HashchainIndex"] - offset - if board["Status"] not in [ - "Stable", - "Testing performance profile", - "Tuning individual chips", - ]: - _error = board["Status"].split(" {")[0] - _error = _error[0].lower() + _error[1:] - errors.append( - BraiinsOSError(error_message=f"Slot {_id} {_error}") - ) + for board in chain_status: + if board.hashchain_index is not None and board.status: + _id = board.hashchain_index - offset + if board.status not in [ + "Stable", + "Testing performance profile", + "Tuning individual chips", + ]: + _error = board.status.split(" {")[0] + _error = _error[0].lower() + _error[1:] + errors.append( + BraiinsOSError( + error_message=f"Slot {_id} {_error}" + ) + ) return errors # type: ignore - except (KeyError, IndexError): - pass + except ValidationError as e: + logger.warning(f"{self} - Failed to parse tunerstatus for errors: {e}") return [] async def _get_fault_light(self) -> bool: if self.light: return self.light try: - data = (await self.ssh.get_led_status()).strip() - self.light = False - if data == "50": - self.light = True - return self.light + led_status_result = await self.ssh.get_led_status() + if led_status_result is not None: + data = led_status_result.strip() + self.light = False + if data == "50": + self.light = True + return self.light + else: + return self.light or False except (TypeError, AttributeError): return self.light or False async def _get_expected_hashrate( - self, rpc_devs: dict | None = None + self, rpc_devs: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_devs is None: try: @@ -564,12 +1001,14 @@ async def _get_expected_hashrate( if rpc_devs is not None: try: + response = BOSDevsWrapper.model_validate(rpc_devs) hr_list = [] - for board in rpc_devs["DEVS"]: - expected_hashrate = float(board["Nominal MHS"]) - if expected_hashrate: - hr_list.append(expected_hashrate) + for board in response.devs: + if board.nominal_mhs is not None: + expected_hashrate = float(board.nominal_mhs) + if expected_hashrate: + hr_list.append(expected_hashrate) if len(hr_list) == 0: return self.algo.hashrate( @@ -584,11 +1023,15 @@ async def _get_expected_hashrate( ), unit=self.algo.unit.MH, # type: ignore[attr-defined] ).into(self.algo.unit.default) # type: ignore[attr-defined] - except (IndexError, KeyError): - pass + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse devs for expected hashrate: {e}" + ) return None - async def _is_mining(self, rpc_devdetails: dict | None = None) -> bool | None: + async def _is_mining( + self, rpc_devdetails: dict[str, Any] | None = None + ) -> bool | None: if rpc_devdetails is None: try: rpc_devdetails = await self.rpc.send_command( @@ -599,12 +1042,16 @@ async def _is_mining(self, rpc_devdetails: dict | None = None) -> bool | None: if rpc_devdetails is not None: try: - return not rpc_devdetails["STATUS"][0]["Msg"] == "Unavailable" - except LookupError: + response = BOSStatusWrapper.model_validate(rpc_devdetails) + if response.status: + return not response.status[0].msg == "Unavailable" + except ValidationError: pass return None - async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: + async def _get_uptime( + self, rpc_summary: dict[str, Any] | None = None + ) -> int | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -613,12 +1060,16 @@ async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: if rpc_summary is not None: try: - return int(rpc_summary["SUMMARY"][0]["Elapsed"]) - except LookupError: - pass + response = BOSSummaryWrapper.model_validate(rpc_summary) + if response.summary and response.summary[0].elapsed is not None: + return int(response.summary[0].elapsed) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse summary for uptime: {e}") return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -628,24 +1079,25 @@ async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: pools_data = [] if rpc_pools is not None: try: - pools = rpc_pools.get("POOLS", []) - for pool_info in pools: - url = pool_info.get("URL") - pool_url = PoolUrl.from_str(url) if url else None + response = BOSPoolsWrapper.model_validate(rpc_pools) + for pool_info in response.pools: + pool_url = ( + PoolUrl.from_str(pool_info.url) if pool_info.url else None + ) pool_data = PoolMetrics( - accepted=pool_info.get("Accepted"), - rejected=pool_info.get("Rejected"), - get_failures=pool_info.get("Get Failures"), - remote_failures=pool_info.get("Remote Failures"), - active=pool_info.get("Stratum Active"), - alive=pool_info.get("Status") == "Alive", + accepted=pool_info.accepted, + rejected=pool_info.rejected, + get_failures=pool_info.get_failures, + remote_failures=pool_info.remote_failures, + active=pool_info.stratum_active, + alive=pool_info.status == "Alive", url=pool_url, - user=pool_info.get("User"), - index=pool_info.get("POOL"), + user=pool_info.user, + index=pool_info.pool, ) pools_data.append(pool_data) - except LookupError: - pass + except ValidationError as e: + logger.warning(f"{self} - Failed to parse pools: {e}") return pools_data async def upgrade_firmware( @@ -861,7 +1313,9 @@ async def set_power_limit(self, wattage: int) -> bool: ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_mac(self, grpc_miner_details: dict | None = None) -> str | None: + async def _get_mac( + self, grpc_miner_details: dict[str, Any] | None = None + ) -> str | None: if grpc_miner_details is None: try: grpc_miner_details = await self.web.get_miner_details() @@ -870,12 +1324,16 @@ async def _get_mac(self, grpc_miner_details: dict | None = None) -> str | None: if grpc_miner_details is not None: try: - return grpc_miner_details["macAddress"].upper() - except (LookupError, TypeError): - pass + response = BOSerMinerDetails.model_validate(grpc_miner_details) + if response.mac_address: + return response.mac_address.upper() + except ValidationError as e: + logger.warning(f"{self} - Failed to parse miner details for MAC: {e}") return None - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -892,30 +1350,31 @@ async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: return self.api_ver - async def _get_fw_ver(self, grpc_miner_details: dict | None = None) -> str | None: + async def _get_fw_ver( + self, grpc_miner_details: dict[str, Any] | None = None + ) -> str | None: if grpc_miner_details is None: try: grpc_miner_details = await self.web.get_miner_details() except APIError: return None - fw_ver = None - if grpc_miner_details is not None: try: - fw_ver = grpc_miner_details["bosVersion"]["current"] - except (KeyError, TypeError): - pass - - # if we get the version data, parse it - if fw_ver is not None: - ver = fw_ver.split("-")[5] - if "." in ver: - self.fw_ver = ver + response = BOSerMinerDetails.model_validate(grpc_miner_details) + if response.bos_version and response.bos_version.current: + fw_ver = response.bos_version.current + ver = fw_ver.split("-")[5] + if "." in ver: + self.fw_ver = ver + except (ValidationError, IndexError) as e: + logger.warning(f"{self} - Failed to parse firmware version: {e}") return self.fw_ver - async def _get_hostname(self, grpc_miner_details: dict | None = None) -> str | None: + async def _get_hostname( + self, grpc_miner_details: dict[str, Any] | None = None + ) -> str | None: if grpc_miner_details is None: try: grpc_miner_details = await self.web.get_miner_details() @@ -924,13 +1383,14 @@ async def _get_hostname(self, grpc_miner_details: dict | None = None) -> str | N if grpc_miner_details is not None: try: - return grpc_miner_details["hostname"] - except LookupError: - pass + response = BOSerMinerDetails.model_validate(grpc_miner_details) + return response.hostname + except ValidationError as e: + logger.warning(f"{self} - Failed to parse hostname: {e}") return None async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: @@ -940,16 +1400,18 @@ async def _get_hashrate( if rpc_summary is not None: try: - return self.algo.hashrate( - rate=float(rpc_summary["SUMMARY"][0]["MHS 1m"]), - unit=self.algo.unit.MH, # type: ignore[attr-defined] - ).into(self.algo.unit.default) # type: ignore[attr-defined] - except (KeyError, IndexError, ValueError, TypeError): - pass + response = BOSSummaryWrapper.model_validate(rpc_summary) + if response.summary and response.summary[0].mhs_1m is not None: + return self.algo.hashrate( + rate=float(response.summary[0].mhs_1m), + unit=self.algo.unit.MH, # type: ignore[attr-defined] + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except ValidationError as e: + logger.warning(f"{self} - Failed to parse summary for hashrate: {e}") return None async def _get_expected_hashrate( - self, grpc_miner_details: dict | None = None + self, grpc_miner_details: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if grpc_miner_details is None: try: @@ -959,18 +1421,21 @@ async def _get_expected_hashrate( if grpc_miner_details is not None: try: - return self.algo.hashrate( - rate=float( - grpc_miner_details["stickerHashrate"]["gigahashPerSecond"] - ), - unit=self.algo.unit.GH, # type: ignore[attr-defined] - ).into(self.algo.unit.default) # type: ignore[attr-defined] - except LookupError: - pass + response = BOSerMinerDetails.model_validate(grpc_miner_details) + if ( + response.sticker_hashrate + and response.sticker_hashrate.gigahashPerSecond is not None + ): + return self.algo.hashrate( + rate=float(response.sticker_hashrate.gigahashPerSecond), + unit=self.algo.unit.GH, # type: ignore[attr-defined] + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except ValidationError as e: + logger.warning(f"{self} - Failed to parse expected hashrate: {e}") return None async def _get_hashboards( - self, grpc_hashboards: dict | None = None + self, grpc_hashboards: dict[str, Any] | None = None ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -987,35 +1452,45 @@ async def _get_hashboards( return hashboards if grpc_hashboards is not None: - grpc_boards = sorted( - grpc_hashboards["hashboards"], key=lambda x: int(x["id"]) - ) - for idx, board in enumerate(grpc_boards): - if board.get("chipsCount") is not None: - hashboards[idx].chips = board["chipsCount"] - if board.get("boardTemp") is not None: - hashboards[idx].temp = int(board["boardTemp"]["degreeC"]) - if board.get("highestChipTemp") is not None: - hashboards[idx].chip_temp = int( - board["highestChipTemp"]["temperature"]["degreeC"] - ) - if board.get("stats") is not None: - if not board["stats"]["realHashrate"]["last5S"] == {}: - hashboards[idx].hashrate = self.algo.hashrate( - rate=float( - board["stats"]["realHashrate"]["last5S"][ - "gigahashPerSecond" - ] - ), - unit=self.algo.unit.GH, # type: ignore[attr-defined] - ).into( - self.algo.unit.default # type: ignore[attr-defined] - ) - hashboards[idx].missing = False + try: + response = BOSerHashBoards.model_validate(grpc_hashboards) + grpc_boards = sorted( + response.hashboards, key=lambda x: int(x.id) if x.id else 0 + ) + for idx, board in enumerate(grpc_boards): + if idx < len(hashboards): + if board.chips_count is not None: + hashboards[idx].chips = board.chips_count + if board.board_temp and board.board_temp.degree_c is not None: + hashboards[idx].temp = int(board.board_temp.degree_c) + if ( + board.highest_chip_temp + and board.highest_chip_temp.temperature + and board.highest_chip_temp.temperature.degree_c is not None + ): + hashboards[idx].chip_temp = int( + board.highest_chip_temp.temperature.degree_c + ) + if board.stats and board.stats.real_hashrate: + last_5s = board.stats.real_hashrate.get("last5S") + if ( + last_5s + and isinstance(last_5s, dict) + and last_5s.get("gigahashPerSecond") is not None + ): + hashboards[idx].hashrate = self.algo.hashrate( + rate=float(last_5s["gigahashPerSecond"]), + unit=self.algo.unit.GH, + ).into(self.algo.unit.default) + hashboards[idx].missing = False + except (ValidationError, ValueError) as e: + logger.warning(f"{self} - Failed to parse hashboards: {e}") return hashboards - async def _get_wattage(self, grpc_miner_stats: dict | None = None) -> int | None: + async def _get_wattage( + self, grpc_miner_stats: dict[str, Any] | None = None + ) -> int | None: if grpc_miner_stats is None: try: grpc_miner_stats = await self.web.get_miner_stats() @@ -1024,13 +1499,19 @@ async def _get_wattage(self, grpc_miner_stats: dict | None = None) -> int | None if grpc_miner_stats is not None: try: - return grpc_miner_stats["powerStats"]["approximatedConsumption"]["watt"] - except KeyError: - pass + response = BOSerMinerStats.model_validate(grpc_miner_stats) + if ( + response.power_stats + and response.power_stats.approximated_consumption + and response.power_stats.approximated_consumption.watt is not None + ): + return int(response.power_stats.approximated_consumption.watt) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse power stats: {e}") return None async def _get_wattage_limit( - self, grpc_active_performance_mode: dict | None = None + self, grpc_active_performance_mode: dict[str, Any] | None = None ) -> int | None: if grpc_active_performance_mode is None: try: @@ -1042,14 +1523,20 @@ async def _get_wattage_limit( if grpc_active_performance_mode is not None: try: - return grpc_active_performance_mode["tunerMode"]["powerTarget"][ - "powerTarget" - ]["watt"] - except KeyError: - pass + response = BOSerActivePerformanceMode.model_validate( + grpc_active_performance_mode + ) + if response.tuner_mode and response.tuner_mode.power_target: + power_target = response.tuner_mode.power_target.get("powerTarget") + if isinstance(power_target, dict) and "watt" in power_target: + return int(power_target["watt"]) + except (ValidationError, ValueError) as e: + logger.warning(f"{self} - Failed to parse wattage limit: {e}") return None - async def _get_fans(self, grpc_cooling_state: dict | None = None) -> list[Fan]: + async def _get_fans( + self, grpc_cooling_state: dict[str, Any] | None = None + ) -> list[Fan]: if self.expected_fans is None: return [] @@ -1060,17 +1547,21 @@ async def _get_fans(self, grpc_cooling_state: dict | None = None) -> list[Fan]: return [Fan() for _ in range(self.expected_fans)] if grpc_cooling_state is not None: - fans = [] - for n in range(self.expected_fans): - try: - fans.append(Fan(speed=grpc_cooling_state["fans"][n]["rpm"])) - except (IndexError, KeyError): - pass - return fans + try: + response = BOSerCoolingState.model_validate(grpc_cooling_state) + fans = [] + for n in range(self.expected_fans): + if n < len(response.fans) and response.fans[n].rpm is not None: + fans.append(Fan(speed=response.fans[n].rpm)) + else: + fans.append(Fan()) + return fans + except ValidationError as e: + logger.warning(f"{self} - Failed to parse cooling state: {e}") return [Fan() for _ in range(self.expected_fans)] async def _get_errors( - self, rpc_tunerstatus: dict | None = None + self, rpc_tunerstatus: dict[str, Any] | None = None ) -> list[MinerErrorData]: if rpc_tunerstatus is None: try: @@ -1081,31 +1572,36 @@ async def _get_errors( if rpc_tunerstatus is not None: errors = [] try: - chain_status = rpc_tunerstatus["TUNERSTATUS"][0]["TunerChainStatus"] - if chain_status and len(chain_status) > 0: - offset = ( - 6 if int(chain_status[0]["HashchainIndex"]) in [6, 7, 8] else 0 - ) + response = BOSTunerStatusWrapper.model_validate(rpc_tunerstatus) + if response.tunerstatus and response.tunerstatus[0].tuner_chain_status: + chain_status = response.tunerstatus[0].tuner_chain_status + if chain_status: + offset = ( + 6 if chain_status[0].hashchain_index in [6, 7, 8] else 0 + ) - for board in chain_status: - _id = board["HashchainIndex"] - offset - if board["Status"] not in [ - "Stable", - "Testing performance profile", - "Tuning individual chips", - ]: - _error = board["Status"].split(" {")[0] - _error = _error[0].lower() + _error[1:] - errors.append( - BraiinsOSError(error_message=f"Slot {_id} {_error}") - ) + for board in chain_status: + if board.hashchain_index is not None and board.status: + _id = board.hashchain_index - offset + if board.status not in [ + "Stable", + "Testing performance profile", + "Tuning individual chips", + ]: + _error = board.status.split(" {")[0] + _error = _error[0].lower() + _error[1:] + errors.append( + BraiinsOSError( + error_message=f"Slot {_id} {_error}" + ) + ) return errors # type: ignore - except LookupError: - pass + except ValidationError as e: + logger.warning(f"{self} - Failed to parse tunerstatus for errors: {e}") return [] async def _get_fault_light( - self, grpc_locate_device_status: dict | None = None + self, grpc_locate_device_status: dict[str, Any] | None = None ) -> bool: if self.light is not None: return self.light @@ -1120,12 +1616,18 @@ async def _get_fault_light( if grpc_locate_device_status == {}: return False try: - return grpc_locate_device_status["enabled"] - except LookupError: + response = BOSerLocateDeviceStatus.model_validate( + grpc_locate_device_status + ) + if isinstance(response.enabled, bool): + return response.enabled + except ValidationError: pass return False - async def _is_mining(self, rpc_devdetails: dict | None = None) -> bool | None: + async def _is_mining( + self, rpc_devdetails: dict[str, Any] | None = None + ) -> bool | None: if rpc_devdetails is None: try: rpc_devdetails = await self.rpc.send_command( @@ -1136,12 +1638,17 @@ async def _is_mining(self, rpc_devdetails: dict | None = None) -> bool | None: if rpc_devdetails is not None: try: - return not rpc_devdetails["STATUS"][0]["Msg"] == "Unavailable" - except LookupError: + response = BOSStatusWrapper.model_validate(rpc_devdetails) + if response.status: + return not response.status[0].msg == "Unavailable" + except ValidationError: + # If validation fails, might still be mining pass return None - async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: + async def _get_uptime( + self, rpc_summary: dict[str, Any] | None = None + ) -> int | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -1150,33 +1657,47 @@ async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: if rpc_summary is not None: try: - return int(rpc_summary["SUMMARY"][0]["Elapsed"]) - except LookupError: - pass + response = BOSSummaryWrapper.model_validate(rpc_summary) + if response.summary and response.summary[0].elapsed is not None: + return int(response.summary[0].elapsed) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse summary for uptime: {e}") return None async def _get_pools( - self, grpc_pool_groups: dict | None = None + self, grpc_pool_groups: dict[str, Any] | None = None ) -> list[PoolMetrics]: if grpc_pool_groups is None: try: grpc_pool_groups = await self.web.get_pool_groups() except APIError: return [] + pools_data = [] - for group in grpc_pool_groups["poolGroups"]: - for idx, pool_info in enumerate(group["pools"]): - pool_data = PoolMetrics( - url=PoolUrl.from_str(pool_info["url"]), - user=pool_info["user"], - index=idx, - accepted=pool_info["stats"].get("acceptedShares", 0), - rejected=pool_info["stats"].get("rejectedShares", 0), - get_failures=0, - remote_failures=0, - active=pool_info.get("active", False), - alive=pool_info.get("alive"), - ) - pools_data.append(pool_data) + if grpc_pool_groups is not None: + try: + response = BOSerPoolGroups.model_validate(grpc_pool_groups) + for group in response.pool_groups: + for idx, pool_info in enumerate(group.pools): + pool_data = PoolMetrics( + url=PoolUrl.from_str(pool_info.url) + if pool_info.url + else None, + user=pool_info.user, + index=idx, + accepted=pool_info.stats.accepted_shares + if pool_info.stats + else 0, + rejected=pool_info.stats.rejected_shares + if pool_info.stats + else 0, + get_failures=0, + remote_failures=0, + active=pool_info.active, + alive=pool_info.alive, + ) + pools_data.append(pool_data) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse pool groups: {e}") return pools_data diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index b3fcd5f0d..5f8a26840 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -15,27 +15,179 @@ # ------------------------------------------------------------------------------ import asyncio import logging +from typing import Any import aiofiles import semver +from pydantic import BaseModel, Field, ValidationError -from pyasic.config import MinerConfig, MiningModeConfig -from pyasic.data import Fan, HashBoard +from pyasic.config import MinerConfig +from pyasic.config.mining import MiningModeConfig +from pyasic.data.boards import HashBoard from pyasic.data.error_codes import MinerErrorData, WhatsminerError +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware -from pyasic.rpc.btminer import BTMinerRPCAPI, BTMinerV3RPCAPI +from pyasic.rpc.btminer import ( + BTMinerRPCAPI, + BTMinerV3DeviceInfoMsg, + BTMinerV3EdevItem, + BTMinerV3Pool, + BTMinerV3RPCAPI, + BTMinerV3Summary, +) + + +class BTMinerMsgInfo(BaseModel): + hostname: str | None = None + mac: str | None = None + model: str | None = None + nettype: str | None = None + + class Config: + extra = "allow" + + +class BTMinerGetMinerInfoResponse(BaseModel): + msg: BTMinerMsgInfo = Field(alias="Msg") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerGetVersionResponse(BaseModel): + code: int = Field(alias="Code") + msg: dict[str, Any] | str = Field(alias="Msg") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerSummaryItem(BaseModel): + mac: str | None = Field(None, alias="MAC") + firmware_version: str | None = Field(None, alias="Firmware Version") + mhs_1m: float | None = Field(None, alias="MHS 1m") + env_temp: float | None = Field(None, alias="Env Temp") + power: int | None = Field(None, alias="Power") + power_limit: int | None = Field(None, alias="Power Limit") + fan_speed_in: int | None = Field(None, alias="Fan Speed In") + fan_speed_out: int | None = Field(None, alias="Fan Speed Out") + power_fanspeed: int | None = Field(None, alias="Power Fanspeed") + factory_ghs: float | None = Field(None, alias="Factory GHS") + error_code_count: int = Field(0, alias="Error Code Count") + elapsed: int | None = Field(None, alias="Elapsed") + power_mode: str | None = Field(None, alias="Power Mode") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerSummaryResponse(BaseModel): + summary: list[BTMinerSummaryItem] = Field([], alias="SUMMARY") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerDevItem(BaseModel): + asc: int | None = Field(None, alias="ASC") + slot: int | None = Field(None, alias="Slot") + chip_temp_avg: float | None = Field(None, alias="Chip Temp Avg") + temperature: float | None = Field(None, alias="Temperature") + mhs_1m: float | None = Field(None, alias="MHS 1m") + effective_chips: int | None = Field(None, alias="Effective Chips") + pcb_sn: str | None = Field(None, alias="PCB SN") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerDevsResponse(BaseModel): + devs: list[BTMinerDevItem] = Field([], alias="DEVS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerStatusMsg(BaseModel): + btmineroff: bool | None = None + mineroff: str | None = None + + class Config: + extra = "allow" + + +class BTMinerStatusResponse(BaseModel): + msg: BTMinerStatusMsg = Field(alias="Msg") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerGetPSUResponse(BaseModel): + msg: dict[str, Any] = Field(alias="Msg") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerGetErrorCodeResponse(BaseModel): + msg: dict[str, Any] = Field(alias="Msg") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerPoolItem(BaseModel): + pool: int | None = Field(None, alias="POOL") + url: str | None = Field(None, alias="URL") + user: str | None = Field(None, alias="User") + accepted: int | None = Field(None, alias="Accepted") + rejected: int | None = Field(None, alias="Rejected") + get_failures: int | None = Field(None, alias="Get Failures") + remote_failures: int | None = Field(None, alias="Remote Failures") + stratum_active: bool | None = Field(None, alias="Stratum Active") + status: str | None = Field(None, alias="Status") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerPoolsResponse(BaseModel): + pools: list[BTMinerPoolItem] = Field([], alias="POOLS") + + class Config: + populate_by_name = True + extra = "allow" + + +class BTMinerV3CommandResponse(BaseModel): + code: int | None = None + msg: str | None = None + + class Config: + extra = "allow" class BTMiner(StockFirmware): - def __new__(cls, ip: str, version: str | None = None): + def __new__(cls, ip: str, version: str | None = None) -> Any: bases = cls.__bases__ bases = bases[1:] - def get_new(v: str | None): + def get_new(v: str | None) -> type[BTMinerV2] | type[BTMinerV3]: if v is None: return BTMinerV2 try: @@ -54,8 +206,8 @@ def get_new(v: str | None): bases = (inject,) + bases - cls = type(cls.__name__, bases, {})(ip=ip, version=version) - return cls + new_class = type(cls.__name__, bases, {}) + return new_class(ip=ip, version=version) BTMINER_DATA_LOC = DataLocations( @@ -158,7 +310,7 @@ class BTMinerV2(StockFirmware): supports_shutdown = True supports_power_modes = True - async def _reset_rpc_pwd_to_admin(self, pwd: str): + async def _reset_rpc_pwd_to_admin(self, pwd: str) -> bool: try: data = await self.rpc.update_pwd(pwd, "admin") except APIError: @@ -284,11 +436,17 @@ async def get_config(self) -> MinerConfig: return cfg if summary is not None: - mining_mode = None try: - mining_mode = summary["SUMMARY"][0]["Power Mode"] - except LookupError: - pass + response = BTMinerSummaryResponse.model_validate(summary) + if response.summary: + mining_mode = response.summary[0].power_mode + power_lim = response.summary[0].power_limit + else: + mining_mode = None + power_lim = None + except ValidationError: + mining_mode = None + power_lim = None if mining_mode == "High": cfg.mining_mode = MiningModeConfig.high() @@ -296,10 +454,6 @@ async def get_config(self) -> MinerConfig: elif mining_mode == "Low": cfg.mining_mode = MiningModeConfig.low() return cfg - try: - power_lim = summary["SUMMARY"][0]["Power Limit"] - except LookupError: - power_lim = None if power_lim is None: cfg.mining_mode = MiningModeConfig.normal() @@ -327,7 +481,9 @@ async def set_power_limit(self, wattage: int) -> bool: ################################################## async def _get_mac( - self, rpc_summary: dict | None = None, rpc_get_miner_info: dict | None = None + self, + rpc_summary: dict[str, Any] | None = None, + rpc_get_miner_info: dict[str, Any] | None = None, ) -> str | None: if rpc_get_miner_info is None: try: @@ -337,9 +493,12 @@ async def _get_mac( if rpc_get_miner_info is not None: try: - mac = rpc_get_miner_info["Msg"]["mac"] - return str(mac).upper() - except KeyError: + response = BTMinerGetMinerInfoResponse.model_validate( + rpc_get_miner_info + ) + if response.msg.mac: + return str(response.msg.mac).upper() + except ValidationError: pass if rpc_summary is None: @@ -350,14 +509,17 @@ async def _get_mac( if rpc_summary is not None: try: - mac = rpc_summary["SUMMARY"][0]["MAC"] - return str(mac).upper() - except LookupError: + summary_response = BTMinerSummaryResponse.model_validate(rpc_summary) + if summary_response.summary and summary_response.summary[0].mac: + return str(summary_response.summary[0].mac).upper() + except ValidationError: pass return None - async def _get_api_ver(self, rpc_get_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_get_version: dict[str, Any] | None = None + ) -> str | None: if rpc_get_version is None: try: rpc_get_version = await self.rpc.get_version() @@ -365,22 +527,25 @@ async def _get_api_ver(self, rpc_get_version: dict | None = None) -> str | None: pass if rpc_get_version is not None: - if "Code" in rpc_get_version.keys(): - if rpc_get_version["Code"] == 131: - try: - rpc_ver = rpc_get_version["Msg"] - if not isinstance(rpc_ver, str): - rpc_ver = rpc_ver["rpc_ver"] - self.api_ver = rpc_ver.replace("whatsminer v", "") - except (KeyError, TypeError): - pass - else: - return self.api_ver + try: + response = BTMinerGetVersionResponse.model_validate(rpc_get_version) + if response.code == 131: + if isinstance(response.msg, str): + self.api_ver = response.msg.replace("whatsminer v", "") + elif isinstance(response.msg, dict) and "rpc_ver" in response.msg: + self.api_ver = response.msg["rpc_ver"].replace( + "whatsminer v", "" + ) + return self.api_ver + except ValidationError: + pass return self.api_ver async def _get_fw_ver( - self, rpc_get_version: dict | None = None, rpc_summary: dict | None = None + self, + rpc_get_version: dict[str, Any] | None = None, + rpc_summary: dict[str, Any] | None = None, ) -> str | None: if rpc_get_version is None: try: @@ -389,14 +554,17 @@ async def _get_fw_ver( pass if rpc_get_version is not None: - if "Code" in rpc_get_version.keys(): - if rpc_get_version["Code"] == 131: - try: - self.fw_ver = rpc_get_version["Msg"]["fw_ver"] - except (KeyError, TypeError): - pass - else: - return self.fw_ver + try: + response = BTMinerGetVersionResponse.model_validate(rpc_get_version) + if ( + response.code == 131 + and isinstance(response.msg, dict) + and "fw_ver" in response.msg + ): + self.fw_ver = response.msg["fw_ver"] + return self.fw_ver + except ValidationError: + pass if rpc_summary is None: try: @@ -406,16 +574,23 @@ async def _get_fw_ver( if rpc_summary: try: - self.fw_ver = rpc_summary["SUMMARY"][0]["Firmware Version"].replace( - "'", "" - ) - except LookupError: + summary_response = BTMinerSummaryResponse.model_validate(rpc_summary) + if ( + summary_response.summary + and summary_response.summary[0].firmware_version + ): + self.fw_ver = summary_response.summary[0].firmware_version.replace( + "'", "" + ) + return self.fw_ver + except ValidationError: pass return self.fw_ver - async def _get_hostname(self, rpc_get_miner_info: dict | None = None) -> str | None: - hostname = None + async def _get_hostname( + self, rpc_get_miner_info: dict[str, Any] | None = None + ) -> str | None: if rpc_get_miner_info is None: try: rpc_get_miner_info = await self.rpc.get_miner_info() @@ -424,14 +599,16 @@ async def _get_hostname(self, rpc_get_miner_info: dict | None = None) -> str | N if rpc_get_miner_info is not None: try: - hostname = rpc_get_miner_info["Msg"]["hostname"] - except KeyError: - return None - - return hostname + response = BTMinerGetMinerInfoResponse.model_validate( + rpc_get_miner_info + ) + return response.msg.hostname + except ValidationError: + pass + return None async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: @@ -449,7 +626,9 @@ async def _get_hashrate( pass return None - async def _get_hashboards(self, rpc_devs: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_devs: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -491,7 +670,9 @@ async def _get_hashboards(self, rpc_devs: dict | None = None) -> list[HashBoard] return hashboards - async def _get_env_temp(self, rpc_summary: dict | None = None) -> float | None: + async def _get_env_temp( + self, rpc_summary: dict[str, Any] | None = None + ) -> float | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -500,12 +681,14 @@ async def _get_env_temp(self, rpc_summary: dict | None = None) -> float | None: if rpc_summary is not None: try: - return rpc_summary["SUMMARY"][0]["Env Temp"] + return float(rpc_summary["SUMMARY"][0]["Env Temp"]) except LookupError: pass return None - async def _get_wattage(self, rpc_summary: dict | None = None) -> int | None: + async def _get_wattage( + self, rpc_summary: dict[str, Any] | None = None + ) -> int | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -520,7 +703,9 @@ async def _get_wattage(self, rpc_summary: dict | None = None) -> int | None: pass return None - async def _get_wattage_limit(self, rpc_summary: dict | None = None) -> int | None: + async def _get_wattage_limit( + self, rpc_summary: dict[str, Any] | None = None + ) -> int | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -529,13 +714,15 @@ async def _get_wattage_limit(self, rpc_summary: dict | None = None) -> int | Non if rpc_summary is not None: try: - return rpc_summary["SUMMARY"][0]["Power Limit"] + return int(rpc_summary["SUMMARY"][0]["Power Limit"]) except LookupError: pass return None async def _get_fans( - self, rpc_summary: dict | None = None, rpc_get_psu: dict | None = None + self, + rpc_summary: dict[str, Any] | None = None, + rpc_get_psu: dict[str, Any] | None = None, ) -> list[Fan]: if self.expected_fans is None: return [] @@ -560,7 +747,9 @@ async def _get_fans( return fans async def _get_fan_psu( - self, rpc_summary: dict | None = None, rpc_get_psu: dict | None = None + self, + rpc_summary: dict[str, Any] | None = None, + rpc_get_psu: dict[str, Any] | None = None, ) -> int | None: if rpc_summary is None: try: @@ -588,7 +777,9 @@ async def _get_fan_psu( return None async def _get_errors( - self, rpc_summary: dict | None = None, rpc_get_error_code: dict | None = None + self, + rpc_summary: dict[str, Any] | None = None, + rpc_get_error_code: dict[str, Any] | None = None, ) -> list[MinerErrorData]: errors = [] if rpc_get_error_code is None and rpc_summary is None: @@ -599,14 +790,19 @@ async def _get_errors( if rpc_get_error_code is not None: try: - for err in rpc_get_error_code["Msg"]["error_code"]: - if isinstance(err, dict): - for code in err: - errors.append(WhatsminerError(error_code=int(code))) - else: - errors.append(WhatsminerError(error_code=int(err))) - except KeyError: - pass + error_response = BTMinerGetErrorCodeResponse.model_validate( + rpc_get_error_code + ) + if "error_code" in error_response.msg: + for err in error_response.msg["error_code"]: + if isinstance(err, dict): + for code in err: + errors.append(WhatsminerError(error_code=int(code))) + else: + errors.append(WhatsminerError(error_code=int(err))) + except (ValidationError, ValueError, TypeError) as e: + if isinstance(e, ValidationError): + logging.warning(f"{self} - Failed to parse error codes: {e}") if rpc_summary is None: try: @@ -616,16 +812,20 @@ async def _get_errors( if rpc_summary is not None: try: - for i in range(rpc_summary["SUMMARY"][0]["Error Code Count"]): - err = rpc_summary["SUMMARY"][0].get(f"Error Code {i}") - if err: - errors.append(WhatsminerError(error_code=err)) - except (LookupError, ValueError, TypeError): - pass + response = BTMinerSummaryResponse.model_validate(rpc_summary) + if response.summary: + for i in range(response.summary[0].error_code_count): + raw_data = rpc_summary["SUMMARY"][0] + err = raw_data.get(f"Error Code {i}") + if err: + errors.append(WhatsminerError(error_code=err)) + except (ValidationError, ValueError, TypeError) as e: + if isinstance(e, ValidationError): + logging.warning(f"{self} - Failed to parse summary for errors: {e}") return errors # type: ignore[return-value] async def _get_expected_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: @@ -647,7 +847,7 @@ async def _get_expected_hashrate( return None async def _get_fault_light( - self, rpc_get_miner_info: dict | None = None + self, rpc_get_miner_info: dict[str, Any] | None = None ) -> bool | None: if rpc_get_miner_info is None: try: @@ -671,7 +871,7 @@ async def set_static_ip( gateway: str, subnet_mask: str = "255.255.255.0", hostname: str | None = None, - ): + ) -> None: if not hostname: hostname = await self.get_hostname() if hostname is None: @@ -680,15 +880,15 @@ async def set_static_ip( ip=ip, mask=subnet_mask, dns=dns, gate=gateway, host=hostname, dhcp=False ) - async def set_dhcp(self, hostname: str | None = None): + async def set_dhcp(self, hostname: str | None = None) -> None: if hostname: await self.set_hostname(hostname) await self.rpc.net_config() - async def set_hostname(self, hostname: str): + async def set_hostname(self, hostname: str) -> None: await self.rpc.set_hostname(hostname) - async def _is_mining(self, rpc_status: dict | None = None) -> bool | None: + async def _is_mining(self, rpc_status: dict[str, Any] | None = None) -> bool | None: if rpc_status is None: try: rpc_status = await self.rpc.status() @@ -708,7 +908,9 @@ async def _is_mining(self, rpc_status: dict | None = None) -> bool | None: pass return False - async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: + async def _get_uptime( + self, rpc_summary: dict[str, Any] | None = None + ) -> int | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() @@ -722,7 +924,9 @@ async def _get_uptime(self, rpc_summary: dict | None = None) -> int | None: pass return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -850,7 +1054,7 @@ async def upgrade_firmware( ), str(DataOptions.POOLS): DataFunction( "_get_pools", - [RPCAPICommand("rpc_get_miner_status_summary", "get.miner.status:summary")], + [RPCAPICommand("rpc_get_miner_status_pools", "get.miner.status:pools")], ), str(DataOptions.UPTIME): DataFunction( "_get_uptime", @@ -891,9 +1095,16 @@ async def get_config(self) -> MinerConfig: settings = None device_info = None try: - pools = await self.rpc.get_miner_status_pools() - settings = await self.rpc.get_miner_setting() - device_info = await self.rpc.get_device_info() + pools_list = await self.rpc.get_miner_status_pools() + pools = { + "msg": { + "pools": [pool.model_dump(by_alias=True) for pool in pools_list] + } + } + settings_msg = await self.rpc.get_miner_setting() + settings = settings_msg.model_dump(by_alias=True) + device_info_msg = await self.rpc.get_device_info() + device_info = device_info_msg.model_dump(by_alias=True) except APIError as e: logging.warning(e) except LookupError: @@ -921,69 +1132,49 @@ async def send_config( async def fault_light_off(self) -> bool: try: - data = await self.rpc.set_system_led() + await self.rpc.set_system_led() + self.light = False + return True except APIError: return False - if data: - if "code" in data.keys(): - if data["code"] == 0: - self.light = False - return True - return False async def fault_light_on(self) -> bool: try: - data = await self.rpc.set_system_led( + await self.rpc.set_system_led( leds=[{"color": "red", "period": 60, "duration": 20, "start": 0}] ) + self.light = True + return True except APIError: return False - if data: - if "code" in data.keys(): - if data["code"] == 0: - self.light = True - return True - return False async def reboot(self) -> bool: try: - data = await self.rpc.set_system_reboot() + await self.rpc.set_system_reboot() + return True except APIError: return False - if data and data.get("msg"): - if data["msg"] == "ok": - return True - return False async def restart_backend(self) -> bool: try: - data = await self.rpc.set_miner_service("restart") + await self.rpc.set_miner_service("restart") + return True except APIError: return False - if data and data.get("msg"): - if data["msg"] == "ok": - return True - return False async def stop_mining(self) -> bool: try: - data = await self.rpc.set_miner_service("stop") + await self.rpc.set_miner_service("stop") + return True except APIError: return False - if data and data.get("msg"): - if data["msg"] == "ok": - return True - return False async def resume_mining(self) -> bool: try: - data = await self.rpc.set_miner_service("start") + await self.rpc.set_miner_service("start") + return True except APIError: return False - if data and data.get("msg"): - if data["msg"] == "ok": - return True - return False async def set_power_limit(self, wattage: int) -> bool: try: @@ -994,85 +1185,90 @@ async def set_power_limit(self, wattage: int) -> bool: else: return True - async def _get_mac(self, rpc_get_device_info: dict | None = None) -> str | None: + async def _get_mac( + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, + ) -> str | None: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return None - if rpc_get_device_info is None: - return None - return rpc_get_device_info.get("msg", {}).get("network", {}).get("mac") + if rpc_get_device_info.network.mac: + return rpc_get_device_info.network.mac + return None async def _get_api_version( - self, rpc_get_device_info: dict | None = None + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, ) -> str | None: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return None - if rpc_get_device_info is None: - return None - return rpc_get_device_info.get("msg", {}).get("system", {}).get("api") + if rpc_get_device_info.system.api: + return rpc_get_device_info.system.api + return None async def _get_firmware_version( - self, rpc_get_device_info: dict | None = None + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, ) -> str | None: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return None - if rpc_get_device_info is None: - return None - return rpc_get_device_info.get("msg", {}).get("system", {}).get("fwversion") + if rpc_get_device_info.system.fwversion: + return rpc_get_device_info.system.fwversion + return None async def _get_hostname( - self, rpc_get_device_info: dict | None = None + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, ) -> str | None: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return None - if rpc_get_device_info is None: - return None - return rpc_get_device_info.get("msg", {}).get("network", {}).get("hostname") + if rpc_get_device_info.network.hostname: + return rpc_get_device_info.network.hostname + return None async def _get_light_flashing( - self, rpc_get_device_info: dict | None = None + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, ) -> bool | None: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return None - if rpc_get_device_info is None: - return None - val = rpc_get_device_info.get("msg", {}).get("system", {}).get("ledstatus") - if isinstance(val, str): - return val != "auto" + if rpc_get_device_info.system.ledstatus is not None: + return rpc_get_device_info.system.ledstatus != "auto" return None async def _get_wattage_limit( - self, rpc_get_device_info: dict | None = None + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, ) -> int | None: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return None - if rpc_get_device_info is None: - return None - val = rpc_get_device_info.get("msg", {}).get("miner", {}).get("power-limit-set") try: - return int(float(val)) + if rpc_get_device_info.miner.power_limit_set is not None: + return int(float(rpc_get_device_info.miner.power_limit_set)) except (ValueError, TypeError): - return None + pass + return None async def _get_fans( - self, rpc_get_miner_status_summary: dict | None = None + self, + rpc_get_miner_status_summary: BTMinerV3Summary | None = None, ) -> list[Fan]: if rpc_get_miner_status_summary is None: try: @@ -1080,30 +1276,28 @@ async def _get_fans( except APIError: return [] fans = [] - if rpc_get_miner_status_summary is None: - return [] - summary = rpc_get_miner_status_summary.get("msg", {}).get("summary", {}) - for idx, direction in enumerate(["in", "out"]): - rpm = summary.get(f"fan-speed-{direction}") - if rpm is not None: - fans.append(Fan(speed=rpm)) + if rpc_get_miner_status_summary.fan_speed_in is not None: + fans.append(Fan(speed=rpc_get_miner_status_summary.fan_speed_in)) + if rpc_get_miner_status_summary.fan_speed_out is not None: + fans.append(Fan(speed=rpc_get_miner_status_summary.fan_speed_out)) return fans - async def _get_psu_fans(self, rpc_get_device_info: dict | None = None) -> list[Fan]: + async def _get_psu_fans( + self, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, + ) -> list[Fan]: if rpc_get_device_info is None: try: rpc_get_device_info = await self.rpc.get_device_info() except APIError: return [] - if rpc_get_device_info is None: - return [] - rpm = rpc_get_device_info.get("msg", {}).get("power", {}).get("fanspeed") + rpm = rpc_get_device_info.power.fanspeed return [Fan(speed=rpm)] if rpm is not None else [] async def _get_hashboards( self, - rpc_get_device_info: dict | None = None, - rpc_get_miner_status_edevs: dict | None = None, + rpc_get_device_info: BTMinerV3DeviceInfoMsg | None = None, + rpc_get_miner_status_edevs: list[BTMinerV3EdevItem] | None = None, ) -> list[HashBoard]: if rpc_get_device_info is None: try: @@ -1117,32 +1311,37 @@ async def _get_hashboards( return [] boards = [] - if rpc_get_device_info is None or rpc_get_miner_status_edevs is None: - return [] - board_count = ( - rpc_get_device_info.get("msg", {}).get("hardware", {}).get("boards", 3) - ) - edevs = rpc_get_miner_status_edevs.get("msg", {}).get("edevs", []) + board_count = rpc_get_device_info.hardware.boards + edevs = rpc_get_miner_status_edevs for idx in range(board_count): - board_data = edevs[idx] if idx < len(edevs) else {} + board_data = edevs[idx] if idx < len(edevs) else None + if board_data is None: + boards.append( + HashBoard( + slot=idx, + expected_chips=self.expected_chips, + missing=True, + ) + ) + continue boards.append( HashBoard( slot=idx, hashrate=self.algo.hashrate( - rate=board_data.get("hash-average", 0), + rate=board_data.hash_average or 0, unit=self.algo.unit.TH, # type: ignore[attr-defined] ).into( self.algo.unit.default # type: ignore[attr-defined] ), - temp=board_data.get("chip-temp-min"), - inlet_temp=board_data.get("chip-temp-min"), - outlet_temp=board_data.get("chip-temp-max"), - serial_number=rpc_get_device_info.get("msg", {}) - .get("miner", {}) - .get(f"pcbsn{idx}"), - chips=board_data.get("effective-chips"), + temp=board_data.chip_temp_min, + inlet_temp=board_data.chip_temp_min, + outlet_temp=board_data.chip_temp_max, + serial_number=getattr( + rpc_get_device_info.miner, f"pcbsn{idx}", None + ), + chips=board_data.effective_chips, expected_chips=self.expected_chips, - active=(board_data.get("hash-average") or 0) > 0, + active=(board_data.hash_average or 0) > 0, missing=False, tuned=True, ) @@ -1150,117 +1349,98 @@ async def _get_hashboards( return boards async def _get_pools( - self, rpc_get_miner_status_summary: dict | None = None + self, + rpc_get_miner_status_pools: list[BTMinerV3Pool] | None = None, ) -> list[PoolMetrics]: - if rpc_get_miner_status_summary is None: + if rpc_get_miner_status_pools is None: try: - rpc_get_miner_status_summary = await self.rpc.get_miner_status_summary() + rpc_get_miner_status_pools = await self.rpc.get_miner_status_pools() except APIError: return [] pools = [] - if rpc_get_miner_status_summary is None: - return [] - msg_pools = rpc_get_miner_status_summary.get("msg", {}).get("pools", []) - for idx, pool in enumerate(msg_pools): + for idx, pool in enumerate(rpc_get_miner_status_pools): pools.append( PoolMetrics( index=idx, - user=pool.get("account"), - alive=pool.get("status") == "alive", - active=pool.get("stratum-active"), - url=pool.get("url"), + user=pool.account, + alive=pool.status == "alive", + active=pool.stratum_active, + url=PoolUrl.from_str(pool.url) if pool.url else None, ) ) return pools async def _get_uptime( - self, rpc_get_miner_status_summary: dict | None = None + self, + rpc_get_miner_status_summary: BTMinerV3Summary | None = None, ) -> int | None: if rpc_get_miner_status_summary is None: try: rpc_get_miner_status_summary = await self.rpc.get_miner_status_summary() except APIError: return None - if rpc_get_miner_status_summary is None: - return None - return ( - rpc_get_miner_status_summary.get("msg", {}) - .get("summary", {}) - .get("elapsed") - ) + if rpc_get_miner_status_summary.elapsed is not None: + return rpc_get_miner_status_summary.elapsed + return None - async def _get_wattage( - self, rpc_get_miner_status_summary: dict | None = None - ) -> int | None: + async def _get_hashrate( + self, + rpc_get_miner_status_summary: BTMinerV3Summary | None = None, + ) -> AlgoHashRateType | None: if rpc_get_miner_status_summary is None: try: rpc_get_miner_status_summary = await self.rpc.get_miner_status_summary() except APIError: return None - if rpc_get_miner_status_summary is None: - return None - power_val = ( - rpc_get_miner_status_summary.get("msg", {}) - .get("summary", {}) - .get("power-realtime") - ) try: - return int(float(power_val)) if power_val is not None else None + return self.algo.hashrate( + rate=rpc_get_miner_status_summary.hash_1min, + unit=self.algo.unit.TH, # type: ignore[attr-defined] + ).into(self.algo.unit.default) # type: ignore[attr-defined] except (ValueError, TypeError): - return None + pass + return None - async def _get_hashrate( - self, rpc_get_miner_status_summary: dict | None = None + async def _get_expected_hashrate( + self, + rpc_get_miner_status_summary: BTMinerV3Summary | None = None, ) -> AlgoHashRateType | None: if rpc_get_miner_status_summary is None: try: rpc_get_miner_status_summary = await self.rpc.get_miner_status_summary() except APIError: return None + try: + return self.algo.hashrate( + rate=rpc_get_miner_status_summary.factory_hash, + unit=self.algo.unit.TH, # type: ignore[attr-defined] + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except (ValueError, TypeError): + pass + return None - if rpc_get_miner_status_summary is None: - return None - - return ( - rpc_get_miner_status_summary.get("msg", {}) - .get("summary", {}) - .get("hash-realtime") - ) - - async def _get_expected_hashrate( - self, rpc_get_miner_status_summary: dict | None = None - ) -> AlgoHashRateType | None: + async def _get_wattage( + self, + rpc_get_miner_status_summary: BTMinerV3Summary | None = None, + ) -> int | None: if rpc_get_miner_status_summary is None: try: rpc_get_miner_status_summary = await self.rpc.get_miner_status_summary() except APIError: return None - if rpc_get_miner_status_summary is None: - return None - res = ( - rpc_get_miner_status_summary.get("msg", {}) - .get("summary", {}) - .get("factory-hash") - ) - - if self.expected_hashboards is not None and res == ( - -0.001 * self.expected_hashboards - ): - return None - return res + try: + return int(rpc_get_miner_status_summary.power_rate) + except (ValueError, TypeError): + pass + return None async def _get_env_temp( - self, rpc_get_miner_status_summary: dict | None = None + self, + rpc_get_miner_status_summary: BTMinerV3Summary | None = None, ) -> float | None: if rpc_get_miner_status_summary is None: try: rpc_get_miner_status_summary = await self.rpc.get_miner_status_summary() except APIError: return None - if rpc_get_miner_status_summary is None: - return None - return ( - rpc_get_miner_status_summary.get("msg", {}) - .get("summary", {}) - .get("environment-temperature") - ) + return rpc_get_miner_status_summary.environment_temperature diff --git a/pyasic/miners/backends/cgminer.py b/pyasic/miners/backends/cgminer.py index 483d07b71..d5c432ed7 100644 --- a/pyasic/miners/backends/cgminer.py +++ b/pyasic/miners/backends/cgminer.py @@ -15,9 +15,11 @@ # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.config import MinerConfig from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import StockFirmware @@ -83,7 +85,9 @@ async def get_config(self) -> MinerConfig: ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -98,7 +102,9 @@ async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: return self.api_ver - async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_fw_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -114,7 +120,7 @@ async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: return self.fw_ver async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: @@ -134,7 +140,7 @@ async def _get_hashrate( pass return None - async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: + async def _get_uptime(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -148,7 +154,9 @@ async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: pass return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() diff --git a/pyasic/miners/backends/elphapex.py b/pyasic/miners/backends/elphapex.py index 8318d23d8..a36269140 100644 --- a/pyasic/miners/backends/elphapex.py +++ b/pyasic/miners/backends/elphapex.py @@ -14,10 +14,15 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from pyasic import APIError, MinerConfig -from pyasic.data import Fan, HashBoard, X19Error +from typing import Any + +from pyasic.config import MinerConfig +from pyasic.data.boards import HashBoard +from pyasic.data.error_codes import X19Error +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType +from pyasic.errors import APIError from pyasic.miners.data import ( DataFunction, DataLocations, @@ -123,7 +128,9 @@ async def reboot(self) -> bool: return True return False - async def _get_api_ver(self, web_summary: dict | None = None) -> str | None: + async def _get_api_ver( + self, web_summary: dict[str, Any] | None = None + ) -> str | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -138,7 +145,9 @@ async def _get_api_ver(self, web_summary: dict | None = None) -> str | None: return self.api_ver - async def _get_fw_ver(self, web_get_system_info: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_get_system_info: dict[str, Any] | None = None + ) -> str | None: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -158,7 +167,7 @@ async def _get_fw_ver(self, web_get_system_info: dict | None = None) -> str | No return self.fw_ver async def _get_hostname( - self, web_get_system_info: dict | None = None + self, web_get_system_info: dict[str, Any] | None = None ) -> str | None: if web_get_system_info is None: try: @@ -168,12 +177,14 @@ async def _get_hostname( if web_get_system_info is not None: try: - return web_get_system_info["hostname"] + return str(web_get_system_info["hostname"]) except KeyError: pass return None - async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: + async def _get_mac( + self, web_get_system_info: dict[str, Any] | None = None + ) -> str | None: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -182,20 +193,20 @@ async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: if web_get_system_info is not None: try: - return web_get_system_info["macaddr"] + return str(web_get_system_info["macaddr"]) except KeyError: pass try: data = await self.web.get_network_info() if data: - return data["macaddr"] + return str(data["macaddr"]) except KeyError: pass return None async def _get_errors( # type: ignore[override] - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> list[X19Error]: if web_summary is None: try: @@ -216,7 +227,9 @@ async def _get_errors( # type: ignore[override] pass return errors - async def _get_hashboards(self, web_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, web_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -261,7 +274,7 @@ async def _get_hashboards(self, web_stats: dict | None = None) -> list[HashBoard return hashboards async def _get_fault_light( - self, web_get_blink_status: dict | None = None + self, web_get_blink_status: dict[str, Any] | None = None ) -> bool | None: if self.light: return self.light @@ -280,7 +293,7 @@ async def _get_fault_light( return self.light async def _get_expected_hashrate( - self, web_stats: dict | None = None + self, web_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_stats is None: try: @@ -302,7 +315,9 @@ async def _get_expected_hashrate( pass return None - async def _is_mining(self, web_get_miner_conf: dict | None = None) -> bool | None: + async def _is_mining( + self, web_get_miner_conf: dict[str, Any] | None = None + ) -> bool | None: if web_get_miner_conf is None: try: web_get_miner_conf = await self.web.get_miner_conf() @@ -320,7 +335,9 @@ async def _is_mining(self, web_get_miner_conf: dict | None = None) -> bool | Non pass return None - async def _get_uptime(self, web_summary: dict | None = None) -> int | None: + async def _get_uptime( + self, web_summary: dict[str, Any] | None = None + ) -> int | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -334,7 +351,7 @@ async def _get_uptime(self, web_summary: dict | None = None) -> int | None: pass return None - async def _get_fans(self, web_stats: dict | None = None) -> list[Fan]: + async def _get_fans(self, web_stats: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -354,7 +371,9 @@ async def _get_fans(self, web_stats: dict | None = None) -> list[Fan]: return fans - async def _get_pools(self, web_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, web_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if web_pools is None: try: web_pools = await self.web.pools() diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index c4b6a18e8..18e7adb97 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -15,17 +15,69 @@ # ------------------------------------------------------------------------------ +from typing import Any + +from pydantic import BaseModel, Field, RootModel, ValidationError + from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard from pyasic.data.error_codes import MinerErrorData, X19Error +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType, ScryptAlgo +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType +from pyasic.device.algorithm.scrypt import ScryptAlgo from pyasic.errors import APIError from pyasic.logger import logger from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.miners.device.firmware import ePICFirmware from pyasic.web.epic import ePICWebAPI + +class EpicSuccessResponse(BaseModel): + success: bool + + +class PowerSupplyStats(BaseModel): + input_power: float = Field(alias="Input Power") + + +class SessionInfo(BaseModel): + uptime: int = Field(alias="Uptime") + + +class MiscInfo(BaseModel): + locate_miner_state: bool = Field(alias="Locate Miner State") + + +class EpicSummaryResponse(BaseModel): + hostname: str | None = Field(None, alias="Hostname") + software: str | None = Field(None, alias="Software") + power_supply_stats: PowerSupplyStats | None = Field( + None, alias="Power Supply Stats" + ) + session: SessionInfo | None = Field(None, alias="Session") + misc: MiscInfo | None = Field(None, alias="Misc") + + class Config: + populate_by_name = True + extra = "allow" + + +class EpicNetworkInterface(BaseModel): + mac_address: str + + class Config: + extra = "allow" + + +class EpicNetworkResponse(RootModel[dict[str, EpicNetworkInterface]]): + def __getitem__(self, key: str) -> EpicNetworkInterface: + return self.root[key] + + def __iter__(self) -> Any: + return iter(self.root) + + EPIC_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -148,8 +200,9 @@ async def restart_backend(self) -> bool: data = await self.web.restart_epic() if data: try: - return data["success"] - except KeyError: + response = EpicSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False @@ -157,8 +210,9 @@ async def stop_mining(self) -> bool: data = await self.web.stop_mining() if data: try: - return data["success"] - except KeyError: + response = EpicSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False @@ -166,8 +220,9 @@ async def resume_mining(self) -> bool: data = await self.web.resume_mining() if data: try: - return data["success"] - except KeyError: + response = EpicSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False @@ -175,12 +230,13 @@ async def reboot(self) -> bool: data = await self.web.reboot() if data: try: - return data["success"] - except KeyError: + response = EpicSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False - async def _get_mac(self, web_network: dict | None = None) -> str | None: + async def _get_mac(self, web_network: dict[str, Any] | None = None) -> str | None: if web_network is None: try: web_network = await self.web.network() @@ -189,14 +245,16 @@ async def _get_mac(self, web_network: dict | None = None) -> str | None: if web_network is not None: try: - for network in web_network: - mac = web_network[network]["mac_address"] - return mac - except KeyError: + network_response = EpicNetworkResponse.model_validate(web_network) + for network in network_response: + return network_response[network].mac_address + except ValidationError: pass return None - async def _get_hostname(self, web_summary: dict | None = None) -> str | None: + async def _get_hostname( + self, web_summary: dict[str, Any] | None = None + ) -> str | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -205,13 +263,15 @@ async def _get_hostname(self, web_summary: dict | None = None) -> str | None: if web_summary is not None: try: - hostname = web_summary["Hostname"] - return hostname - except KeyError: + summary = EpicSummaryResponse.model_validate(web_summary) + return summary.hostname + except ValidationError: pass return None - async def _get_wattage(self, web_summary: dict | None = None) -> int | None: + async def _get_wattage( + self, web_summary: dict[str, Any] | None = None + ) -> int | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -220,15 +280,15 @@ async def _get_wattage(self, web_summary: dict | None = None) -> int | None: if web_summary is not None: try: - wattage = web_summary["Power Supply Stats"]["Input Power"] - wattage = round(wattage) - return wattage - except KeyError: + summary = EpicSummaryResponse.model_validate(web_summary) + if summary.power_supply_stats: + return round(summary.power_supply_stats.input_power) + except ValidationError: pass return None async def _get_hashrate( - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_summary is None: try: @@ -251,7 +311,7 @@ async def _get_hashrate( return None async def _get_expected_hashrate( - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_summary is None: try: @@ -278,7 +338,9 @@ async def _get_expected_hashrate( pass return None - async def _get_fw_ver(self, web_summary: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_summary: dict[str, Any] | None = None + ) -> str | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -287,14 +349,15 @@ async def _get_fw_ver(self, web_summary: dict | None = None) -> str | None: if web_summary is not None: try: - fw_ver = web_summary["Software"] - fw_ver = fw_ver.split(" ")[1].replace("v", "") - return fw_ver - except KeyError: + summary = EpicSummaryResponse.model_validate(web_summary) + if summary.software: + fw_ver = summary.software.split(" ")[1].replace("v", "") + return fw_ver + except ValidationError: pass return None - async def _get_fans(self, web_summary: dict | None = None) -> list[Fan]: + async def _get_fans(self, web_summary: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -315,7 +378,9 @@ async def _get_fans(self, web_summary: dict | None = None) -> list[Fan]: return fans async def _get_hashboards( - self, web_summary: dict | None = None, web_capabilities: dict | None = None + self, + web_summary: dict[str, Any] | None = None, + web_capabilities: dict[str, Any] | None = None, ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -383,7 +448,9 @@ async def _get_hashboards( return hb_list return hb_list - async def _is_mining(self, web_summary: dict | None = None) -> bool | None: + async def _is_mining( + self, web_summary: dict[str, Any] | None = None + ) -> bool | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -397,7 +464,9 @@ async def _is_mining(self, web_summary: dict | None = None) -> bool | None: pass return None - async def _get_uptime(self, web_summary: dict | None = None) -> int | None: + async def _get_uptime( + self, web_summary: dict[str, Any] | None = None + ) -> int | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -406,13 +475,16 @@ async def _get_uptime(self, web_summary: dict | None = None) -> int | None: if web_summary is not None: try: - uptime = web_summary["Session"]["Uptime"] - return uptime - except KeyError: + summary = EpicSummaryResponse.model_validate(web_summary) + if summary.session: + return summary.session.uptime + except ValidationError: pass return None - async def _get_fault_light(self, web_summary: dict | None = None) -> bool | None: + async def _get_fault_light( + self, web_summary: dict[str, Any] | None = None + ) -> bool | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -421,14 +493,15 @@ async def _get_fault_light(self, web_summary: dict | None = None) -> bool | None if web_summary is not None: try: - light = web_summary["Misc"]["Locate Miner State"] - return light - except KeyError: + summary = EpicSummaryResponse.model_validate(web_summary) + if summary.misc: + return summary.misc.locate_miner_state + except ValidationError: pass return False async def _get_errors( - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> list[MinerErrorData]: if not web_summary: try: @@ -446,7 +519,9 @@ async def _get_errors( pass return errors # type: ignore[return-value] - async def _get_pools(self, web_summary: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, web_summary: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if web_summary is None: try: web_summary = await self.web.summary() diff --git a/pyasic/miners/backends/espminer.py b/pyasic/miners/backends/espminer.py index be922ef73..433d81e47 100644 --- a/pyasic/miners/backends/espminer.py +++ b/pyasic/miners/backends/espminer.py @@ -1,11 +1,45 @@ -from pyasic import APIError, MinerConfig -from pyasic.data import Fan, HashBoard -from pyasic.device.algorithm import AlgoHashRateType +from typing import Any + +from pydantic import BaseModel, ValidationError + +from pyasic.config import MinerConfig +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.device.firmware import MinerFirmware +from pyasic.errors import APIError +from pyasic.logger import logger from pyasic.miners.base import BaseMiner from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.web.espminer import ESPMinerWebAPI + +class ESPMinerSystemInfo(BaseModel): + power: float | int | None = None + hashRate: float = 0 + smallCoreCount: int | None = None + asicCount: int | None = None + frequency: float | None = None + uptimeSeconds: int = 0 + temp: float | None = None + vrTemp: float | None = None + voltage: float | None = None + fanrpm: int | None = None + hostname: str = "" + version: str = "" + macAddr: str = "" + + class Config: + extra = "allow" + + +class ESPMinerAsicInfo(BaseModel): + asicCount: int | None = None + + class Config: + extra = "allow" + + ESPMINER_DATA_LOC = DataLocations( **{ str(DataOptions.HASHRATE): DataFunction( @@ -75,7 +109,9 @@ async def send_config( ) -> None: await self.web.update_settings(**config.as_espminer()) - async def _get_wattage(self, web_system_info: dict | None = None) -> int | None: + async def _get_wattage( + self, web_system_info: dict[str, Any] | None = None + ) -> int | None: if web_system_info is None: try: web_system_info = await self.web.system_info() @@ -84,13 +120,15 @@ async def _get_wattage(self, web_system_info: dict | None = None) -> int | None: if web_system_info is not None: try: - return round(web_system_info["power"]) - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + if system_info.power is not None: + return round(system_info.power) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse system info for wattage: {e}") return None async def _get_hashrate( - self, web_system_info: dict | None = None + self, web_system_info: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_system_info is None: try: @@ -100,18 +138,21 @@ async def _get_hashrate( if web_system_info is not None: try: + system_info = ESPMinerSystemInfo.model_validate(web_system_info) return self.algo.hashrate( - rate=float(web_system_info["hashRate"]), + rate=system_info.hashRate, unit=self.algo.unit.GH, # type: ignore[attr-defined] ).into( self.algo.unit.default # type: ignore[attr-defined] ) - except KeyError: - pass + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for hashrate: {e}" + ) return None async def _get_expected_hashrate( - self, web_system_info: dict | None = None + self, web_system_info: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_system_info is None: try: @@ -121,15 +162,17 @@ async def _get_expected_hashrate( if web_system_info is not None: try: - small_core_count = web_system_info.get("smallCoreCount") - asic_count = web_system_info.get("asicCount") - frequency = web_system_info.get("frequency") + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + small_core_count = system_info.smallCoreCount + asic_count = system_info.asicCount + frequency = system_info.frequency if asic_count is None: try: - asic_info = await self.web.asic_info() - asic_count = asic_info.get("asicCount") - except APIError: + asic_info_data = await self.web.asic_info() + asic_info = ESPMinerAsicInfo.model_validate(asic_info_data) + asic_count = asic_info.asicCount + except (APIError, ValidationError): pass if ( @@ -144,11 +187,15 @@ async def _get_expected_hashrate( ).into( self.algo.unit.default # type: ignore[attr-defined] ) - except KeyError: - pass + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for expected hashrate: {e}" + ) return None - async def _get_uptime(self, web_system_info: dict | None = None) -> int | None: + async def _get_uptime( + self, web_system_info: dict[str, Any] | None = None + ) -> int | None: if web_system_info is None: try: web_system_info = await self.web.system_info() @@ -157,13 +204,14 @@ async def _get_uptime(self, web_system_info: dict | None = None) -> int | None: if web_system_info is not None: try: - return web_system_info["uptimeSeconds"] - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + return system_info.uptimeSeconds + except ValidationError as e: + logger.warning(f"{self} - Failed to parse system info for uptime: {e}") return None async def _get_hashboards( - self, web_system_info: dict | None = None + self, web_system_info: dict[str, Any] | None = None ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -176,28 +224,33 @@ async def _get_hashboards( if web_system_info is not None: try: + system_info = ESPMinerSystemInfo.model_validate(web_system_info) return [ HashBoard( hashrate=self.algo.hashrate( - rate=float(web_system_info["hashRate"]), + rate=system_info.hashRate, unit=self.algo.unit.GH, # type: ignore[attr-defined] ).into( self.algo.unit.default # type: ignore[attr-defined] ), - chip_temp=web_system_info.get("temp"), - temp=web_system_info.get("vrTemp"), - chips=web_system_info.get("asicCount", 1), + chip_temp=system_info.temp, + temp=system_info.vrTemp, + chips=system_info.asicCount or 1, expected_chips=self.expected_chips, missing=False, active=True, - voltage=web_system_info.get("voltage"), + voltage=system_info.voltage, ) ] - except KeyError: - pass + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for hashboards: {e}" + ) return [] - async def _get_fans(self, web_system_info: dict | None = None) -> list[Fan]: + async def _get_fans( + self, web_system_info: dict[str, Any] | None = None + ) -> list[Fan]: if self.expected_fans is None: return [] @@ -209,12 +262,16 @@ async def _get_fans(self, web_system_info: dict | None = None) -> list[Fan]: if web_system_info is not None: try: - return [Fan(speed=web_system_info["fanrpm"])] - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + if system_info.fanrpm is not None: + return [Fan(speed=system_info.fanrpm)] + except ValidationError as e: + logger.warning(f"{self} - Failed to parse system info for fans: {e}") return [] - async def _get_hostname(self, web_system_info: dict | None = None) -> str | None: + async def _get_hostname( + self, web_system_info: dict[str, Any] | None = None + ) -> str | None: if web_system_info is None: try: web_system_info = await self.web.system_info() @@ -223,12 +280,17 @@ async def _get_hostname(self, web_system_info: dict | None = None) -> str | None if web_system_info is not None: try: - return web_system_info["hostname"] - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + return system_info.hostname if system_info.hostname else None + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for hostname: {e}" + ) return None - async def _get_api_ver(self, web_system_info: dict | None = None) -> str | None: + async def _get_api_ver( + self, web_system_info: dict[str, Any] | None = None + ) -> str | None: if web_system_info is None: try: web_system_info = await self.web.system_info() @@ -237,12 +299,17 @@ async def _get_api_ver(self, web_system_info: dict | None = None) -> str | None: if web_system_info is not None: try: - return web_system_info["version"] - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + return system_info.version if system_info.version else None + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for API version: {e}" + ) return None - async def _get_fw_ver(self, web_system_info: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_system_info: dict[str, Any] | None = None + ) -> str | None: if web_system_info is None: try: web_system_info = await self.web.system_info() @@ -251,12 +318,17 @@ async def _get_fw_ver(self, web_system_info: dict | None = None) -> str | None: if web_system_info is not None: try: - return web_system_info["version"] - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + return system_info.version if system_info.version else None + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for firmware version: {e}" + ) return None - async def _get_mac(self, web_system_info: dict | None = None) -> str | None: + async def _get_mac( + self, web_system_info: dict[str, Any] | None = None + ) -> str | None: if web_system_info is None: try: web_system_info = await self.web.system_info() @@ -265,7 +337,10 @@ async def _get_mac(self, web_system_info: dict | None = None) -> str | None: if web_system_info is not None: try: - return web_system_info["macAddr"].upper() - except KeyError: - pass + system_info = ESPMinerSystemInfo.model_validate(web_system_info) + return system_info.macAddr.upper() if system_info.macAddr else None + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse system info for MAC address: {e}" + ) return None diff --git a/pyasic/miners/backends/goldshell.py b/pyasic/miners/backends/goldshell.py index ee0eadafb..cf86d51b5 100644 --- a/pyasic/miners/backends/goldshell.py +++ b/pyasic/miners/backends/goldshell.py @@ -14,8 +14,13 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from pyasic.config import MinerConfig, MiningModeConfig -from pyasic.data import HashBoard +from typing import Any + +from pydantic import BaseModel, Field, ValidationError + +from pyasic.config import MinerConfig +from pyasic.config.mining import MiningModeConfig +from pyasic.data.boards import HashBoard from pyasic.errors import APIError from pyasic.logger import logger from pyasic.miners.backends import BFGMiner @@ -28,6 +33,60 @@ ) from pyasic.web.goldshell import GoldshellWebAPI + +class GoldshellPoolInfo(BaseModel): + url: str = "" + user: str = "" + pass_: str = Field("", alias="pass") + dragid: int = 0 + + class Config: + extra = "allow" + populate_by_name = True + + +class GoldshellPoolsWrapper(BaseModel): + pools: list[GoldshellPoolInfo] + + class Config: + extra = "allow" + + +class GoldshellPowerPlan(BaseModel): + level: str | None = None + + class Config: + extra = "allow" + + +class GoldshellSettings(BaseModel): + name: str = "" + firmware: str = "" + powerplans: list[GoldshellPowerPlan] = [] + select: int = 0 + + class Config: + extra = "allow" + + +class GoldshellDevInfo(BaseModel): + ID: int | None = None + MHS_20s: float = Field(0, alias="MHS 20s") + tstemp_2: float = Field(0, alias="tstemp-2") + chips_nr: int = Field(0, alias="chips-nr") + + class Config: + extra = "allow" + populate_by_name = True + + +class GoldshellDevsWrapper(BaseModel): + DEVS: list[GoldshellDevInfo] | None = None + + class Config: + extra = "allow" + + GOLDSHELL_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -87,8 +146,20 @@ async def get_config(self) -> MinerConfig: except APIError: if self.config is not None: return self.config + return MinerConfig() - self.config = MinerConfig.from_goldshell(pools) + try: + if isinstance(pools, dict): + pools_wrapper = GoldshellPoolsWrapper.model_validate(pools) + pools_list = [ + pool.model_dump(by_alias=True) for pool in pools_wrapper.pools + ] + else: + pools_list = pools + self.config = MinerConfig.from_goldshell_list(pools_list) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse pools config: {e}") + self.config = MinerConfig() return self.config async def send_config( @@ -96,29 +167,49 @@ async def send_config( ) -> None: pools_data = await self.web.pools() # have to delete all the pools one at a time first - for pool in pools_data: - await self.web.delpool( - url=pool["url"], - user=pool["user"], - password=pool["pass"], - dragid=pool["dragid"], - ) + try: + if isinstance(pools_data, dict): + pools_wrapper = GoldshellPoolsWrapper.model_validate(pools_data) + pools_list = pools_wrapper.pools + else: + pools_list = [] + for pool in pools_data: + try: + pool_info = GoldshellPoolInfo.model_validate(pool) + pools_list.append(pool_info) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse pool info: {e}") + continue + + for pool in pools_list: + await self.web.delpool( + url=pool.url, + user=pool.user, + password=pool.pass_, + dragid=pool.dragid, + ) + except ValidationError as e: + logger.warning(f"{self} - Failed to parse pools for deletion: {e}") self.config = config cfg = config.as_goldshell(user_suffix=user_suffix) # send them back 1 at a time for pool in cfg["pools"]: + pool_info = GoldshellPoolInfo.model_validate(pool) await self.web.newpool( - url=pool["url"], user=pool["user"], password=pool["pass"] + url=pool_info.url, user=pool_info.user, password=pool_info.pass_ ) - settings = await self.web.setting() - for idx, plan in enumerate(settings["powerplans"]): - if plan["level"] == cfg["settings"]["level"]: - settings["select"] = idx - await self.web.set_setting(settings) + settings_data = await self.web.setting() + settings = GoldshellSettings.model_validate(settings_data) + cfg_level = cfg.get("settings", {}).get("level") + for idx, plan in enumerate(settings.powerplans): + if plan.level == cfg_level: + settings.select = idx + break + await self.web.set_setting(settings.model_dump()) - async def _get_mac(self, web_setting: dict | None = None) -> str | None: + async def _get_mac(self, web_setting: dict[str, Any] | None = None) -> str | None: if web_setting is None: try: web_setting = await self.web.setting() @@ -127,12 +218,13 @@ async def _get_mac(self, web_setting: dict | None = None) -> str | None: if web_setting is not None: try: - return web_setting["name"] - except KeyError: - pass + settings = GoldshellSettings.model_validate(web_setting) + return settings.name if settings.name else None + except ValidationError as e: + logger.warning(f"{self} - Failed to parse settings for MAC: {e}") return None - async def _get_fw_ver(self, web_status: dict | None = None) -> str | None: + async def _get_fw_ver(self, web_status: dict[str, Any] | None = None) -> str | None: if web_status is None: try: web_status = await self.web.setting() @@ -141,13 +233,18 @@ async def _get_fw_ver(self, web_status: dict | None = None) -> str | None: if web_status is not None: try: - return web_status["firmware"] - except KeyError: - pass + settings = GoldshellSettings.model_validate(web_status) + return settings.firmware if settings.firmware else None + except ValidationError as e: + logger.warning( + f"{self} - Failed to parse settings for firmware version: {e}" + ) return None async def _get_hashboards( - self, rpc_devs: dict | None = None, rpc_devdetails: dict | None = None + self, + rpc_devs: dict[str, Any] | None = None, + rpc_devdetails: dict[str, Any] | None = None, ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -164,23 +261,23 @@ async def _get_hashboards( ] if rpc_devs is not None: - if rpc_devs.get("DEVS"): - for board in rpc_devs["DEVS"]: - if board.get("ID") is not None: - try: - b_id = board["ID"] - hashboards[b_id].hashrate = self.algo.hashrate( - rate=float(board["MHS 20s"]), + try: + devs_wrapper = GoldshellDevsWrapper.model_validate(rpc_devs) + if devs_wrapper.DEVS: + for board in devs_wrapper.DEVS: + if board.ID is not None: + hashboards[board.ID].hashrate = self.algo.hashrate( + rate=board.MHS_20s, unit=self.algo.unit.MH, # type: ignore[attr-defined] ).into( self.algo.unit.default # type: ignore[attr-defined] ) - hashboards[b_id].temp = board["tstemp-2"] - hashboards[b_id].missing = False - except KeyError: - pass - else: - logger.error(self, rpc_devs) + hashboards[board.ID].temp = round(board.tstemp_2) + hashboards[board.ID].missing = False + else: + logger.error(f"{self} - No DEVS data found in response") + except ValidationError as e: + logger.error(f"{self} - Failed to parse devs: {e}") if rpc_devdetails is None: try: @@ -189,37 +286,41 @@ async def _get_hashboards( pass if rpc_devdetails is not None: - if rpc_devdetails.get("DEVS"): - for board in rpc_devdetails["DEVS"]: - if board.get("ID") is not None: - try: - b_id = board["ID"] - hashboards[b_id].chips = board["chips-nr"] - except KeyError: - pass - else: - logger.error(self, rpc_devdetails) + try: + devdetails_wrapper = GoldshellDevsWrapper.model_validate(rpc_devdetails) + if devdetails_wrapper.DEVS: + for board in devdetails_wrapper.DEVS: + if board.ID is not None: + hashboards[board.ID].chips = board.chips_nr + else: + logger.error(f"{self} - No DEVS data found in devdetails response") + except ValidationError as e: + logger.error(f"{self} - Failed to parse devdetails: {e}") return hashboards async def stop_mining(self) -> bool: - settings = await self.web.setting() + settings_data = await self.web.setting() + settings = GoldshellSettings.model_validate(settings_data) mode = MiningModeConfig.sleep() cfg = mode.as_goldshell() - level = cfg["settings"]["level"] - for idx, plan in enumerate(settings["powerplans"]): - if plan["level"] == level: - settings["select"] = idx - await self.web.set_setting(settings) + level = cfg.get("settings", {}).get("level") + for idx, plan in enumerate(settings.powerplans): + if plan.level == level: + settings.select = idx + break + await self.web.set_setting(settings.model_dump()) return True async def resume_mining(self) -> bool: - settings = await self.web.setting() + settings_data = await self.web.setting() + settings = GoldshellSettings.model_validate(settings_data) mode = MiningModeConfig.normal() cfg = mode.as_goldshell() - level = cfg["settings"]["level"] - for idx, plan in enumerate(settings["powerplans"]): - if plan["level"] == level: - settings["select"] = idx - await self.web.set_setting(settings) + level = cfg.get("settings", {}).get("level") + for idx, plan in enumerate(settings.powerplans): + if plan.level == level: + settings.select = idx + break + await self.web.set_setting(settings.model_dump()) return True diff --git a/pyasic/miners/backends/hammer.py b/pyasic/miners/backends/hammer.py index 96fd53c0b..befa3c807 100644 --- a/pyasic/miners/backends/hammer.py +++ b/pyasic/miners/backends/hammer.py @@ -14,13 +14,17 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from typing import cast +from typing import Any -from pyasic import MinerConfig -from pyasic.data import Fan, HashBoard -from pyasic.data.error_codes import MinerErrorData, X19Error +from pydantic import BaseModel, Field, ValidationError + +from pyasic.config import MinerConfig +from pyasic.data.boards import HashBoard +from pyasic.data.error_codes import MinerErrorData +from pyasic.data.error_codes.X19 import X19Error +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import ( DataFunction, @@ -33,6 +37,140 @@ from pyasic.rpc.ccminer import CCMinerRPCAPI from pyasic.web.hammer import HammerWebAPI + +class HammerSystemInfo(BaseModel): + hostname: str + macaddr: str + + class Config: + extra = "allow" + + +class HammerNetworkInfo(BaseModel): + macaddr: str + conf_dnsservers: str + conf_gateway: str + conf_ipaddress: str + conf_netmask: str + conf_nettype: str + + class Config: + extra = "allow" + + +class HammerBlinkStatus(BaseModel): + blink: bool + + class Config: + extra = "allow" + + +class HammerBlinkResponse(BaseModel): + code: str + + class Config: + extra = "allow" + + +class HammerMinerConf(BaseModel): + bitmain_work_mode: str | int = Field(alias="bitmain-work-mode") + + class Config: + extra = "allow" + populate_by_name = True + + +class HammerVersionInfo(BaseModel): + API: str + CompileTime: str + + class Config: + extra = "allow" + + +class HammerVersionWrapper(BaseModel): + VERSION: list[HammerVersionInfo] + + class Config: + extra = "allow" + + +class HammerSummaryInfo(BaseModel): + MHS_5s: float = Field(alias="MHS 5s") + + class Config: + extra = "allow" + populate_by_name = True + + +class HammerSummaryWrapper(BaseModel): + SUMMARY: list[HammerSummaryInfo] + + class Config: + extra = "allow" + + +class HammerStatsInfo(BaseModel): + Elapsed: int + total_rateideal: float | None = None + rate_unit: str = "MH" + + class Config: + extra = "allow" + + +class HammerStatsWrapper(BaseModel): + STATS: list[Any] # Mixed list with different structures + + class Config: + extra = "allow" + + +class HammerPoolInfo(BaseModel): + URL: str | None = None + User: str | None = None + Status: str = "" + Stratum_Active: bool = Field(False, alias="Stratum Active") + Accepted: int = 0 + Rejected: int = 0 + Get_Failures: int = Field(0, alias="Get Failures") + Remote_Failures: int = Field(0, alias="Remote Failures") + POOL: int = 0 + + class Config: + extra = "allow" + populate_by_name = True + + +class HammerPoolsWrapper(BaseModel): + POOLS: list[HammerPoolInfo] + + class Config: + extra = "allow" + + +class HammerStatusItem(BaseModel): + status: str + msg: str + + class Config: + extra = "allow" + + +class HammerWebSummaryInfo(BaseModel): + status: list[HammerStatusItem] + + class Config: + extra = "allow" + + +class HammerWebSummaryWrapper(BaseModel): + SUMMARY: list[HammerWebSummaryInfo] + + class Config: + extra = "allow" + + HAMMER_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -117,15 +255,23 @@ async def send_config( async def fault_light_on(self) -> bool: data = await self.web.blink(blink=True) if data: - if data.get("code") == "B000": - self.light = True + try: + response = HammerBlinkResponse.model_validate(data) + if response.code == "B000": + self.light = True + except ValidationError: + pass return self.light or False async def fault_light_off(self) -> bool: data = await self.web.blink(blink=False) if data: - if data.get("code") == "B100": - self.light = False + try: + response = HammerBlinkResponse.model_validate(data) + if response.code == "B100": + self.light = False + except ValidationError: + pass return self.light or False async def reboot(self) -> bool: @@ -134,7 +280,9 @@ async def reboot(self) -> bool: return True return False - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -143,13 +291,16 @@ async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: if rpc_version is not None: try: - self.api_ver = rpc_version["VERSION"][0]["API"] - except LookupError: + version_response = HammerVersionWrapper.model_validate(rpc_version) + self.api_ver = version_response.VERSION[0].API + except ValidationError: pass return self.api_ver - async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_fw_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -158,14 +309,15 @@ async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: if rpc_version is not None: try: - self.fw_ver = rpc_version["VERSION"][0]["CompileTime"] - except LookupError: + version_response = HammerVersionWrapper.model_validate(rpc_version) + self.fw_ver = version_response.VERSION[0].CompileTime + except ValidationError: pass return self.fw_ver async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # get hr from API if rpc_summary is None: @@ -176,15 +328,18 @@ async def _get_hashrate( if rpc_summary is not None: try: + summary_response = HammerSummaryWrapper.model_validate(rpc_summary) return self.algo.hashrate( - rate=float(rpc_summary["SUMMARY"][0]["MHS 5s"]), + rate=summary_response.SUMMARY[0].MHS_5s, unit=self.algo.unit.MH, # type: ignore[attr-defined] ).into(self.algo.unit.default) # type: ignore[attr-defined] - except (LookupError, ValueError, TypeError): + except ValidationError: pass return None - async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -198,12 +353,14 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard if rpc_stats is not None: try: - board_offset = -1 - boards = rpc_stats["STATS"] + stats_wrapper = HammerStatsWrapper.model_validate(rpc_stats) + boards = stats_wrapper.STATS if len(boards) > 1: + board_data = boards[1] + board_offset = -1 for board_num in range(1, 16, 5): for _b_num in range(5): - b = boards[1].get(f"chain_acn{board_num + _b_num}") + b = board_data.get(f"chain_acn{board_num + _b_num}") if b and not b == 0 and board_offset == -1: board_offset = board_num @@ -213,12 +370,9 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard real_slots = [] for i in range(board_offset, board_offset + 4): - try: - key = f"chain_acs{i}" - if boards[1].get(key, "") != "": - real_slots.append(i) - except LookupError: - pass + key = f"chain_acs{i}" + if board_data.get(key, "") != "": + real_slots.append(i) if len(real_slots) < 3: real_slots = list( @@ -229,12 +383,12 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard hashboard = HashBoard( slot=i - board_offset, expected_chips=self.expected_chips ) - temp = boards[1].get(f"temp{i}") + temp = board_data.get(f"temp{i}") if temp: hashboard.chip_temp = round(temp) hashboard.temp = round(temp) - hashrate = boards[1].get(f"chain_rate{i}") + hashrate = board_data.get(f"chain_rate{i}") if hashrate: hashboard.hashrate = self.algo.hashrate( rate=float(hashrate), @@ -243,19 +397,19 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard self.algo.unit.default # type: ignore[attr-defined] ) - chips = boards[1].get(f"chain_acn{i}") + chips = board_data.get(f"chain_acn{i}") if chips: hashboard.chips = chips hashboard.missing = False if (not chips) or (not chips > 0): hashboard.missing = True hashboards.append(hashboard) - except (LookupError, ValueError, TypeError): + except (ValidationError, ValueError, TypeError): pass return hashboards - async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_stats: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -268,27 +422,28 @@ async def _get_fans(self, rpc_stats: dict | None = None) -> list[Fan]: fans = [Fan() for _ in range(self.expected_fans)] if rpc_stats is not None: try: - fan_offset = -1 - - for fan_num in range(1, 8, 4): - for _f_num in range(4): - f = rpc_stats["STATS"][1].get(f"fan{fan_num + _f_num}", 0) - if f and not f == 0 and fan_offset == -1: - fan_offset = fan_num - if fan_offset == -1: - fan_offset = 1 - - for fan in range(self.expected_fans): - fans[fan].speed = rpc_stats["STATS"][1].get( - f"fan{fan_offset + fan}", 0 - ) - except LookupError: + stats_wrapper = HammerStatsWrapper.model_validate(rpc_stats) + if len(stats_wrapper.STATS) > 1: + fan_data = stats_wrapper.STATS[1] + fan_offset = -1 + + for fan_num in range(1, 8, 4): + for _f_num in range(4): + f = fan_data.get(f"fan{fan_num + _f_num}", 0) + if f and not f == 0 and fan_offset == -1: + fan_offset = fan_num + if fan_offset == -1: + fan_offset = 1 + + for fan in range(self.expected_fans): + fans[fan].speed = fan_data.get(f"fan{fan_offset + fan}", 0) + except ValidationError: pass return fans async def _get_hostname( - self, web_get_system_info: dict | None = None + self, web_get_system_info: dict[str, Any] | None = None ) -> str | None: if web_get_system_info is None: try: @@ -298,12 +453,15 @@ async def _get_hostname( if web_get_system_info is not None: try: - return web_get_system_info["hostname"] - except KeyError: + system_info = HammerSystemInfo.model_validate(web_get_system_info) + return system_info.hostname + except ValidationError: pass return None - async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: + async def _get_mac( + self, web_get_system_info: dict[str, Any] | None = None + ) -> str | None: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -312,20 +470,22 @@ async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: if web_get_system_info is not None: try: - return web_get_system_info["macaddr"] - except KeyError: + system_info = HammerSystemInfo.model_validate(web_get_system_info) + return system_info.macaddr + except ValidationError: pass try: data = await self.web.get_network_info() if data: - return data["macaddr"] - except KeyError: + network_info = HammerNetworkInfo.model_validate(data) + return network_info.macaddr + except (APIError, ValidationError): pass return None async def _get_errors( - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> list[MinerErrorData]: if web_summary is None: try: @@ -336,18 +496,16 @@ async def _get_errors( errors = [] if web_summary is not None: try: - for item in web_summary["SUMMARY"][0]["status"]: - try: - if not item["status"] == "s": - errors.append(X19Error(error_message=item["msg"])) - except KeyError: - continue - except LookupError: + summary_response = HammerWebSummaryWrapper.model_validate(web_summary) + for item in summary_response.SUMMARY[0].status: + if item.status != "s": + errors.append(X19Error(error_message=item.msg)) + except ValidationError: pass - return cast(list[MinerErrorData], errors) + return errors # type: ignore[return-value] async def _get_fault_light( - self, web_get_blink_status: dict | None = None + self, web_get_blink_status: dict[str, Any] | None = None ) -> bool | None: if self.light: return self.light @@ -360,13 +518,14 @@ async def _get_fault_light( if web_get_blink_status is not None: try: - self.light = web_get_blink_status["blink"] - except KeyError: + blink_status = HammerBlinkStatus.model_validate(web_get_blink_status) + self.light = blink_status.blink + except ValidationError: pass return self.light async def _get_expected_hashrate( - self, rpc_stats: dict | None = None + self, rpc_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_stats is None: try: @@ -376,22 +535,23 @@ async def _get_expected_hashrate( if rpc_stats is not None: try: - expected_rate = rpc_stats["STATS"][1].get("total_rateideal") - if expected_rate is None: - if ( - hasattr(self, "sticker_hashrate") - and self.sticker_hashrate is not None - ): - return self.sticker_hashrate.into(self.algo.unit.default) # type: ignore[attr-defined] - return None - try: - rate_unit = rpc_stats["STATS"][1]["rate_unit"] - except KeyError: - rate_unit = "MH" - return self.algo.hashrate( - rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit) - ).into(self.algo.unit.default) # type: ignore[attr-defined] - except LookupError: + stats_wrapper = HammerStatsWrapper.model_validate(rpc_stats) + if len(stats_wrapper.STATS) > 1: + stats_info = HammerStatsInfo.model_validate(stats_wrapper.STATS[1]) + expected_rate = stats_info.total_rateideal + if expected_rate is None: + if ( + hasattr(self, "sticker_hashrate") + and self.sticker_hashrate is not None + ): + result = self.sticker_hashrate.into(self.algo.unit.default) # type: ignore[attr-defined] + return result # type: ignore[no-any-return] + return None + return self.algo.hashrate( + rate=expected_rate, + unit=self.algo.unit.from_str(stats_info.rate_unit), + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except ValidationError: pass return None @@ -402,7 +562,7 @@ async def set_static_ip( gateway: str, subnet_mask: str = "255.255.255.0", hostname: str | None = None, - ): + ) -> None: if not hostname: hostname = await self.get_hostname() or "" await self.web.set_network_conf( @@ -414,20 +574,21 @@ async def set_static_ip( protocol=2, ) - async def set_dhcp(self, hostname: str | None = None): + async def set_dhcp(self, hostname: str | None = None) -> None: if not hostname: hostname = await self.get_hostname() or "" await self.web.set_network_conf( ip="", dns="", gateway="", subnet_mask="", hostname=hostname, protocol=1 ) - async def set_hostname(self, hostname: str): + async def set_hostname(self, hostname: str) -> None: cfg = await self.web.get_network_info() - dns = cfg["conf_dnsservers"] - gateway = cfg["conf_gateway"] - ip = cfg["conf_ipaddress"] - subnet_mask = cfg["conf_netmask"] - protocol = 1 if cfg["conf_nettype"] == "DHCP" else 2 + network_info = HammerNetworkInfo.model_validate(cfg) + dns = network_info.conf_dnsservers + gateway = network_info.conf_gateway + ip = network_info.conf_ipaddress + subnet_mask = network_info.conf_netmask + protocol = 1 if network_info.conf_nettype == "DHCP" else 2 await self.web.set_network_conf( ip=ip, dns=dns, @@ -437,7 +598,9 @@ async def set_hostname(self, hostname: str): protocol=protocol, ) - async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: + async def _is_mining( + self, web_get_conf: dict[str, Any] | None = None + ) -> bool | None: if web_get_conf is None: try: web_get_conf = await self.web.get_miner_conf() @@ -446,16 +609,16 @@ async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: if web_get_conf is not None: try: - if str(web_get_conf["bitmain-work-mode"]).isdigit(): - return ( - False if int(web_get_conf["bitmain-work-mode"]) == 1 else True - ) + miner_conf = HammerMinerConf.model_validate(web_get_conf) + work_mode = str(miner_conf.bitmain_work_mode) + if work_mode.isdigit(): + return int(work_mode) != 1 return False - except LookupError: + except ValidationError: pass return None - async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: + async def _get_uptime(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -464,12 +627,17 @@ async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: if rpc_stats is not None: try: - return int(rpc_stats["STATS"][1]["Elapsed"]) - except LookupError: + stats_wrapper = HammerStatsWrapper.model_validate(rpc_stats) + if len(stats_wrapper.STATS) > 1: + stats_info = HammerStatsInfo.model_validate(stats_wrapper.STATS[1]) + return stats_info.Elapsed + except ValidationError: pass return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -479,22 +647,23 @@ async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: pools_data = [] if rpc_pools is not None: try: - pools = rpc_pools.get("POOLS", []) - for pool_info in pools: - url = pool_info.get("URL") - pool_url = PoolUrl.from_str(url) if url else None + pools_response = HammerPoolsWrapper.model_validate(rpc_pools) + for pool_info in pools_response.POOLS: + pool_url = ( + PoolUrl.from_str(pool_info.URL) if pool_info.URL else None + ) pool_data = PoolMetrics( - accepted=pool_info.get("Accepted"), - rejected=pool_info.get("Rejected"), - get_failures=pool_info.get("Get Failures"), - remote_failures=pool_info.get("Remote Failures"), - active=pool_info.get("Stratum Active"), - alive=pool_info.get("Status") == "Alive", + accepted=pool_info.Accepted, + rejected=pool_info.Rejected, + get_failures=pool_info.Get_Failures, + remote_failures=pool_info.Remote_Failures, + active=pool_info.Stratum_Active, + alive=pool_info.Status == "Alive", url=pool_url, - user=pool_info.get("User"), - index=pool_info.get("POOL"), + user=pool_info.User, + index=pool_info.POOL, ) pools_data.append(pool_data) - except LookupError: + except ValidationError: pass return pools_data diff --git a/pyasic/miners/backends/hiveon.py b/pyasic/miners/backends/hiveon.py index 7b1bbe1f4..0972007be 100644 --- a/pyasic/miners/backends/hiveon.py +++ b/pyasic/miners/backends/hiveon.py @@ -14,8 +14,11 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from pyasic import APIError -from pyasic.config import MinerConfig, MiningModeConfig +from typing import Any + +from pyasic.config import MinerConfig +from pyasic.config.mining import MiningModeConfig +from pyasic.errors import APIError from pyasic.miners.backends import BMMiner from pyasic.miners.data import ( DataFunction, @@ -135,7 +138,7 @@ async def resume_mining(self) -> bool: return True return False - async def _get_wattage(self, rpc_stats: dict | None = None) -> int | None: + async def _get_wattage(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if not rpc_stats: try: rpc_stats = await self.rpc.stats() @@ -155,7 +158,7 @@ async def _get_wattage(self, rpc_stats: dict | None = None) -> int | None: return None async def _get_hostname( - self, web_get_system_info: dict | None = None + self, web_get_system_info: dict[str, Any] | None = None ) -> str | None: if web_get_system_info is None: try: @@ -165,12 +168,14 @@ async def _get_hostname( if web_get_system_info is not None: try: - return web_get_system_info["hostname"] + return str(web_get_system_info["hostname"]) except KeyError: pass return None - async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: + async def _get_mac( + self, web_get_system_info: dict[str, Any] | None = None + ) -> str | None: if web_get_system_info is None: try: web_get_system_info = await self.web.get_system_info() @@ -179,20 +184,20 @@ async def _get_mac(self, web_get_system_info: dict | None = None) -> str | None: if web_get_system_info is not None: try: - return web_get_system_info["macaddr"] + return str(web_get_system_info["macaddr"]) except KeyError: pass try: data = await self.web.get_network_info() if data: - return data["macaddr"] + return str(data["macaddr"]) except KeyError: pass return None async def _get_fault_light( - self, web_get_blink_status: dict | None = None + self, web_get_blink_status: dict[str, Any] | None = None ) -> bool | None: if self.light: return self.light @@ -210,7 +215,9 @@ async def _get_fault_light( pass return self.light - async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: + async def _is_mining( + self, web_get_conf: dict[str, Any] | None = None + ) -> bool | None: if web_get_conf is None: try: web_get_conf = await self.web.get_miner_conf() @@ -274,7 +281,7 @@ async def _is_mining(self, web_get_conf: dict | None = None) -> bool | None: class HiveonOld(HiveonFirmware, BMMiner): data_locations = HIVEON_OLD_DATA_LOC - async def _get_wattage(self, rpc_stats: dict | None = None) -> int | None: + async def _get_wattage(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if not rpc_stats: try: rpc_stats = await self.rpc.stats() diff --git a/pyasic/miners/backends/iceriver.py b/pyasic/miners/backends/iceriver.py index f3a49ef18..e37f2bd18 100644 --- a/pyasic/miners/backends/iceriver.py +++ b/pyasic/miners/backends/iceriver.py @@ -1,12 +1,73 @@ -from pyasic import MinerConfig -from pyasic.data import Fan, HashBoard +from typing import Any + +from pydantic import BaseModel, ValidationError + +from pyasic.config import MinerConfig +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType, MinerAlgo +from pyasic.device.algorithm import MinerAlgo +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.miners.device.firmware import StockFirmware from pyasic.web.iceriver import IceRiverWebAPI + +class IceRiverBoardInfo(BaseModel): + no: int + outtmp: float + intmp: float + rtpow: str + chipnum: int + + class Config: + extra = "allow" + + +class IceRiverPoolInfo(BaseModel): + no: int | None = None + addr: str = "" + user: str | None = None + accepted: int = 0 + rejected: int = 0 + connect: bool = False + state: int = 0 + + class Config: + extra = "allow" + + +class IceRiverData(BaseModel): + mac: str + host: str + fans: list[int] + unit: str + rtpow: str + locate: bool + powstate: bool + boards: list[IceRiverBoardInfo] + runtime: str + pools: list[IceRiverPoolInfo] + + class Config: + extra = "allow" + + +class IceRiverUserPanel(BaseModel): + data: IceRiverData + + class Config: + extra = "allow" + + +class IceRiverUserPanelWrapper(BaseModel): + userpanel: IceRiverUserPanel + + class Config: + extra = "allow" + + ICERIVER_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -76,7 +137,7 @@ async def get_config(self) -> MinerConfig: return MinerConfig.from_iceriver(web_userpanel) - async def _get_fans(self, web_userpanel: dict | None = None) -> list[Fan]: + async def _get_fans(self, web_userpanel: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -88,14 +149,13 @@ async def _get_fans(self, web_userpanel: dict | None = None) -> list[Fan]: if web_userpanel is not None: try: - return [ - Fan(speed=spd) for spd in web_userpanel["userpanel"]["data"]["fans"] - ] - except (LookupError, ValueError, TypeError): + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + return [Fan(speed=spd) for spd in panel_response.userpanel.data.fans] + except ValidationError: pass return [] - async def _get_mac(self, web_userpanel: dict | None = None) -> str | None: + async def _get_mac(self, web_userpanel: dict[str, Any] | None = None) -> str | None: if web_userpanel is None: try: web_userpanel = await self.web.userpanel() @@ -104,14 +164,15 @@ async def _get_mac(self, web_userpanel: dict | None = None) -> str | None: if web_userpanel is not None: try: - return ( - web_userpanel["userpanel"]["data"]["mac"].upper().replace("-", ":") - ) - except (LookupError, ValueError, TypeError): + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + return panel_response.userpanel.data.mac.upper().replace("-", ":") + except ValidationError: pass return None - async def _get_hostname(self, web_userpanel: dict | None = None) -> str | None: + async def _get_hostname( + self, web_userpanel: dict[str, Any] | None = None + ) -> str | None: if web_userpanel is None: try: web_userpanel = await self.web.userpanel() @@ -120,13 +181,14 @@ async def _get_hostname(self, web_userpanel: dict | None = None) -> str | None: if web_userpanel is not None: try: - return web_userpanel["userpanel"]["data"]["host"] - except (LookupError, ValueError, TypeError): + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + return panel_response.userpanel.data.host + except ValidationError: pass return None async def _get_hashrate( - self, web_userpanel: dict | None = None + self, web_userpanel: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_userpanel is None: try: @@ -136,20 +198,21 @@ async def _get_hashrate( if web_userpanel is not None: try: - base_unit = web_userpanel["userpanel"]["data"]["unit"] + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + base_unit = panel_response.userpanel.data.unit return self.algo.hashrate( rate=float( - web_userpanel["userpanel"]["data"]["rtpow"].replace( - base_unit, "" - ) + panel_response.userpanel.data.rtpow.replace(base_unit, "") ), unit=MinerAlgo.SHA256.unit.from_str(base_unit + "H"), ).into(MinerAlgo.SHA256.unit.default) - except (LookupError, ValueError, TypeError): + except (ValidationError, ValueError): pass return None - async def _get_fault_light(self, web_userpanel: dict | None = None) -> bool: + async def _get_fault_light( + self, web_userpanel: dict[str, Any] | None = None + ) -> bool: if web_userpanel is None: try: web_userpanel = await self.web.userpanel() @@ -158,12 +221,15 @@ async def _get_fault_light(self, web_userpanel: dict | None = None) -> bool: if web_userpanel is not None: try: - return web_userpanel["userpanel"]["data"]["locate"] - except (LookupError, ValueError, TypeError): + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + return panel_response.userpanel.data.locate + except ValidationError: pass return False - async def _is_mining(self, web_userpanel: dict | None = None) -> bool | None: + async def _is_mining( + self, web_userpanel: dict[str, Any] | None = None + ) -> bool | None: if web_userpanel is None: try: web_userpanel = await self.web.userpanel() @@ -172,13 +238,14 @@ async def _is_mining(self, web_userpanel: dict | None = None) -> bool | None: if web_userpanel is not None: try: - return web_userpanel["userpanel"]["data"]["powstate"] - except (LookupError, ValueError, TypeError): + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + return panel_response.userpanel.data.powstate + except ValidationError: pass return False async def _get_hashboards( - self, web_userpanel: dict | None = None + self, web_userpanel: dict[str, Any] | None = None ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -196,23 +263,26 @@ async def _get_hashboards( if web_userpanel is not None: try: - for board in web_userpanel["userpanel"]["data"]["boards"]: - idx = int(board["no"] - 1) - hb_list[idx].chip_temp = round(board["outtmp"]) - hb_list[idx].temp = round(board["intmp"]) + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + for board in panel_response.userpanel.data.boards: + idx = board.no - 1 + hb_list[idx].chip_temp = round(board.outtmp) + hb_list[idx].temp = round(board.intmp) hb_list[idx].hashrate = self.algo.hashrate( - rate=float(board["rtpow"].replace("G", "")), + rate=float(board.rtpow.replace("G", "")), unit=self.algo.unit.GH, # type: ignore[attr-defined] ).into( self.algo.unit.default # type: ignore[attr-defined] ) - hb_list[idx].chips = int(board["chipnum"]) + hb_list[idx].chips = board.chipnum hb_list[idx].missing = False - except LookupError: + except (ValidationError, ValueError): pass return hb_list - async def _get_uptime(self, web_userpanel: dict | None = None) -> int | None: + async def _get_uptime( + self, web_userpanel: dict[str, Any] | None = None + ) -> int | None: if web_userpanel is None: try: web_userpanel = await self.web.userpanel() @@ -221,7 +291,8 @@ async def _get_uptime(self, web_userpanel: dict | None = None) -> int | None: if web_userpanel is not None: try: - runtime = web_userpanel["userpanel"]["data"]["runtime"] + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + runtime = panel_response.userpanel.data.runtime days, hours, minutes, seconds = runtime.split(":") return ( (int(days) * 24 * 60 * 60) @@ -229,11 +300,13 @@ async def _get_uptime(self, web_userpanel: dict | None = None) -> int | None: + (int(minutes) * 60) + int(seconds) ) - except (LookupError, ValueError, TypeError): + except (ValidationError, ValueError): pass return None - async def _get_pools(self, web_userpanel: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, web_userpanel: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if web_userpanel is None: try: web_userpanel = await self.web.userpanel() @@ -243,25 +316,23 @@ async def _get_pools(self, web_userpanel: dict | None = None) -> list[PoolMetric pools_data = [] if web_userpanel is not None: try: - pools = web_userpanel["userpanel"]["data"]["pools"] - for pool_info in pools: - pool_num = pool_info.get("no") - if pool_num is not None: - pool_num = int(pool_num) - if pool_info["addr"] == "": + panel_response = IceRiverUserPanelWrapper.model_validate(web_userpanel) + for pool_info in panel_response.userpanel.data.pools: + if pool_info.addr == "": continue - url = pool_info.get("addr") - pool_url = PoolUrl.from_str(url) if url else None + pool_url = ( + PoolUrl.from_str(pool_info.addr) if pool_info.addr else None + ) pool_data = PoolMetrics( - accepted=pool_info.get("accepted"), - rejected=pool_info.get("rejected"), - active=pool_info.get("connect"), - alive=int(pool_info.get("state", 0)) == 1, + accepted=pool_info.accepted, + rejected=pool_info.rejected, + active=pool_info.connect, + alive=pool_info.state == 1, url=pool_url, - user=pool_info.get("user"), - index=pool_num, + user=pool_info.user, + index=pool_info.no, ) pools_data.append(pool_data) - except LookupError: + except ValidationError: pass return pools_data diff --git a/pyasic/miners/backends/innosilicon.py b/pyasic/miners/backends/innosilicon.py index bd10d000b..cb486aaf1 100644 --- a/pyasic/miners/backends/innosilicon.py +++ b/pyasic/miners/backends/innosilicon.py @@ -14,11 +14,14 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard from pyasic.data.error_codes.innosilicon import InnosiliconError +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.backends import CGMiner from pyasic.miners.data import ( @@ -123,7 +126,7 @@ async def reboot(self) -> bool: except APIError: return False else: - return data["success"] + return bool(data["success"]) async def restart_cgminer(self) -> bool: try: @@ -131,7 +134,7 @@ async def restart_cgminer(self) -> bool: except APIError: return False else: - return data["success"] + return bool(data["success"]) async def restart_backend(self) -> bool: return await self.restart_cgminer() @@ -147,7 +150,9 @@ async def send_config( ################################################## async def _get_mac( - self, web_get_all: dict | None = None, web_overview: dict | None = None + self, + web_get_all: dict[str, Any] | None = None, + web_overview: dict[str, Any] | None = None, ) -> str | None: if web_get_all: web_get_all = web_get_all["all"] @@ -161,20 +166,22 @@ async def _get_mac( if web_get_all is not None: try: mac = web_get_all["mac"] - return mac.upper() + return str(mac).upper() except KeyError: pass if web_overview is not None: try: mac = web_overview["version"]["ethaddr"] - return mac.upper() + return str(mac).upper() except KeyError: pass return None async def _get_hashrate( - self, rpc_summary: dict | None = None, web_get_all: dict | None = None + self, + rpc_summary: dict[str, Any] | None = None, + web_get_all: dict[str, Any] | None = None, ) -> AlgoHashRateType | None: if web_get_all: web_get_all = web_get_all["all"] @@ -217,7 +224,9 @@ async def _get_hashrate( return None async def _get_hashboards( - self, rpc_stats: dict | None = None, web_get_all: dict | None = None + self, + rpc_stats: dict[str, Any] | None = None, + web_get_all: dict[str, Any] | None = None, ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -281,7 +290,9 @@ async def _get_hashboards( return hashboards async def _get_wattage( - self, web_get_all: dict | None = None, rpc_stats: dict | None = None + self, + web_get_all: dict[str, Any] | None = None, + rpc_stats: dict[str, Any] | None = None, ) -> int | None: if web_get_all: web_get_all = web_get_all["all"] @@ -296,7 +307,7 @@ async def _get_wattage( if web_get_all is not None: try: - return web_get_all["power"] + return int(web_get_all["power"]) except KeyError: pass @@ -318,7 +329,7 @@ async def _get_wattage( return wattage return None - async def _get_fans(self, web_get_all: dict | None = None) -> list[Fan]: + async def _get_fans(self, web_get_all: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -347,7 +358,7 @@ async def _get_fans(self, web_get_all: dict | None = None) -> list[Fan]: return fans async def _get_errors( # type: ignore[override] - self, web_get_error_detail: dict | None = None + self, web_get_error_detail: dict[str, Any] | None = None ) -> list[InnosiliconError]: errors = [] if web_get_error_detail is None: @@ -369,7 +380,9 @@ async def _get_errors( # type: ignore[override] errors.append(InnosiliconError(error_code=err)) return errors - async def _get_wattage_limit(self, web_get_all: dict | None = None) -> int | None: + async def _get_wattage_limit( + self, web_get_all: dict[str, Any] | None = None + ) -> int | None: if web_get_all: web_get_all = web_get_all["all"] @@ -393,7 +406,9 @@ async def _get_wattage_limit(self, web_get_all: dict | None = None) -> int | Non return limit return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() diff --git a/pyasic/miners/backends/luxminer.py b/pyasic/miners/backends/luxminer.py index 555c17362..5fb9db046 100644 --- a/pyasic/miners/backends/luxminer.py +++ b/pyasic/miners/backends/luxminer.py @@ -14,17 +14,174 @@ # limitations under the License. - # ------------------------------------------------------------------------------ import logging +from typing import Any + +from pydantic import BaseModel, Field, ValidationError from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePreset -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand from pyasic.miners.device.firmware import LuxOSFirmware from pyasic.rpc.luxminer import LUXMinerRPCAPI + +class LuxConfigResponse(BaseModel): + MACAddr: str + RedLed: str = "off" + + class Config: + extra = "allow" + + +class LuxConfigWrapper(BaseModel): + CONFIG: list[LuxConfigResponse] + + class Config: + extra = "allow" + + +class LuxSummaryResponse(BaseModel): + GHS_5s: float = Field(alias="GHS 5s") + + class Config: + extra = "allow" + populate_by_name = True + + +class LuxSummaryWrapper(BaseModel): + SUMMARY: list[LuxSummaryResponse] + + class Config: + extra = "allow" + + +class LuxPowerResponse(BaseModel): + Watts: float | int + + class Config: + extra = "allow" + + +class LuxPowerWrapper(BaseModel): + POWER: list[LuxPowerResponse] + + class Config: + extra = "allow" + + +class LuxStatsResponse(BaseModel): + Elapsed: int + total_rateideal: float + rate_unit: str = "GH" + chain_rate1: float = 0 + chain_rate2: float = 0 + chain_rate3: float = 0 + chain_acn1: int = 0 + chain_acn2: int = 0 + chain_acn3: int = 0 + temp_chip1: str = "0-0-0-0" + temp_chip2: str = "0-0-0-0" + temp_chip3: str = "0-0-0-0" + temp_pcb1: str = "0-0-0-0" + temp_pcb2: str = "0-0-0-0" + temp_pcb3: str = "0-0-0-0" + + class Config: + extra = "allow" + + +class LuxStatsWrapper(BaseModel): + STATS: list[Any] + + class Config: + extra = "allow" + + +class LuxFanResponse(BaseModel): + RPM: int + + class Config: + extra = "allow" + + +class LuxFansWrapper(BaseModel): + FANS: list[LuxFanResponse] + + class Config: + extra = "allow" + + +class LuxVersionResponse(BaseModel): + Miner: str + API: str + + class Config: + extra = "allow" + + +class LuxVersionWrapper(BaseModel): + VERSION: list[LuxVersionResponse] + + class Config: + extra = "allow" + + +class LuxPoolInfo(BaseModel): + URL: str | None = None + User: str | None = None + Status: str = "" + Stratum_Active: bool = Field(False, alias="Stratum Active") + Accepted: int = 0 + Rejected: int = 0 + Get_Failures: int = Field(0, alias="Get Failures") + Remote_Failures: int = Field(0, alias="Remote Failures") + POOL: int = 0 + + class Config: + extra = "allow" + populate_by_name = True + + +class LuxPoolsWrapper(BaseModel): + POOLS: list[LuxPoolInfo] + + class Config: + extra = "allow" + + +class LuxATMResponse(BaseModel): + Enabled: bool + + class Config: + extra = "allow" + + +class LuxATMWrapper(BaseModel): + ATM: list[LuxATMResponse] + + class Config: + extra = "allow" + + +class LuxProfileResponse(BaseModel): + Profile: str + + class Config: + extra = "allow" + + +class LuxProfileWrapper(BaseModel): + PROFILE: list[LuxProfileResponse] + + class Config: + extra = "allow" + + LUXMINER_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -153,7 +310,7 @@ async def get_config(self) -> MinerConfig: rpc_profiles=data.get("profiles", [{}])[0], ) - async def upgrade_firmware(self, *args, **kwargs) -> bool: + async def upgrade_firmware(self, *args: Any, **kwargs: Any) -> bool: """ Upgrade the firmware on a LuxOS miner by calling the 'updaterun' API command. Returns: @@ -172,8 +329,9 @@ async def upgrade_firmware(self, *args, **kwargs) -> bool: async def atm_enabled(self) -> bool | None: try: result = await self.rpc.atm() - return result["ATM"][0]["Enabled"] - except (APIError, LookupError): + atm_response = LuxATMWrapper.model_validate(result) + return atm_response.ATM[0].Enabled + except (APIError, ValidationError): pass return None @@ -218,49 +376,54 @@ async def set_power_limit(self, wattage: int) -> bool: logging.warning(f"{self} - Failed to set power limit: {e}") return False - if result["PROFILE"][0]["Profile"] == new_preset: - return True - else: + try: + profile_response = LuxProfileWrapper.model_validate(result) + return bool(profile_response.PROFILE[0].Profile == new_preset) + except ValidationError: return False ################################################## ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## - async def _get_mac(self, rpc_config: dict | None = None) -> str | None: + async def _get_mac(self, rpc_config: dict[str, Any] | None = None) -> str | None: if rpc_config is None: try: rpc_config = await self.rpc.config() except APIError: pass - return None if rpc_config is not None: try: - return rpc_config["CONFIG"][0]["MACAddr"].upper() - except KeyError: + config_response = LuxConfigWrapper.model_validate(rpc_config) + return config_response.CONFIG[0].MACAddr.upper() + except ValidationError: pass + return None async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_summary is None: try: rpc_summary = await self.rpc.summary() except APIError: pass - return None if rpc_summary is not None: try: + summary_response = LuxSummaryWrapper.model_validate(rpc_summary) return self.algo.hashrate( - rate=float(rpc_summary["SUMMARY"][0]["GHS 5s"]), - unit=self.algo.unit.GH, - ).into(self.algo.unit.default) - except (LookupError, ValueError, TypeError): + rate=summary_response.SUMMARY[0].GHS_5s, + unit=self.algo.unit.GH, # type: ignore[attr-defined] + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except ValidationError: pass + return None - async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard]: + async def _get_hashboards( + self, rpc_stats: dict[str, Any] | None = None + ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -277,56 +440,73 @@ async def _get_hashboards(self, rpc_stats: dict | None = None) -> list[HashBoard if rpc_stats is not None: try: # TODO: bugged on S9 because of index issues, fix later. - board_stats = rpc_stats["STATS"][1] - for idx in range(3): - board_n = idx + 1 - hashboards[idx].hashrate = self.algo.hashrate( - rate=float(board_stats[f"chain_rate{board_n}"]), - unit=self.algo.unit.GH, # type: ignore[attr-defined] - ).into( - self.algo.unit.default # type: ignore[attr-defined] + stats_wrapper = LuxStatsWrapper.model_validate(rpc_stats) + if len(stats_wrapper.STATS) > 1: + board_stats = LuxStatsResponse.model_validate( + stats_wrapper.STATS[1] ) - hashboards[idx].chips = int(board_stats[f"chain_acn{board_n}"]) - chip_temp_data = list( - filter( - lambda x: not x == 0, - map(int, board_stats[f"temp_chip{board_n}"].split("-")), + for idx in range(3): + board_n = idx + 1 + chain_rate = getattr(board_stats, f"chain_rate{board_n}", 0) + hashboards[idx].hashrate = self.algo.hashrate( + rate=chain_rate, + unit=self.algo.unit.GH, # type: ignore[attr-defined] + ).into( + self.algo.unit.default # type: ignore[attr-defined] ) - ) - hashboards[idx].chip_temp = ( - sum([chip_temp_data[0], chip_temp_data[3]]) / 2 - ) - board_temp_data = list( - filter( - lambda x: not x == 0, - map(int, board_stats[f"temp_pcb{board_n}"].split("-")), + hashboards[idx].chips = getattr( + board_stats, f"chain_acn{board_n}", 0 ) - ) - hashboards[idx].temp = ( - sum([board_temp_data[1], board_temp_data[2]]) / 2 - ) - hashboards[idx].missing = False - except LookupError: + chip_temp_str = getattr( + board_stats, f"temp_chip{board_n}", "0-0-0-0" + ) + chip_temp_data = list( + filter( + lambda x: not x == 0, + map(int, chip_temp_str.split("-")), + ) + ) + if len(chip_temp_data) >= 4: + hashboards[idx].chip_temp = ( + sum([chip_temp_data[0], chip_temp_data[3]]) / 2 + ) + board_temp_str = getattr( + board_stats, f"temp_pcb{board_n}", "0-0-0-0" + ) + board_temp_data = list( + filter( + lambda x: not x == 0, + map(int, board_temp_str.split("-")), + ) + ) + if len(board_temp_data) >= 3: + hashboards[idx].temp = ( + sum([board_temp_data[1], board_temp_data[2]]) / 2 + ) + hashboards[idx].missing = False + except ValidationError: pass return hashboards - async def _get_wattage(self, rpc_power: dict | None = None) -> int | None: + async def _get_wattage(self, rpc_power: dict[str, Any] | None = None) -> int | None: if rpc_power is None: try: rpc_power = await self.rpc.power() except APIError: pass - return None if rpc_power is not None: try: - return rpc_power["POWER"][0]["Watts"] - except (LookupError, ValueError, TypeError): + power_response = LuxPowerWrapper.model_validate(rpc_power) + return round(power_response.POWER[0].Watts) + except ValidationError: pass return None async def _get_wattage_limit( - self, rpc_config: dict | None = None, rpc_profiles: dict | None = None + self, + rpc_config: dict[str, Any] | None = None, + rpc_profiles: dict[str, Any] | None = None, ) -> int | None: if rpc_config is None or rpc_profiles is None: return None @@ -339,7 +519,7 @@ async def _get_wattage_limit( pass return None - async def _get_fans(self, rpc_fans: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_fans: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -352,15 +532,21 @@ async def _get_fans(self, rpc_fans: dict | None = None) -> list[Fan]: fans = [] if rpc_fans is not None: - for fan in range(self.expected_fans): - try: - fans.append(Fan(speed=rpc_fans["FANS"][fan]["RPM"])) - except (LookupError, ValueError, TypeError): - fans.append(Fan()) + try: + fans_response = LuxFansWrapper.model_validate(rpc_fans) + for fan_idx in range(self.expected_fans): + if fan_idx < len(fans_response.FANS): + fans.append(Fan(speed=fans_response.FANS[fan_idx].RPM)) + else: + fans.append(Fan()) + except ValidationError: + fans = [Fan() for _ in range(self.expected_fans)] + else: + fans = [Fan() for _ in range(self.expected_fans)] return fans async def _get_expected_hashrate( - self, rpc_stats: dict | None = None + self, rpc_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_stats is None: try: @@ -370,19 +556,20 @@ async def _get_expected_hashrate( if rpc_stats is not None: try: - expected_rate = rpc_stats["STATS"][1]["total_rateideal"] - try: - rate_unit = rpc_stats["STATS"][1]["rate_unit"] - except KeyError: - rate_unit = "GH" - return self.algo.hashrate( - rate=float(expected_rate), unit=self.algo.unit.from_str(rate_unit) - ).into(self.algo.unit.default) # type: ignore[attr-defined] - except LookupError: + stats_wrapper = LuxStatsWrapper.model_validate(rpc_stats) + if len(stats_wrapper.STATS) > 1: + stats_response = LuxStatsResponse.model_validate( + stats_wrapper.STATS[1] + ) + return self.algo.hashrate( + rate=stats_response.total_rateideal, + unit=self.algo.unit.from_str(stats_response.rate_unit), + ).into(self.algo.unit.default) # type: ignore[attr-defined] + except ValidationError: pass return None - async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: + async def _get_uptime(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -391,12 +578,19 @@ async def _get_uptime(self, rpc_stats: dict | None = None) -> int | None: if rpc_stats is not None: try: - return int(rpc_stats["STATS"][1]["Elapsed"]) - except LookupError: + stats_wrapper = LuxStatsWrapper.model_validate(rpc_stats) + if len(stats_wrapper.STATS) > 1: + stats_response = LuxStatsResponse.model_validate( + stats_wrapper.STATS[1] + ) + return stats_response.Elapsed + except ValidationError: pass return None - async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_fw_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -405,12 +599,15 @@ async def _get_fw_ver(self, rpc_version: dict | None = None) -> str | None: if rpc_version is not None: try: - return rpc_version["VERSION"][0]["Miner"] - except LookupError: + version_response = LuxVersionWrapper.model_validate(rpc_version) + return version_response.VERSION[0].Miner + except ValidationError: pass return None - async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: + async def _get_api_ver( + self, rpc_version: dict[str, Any] | None = None + ) -> str | None: if rpc_version is None: try: rpc_version = await self.rpc.version() @@ -419,12 +616,15 @@ async def _get_api_ver(self, rpc_version: dict | None = None) -> str | None: if rpc_version is not None: try: - return rpc_version["VERSION"][0]["API"] - except LookupError: + version_response = LuxVersionWrapper.model_validate(rpc_version) + return version_response.VERSION[0].API + except ValidationError: pass return None - async def _get_fault_light(self, rpc_config: dict | None = None) -> bool | None: + async def _get_fault_light( + self, rpc_config: dict[str, Any] | None = None + ) -> bool | None: if rpc_config is None: try: rpc_config = await self.rpc.config() @@ -433,12 +633,15 @@ async def _get_fault_light(self, rpc_config: dict | None = None) -> bool | None: if rpc_config is not None: try: - return not rpc_config["CONFIG"][0]["RedLed"] == "off" - except LookupError: + config_response = LuxConfigWrapper.model_validate(rpc_config) + return config_response.CONFIG[0].RedLed != "off" + except ValidationError: pass return None - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -448,22 +651,23 @@ async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: pools_data = [] if rpc_pools is not None: try: - pools = rpc_pools.get("POOLS", []) - for pool_info in pools: - url = pool_info.get("URL") - pool_url = PoolUrl.from_str(url) if url else None + pools_response = LuxPoolsWrapper.model_validate(rpc_pools) + for pool_info in pools_response.POOLS: + pool_url = ( + PoolUrl.from_str(pool_info.URL) if pool_info.URL else None + ) pool_data = PoolMetrics( - accepted=pool_info.get("Accepted"), - rejected=pool_info.get("Rejected"), - get_failures=pool_info.get("Get Failures"), - remote_failures=pool_info.get("Remote Failures"), - active=pool_info.get("Stratum Active"), - alive=pool_info.get("Status") == "Alive", + accepted=pool_info.Accepted, + rejected=pool_info.Rejected, + get_failures=pool_info.Get_Failures, + remote_failures=pool_info.Remote_Failures, + active=pool_info.Stratum_Active, + alive=pool_info.Status == "Alive", url=pool_url, - user=pool_info.get("User"), - index=pool_info.get("POOL"), + user=pool_info.User, + index=pool_info.POOL, ) pools_data.append(pool_data) - except LookupError: + except ValidationError: pass return pools_data diff --git a/pyasic/miners/backends/marathon.py b/pyasic/miners/backends/marathon.py index 5ffbda07d..7be768482 100644 --- a/pyasic/miners/backends/marathon.py +++ b/pyasic/miners/backends/marathon.py @@ -1,8 +1,12 @@ -from pyasic import MinerConfig -from pyasic.config import MiningModeConfig -from pyasic.data import Fan, HashBoard +from typing import Any + +from pydantic import BaseModel, Field, ValidationError + +from pyasic.config import MinerConfig, MiningModeConfig +from pyasic.data.boards import HashBoard +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics, PoolUrl -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand from pyasic.miners.device.firmware import MaraFirmware @@ -10,6 +14,117 @@ from pyasic.rpc.marathon import MaraRPCAPI from pyasic.web.marathon import MaraWebAPI + +class MaraBriefResponse(BaseModel): + power_consumption_estimated: float + status: str + elapsed: int + hashrate_realtime: float + hashrate_ideal: float + + class Config: + extra = "allow" + + +class MaraOverviewResponse(BaseModel): + mac: str + version_firmware: str + + class Config: + extra = "allow" + + +class MaraNetworkConfigResponse(BaseModel): + hostname: str + + class Config: + extra = "allow" + + +class MaraHashboardInfo(BaseModel): + index: int + hashrate_average: float + temperature_pcb: list[float] + temperature_chip: list[float] + asic_num: int + serial_number: str + + class Config: + extra = "allow" + + +class MaraHashboardsResponse(BaseModel): + hashboards: list[MaraHashboardInfo] + + class Config: + extra = "allow" + + +class MaraLocateMinerResponse(BaseModel): + blinking: bool + + class Config: + extra = "allow" + + +class MaraConcordeMode(BaseModel): + power_target: int = Field(alias="power-target") + + class Config: + extra = "allow" + populate_by_name = True + + +class MaraMinerMode(BaseModel): + concorde: MaraConcordeMode + + class Config: + extra = "allow" + + +class MaraMinerConfigResponse(BaseModel): + mode: MaraMinerMode + + class Config: + extra = "allow" + + +class MaraFanInfo(BaseModel): + current_speed: int + + class Config: + extra = "allow" + + +class MaraFansResponse(BaseModel): + fans: list[MaraFanInfo] + + class Config: + extra = "allow" + + +class MaraPoolInfo(BaseModel): + url: str | None = None + user: str | None = None + status: str + priority: int + index: int + accepted: int = 0 + rejected: int = 0 + stale: int = 0 + discarded: int = 0 + + class Config: + extra = "allow" + + +class MaraPoolsResponse(BaseModel): + pools: list[MaraPoolInfo] + + class Config: + extra = "allow" + + MARA_DATA_LOC = DataLocations( **{ str(DataOptions.MAC): DataFunction( @@ -78,11 +193,19 @@ class MaraMiner(MaraFirmware): async def fault_light_off(self) -> bool: res = await self.web.set_locate_miner(blinking=False) - return res.get("blinking") is False + try: + locate = MaraLocateMinerResponse.model_validate(res) + return not locate.blinking + except ValidationError: + return False async def fault_light_on(self) -> bool: res = await self.web.set_locate_miner(blinking=True) - return res.get("blinking") is True + try: + locate = MaraLocateMinerResponse.model_validate(res) + return locate.blinking + except ValidationError: + return False async def get_config(self) -> MinerConfig: data = await self.web.get_miner_config() @@ -125,50 +248,53 @@ async def restart_backend(self) -> bool: await self.web.reload() return True - async def _get_wattage(self, web_brief: dict | None = None) -> int | None: + async def _get_wattage(self, web_brief: dict[str, Any] | None = None) -> int | None: if web_brief is None: try: web_brief = await self.web.brief() except APIError: pass - return None if web_brief is not None: try: - return round(web_brief["power_consumption_estimated"]) - except LookupError: + brief = MaraBriefResponse.model_validate(web_brief) + return round(brief.power_consumption_estimated) + except ValidationError: pass + return None - async def _is_mining(self, web_brief: dict | None = None) -> bool | None: + async def _is_mining(self, web_brief: dict[str, Any] | None = None) -> bool | None: if web_brief is None: try: web_brief = await self.web.brief() except APIError: pass - return None if web_brief is not None: try: - return web_brief["status"] == "Mining" - except LookupError: + brief = MaraBriefResponse.model_validate(web_brief) + return brief.status == "Mining" + except ValidationError: pass + return None - async def _get_uptime(self, web_brief: dict | None = None) -> int | None: + async def _get_uptime(self, web_brief: dict[str, Any] | None = None) -> int | None: if web_brief is None: try: web_brief = await self.web.brief() except APIError: pass - return None if web_brief is not None: try: - return web_brief["elapsed"] - except LookupError: + brief = MaraBriefResponse.model_validate(web_brief) + return brief.elapsed + except ValidationError: pass + return None async def _get_hashboards( - self, web_hashboards: dict | None = None + self, web_hashboards: dict[str, Any] | None = None ) -> list[HashBoard]: if self.expected_hashboards is None: return [] @@ -186,28 +312,31 @@ async def _get_hashboards( if web_hashboards is not None: try: - for hb in web_hashboards["hashboards"]: - idx = hb["index"] + response = MaraHashboardsResponse.model_validate(web_hashboards) + for hb in response.hashboards: + idx = hb.index hashboards[idx].hashrate = self.algo.hashrate( - rate=float(hb["hashrate_average"]), + rate=hb.hashrate_average, unit=self.algo.unit.GH, # type: ignore[attr-defined] ).into( self.algo.unit.default # type: ignore[attr-defined] ) - hashboards[idx].temp = round( - sum(hb["temperature_pcb"]) / len(hb["temperature_pcb"]) - ) - hashboards[idx].chip_temp = round( - sum(hb["temperature_chip"]) / len(hb["temperature_chip"]) - ) - hashboards[idx].chips = hb["asic_num"] - hashboards[idx].serial_number = hb["serial_number"] + if hb.temperature_pcb: + hashboards[idx].temp = round( + sum(hb.temperature_pcb) / len(hb.temperature_pcb) + ) + if hb.temperature_chip: + hashboards[idx].chip_temp = round( + sum(hb.temperature_chip) / len(hb.temperature_chip) + ) + hashboards[idx].chips = hb.asic_num + hashboards[idx].serial_number = hb.serial_number hashboards[idx].missing = False - except LookupError: + except ValidationError: pass return hashboards - async def _get_mac(self, web_overview: dict | None = None) -> str | None: + async def _get_mac(self, web_overview: dict[str, Any] | None = None) -> str | None: if web_overview is None: try: web_overview = await self.web.overview() @@ -216,12 +345,15 @@ async def _get_mac(self, web_overview: dict | None = None) -> str | None: if web_overview is not None: try: - return web_overview["mac"].upper() - except LookupError: + overview = MaraOverviewResponse.model_validate(web_overview) + return overview.mac.upper() + except ValidationError: pass return None - async def _get_fw_ver(self, web_overview: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_overview: dict[str, Any] | None = None + ) -> str | None: if web_overview is None: try: web_overview = await self.web.overview() @@ -230,12 +362,15 @@ async def _get_fw_ver(self, web_overview: dict | None = None) -> str | None: if web_overview is not None: try: - return web_overview["version_firmware"] - except LookupError: + overview = MaraOverviewResponse.model_validate(web_overview) + return overview.version_firmware + except ValidationError: pass return None - async def _get_hostname(self, web_network_config: dict | None = None) -> str | None: + async def _get_hostname( + self, web_network_config: dict[str, Any] | None = None + ) -> str | None: if web_network_config is None: try: web_network_config = await self.web.get_network_config() @@ -244,13 +379,14 @@ async def _get_hostname(self, web_network_config: dict | None = None) -> str | N if web_network_config is not None: try: - return web_network_config["hostname"] - except LookupError: + config = MaraNetworkConfigResponse.model_validate(web_network_config) + return config.hostname + except ValidationError: pass return None async def _get_hashrate( - self, web_brief: dict | None = None + self, web_brief: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_brief is None: try: @@ -260,15 +396,16 @@ async def _get_hashrate( if web_brief is not None: try: + brief = MaraBriefResponse.model_validate(web_brief) return self.algo.hashrate( - rate=float(web_brief["hashrate_realtime"]), + rate=brief.hashrate_realtime, unit=self.algo.unit.TH, # type: ignore[attr-defined] ).into(self.algo.unit.default) # type: ignore[attr-defined] - except LookupError: + except ValidationError: pass return None - async def _get_fans(self, web_fans: dict | None = None) -> list[Fan]: + async def _get_fans(self, web_fans: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -279,16 +416,17 @@ async def _get_fans(self, web_fans: dict | None = None) -> list[Fan]: pass if web_fans is not None: - fans = [] - for n in range(self.expected_fans): - try: - fans.append(Fan(speed=web_fans["fans"][n]["current_speed"])) - except (IndexError, KeyError): - pass - return fans + try: + response = MaraFansResponse.model_validate(web_fans) + return [Fan(speed=fan.current_speed) for fan in response.fans] + except ValidationError: + pass + return [Fan() for _ in range(self.expected_fans)] - async def _get_fault_light(self, web_locate_miner: dict | None = None) -> bool: + async def _get_fault_light( + self, web_locate_miner: dict[str, Any] | None = None + ) -> bool: if web_locate_miner is None: try: web_locate_miner = await self.web.get_locate_miner() @@ -297,13 +435,14 @@ async def _get_fault_light(self, web_locate_miner: dict | None = None) -> bool: if web_locate_miner is not None: try: - return web_locate_miner["blinking"] - except LookupError: + locate = MaraLocateMinerResponse.model_validate(web_locate_miner) + return locate.blinking + except ValidationError: pass return False async def _get_expected_hashrate( - self, web_brief: dict | None = None + self, web_brief: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if web_brief is None: try: @@ -313,16 +452,17 @@ async def _get_expected_hashrate( if web_brief is not None: try: + brief = MaraBriefResponse.model_validate(web_brief) return self.algo.hashrate( - rate=float(web_brief["hashrate_ideal"]), + rate=brief.hashrate_ideal, unit=self.algo.unit.GH, # type: ignore[attr-defined] ).into(self.algo.unit.default) # type: ignore[attr-defined] - except LookupError: + except ValidationError: pass return None async def _get_wattage_limit( - self, web_miner_config: dict | None = None + self, web_miner_config: dict[str, Any] | None = None ) -> int | None: if web_miner_config is None: try: @@ -332,47 +472,47 @@ async def _get_wattage_limit( if web_miner_config is not None: try: - return web_miner_config["mode"]["concorde"]["power-target"] - except LookupError: + config = MaraMinerConfigResponse.model_validate(web_miner_config) + return config.mode.concorde.power_target + except ValidationError: pass return None - async def _get_pools(self, web_pools: list | None = None) -> list[PoolMetrics]: + async def _get_pools(self, web_pools: list[Any] | None = None) -> list[PoolMetrics]: if web_pools is None: try: - web_pools = await self.web.pools() - except APIError: + response = await self.web.pools() + pools_response = MaraPoolsResponse.model_validate(response) + except (APIError, ValidationError): + return [] + else: + try: + pools_response = MaraPoolsResponse.model_validate({"pools": web_pools}) + except ValidationError: return [] active_pool_index = None highest_priority = float("inf") - for pool_info in web_pools: - if ( - pool_info.get("status") == "Alive" - and pool_info.get("priority", float("inf")) < highest_priority - ): - highest_priority = pool_info["priority"] - active_pool_index = pool_info["index"] + for pool_info in pools_response.pools: + if pool_info.status == "Alive" and pool_info.priority < highest_priority: + highest_priority = pool_info.priority + active_pool_index = pool_info.index pools_data = [] - if web_pools is not None: - try: - for pool_info in web_pools: - url = pool_info.get("url") - pool_url = PoolUrl.from_str(url) if url else None - pool_data = PoolMetrics( - accepted=pool_info.get("accepted"), - rejected=pool_info.get("rejected"), - get_failures=pool_info.get("stale"), - remote_failures=pool_info.get("discarded"), - active=pool_info.get("index") == active_pool_index, - alive=pool_info.get("status") == "Alive", - url=pool_url, - user=pool_info.get("user"), - index=pool_info.get("index"), - ) - pools_data.append(pool_data) - except LookupError: - pass + for pool_info in pools_response.pools: + pool_url = PoolUrl.from_str(pool_info.url) if pool_info.url else None + pool_data = PoolMetrics( + accepted=pool_info.accepted, + rejected=pool_info.rejected, + get_failures=pool_info.stale, + remote_failures=pool_info.discarded, + active=pool_info.index == active_pool_index, + alive=pool_info.status == "Alive", + url=pool_url, + user=pool_info.user, + index=pool_info.index, + ) + pools_data.append(pool_data) + return pools_data diff --git a/pyasic/miners/backends/mskminer.py b/pyasic/miners/backends/mskminer.py index ec31312b8..592359799 100644 --- a/pyasic/miners/backends/mskminer.py +++ b/pyasic/miners/backends/mskminer.py @@ -1,5 +1,7 @@ -from pyasic import APIError -from pyasic.device.algorithm import AlgoHashRateType +from typing import Any + +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType +from pyasic.errors import APIError from pyasic.miners.backends import BMMiner from pyasic.miners.data import ( DataFunction, @@ -65,7 +67,7 @@ class MSKMiner(BMMiner): _web_cls = MSKMinerWebAPI async def _get_hashrate( - self, rpc_stats: dict | None = None + self, rpc_stats: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # get hr from API if rpc_stats is None: @@ -86,7 +88,7 @@ async def _get_hashrate( pass return None - async def _get_wattage(self, rpc_stats: dict | None = None) -> int | None: + async def _get_wattage(self, rpc_stats: dict[str, Any] | None = None) -> int | None: if rpc_stats is None: try: rpc_stats = await self.rpc.stats() @@ -95,12 +97,12 @@ async def _get_wattage(self, rpc_stats: dict | None = None) -> int | None: if rpc_stats is not None: try: - return rpc_stats["STATS"][0]["total_power"] + return int(rpc_stats["STATS"][0]["total_power"]) except (LookupError, ValueError, TypeError): pass return None - async def _get_mac(self, web_info_v1: dict | None = None) -> str | None: + async def _get_mac(self, web_info_v1: dict[str, Any] | None = None) -> str | None: if web_info_v1 is None: try: web_info_v1 = await self.web.info_v1() @@ -109,7 +111,10 @@ async def _get_mac(self, web_info_v1: dict | None = None) -> str | None: if web_info_v1 is not None: try: - return web_info_v1["network_info"]["result"]["macaddr"].upper() + return str(web_info_v1["network_info"]["result"]["macaddr"]).upper() except (LookupError, ValueError, TypeError): pass return None + + async def _get_serial_number(self) -> str | None: + return None diff --git a/pyasic/miners/backends/unknown.py b/pyasic/miners/backends/unknown.py index 1bcec0e54..f0b48f217 100644 --- a/pyasic/miners/backends/unknown.py +++ b/pyasic/miners/backends/unknown.py @@ -13,11 +13,14 @@ # limitations under the License. +from typing import Any + from pyasic.config import MinerConfig -from pyasic.data import Fan, HashBoard +from pyasic.data.boards import HashBoard from pyasic.data.error_codes import MinerErrorData +from pyasic.data.fans import Fan from pyasic.data.pools import PoolMetrics -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.miners.base import BaseMiner from pyasic.rpc.unknown import UnknownRPCAPI @@ -26,8 +29,8 @@ class UnknownMiner(BaseMiner): def __init__( self, ip: str, - *args, - **kwargs, # noqa - ignore *args and **kwargs for signature consistency + *args: Any, + **kwargs: Any, # noqa - ignore *args and **kwargs for signature consistency ) -> None: super().__init__(ip) self.ip = ip @@ -122,10 +125,10 @@ async def _get_fault_light(self) -> bool: async def _get_expected_hashrate(self) -> AlgoHashRateType | None: return None - async def _is_mining(self, *args, **kwargs) -> bool | None: + async def _is_mining(self, *args: Any, **kwargs: Any) -> bool | None: return None - async def _get_uptime(self, *args, **kwargs) -> int | None: + async def _get_uptime(self, *args: Any, **kwargs: Any) -> int | None: return None async def _get_pools(self) -> list[PoolMetrics]: diff --git a/pyasic/miners/backends/vnish.py b/pyasic/miners/backends/vnish.py index 148f43ab4..10b1c8a36 100644 --- a/pyasic/miners/backends/vnish.py +++ b/pyasic/miners/backends/vnish.py @@ -15,11 +15,14 @@ # ------------------------------------------------------------------------------ import logging +from typing import Any -from pyasic import MinerConfig +from pydantic import BaseModel, ValidationError + +from pyasic.config import MinerConfig from pyasic.config.mining import MiningModePreset from pyasic.data.error_codes import VnishError -from pyasic.device.algorithm import AlgoHashRateType +from pyasic.device.algorithm.hashrate.base import AlgoHashRateType from pyasic.errors import APIError from pyasic.miners.backends.bmminer import BMMiner from pyasic.miners.data import ( @@ -94,6 +97,94 @@ ) +class VnishSuccessResponse(BaseModel): + success: bool + + +class VnishNetworkStatus(BaseModel): + mac: str + hostname: str + + class Config: + extra = "allow" + + +class VnishSystemInfo(BaseModel): + network_status: VnishNetworkStatus + + class Config: + extra = "allow" + + +class VnishMinerStatus(BaseModel): + miner_state: str + + class Config: + extra = "allow" + + +class VnishOverclock(BaseModel): + preset: str + + class Config: + extra = "allow" + + +class VnishMinerInfo(BaseModel): + power_usage: float + miner_type: str + miner_status: VnishMinerStatus + overclock: VnishOverclock + + class Config: + extra = "allow" + + +class VnishChainStatus(BaseModel): + state: str + description: str = "" + + class Config: + extra = "allow" + + +class VnishChain(BaseModel): + status: VnishChainStatus + + class Config: + extra = "allow" + + +class VnishWebSummary(BaseModel): + system: VnishSystemInfo + miner: VnishMinerInfo + chains: list[VnishChain] = [] + + class Config: + extra = "allow" + + +class VnishWebInfo(BaseModel): + system: VnishSystemInfo + + class Config: + extra = "allow" + + +class VnishWebSettings(BaseModel): + miner: VnishMinerInfo + + class Config: + extra = "allow" + + +class VnishFindMinerResponse(BaseModel): + on: bool + + class Config: + extra = "allow" + + class VNish(VNishFirmware, BMMiner): """Handler for VNish miners""" @@ -117,8 +208,9 @@ async def restart_backend(self) -> bool: data = await self.web.restart_vnish() if data: try: - return data["success"] - except KeyError: + response = VnishSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False @@ -126,8 +218,9 @@ async def stop_mining(self) -> bool: data = await self.web.stop_mining() if data: try: - return data["success"] - except KeyError: + response = VnishSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False @@ -135,8 +228,9 @@ async def resume_mining(self) -> bool: data = await self.web.resume_mining() if data: try: - return data["success"] - except KeyError: + response = VnishSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False @@ -144,26 +238,27 @@ async def reboot(self) -> bool: data = await self.web.reboot() if data: try: - return data["success"] - except KeyError: + response = VnishSuccessResponse.model_validate(data) + return response.success + except ValidationError: pass return False - async def _get_mac(self, web_summary: dict | None = None) -> str | None: + async def _get_mac(self, web_summary: dict[str, Any] | None = None) -> str | None: if web_summary is not None: try: - mac = web_summary["system"]["network_status"]["mac"] - return mac - except KeyError: + summary = VnishWebSummary.model_validate(web_summary) + return summary.system.network_status.mac + except ValidationError: pass web_info = await self.web.info() if web_info is not None: try: - mac = web_info["system"]["network_status"]["mac"] - return mac - except KeyError: + info = VnishWebInfo.model_validate(web_info) + return info.system.network_status.mac + except ValidationError: pass return None @@ -171,54 +266,65 @@ async def _get_mac(self, web_summary: dict | None = None) -> str | None: async def fault_light_off(self) -> bool: result = await self.web.find_miner() if result is not None: - if result.get("on") is False: - return True - else: - await self.web.find_miner() + try: + response = VnishFindMinerResponse.model_validate(result) + if not response.on: + return True + else: + await self.web.find_miner() + except ValidationError: + pass return False async def fault_light_on(self) -> bool: result = await self.web.find_miner() if result is not None: - if result.get("on") is True: - return True - else: - await self.web.find_miner() + try: + response = VnishFindMinerResponse.model_validate(result) + if response.on: + return True + else: + await self.web.find_miner() + except ValidationError: + pass return False - async def _get_hostname(self, web_summary: dict | None = None) -> str | None: + async def _get_hostname( + self, web_summary: dict[str, Any] | None = None + ) -> str | None: if web_summary is None: web_info = await self.web.info() if web_info is not None: try: - hostname = web_info["system"]["network_status"]["hostname"] - return hostname - except KeyError: + info = VnishWebInfo.model_validate(web_info) + return info.system.network_status.hostname + except ValidationError: pass else: try: - hostname = web_summary["system"]["network_status"]["hostname"] - return hostname - except KeyError: + summary = VnishWebSummary.model_validate(web_summary) + return summary.system.network_status.hostname + except ValidationError: pass return None - async def _get_wattage(self, web_summary: dict | None = None) -> int | None: + async def _get_wattage( + self, web_summary: dict[str, Any] | None = None + ) -> int | None: if web_summary is None: web_summary = await self.web.summary() if web_summary is not None: try: - wattage = web_summary["miner"]["power_usage"] - wattage = round(wattage) - return wattage - except KeyError: + summary = VnishWebSummary.model_validate(web_summary) + return round(summary.miner.power_usage) + except ValidationError: pass return None async def _get_hashrate( - self, rpc_summary: dict | None = None + self, rpc_summary: dict[str, Any] | None = None ) -> AlgoHashRateType | None: # get hr from API if rpc_summary is None: @@ -238,35 +344,45 @@ async def _get_hashrate( return None - async def _get_wattage_limit(self, web_settings: dict | None = None) -> int | None: + async def _get_wattage_limit( + self, web_settings: dict[str, Any] | None = None + ) -> int | None: if web_settings is None: web_settings = await self.web.summary() if web_settings is not None: try: - wattage_limit = web_settings["miner"]["overclock"]["preset"] - if wattage_limit == "disabled": + summary = VnishWebSummary.model_validate(web_settings) + preset = summary.miner.overclock.preset + if preset == "disabled": return None - return int(wattage_limit) - except (KeyError, TypeError): + if preset.isdigit(): + return int(preset) + except ValidationError: pass return None - async def _get_fw_ver(self, web_summary: dict | None = None) -> str | None: + async def _get_fw_ver( + self, web_summary: dict[str, Any] | None = None + ) -> str | None: if web_summary is None: web_summary = await self.web.summary() - fw_ver = None if web_summary is not None: try: - fw_ver = web_summary["miner"]["miner_type"] - fw_ver = fw_ver.split("(Vnish ")[1].replace(")", "") - return fw_ver - except LookupError: - return fw_ver + summary = VnishWebSummary.model_validate(web_summary) + fw_ver = summary.miner.miner_type + if "(Vnish " in fw_ver: + fw_ver = fw_ver.split("(Vnish ")[1].replace(")", "") + return fw_ver + except (ValidationError, IndexError): + pass + return None - async def _is_mining(self, web_summary: dict | None = None) -> bool | None: + async def _is_mining( + self, web_summary: dict[str, Any] | None = None + ) -> bool | None: if web_summary is None: try: web_summary = await self.web.summary() @@ -275,19 +391,16 @@ async def _is_mining(self, web_summary: dict | None = None) -> bool | None: if web_summary is not None: try: - is_mining = web_summary["miner"]["miner_status"]["miner_state"] not in [ - "stopped", - "shutting-down", - "failure", - ] - return is_mining - except LookupError: + summary = VnishWebSummary.model_validate(web_summary) + state = summary.miner.miner_status.miner_state + return state not in ["stopped", "shutting-down", "failure"] + except ValidationError: pass return None async def _get_errors( # type: ignore[override] - self, web_summary: dict | None = None + self, web_summary: dict[str, Any] | None = None ) -> list[VnishError]: errors: list[VnishError] = [] @@ -298,12 +411,15 @@ async def _get_errors( # type: ignore[override] return errors if web_summary is not None: - chains = web_summary.get("miner", {}).get("chains", []) - for chain in chains: - state = chain.get("status", {}).get("state") - description = chain.get("status", {}).get("description", "") - if state == "failure": - errors.append(VnishError(error_message=description)) + try: + summary = VnishWebSummary.model_validate(web_summary) + for chain in summary.chains: + if chain.status.state == "failure": + errors.append( + VnishError(error_message=chain.status.description) + ) + except ValidationError: + pass return errors diff --git a/pyasic/miners/base.py b/pyasic/miners/base.py index eae6fb7c2..f04426cd3 100644 --- a/pyasic/miners/base.py +++ b/pyasic/miners/base.py @@ -16,7 +16,7 @@ import asyncio import ipaddress import warnings -from typing import Any, Protocol, TypeVar +from typing import Any, Protocol, TypeVar, runtime_checkable from pyasic.config import MinerConfig from pyasic.data import Fan, HashBoard, MinerData @@ -33,6 +33,7 @@ from pyasic.miners.data import DataOptions, RPCAPICommand, WebAPICommand +@runtime_checkable class MinerProtocol(Protocol): _rpc_cls: type[Any] | None = None _web_cls: type[Any] | None = None @@ -64,16 +65,40 @@ class MinerProtocol(Protocol): light: bool | None = None config: MinerConfig | None = None - def __repr__(self): + def __repr__(self) -> str: return f"{self.model}: {str(self.ip)}" - def __lt__(self, other): - return ipaddress.ip_address(self.ip) < ipaddress.ip_address(other.ip) - - def __gt__(self, other): - return ipaddress.ip_address(self.ip) > ipaddress.ip_address(other.ip) - - def __eq__(self, other): + def __lt__(self, other: object) -> bool: + if not isinstance(other, MinerProtocol): + return NotImplemented + if self.ip is None or other.ip is None: + return NotImplemented + # Convert to strings to compare regardless of IPv4/IPv6 + self_ip = ipaddress.ip_address(self.ip) + other_ip = ipaddress.ip_address(other.ip) + # Compare by string if different families + if type(self_ip) is not type(other_ip): + return str(self_ip) < str(other_ip) + return self_ip < other_ip # type: ignore[operator] + + def __gt__(self, other: object) -> bool: + if not isinstance(other, MinerProtocol): + return NotImplemented + if self.ip is None or other.ip is None: + return NotImplemented + # Convert to strings to compare regardless of IPv4/IPv6 + self_ip = ipaddress.ip_address(self.ip) + other_ip = ipaddress.ip_address(other.ip) + # Compare by string if different families + if type(self_ip) is not type(other_ip): + return str(self_ip) > str(other_ip) + return self_ip > other_ip # type: ignore[operator] + + def __eq__(self, other: object) -> bool: + if not isinstance(other, MinerProtocol): + return NotImplemented + if self.ip is None or other.ip is None: + return False return ipaddress.ip_address(self.ip) == ipaddress.ip_address(other.ip) @property @@ -95,7 +120,7 @@ def device_info(self) -> DeviceInfo: ) @property - def api(self): + def api(self) -> Any: return self.rpc async def check_light(self) -> bool: @@ -451,7 +476,7 @@ async def _get_data( allow_warning: bool, include: list[str | DataOptions] | None = None, exclude: list[str | DataOptions] | None = None, - ) -> dict: + ) -> dict[str, Any]: # handle include if include is not None: include = [str(i) for i in include] @@ -517,7 +542,7 @@ async def _get_data( if api_command_data is None: api_command_data = {} - miner_data = {} + miner_data: dict[str, Any] = {} for data_name in include: try: @@ -545,7 +570,7 @@ async def _get_data( function = getattr( self, getattr(self.data_locations, str(data_name)).cmd ) - miner_data[data_name] = await function(**args_to_send) + miner_data[str(data_name)] = await function(**args_to_send) except Exception as e: raise APIError( f"Failed to call {data_name} on {self} while getting data." diff --git a/pyasic/miners/bitaxe/__init__.py b/pyasic/miners/bitaxe/__init__.py index 6068242d7..9a48fc48f 100644 --- a/pyasic/miners/bitaxe/__init__.py +++ b/pyasic/miners/bitaxe/__init__.py @@ -1 +1,8 @@ -from .espminer import * +from .espminer import BitAxeGamma, BitAxeMax, BitAxeSupra, BitAxeUltra + +__all__ = [ + "BitAxeGamma", + "BitAxeMax", + "BitAxeSupra", + "BitAxeUltra", +] diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1366.py b/pyasic/miners/bitaxe/espminer/BM/BM1366.py index d2507541e..0d4a7be81 100644 --- a/pyasic/miners/bitaxe/espminer/BM/BM1366.py +++ b/pyasic/miners/bitaxe/espminer/BM/BM1366.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.bitaxe import BitAxe -from pyasic.miners.device.models.bitaxe import Ultra +from pyasic.miners.device.models.bitaxe.BM import Ultra class BitAxeUltra(BitAxe, Ultra): diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1368.py b/pyasic/miners/bitaxe/espminer/BM/BM1368.py index ea2e3b7bb..b261522ac 100644 --- a/pyasic/miners/bitaxe/espminer/BM/BM1368.py +++ b/pyasic/miners/bitaxe/espminer/BM/BM1368.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.bitaxe import BitAxe -from pyasic.miners.device.models.bitaxe import Supra +from pyasic.miners.device.models.bitaxe.BM import Supra class BitAxeSupra(BitAxe, Supra): diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1370.py b/pyasic/miners/bitaxe/espminer/BM/BM1370.py index 56bdd9081..b5cf9d94f 100644 --- a/pyasic/miners/bitaxe/espminer/BM/BM1370.py +++ b/pyasic/miners/bitaxe/espminer/BM/BM1370.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.bitaxe import BitAxe -from pyasic.miners.device.models.bitaxe import Gamma +from pyasic.miners.device.models.bitaxe.BM import Gamma class BitAxeGamma(BitAxe, Gamma): diff --git a/pyasic/miners/bitaxe/espminer/BM/BM1397.py b/pyasic/miners/bitaxe/espminer/BM/BM1397.py index 722b1d355..4972c2fc2 100644 --- a/pyasic/miners/bitaxe/espminer/BM/BM1397.py +++ b/pyasic/miners/bitaxe/espminer/BM/BM1397.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.bitaxe import BitAxe -from pyasic.miners.device.models.bitaxe import Max +from pyasic.miners.device.models.bitaxe.BM import Max class BitAxeMax(BitAxe, Max): diff --git a/pyasic/miners/bitaxe/espminer/BM/__init__.py b/pyasic/miners/bitaxe/espminer/BM/__init__.py index c633cd00c..d15658f75 100644 --- a/pyasic/miners/bitaxe/espminer/BM/__init__.py +++ b/pyasic/miners/bitaxe/espminer/BM/__init__.py @@ -2,3 +2,5 @@ from .BM1368 import BitAxeSupra from .BM1370 import BitAxeGamma from .BM1397 import BitAxeMax + +__all__ = ["BitAxeUltra", "BitAxeSupra", "BitAxeGamma", "BitAxeMax"] diff --git a/pyasic/miners/bitaxe/espminer/__init__.py b/pyasic/miners/bitaxe/espminer/__init__.py index dc06b2e07..87550001c 100644 --- a/pyasic/miners/bitaxe/espminer/__init__.py +++ b/pyasic/miners/bitaxe/espminer/__init__.py @@ -1 +1,3 @@ -from .BM import * +from .BM import BitAxeGamma, BitAxeMax, BitAxeSupra, BitAxeUltra + +__all__ = ["BitAxeGamma", "BitAxeMax", "BitAxeSupra", "BitAxeUltra"] diff --git a/pyasic/miners/braiins/__init__.py b/pyasic/miners/braiins/__init__.py index 89ca6f801..c65b3204e 100644 --- a/pyasic/miners/braiins/__init__.py +++ b/pyasic/miners/braiins/__init__.py @@ -14,4 +14,9 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .braiins import * +from .braiins import BraiinsBMM100, BraiinsBMM101 + +__all__ = [ + "BraiinsBMM100", + "BraiinsBMM101", +] diff --git a/pyasic/miners/braiins/braiins/BMM/__init__.py b/pyasic/miners/braiins/braiins/BMM/__init__.py index c34ba65e6..e515f0c06 100644 --- a/pyasic/miners/braiins/braiins/BMM/__init__.py +++ b/pyasic/miners/braiins/braiins/BMM/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .BMM import BraiinsBMM100, BraiinsBMM101 + +__all__ = ["BraiinsBMM100", "BraiinsBMM101"] diff --git a/pyasic/miners/braiins/braiins/__init__.py b/pyasic/miners/braiins/braiins/__init__.py index d91f39d86..e515f0c06 100644 --- a/pyasic/miners/braiins/braiins/__init__.py +++ b/pyasic/miners/braiins/braiins/__init__.py @@ -14,4 +14,6 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .BMM import * +from .BMM import BraiinsBMM100, BraiinsBMM101 + +__all__ = ["BraiinsBMM100", "BraiinsBMM101"] diff --git a/pyasic/miners/data.py b/pyasic/miners/data.py index 818824db8..19f66a5ae 100644 --- a/pyasic/miners/data.py +++ b/pyasic/miners/data.py @@ -39,10 +39,10 @@ class DataOptions(Enum): CONFIG = "config" POOLS = "pools" - def __str__(self): + def __str__(self) -> str: return self.value - def default_command(self): + def default_command(self) -> str: if str(self.value) == "config": return "get_config" elif str(self.value) == "is_mining": @@ -68,7 +68,7 @@ class DataFunction: cmd: str kwargs: list[RPCAPICommand | WebAPICommand] = field(default_factory=list) - def __call__(self, *args, **kwargs): + def __call__(self, *args: object, **kwargs: object) -> "DataFunction": return self diff --git a/pyasic/miners/device/models/antminer/X15/__init__.py b/pyasic/miners/device/models/antminer/X15/__init__.py index 7c529851d..5d4385055 100644 --- a/pyasic/miners/device/models/antminer/X15/__init__.py +++ b/pyasic/miners/device/models/antminer/X15/__init__.py @@ -14,3 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .Z15 import Z15, Z15Pro + +__all__ = [ + "Z15", + "Z15Pro", +] diff --git a/pyasic/miners/device/models/antminer/X17/__init__.py b/pyasic/miners/device/models/antminer/X17/__init__.py index e21b2111d..5deab59df 100644 --- a/pyasic/miners/device/models/antminer/X17/__init__.py +++ b/pyasic/miners/device/models/antminer/X17/__init__.py @@ -16,3 +16,13 @@ from .S17 import S17, S17e, S17Plus, S17Pro from .T17 import T17, T17e, T17Plus + +__all__ = [ + "S17", + "S17e", + "S17Plus", + "S17Pro", + "T17", + "T17e", + "T17Plus", +] diff --git a/pyasic/miners/device/models/antminer/X19/__init__.py b/pyasic/miners/device/models/antminer/X19/__init__.py index 3ec0e61a5..d3ab1a2b2 100644 --- a/pyasic/miners/device/models/antminer/X19/__init__.py +++ b/pyasic/miners/device/models/antminer/X19/__init__.py @@ -43,3 +43,33 @@ S19XPHydro, ) from .T19 import T19 + +__all__ = [ + "S19", + "S19L", + "S19XP", + "S19a", + "S19aPro", + "S19Hydro", + "S19i", + "S19j", + "S19jNoPIC", + "S19jPlus", + "S19jPro", + "S19jProNoPIC", + "S19jProPlus", + "S19jProPlusNoPIC", + "S19jXP", + "S19KPro", + "S19kPro", + "S19kProNoPIC", + "S19NoPIC", + "S19Plus", + "S19Pro", + "S19ProA", + "S19ProHydro", + "S19ProPlus", + "S19ProPlusHydro", + "S19XPHydro", + "T19", +] diff --git a/pyasic/miners/device/models/antminer/X21/__init__.py b/pyasic/miners/device/models/antminer/X21/__init__.py index 70b40e1de..bd2ea0ab8 100644 --- a/pyasic/miners/device/models/antminer/X21/__init__.py +++ b/pyasic/miners/device/models/antminer/X21/__init__.py @@ -16,3 +16,12 @@ from .S21 import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro from .T21 import T21 + +__all__ = [ + "S21", + "S21Hydro", + "S21Plus", + "S21PlusHydro", + "S21Pro", + "T21", +] diff --git a/pyasic/miners/device/models/antminer/X3/__init__.py b/pyasic/miners/device/models/antminer/X3/__init__.py index 688b81927..4d1ee6e9d 100644 --- a/pyasic/miners/device/models/antminer/X3/__init__.py +++ b/pyasic/miners/device/models/antminer/X3/__init__.py @@ -18,3 +18,11 @@ from .KA3 import KA3 from .KS3 import KS3 from .L3 import L3Plus + +__all__ = [ + "D3", + "HS3", + "KA3", + "KS3", + "L3Plus", +] diff --git a/pyasic/miners/device/models/antminer/X5/__init__.py b/pyasic/miners/device/models/antminer/X5/__init__.py index 5ff4d06e2..8edbcc65c 100644 --- a/pyasic/miners/device/models/antminer/X5/__init__.py +++ b/pyasic/miners/device/models/antminer/X5/__init__.py @@ -15,3 +15,9 @@ # ------------------------------------------------------------------------------ from .DR5 import DR5 from .KS5 import KS5, KS5Pro + +__all__ = [ + "DR5", + "KS5", + "KS5Pro", +] diff --git a/pyasic/miners/device/models/antminer/X7/__init__.py b/pyasic/miners/device/models/antminer/X7/__init__.py index 673431cb5..cd4843bf6 100644 --- a/pyasic/miners/device/models/antminer/X7/__init__.py +++ b/pyasic/miners/device/models/antminer/X7/__init__.py @@ -16,3 +16,9 @@ from .D7 import D7 from .K7 import K7 from .L7 import L7 + +__all__ = [ + "D7", + "K7", + "L7", +] diff --git a/pyasic/miners/device/models/antminer/X9/__init__.py b/pyasic/miners/device/models/antminer/X9/__init__.py index 1bc6d81a9..29de398e5 100644 --- a/pyasic/miners/device/models/antminer/X9/__init__.py +++ b/pyasic/miners/device/models/antminer/X9/__init__.py @@ -19,3 +19,13 @@ from .L9 import L9 from .S9 import S9, S9i, S9j from .T9 import T9 + +__all__ = [ + "D9", + "E9Pro", + "L9", + "S9", + "S9i", + "S9j", + "T9", +] diff --git a/pyasic/miners/device/models/auradine/AD/__init__.py b/pyasic/miners/device/models/auradine/AD/__init__.py index a46be8b9a..29de0d5aa 100644 --- a/pyasic/miners/device/models/auradine/AD/__init__.py +++ b/pyasic/miners/device/models/auradine/AD/__init__.py @@ -1,2 +1,7 @@ from .AD2 import AuradineAD2500 from .AD3 import AuradineAD3500 + +__all__ = [ + "AuradineAD2500", + "AuradineAD3500", +] diff --git a/pyasic/miners/device/models/auradine/AI/__init__.py b/pyasic/miners/device/models/auradine/AI/__init__.py index b6fa8ad31..2fd02239b 100644 --- a/pyasic/miners/device/models/auradine/AI/__init__.py +++ b/pyasic/miners/device/models/auradine/AI/__init__.py @@ -1,2 +1,7 @@ from .AI2 import AuradineAI2500 from .AI3 import AuradineAI3680 + +__all__ = [ + "AuradineAI2500", + "AuradineAI3680", +] diff --git a/pyasic/miners/device/models/auradine/AT/__init__.py b/pyasic/miners/device/models/auradine/AT/__init__.py index 070612a0f..70a64d099 100644 --- a/pyasic/miners/device/models/auradine/AT/__init__.py +++ b/pyasic/miners/device/models/auradine/AT/__init__.py @@ -1,2 +1,8 @@ from .AT1 import AuradineAT1500 from .AT2 import AuradineAT2860, AuradineAT2880 + +__all__ = [ + "AuradineAT1500", + "AuradineAT2860", + "AuradineAT2880", +] diff --git a/pyasic/miners/device/models/avalonminer/A10X/__init__.py b/pyasic/miners/device/models/avalonminer/A10X/__init__.py index 360f17521..c49df114f 100644 --- a/pyasic/miners/device/models/avalonminer/A10X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A10X/__init__.py @@ -17,3 +17,5 @@ from .A1026 import Avalon1026 from .A1047 import Avalon1047 from .A1066 import Avalon1066 + +__all__ = ["Avalon1026", "Avalon1047", "Avalon1066"] diff --git a/pyasic/miners/device/models/avalonminer/A11X/__init__.py b/pyasic/miners/device/models/avalonminer/A11X/__init__.py index 90bbc95f3..fab4d5623 100644 --- a/pyasic/miners/device/models/avalonminer/A11X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A11X/__init__.py @@ -17,3 +17,5 @@ from .A1126 import Avalon1126Pro from .A1166 import Avalon1166Pro + +__all__ = ["Avalon1126Pro", "Avalon1166Pro"] diff --git a/pyasic/miners/device/models/avalonminer/A12X/__init__.py b/pyasic/miners/device/models/avalonminer/A12X/__init__.py index f2cd434e3..ad34fd8c0 100644 --- a/pyasic/miners/device/models/avalonminer/A12X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A12X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A1246 import Avalon1246 + +__all__ = ["Avalon1246"] diff --git a/pyasic/miners/device/models/avalonminer/A15X/__init__.py b/pyasic/miners/device/models/avalonminer/A15X/__init__.py index 665f33bbf..914dd420e 100644 --- a/pyasic/miners/device/models/avalonminer/A15X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A15X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A1566 import Avalon1566 + +__all__ = ["Avalon1566"] diff --git a/pyasic/miners/device/models/avalonminer/A7X/__init__.py b/pyasic/miners/device/models/avalonminer/A7X/__init__.py index 5302671d1..14056a062 100644 --- a/pyasic/miners/device/models/avalonminer/A7X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A7X/__init__.py @@ -17,3 +17,5 @@ from .A721 import Avalon721 from .A741 import Avalon741 from .A761 import Avalon761 + +__all__ = ["Avalon721", "Avalon741", "Avalon761"] diff --git a/pyasic/miners/device/models/avalonminer/A8X/__init__.py b/pyasic/miners/device/models/avalonminer/A8X/__init__.py index 8e0e24bae..da1515a58 100644 --- a/pyasic/miners/device/models/avalonminer/A8X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A8X/__init__.py @@ -17,3 +17,5 @@ from .A821 import Avalon821 from .A841 import Avalon841 from .A851 import Avalon851 + +__all__ = ["Avalon821", "Avalon841", "Avalon851"] diff --git a/pyasic/miners/device/models/avalonminer/A9X/__init__.py b/pyasic/miners/device/models/avalonminer/A9X/__init__.py index 4f134c7b2..a19a5e562 100644 --- a/pyasic/miners/device/models/avalonminer/A9X/__init__.py +++ b/pyasic/miners/device/models/avalonminer/A9X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A921 import Avalon921 + +__all__ = ["Avalon921"] diff --git a/pyasic/miners/device/models/avalonminer/Q/__init__.py b/pyasic/miners/device/models/avalonminer/Q/__init__.py index 62201b149..9fb405a84 100644 --- a/pyasic/miners/device/models/avalonminer/Q/__init__.py +++ b/pyasic/miners/device/models/avalonminer/Q/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .Q import AvalonQHome + +__all__ = ["AvalonQHome"] diff --git a/pyasic/miners/device/models/avalonminer/nano/__init__.py b/pyasic/miners/device/models/avalonminer/nano/__init__.py index 643285a64..1899ef870 100644 --- a/pyasic/miners/device/models/avalonminer/nano/__init__.py +++ b/pyasic/miners/device/models/avalonminer/nano/__init__.py @@ -1 +1,3 @@ from .nano3 import AvalonNano3, AvalonNano3s + +__all__ = ["AvalonNano3", "AvalonNano3s"] diff --git a/pyasic/miners/device/models/bitaxe/BM/__init__.py b/pyasic/miners/device/models/bitaxe/BM/__init__.py index e5a822872..d6ab492f0 100644 --- a/pyasic/miners/device/models/bitaxe/BM/__init__.py +++ b/pyasic/miners/device/models/bitaxe/BM/__init__.py @@ -2,3 +2,5 @@ from .BM1368 import Supra from .BM1370 import Gamma from .BM1397 import Max + +__all__ = ["Ultra", "Supra", "Gamma", "Max"] diff --git a/pyasic/miners/device/models/elphapex/DGX/__init__.py b/pyasic/miners/device/models/elphapex/DGX/__init__.py index 86e0e05de..e9d931370 100644 --- a/pyasic/miners/device/models/elphapex/DGX/__init__.py +++ b/pyasic/miners/device/models/elphapex/DGX/__init__.py @@ -1 +1,3 @@ from .DG1 import DG1, DG1Home, DG1Plus + +__all__ = ["DG1", "DG1Home", "DG1Plus"] diff --git a/pyasic/miners/device/models/goldshell/X5/__init__.py b/pyasic/miners/device/models/goldshell/X5/__init__.py index 09a7c931f..bf8046461 100644 --- a/pyasic/miners/device/models/goldshell/X5/__init__.py +++ b/pyasic/miners/device/models/goldshell/X5/__init__.py @@ -16,3 +16,5 @@ from .CK5 import CK5 from .HS5 import HS5 from .KD5 import KD5 + +__all__ = ["CK5", "HS5", "KD5"] diff --git a/pyasic/miners/device/models/goldshell/XBox/__init__.py b/pyasic/miners/device/models/goldshell/XBox/__init__.py index 7dfb1f8f1..66a34031c 100644 --- a/pyasic/miners/device/models/goldshell/XBox/__init__.py +++ b/pyasic/miners/device/models/goldshell/XBox/__init__.py @@ -1 +1,3 @@ from .KDBox import KDBoxII, KDBoxPro + +__all__ = ["KDBoxII", "KDBoxPro"] diff --git a/pyasic/miners/device/models/goldshell/XMax/__init__.py b/pyasic/miners/device/models/goldshell/XMax/__init__.py index 5c10f6bdb..d9243936c 100644 --- a/pyasic/miners/device/models/goldshell/XMax/__init__.py +++ b/pyasic/miners/device/models/goldshell/XMax/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .KDMax import KDMax + +__all__ = ["KDMax"] diff --git a/pyasic/miners/device/models/goldshell/byte/__init__.py b/pyasic/miners/device/models/goldshell/byte/__init__.py index 16d9d31a7..814333c08 100644 --- a/pyasic/miners/device/models/goldshell/byte/__init__.py +++ b/pyasic/miners/device/models/goldshell/byte/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .byte import Byte + +__all__ = ["Byte"] diff --git a/pyasic/miners/device/models/goldshell/mini_doge/__init__.py b/pyasic/miners/device/models/goldshell/mini_doge/__init__.py index 84436bd60..07dd0e3d7 100644 --- a/pyasic/miners/device/models/goldshell/mini_doge/__init__.py +++ b/pyasic/miners/device/models/goldshell/mini_doge/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .mini_doge import MiniDoge + +__all__ = ["MiniDoge"] diff --git a/pyasic/miners/device/models/hammer/DX/__init__.py b/pyasic/miners/device/models/hammer/DX/__init__.py index 858e19b3e..789232660 100644 --- a/pyasic/miners/device/models/hammer/DX/__init__.py +++ b/pyasic/miners/device/models/hammer/DX/__init__.py @@ -1 +1,5 @@ from .D10 import D10 + +__all__ = [ + "D10", +] diff --git a/pyasic/miners/device/models/iceriver/ALX/__init__.py b/pyasic/miners/device/models/iceriver/ALX/__init__.py index 67e10d20d..f24893767 100644 --- a/pyasic/miners/device/models/iceriver/ALX/__init__.py +++ b/pyasic/miners/device/models/iceriver/ALX/__init__.py @@ -1 +1,5 @@ from .AL3 import AL3 + +__all__ = [ + "AL3", +] diff --git a/pyasic/miners/device/models/iceriver/KSX/__init__.py b/pyasic/miners/device/models/iceriver/KSX/__init__.py index a34c6cfb8..f607db450 100644 --- a/pyasic/miners/device/models/iceriver/KSX/__init__.py +++ b/pyasic/miners/device/models/iceriver/KSX/__init__.py @@ -3,3 +3,15 @@ from .KS2 import KS2 from .KS3 import KS3, KS3L, KS3M from .KS5 import KS5, KS5L, KS5M + +__all__ = [ + "KS0", + "KS1", + "KS2", + "KS3", + "KS3L", + "KS3M", + "KS5", + "KS5L", + "KS5M", +] diff --git a/pyasic/miners/device/models/iceriver/__init__.py b/pyasic/miners/device/models/iceriver/__init__.py index 1714b4960..187b9963c 100644 --- a/pyasic/miners/device/models/iceriver/__init__.py +++ b/pyasic/miners/device/models/iceriver/__init__.py @@ -1,5 +1,5 @@ -from .ALX import * -from .KSX import * +from .ALX import AL3 +from .KSX import KS0, KS1, KS2, KS3L, KS3M, KS5L, KS5M # Define what gets exported with wildcard to exclude KS3 and KS5 # which conflict with antminer models diff --git a/pyasic/miners/device/models/innosilicon/T3X/__init__.py b/pyasic/miners/device/models/innosilicon/T3X/__init__.py index b7e64c8b9..0d7535ae5 100644 --- a/pyasic/miners/device/models/innosilicon/T3X/__init__.py +++ b/pyasic/miners/device/models/innosilicon/T3X/__init__.py @@ -15,3 +15,7 @@ # ------------------------------------------------------------------------------ from .T3H import T3HPlus + +__all__ = [ + "T3HPlus", +] diff --git a/pyasic/miners/device/models/luckyminer/LV/__init__.py b/pyasic/miners/device/models/luckyminer/LV/__init__.py index aaff5c355..05dcc6815 100644 --- a/pyasic/miners/device/models/luckyminer/LV/__init__.py +++ b/pyasic/miners/device/models/luckyminer/LV/__init__.py @@ -1,2 +1,4 @@ from .LV07 import LV07 from .LV08 import LV08 + +__all__ = ["LV07", "LV08"] diff --git a/pyasic/miners/device/models/volcminer/DX/__init__.py b/pyasic/miners/device/models/volcminer/DX/__init__.py index 4d16d40db..fe3bd3af2 100644 --- a/pyasic/miners/device/models/volcminer/DX/__init__.py +++ b/pyasic/miners/device/models/volcminer/DX/__init__.py @@ -1 +1,5 @@ from .D1 import D1 + +__all__ = [ + "D1", +] diff --git a/pyasic/miners/device/models/whatsminer/M2X/__init__.py b/pyasic/miners/device/models/whatsminer/M2X/__init__.py index 627f63259..07ee2e99a 100644 --- a/pyasic/miners/device/models/whatsminer/M2X/__init__.py +++ b/pyasic/miners/device/models/whatsminer/M2X/__init__.py @@ -6,3 +6,19 @@ from .M21S import M21SV20, M21SV60, M21SV70 from .M21S_Plus import M21SPlusV20 from .M29 import M29V10 + +__all__ = [ + "M20V10", + "M20PV10", + "M20PV30", + "M20SV10", + "M20SV20", + "M20SV30", + "M20SPlusV30", + "M21V10", + "M21SV20", + "M21SV60", + "M21SV70", + "M21SPlusV20", + "M29V10", +] diff --git a/pyasic/miners/device/models/whatsminer/M3X/__init__.py b/pyasic/miners/device/models/whatsminer/M3X/__init__.py index 97d84cd44..d74d7acbb 100644 --- a/pyasic/miners/device/models/whatsminer/M3X/__init__.py +++ b/pyasic/miners/device/models/whatsminer/M3X/__init__.py @@ -151,3 +151,164 @@ from .M36S_Plus import M36SPlusVG30 from .M36S_Plus_Plus import M36SPlusPlusVH30 from .M39 import M39V10, M39V20, M39V30 + +__all__ = [ + "M30V10", + "M30V20", + "M30KV10", + "M30LV10", + "M30SV10", + "M30SV20", + "M30SV30", + "M30SV40", + "M30SV50", + "M30SV60", + "M30SV70", + "M30SV80", + "M30SVE10", + "M30SVE20", + "M30SVE30", + "M30SVE40", + "M30SVE50", + "M30SVE60", + "M30SVE70", + "M30SVF10", + "M30SVF20", + "M30SVF30", + "M30SVG10", + "M30SVG20", + "M30SVG30", + "M30SVG40", + "M30SVH10", + "M30SVH20", + "M30SVH30", + "M30SVH40", + "M30SVH50", + "M30SVH60", + "M30SVI20", + "M30SVJ30", + "M30SPlusV10", + "M30SPlusV20", + "M30SPlusV30", + "M30SPlusV40", + "M30SPlusV50", + "M30SPlusV60", + "M30SPlusV70", + "M30SPlusV80", + "M30SPlusV90", + "M30SPlusV100", + "M30SPlusVE30", + "M30SPlusVE40", + "M30SPlusVE50", + "M30SPlusVE60", + "M30SPlusVE70", + "M30SPlusVE80", + "M30SPlusVE90", + "M30SPlusVE100", + "M30SPlusVF20", + "M30SPlusVF30", + "M30SPlusVG20", + "M30SPlusVG30", + "M30SPlusVG40", + "M30SPlusVG50", + "M30SPlusVG60", + "M30SPlusVH10", + "M30SPlusVH20", + "M30SPlusVH30", + "M30SPlusVH40", + "M30SPlusVH50", + "M30SPlusVH60", + "M30SPlusVH70", + "M30SPlusVI30", + "M30SPlusVJ30", + "M30SPlusVJ40", + "M30SPlusPlusV10", + "M30SPlusPlusV20", + "M30SPlusPlusVE30", + "M30SPlusPlusVE40", + "M30SPlusPlusVE50", + "M30SPlusPlusVF40", + "M30SPlusPlusVG30", + "M30SPlusPlusVG40", + "M30SPlusPlusVG50", + "M30SPlusPlusVH10", + "M30SPlusPlusVH20", + "M30SPlusPlusVH30", + "M30SPlusPlusVH40", + "M30SPlusPlusVH50", + "M30SPlusPlusVH60", + "M30SPlusPlusVH70", + "M30SPlusPlusVH80", + "M30SPlusPlusVH90", + "M30SPlusPlusVH100", + "M30SPlusPlusVH110", + "M30SPlusPlusVI30", + "M30SPlusPlusVJ20", + "M30SPlusPlusVJ30", + "M30SPlusPlusVJ50", + "M30SPlusPlusVJ60", + "M30SPlusPlusVJ70", + "M30SPlusPlusVK30", + "M30SPlusPlusVK40", + "M31V10", + "M31V20", + "M31HV10", + "M31HV40", + "M31LV10", + "M31SV10", + "M31SV20", + "M31SV30", + "M31SV40", + "M31SV50", + "M31SV60", + "M31SV70", + "M31SV80", + "M31SV90", + "M31SVE10", + "M31SVE20", + "M31SVE30", + "M31SPlusV10", + "M31SPlusV20", + "M31SPlusV30", + "M31SPlusV40", + "M31SPlusV50", + "M31SPlusV60", + "M31SPlusV80", + "M31SPlusV90", + "M31SPlusV100", + "M31SPlusVE10", + "M31SPlusVE20", + "M31SPlusVE30", + "M31SPlusVE40", + "M31SPlusVE50", + "M31SPlusVE60", + "M31SPlusVE80", + "M31SPlusVF20", + "M31SPlusVF30", + "M31SPlusVG20", + "M31SPlusVG30", + "M31SEV10", + "M31SEV20", + "M31SEV30", + "M32V10", + "M32V20", + "M32S", + "M33V10", + "M33V20", + "M33V30", + "M33SVG30", + "M33SPlusVG20", + "M33SPlusVG30", + "M33SPlusVH20", + "M33SPlusVH30", + "M33SPlusPlusVG40", + "M33SPlusPlusVH20", + "M33SPlusPlusVH30", + "M34SPlusVE10", + "M36SVE10", + "M36SPlusVG30", + "M36SPlusPlusVH30", + "M39V10", + "M39V20", + "M39V30", +] diff --git a/pyasic/miners/device/models/whatsminer/M5X/__init__.py b/pyasic/miners/device/models/whatsminer/M5X/__init__.py index 1932d9909..acd2decb1 100644 --- a/pyasic/miners/device/models/whatsminer/M5X/__init__.py +++ b/pyasic/miners/device/models/whatsminer/M5X/__init__.py @@ -90,3 +90,104 @@ M56SPlusPlusVK50, ) from .M59 import M59VH30 + +__all__ = [ + "M50VE30", + "M50VG30", + "M50VH10", + "M50VH20", + "M50VH30", + "M50VH40", + "M50VH50", + "M50VH60", + "M50VH70", + "M50VH80", + "M50VH90", + "M50VJ10", + "M50VJ20", + "M50VJ30", + "M50VJ40", + "M50VJ60", + "M50VK40", + "M50VK50", + "M50SVH10", + "M50SVH20", + "M50SVH30", + "M50SVH40", + "M50SVH50", + "M50SVJ10", + "M50SVJ20", + "M50SVJ30", + "M50SVJ40", + "M50SVJ50", + "M50SVK10", + "M50SVK20", + "M50SVK30", + "M50SVK50", + "M50SVK60", + "M50SVK70", + "M50SVK80", + "M50SVL20", + "M50SVL30", + "M50SPlusVH30", + "M50SPlusVH40", + "M50SPlusVJ30", + "M50SPlusVJ40", + "M50SPlusVJ60", + "M50SPlusVK10", + "M50SPlusVK20", + "M50SPlusVK30", + "M50SPlusVL10", + "M50SPlusVL20", + "M50SPlusVL30", + "M50SPlusPlusVK10", + "M50SPlusPlusVK20", + "M50SPlusPlusVK30", + "M50SPlusPlusVK40", + "M50SPlusPlusVK50", + "M50SPlusPlusVK60", + "M50SPlusPlusVL20", + "M50SPlusPlusVL30", + "M50SPlusPlusVL40", + "M50SPlusPlusVL50", + "M50SPlusPlusVL60", + "M52SVK30", + "M52SPlusPlusVL10", + "M53VH30", + "M53VH40", + "M53VH50", + "M53VK30", + "M53VK60", + "M53HVH10", + "M53SVH20", + "M53SVH30", + "M53SVJ30", + "M53SVJ40", + "M53SVK30", + "M53SPlusVJ30", + "M53SPlusVJ40", + "M53SPlusVJ50", + "M53SPlusVK30", + "M53SPlusPlusVK10", + "M53SPlusPlusVK20", + "M53SPlusPlusVK30", + "M53SPlusPlusVK50", + "M53SPlusPlusVL10", + "M53SPlusPlusVL30", + "M54SPlusPlusVK30", + "M54SPlusPlusVL30", + "M54SPlusPlusVL40", + "M56VH30", + "M56SVH30", + "M56SVJ30", + "M56SVJ40", + "M56SPlusVJ30", + "M56SPlusVK30", + "M56SPlusVK40", + "M56SPlusVK50", + "M56SPlusPlusVK10", + "M56SPlusPlusVK30", + "M56SPlusPlusVK40", + "M56SPlusPlusVK50", + "M59VH30", +] diff --git a/pyasic/miners/device/models/whatsminer/M6X/__init__.py b/pyasic/miners/device/models/whatsminer/M6X/__init__.py index 338417e02..6ff1a95d9 100644 --- a/pyasic/miners/device/models/whatsminer/M6X/__init__.py +++ b/pyasic/miners/device/models/whatsminer/M6X/__init__.py @@ -87,3 +87,99 @@ ) from .M66S_Plus_Plus import M66SPlusPlusVL20 from .M67S import M67SVK30 + +__all__ = [ + "M60VK6A", + "M60VK10", + "M60VK20", + "M60VK30", + "M60VK40", + "M60VL10", + "M60VL20", + "M60VL30", + "M60VL40", + "M60VL50", + "M60SVK10", + "M60SVK20", + "M60SVK30", + "M60SVK40", + "M60SVL10", + "M60SVL20", + "M60SVL30", + "M60SVL40", + "M60SVL50", + "M60SVL60", + "M60SVL70", + "M60SPlusVK30", + "M60SPlusVK40", + "M60SPlusVK50", + "M60SPlusVK60", + "M60SPlusVK70", + "M60SPlusVL10", + "M60SPlusVL30", + "M60SPlusVL40", + "M60SPlusVL50", + "M60SPlusVL60", + "M60SPlusPlusVL30", + "M60SPlusPlusVL40", + "M61VK10", + "M61VK20", + "M61VK30", + "M61VK40", + "M61VL10", + "M61VL30", + "M61VL40", + "M61VL50", + "M61VL60", + "M61SVL10", + "M61SVL20", + "M61SVL30", + "M61SPlusVL30", + "M62SPlusVK30", + "M63VK10", + "M63VK20", + "M63VK30", + "M63VL10", + "M63VL30", + "M63SVK10", + "M63SVK20", + "M63SVK30", + "M63SVK60", + "M63SVL10", + "M63SVL50", + "M63SVL60", + "M63SPlusVK30", + "M63SPlusVL10", + "M63SPlusVL20", + "M63SPlusVL30", + "M63SPlusVL50", + "M63SPlusPlusVL20", + "M64VL30", + "M64VL40", + "M64SVL30", + "M65SVK20", + "M65SVL60", + "M65SPlusVK30", + "M66VK20", + "M66VK30", + "M66VL20", + "M66VL30", + "M66SVK20", + "M66SVK30", + "M66SVK40", + "M66SVK50", + "M66SVK60", + "M66SVL10", + "M66SVL20", + "M66SVL30", + "M66SVL40", + "M66SVL50", + "M66SPlusVK30", + "M66SPlusVL10", + "M66SPlusVL20", + "M66SPlusVL30", + "M66SPlusVL40", + "M66SPlusVL60", + "M66SPlusPlusVL20", + "M67SVK30", +] diff --git a/pyasic/miners/device/models/whatsminer/M7X/__init__.py b/pyasic/miners/device/models/whatsminer/M7X/__init__.py index a570f99a1..df0f41d46 100644 --- a/pyasic/miners/device/models/whatsminer/M7X/__init__.py +++ b/pyasic/miners/device/models/whatsminer/M7X/__init__.py @@ -1 +1,5 @@ from .M70 import M70VM30 + +__all__ = [ + "M70VM30", +] diff --git a/pyasic/miners/elphapex/__init__.py b/pyasic/miners/elphapex/__init__.py index 1fecf8e23..3f702f771 100644 --- a/pyasic/miners/elphapex/__init__.py +++ b/pyasic/miners/elphapex/__init__.py @@ -1 +1,7 @@ -from .daoge import * +from .daoge import ElphapexDG1, ElphapexDG1Home, ElphapexDG1Plus + +__all__ = [ + "ElphapexDG1", + "ElphapexDG1Home", + "ElphapexDG1Plus", +] diff --git a/pyasic/miners/elphapex/daoge/DGX/DG1.py b/pyasic/miners/elphapex/daoge/DGX/DG1.py index ad81e4fbd..10648e362 100644 --- a/pyasic/miners/elphapex/daoge/DGX/DG1.py +++ b/pyasic/miners/elphapex/daoge/DGX/DG1.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.elphapex import ElphapexMiner -from pyasic.miners.device.models import DG1, DG1Home, DG1Plus +from pyasic.miners.device.models.elphapex.DGX import DG1, DG1Home, DG1Plus class ElphapexDG1Plus(ElphapexMiner, DG1Plus): diff --git a/pyasic/miners/elphapex/daoge/DGX/__init__.py b/pyasic/miners/elphapex/daoge/DGX/__init__.py index 830fdcab5..ef60e7dc6 100644 --- a/pyasic/miners/elphapex/daoge/DGX/__init__.py +++ b/pyasic/miners/elphapex/daoge/DGX/__init__.py @@ -1 +1,3 @@ from .DG1 import ElphapexDG1, ElphapexDG1Home, ElphapexDG1Plus + +__all__ = ["ElphapexDG1", "ElphapexDG1Home", "ElphapexDG1Plus"] diff --git a/pyasic/miners/elphapex/daoge/__init__.py b/pyasic/miners/elphapex/daoge/__init__.py index a64ecbeac..161572084 100644 --- a/pyasic/miners/elphapex/daoge/__init__.py +++ b/pyasic/miners/elphapex/daoge/__init__.py @@ -1 +1,3 @@ -from .DGX import * +from .DGX import ElphapexDG1, ElphapexDG1Home, ElphapexDG1Plus + +__all__ = ["ElphapexDG1", "ElphapexDG1Home", "ElphapexDG1Plus"] diff --git a/pyasic/miners/factory.py b/pyasic/miners/factory.py index 5c40916ac..e3da35156 100644 --- a/pyasic/miners/factory.py +++ b/pyasic/miners/factory.py @@ -22,31 +22,685 @@ import re import warnings from collections.abc import AsyncGenerator, Callable -from typing import Any, cast +from typing import Any import anyio import httpx from pyasic import settings from pyasic.logger import logger -from pyasic.miners.antminer import * -from pyasic.miners.auradine import * -from pyasic.miners.avalonminer import * -from pyasic.miners.backends import * +from pyasic.miners.antminer.bmminer.X3 import ( + BMMinerHS3, + BMMinerKA3, + BMMinerKS3, + BMMinerL3Plus, +) +from pyasic.miners.antminer.bmminer.X5 import ( + BMMinerKS5, + BMMinerKS5Pro, +) +from pyasic.miners.antminer.bmminer.X7 import ( + BMMinerD7, + BMMinerK7, + BMMinerL7, +) +from pyasic.miners.antminer.bmminer.X9 import ( + BMMinerD9, + BMMinerE9Pro, + BMMinerL9, + BMMinerS9, + BMMinerS9i, + BMMinerS9j, + BMMinerT9, +) +from pyasic.miners.antminer.bmminer.X15 import ( + BMMinerZ15Pro, +) +from pyasic.miners.antminer.bmminer.X17 import ( + BMMinerS17, + BMMinerS17e, + BMMinerS17Plus, + BMMinerS17Pro, + BMMinerT17, + BMMinerT17e, + BMMinerT17Plus, +) +from pyasic.miners.antminer.bmminer.X19 import ( + BMMinerS19, + BMMinerS19a, + BMMinerS19aPro, + BMMinerS19Hydro, + BMMinerS19i, + BMMinerS19j, + BMMinerS19jNoPIC, + BMMinerS19jPlus, + BMMinerS19jPro, + BMMinerS19jProPlus, + BMMinerS19jXP, + BMMinerS19KPro, + BMMinerS19L, + BMMinerS19Plus, + BMMinerS19Pro, + BMMinerS19ProHydro, + BMMinerS19ProPlus, + BMMinerS19ProPlusHydro, + BMMinerS19XP, + BMMinerT19, +) +from pyasic.miners.antminer.bmminer.X21 import ( + BMMinerS21, + BMMinerS21Hydro, + BMMinerS21Plus, + BMMinerS21PlusHydro, + BMMinerS21Pro, + BMMinerT21, +) +from pyasic.miners.antminer.bosminer import ( + BOSMinerS9, + BOSMinerS17, + BOSMinerS17e, + BOSMinerS17Plus, + BOSMinerS17Pro, + BOSMinerS19, + BOSMinerS19a, + BOSMinerS19aPro, + BOSMinerS19j, + BOSMinerS19jNoPIC, + BOSMinerS19jPro, + BOSMinerS19jProNoPIC, + BOSMinerS19jProPlus, + BOSMinerS19jProPlusNoPIC, + BOSMinerS19kProNoPIC, + BOSMinerS19Plus, + BOSMinerS19Pro, + BOSMinerS19ProPlusHydro, + BOSMinerS19XP, + BOSMinerS19XPHydro, + BOSMinerS21, + BOSMinerS21Hydro, + BOSMinerS21Plus, + BOSMinerS21PlusHydro, + BOSMinerS21Pro, + BOSMinerT17, + BOSMinerT17e, + BOSMinerT17Plus, + BOSMinerT19, + BOSMinerT21, +) +from pyasic.miners.antminer.cgminer.X3 import ( + CGMinerD3, +) +from pyasic.miners.antminer.cgminer.X5 import ( + CGMinerDR5, +) +from pyasic.miners.antminer.cgminer.X15 import ( + CGMinerZ15, +) +from pyasic.miners.antminer.epic import ( + ePICS19, + ePICS19j, + ePICS19jPro, + ePICS19jProDual, + ePICS19jProPlus, + ePICS19kPro, + ePICS19kProDual, + ePICS19Pro, + ePICS19XP, + ePICS21, + ePICS21Pro, + ePICT21, +) +from pyasic.miners.antminer.hiveon import ( + HiveonS19, + HiveonS19jPro, + HiveonS19kPro, + HiveonS19NoPIC, + HiveonT9, +) +from pyasic.miners.antminer.luxos import ( + LUXMinerS9, + LUXMinerS19, + LUXMinerS19jPro, + LUXMinerS19jProPlus, + LUXMinerS19kPro, + LUXMinerS19Pro, + LUXMinerS19XP, + LUXMinerS21, + LUXMinerT19, + LUXMinerT21, +) +from pyasic.miners.antminer.marathon import ( + MaraS19, + MaraS19j, + MaraS19jNoPIC, + MaraS19jPro, + MaraS19KPro, + MaraS19Pro, + MaraS19XP, + MaraS21, + MaraT21, +) +from pyasic.miners.antminer.mskminer import ( + MSKMinerS19NoPIC, +) +from pyasic.miners.antminer.vnish import ( + VNishL3Plus, + VNishL7, + VNishL9, + VNishS17Plus, + VNishS17Pro, + VNishS19, + VNishS19a, + VNishS19aPro, + VNishS19Hydro, + VNishS19i, + VNishS19j, + VNishS19jPro, + VNishS19kPro, + VNishS19NoPIC, + VNishS19Pro, + VNishS19ProA, + VNishS19ProHydro, + VNishS19XP, + VNishS19XPHydro, + VNishS21, + VNishS21Hydro, + VNishS21Plus, + VNishS21PlusHydro, + VNishS21Pro, + VNishT19, + VNishT21, +) +from pyasic.miners.auradine.flux import ( + AuradineFluxAD2500, + AuradineFluxAD3500, + AuradineFluxAI2500, + AuradineFluxAI3680, + AuradineFluxAT1500, + AuradineFluxAT2860, + AuradineFluxAT2880, +) +from pyasic.miners.avalonminer.cgminer import ( + CGMinerAvalon721, + CGMinerAvalon741, + CGMinerAvalon761, + CGMinerAvalon821, + CGMinerAvalon841, + CGMinerAvalon851, + CGMinerAvalon921, + CGMinerAvalon1026, + CGMinerAvalon1047, + CGMinerAvalon1066, + CGMinerAvalon1126Pro, + CGMinerAvalon1166Pro, + CGMinerAvalon1246, + CGMinerAvalon1566, + CGMinerAvalonNano3, + CGMinerAvalonNano3s, + CGMinerAvalonQHome, +) +from pyasic.miners.backends.auradine import Auradine +from pyasic.miners.backends.avalonminer import AvalonMiner +from pyasic.miners.backends.bitaxe import BitAxe +from pyasic.miners.backends.bmminer import BMMiner +from pyasic.miners.backends.braiins_os import BOSMiner +from pyasic.miners.backends.btminer import BTMiner +from pyasic.miners.backends.elphapex import ElphapexMiner +from pyasic.miners.backends.epic import ePIC +from pyasic.miners.backends.goldshell import GoldshellMiner +from pyasic.miners.backends.hammer import BlackMiner +from pyasic.miners.backends.hiveon import HiveonModern +from pyasic.miners.backends.iceriver import IceRiver +from pyasic.miners.backends.innosilicon import Innosilicon +from pyasic.miners.backends.luckyminer import LuckyMiner +from pyasic.miners.backends.luxminer import LUXMiner +from pyasic.miners.backends.marathon import MaraMiner +from pyasic.miners.backends.mskminer import MSKMiner +from pyasic.miners.backends.unknown import UnknownMiner +from pyasic.miners.backends.vnish import VNish from pyasic.miners.base import AnyMiner -from pyasic.miners.bitaxe import * -from pyasic.miners.blockminer import * -from pyasic.miners.braiins import * -from pyasic.miners.device.makes import * -from pyasic.miners.elphapex import * -from pyasic.miners.goldshell import * -from pyasic.miners.hammer import * -from pyasic.miners.iceriver import * +from pyasic.miners.bitaxe import ( + BitAxeGamma, + BitAxeMax, + BitAxeSupra, + BitAxeUltra, +) +from pyasic.miners.blockminer.epic import ( + ePICBlockMiner520i, + ePICBlockMiner720i, + ePICBlockMinerELITE1, +) +from pyasic.miners.braiins import ( + BraiinsBMM100, + BraiinsBMM101, +) +from pyasic.miners.device.makes import ( + AntMinerMake, + AuradineMake, + AvalonMinerMake, + ElphapexMake, + GoldshellMake, + HammerMake, + IceRiverMake, + InnosiliconMake, + VolcMinerMake, + WhatsMinerMake, +) +from pyasic.miners.elphapex import ( + ElphapexDG1, + ElphapexDG1Home, + ElphapexDG1Plus, +) +from pyasic.miners.goldshell import ( + GoldshellByte, + GoldshellCK5, + GoldshellHS5, + GoldshellKD5, + GoldshellKDBoxII, + GoldshellKDBoxPro, + GoldshellKDMax, + GoldshellMiniDoge, +) +from pyasic.miners.hammer import ( + HammerD10, +) from pyasic.miners.iceriver.iceminer.ALX import IceRiverAL3 -from pyasic.miners.innosilicon import * -from pyasic.miners.luckyminer import * -from pyasic.miners.volcminer import * -from pyasic.miners.whatsminer import * +from pyasic.miners.iceriver.iceminer.KSX import ( + IceRiverKS0, + IceRiverKS1, + IceRiverKS2, + IceRiverKS3, + IceRiverKS3L, + IceRiverKS3M, + IceRiverKS5, + IceRiverKS5L, + IceRiverKS5M, +) +from pyasic.miners.innosilicon import ( + InnosiliconA10X, + InnosiliconA11, + InnosiliconA11MX, + InnosiliconT3HPlus, +) +from pyasic.miners.luckyminer import ( + LuckyMinerLV07, + LuckyMinerLV08, +) +from pyasic.miners.volcminer import ( + VolcMinerD1, +) +from pyasic.miners.whatsminer.btminer.M2X import ( + BTMinerM20PV10, + BTMinerM20PV30, + BTMinerM20SPlusV30, + BTMinerM20SV10, + BTMinerM20SV20, + BTMinerM20SV30, + BTMinerM20V10, + BTMinerM21SPlusV20, + BTMinerM21SV20, + BTMinerM21SV60, + BTMinerM21SV70, + BTMinerM21V10, + BTMinerM29V10, +) +from pyasic.miners.whatsminer.btminer.M3X import ( + BTMinerM30KV10, + BTMinerM30LV10, + BTMinerM30SPlusPlusV10, + BTMinerM30SPlusPlusV20, + BTMinerM30SPlusPlusVE30, + BTMinerM30SPlusPlusVE40, + BTMinerM30SPlusPlusVE50, + BTMinerM30SPlusPlusVF40, + BTMinerM30SPlusPlusVG30, + BTMinerM30SPlusPlusVG40, + BTMinerM30SPlusPlusVG50, + BTMinerM30SPlusPlusVH10, + BTMinerM30SPlusPlusVH20, + BTMinerM30SPlusPlusVH30, + BTMinerM30SPlusPlusVH40, + BTMinerM30SPlusPlusVH50, + BTMinerM30SPlusPlusVH60, + BTMinerM30SPlusPlusVH70, + BTMinerM30SPlusPlusVH80, + BTMinerM30SPlusPlusVH90, + BTMinerM30SPlusPlusVH100, + BTMinerM30SPlusPlusVH110, + BTMinerM30SPlusPlusVI30, + BTMinerM30SPlusPlusVJ20, + BTMinerM30SPlusPlusVJ30, + BTMinerM30SPlusPlusVJ50, + BTMinerM30SPlusPlusVJ60, + BTMinerM30SPlusPlusVJ70, + BTMinerM30SPlusPlusVK30, + BTMinerM30SPlusPlusVK40, + BTMinerM30SPlusV10, + BTMinerM30SPlusV20, + BTMinerM30SPlusV30, + BTMinerM30SPlusV40, + BTMinerM30SPlusV50, + BTMinerM30SPlusV60, + BTMinerM30SPlusV70, + BTMinerM30SPlusV80, + BTMinerM30SPlusV90, + BTMinerM30SPlusV100, + BTMinerM30SPlusVE30, + BTMinerM30SPlusVE40, + BTMinerM30SPlusVE50, + BTMinerM30SPlusVE60, + BTMinerM30SPlusVE70, + BTMinerM30SPlusVE80, + BTMinerM30SPlusVE90, + BTMinerM30SPlusVE100, + BTMinerM30SPlusVF20, + BTMinerM30SPlusVF30, + BTMinerM30SPlusVG20, + BTMinerM30SPlusVG30, + BTMinerM30SPlusVG40, + BTMinerM30SPlusVG50, + BTMinerM30SPlusVG60, + BTMinerM30SPlusVH10, + BTMinerM30SPlusVH20, + BTMinerM30SPlusVH30, + BTMinerM30SPlusVH40, + BTMinerM30SPlusVH50, + BTMinerM30SPlusVH60, + BTMinerM30SPlusVH70, + BTMinerM30SPlusVI30, + BTMinerM30SPlusVJ30, + BTMinerM30SPlusVJ40, + BTMinerM30SV10, + BTMinerM30SV20, + BTMinerM30SV30, + BTMinerM30SV40, + BTMinerM30SV50, + BTMinerM30SV60, + BTMinerM30SV70, + BTMinerM30SV80, + BTMinerM30SVE10, + BTMinerM30SVE20, + BTMinerM30SVE30, + BTMinerM30SVE40, + BTMinerM30SVE50, + BTMinerM30SVE60, + BTMinerM30SVE70, + BTMinerM30SVF10, + BTMinerM30SVF20, + BTMinerM30SVF30, + BTMinerM30SVG10, + BTMinerM30SVG20, + BTMinerM30SVG30, + BTMinerM30SVG40, + BTMinerM30SVH10, + BTMinerM30SVH20, + BTMinerM30SVH30, + BTMinerM30SVH40, + BTMinerM30SVH50, + BTMinerM30SVH60, + BTMinerM30SVI20, + BTMinerM30SVJ30, + BTMinerM30V10, + BTMinerM30V20, + BTMinerM31HV10, + BTMinerM31HV40, + BTMinerM31LV10, + BTMinerM31SEV10, + BTMinerM31SEV20, + BTMinerM31SEV30, + BTMinerM31SPlusV10, + BTMinerM31SPlusV20, + BTMinerM31SPlusV30, + BTMinerM31SPlusV40, + BTMinerM31SPlusV50, + BTMinerM31SPlusV60, + BTMinerM31SPlusV80, + BTMinerM31SPlusV90, + BTMinerM31SPlusV100, + BTMinerM31SPlusVE10, + BTMinerM31SPlusVE20, + BTMinerM31SPlusVE30, + BTMinerM31SPlusVE40, + BTMinerM31SPlusVE50, + BTMinerM31SPlusVE60, + BTMinerM31SPlusVE80, + BTMinerM31SPlusVF20, + BTMinerM31SPlusVF30, + BTMinerM31SPlusVG20, + BTMinerM31SPlusVG30, + BTMinerM31SV10, + BTMinerM31SV20, + BTMinerM31SV30, + BTMinerM31SV40, + BTMinerM31SV50, + BTMinerM31SV60, + BTMinerM31SV70, + BTMinerM31SV80, + BTMinerM31SV90, + BTMinerM31SVE10, + BTMinerM31SVE20, + BTMinerM31SVE30, + BTMinerM31V10, + BTMinerM31V20, + BTMinerM32V10, + BTMinerM32V20, + BTMinerM33SPlusPlusVG40, + BTMinerM33SPlusPlusVH20, + BTMinerM33SPlusPlusVH30, + BTMinerM33SPlusVG20, + BTMinerM33SPlusVG30, + BTMinerM33SPlusVH20, + BTMinerM33SPlusVH30, + BTMinerM33SVG30, + BTMinerM33V10, + BTMinerM33V20, + BTMinerM33V30, + BTMinerM34SPlusVE10, + BTMinerM36SPlusPlusVH30, + BTMinerM36SPlusVG30, + BTMinerM36SVE10, + BTMinerM39V10, + BTMinerM39V20, + BTMinerM39V30, +) +from pyasic.miners.whatsminer.btminer.M5X import ( + BTMinerM50SPlusPlusVK10, + BTMinerM50SPlusPlusVK20, + BTMinerM50SPlusPlusVK30, + BTMinerM50SPlusPlusVK40, + BTMinerM50SPlusPlusVK50, + BTMinerM50SPlusPlusVK60, + BTMinerM50SPlusPlusVL20, + BTMinerM50SPlusPlusVL30, + BTMinerM50SPlusPlusVL40, + BTMinerM50SPlusPlusVL50, + BTMinerM50SPlusPlusVL60, + BTMinerM50SPlusVH30, + BTMinerM50SPlusVH40, + BTMinerM50SPlusVJ30, + BTMinerM50SPlusVJ40, + BTMinerM50SPlusVJ60, + BTMinerM50SPlusVK10, + BTMinerM50SPlusVK20, + BTMinerM50SPlusVK30, + BTMinerM50SPlusVL10, + BTMinerM50SPlusVL20, + BTMinerM50SPlusVL30, + BTMinerM50SVH10, + BTMinerM50SVH20, + BTMinerM50SVH30, + BTMinerM50SVH40, + BTMinerM50SVH50, + BTMinerM50SVJ10, + BTMinerM50SVJ20, + BTMinerM50SVJ30, + BTMinerM50SVJ40, + BTMinerM50SVJ50, + BTMinerM50SVK10, + BTMinerM50SVK20, + BTMinerM50SVK30, + BTMinerM50SVK50, + BTMinerM50SVK60, + BTMinerM50SVK70, + BTMinerM50SVK80, + BTMinerM50SVL20, + BTMinerM50SVL30, + BTMinerM50VE30, + BTMinerM50VG30, + BTMinerM50VH10, + BTMinerM50VH20, + BTMinerM50VH30, + BTMinerM50VH40, + BTMinerM50VH50, + BTMinerM50VH60, + BTMinerM50VH70, + BTMinerM50VH80, + BTMinerM50VH90, + BTMinerM50VJ10, + BTMinerM50VJ20, + BTMinerM50VJ30, + BTMinerM50VJ40, + BTMinerM50VJ60, + BTMinerM50VK40, + BTMinerM50VK50, + BTMinerM52SPlusPlusVL10, + BTMinerM52SVK30, + BTMinerM53HVH10, + BTMinerM53SPlusPlusVK10, + BTMinerM53SPlusPlusVK20, + BTMinerM53SPlusPlusVK30, + BTMinerM53SPlusPlusVK50, + BTMinerM53SPlusPlusVL10, + BTMinerM53SPlusPlusVL30, + BTMinerM53SPlusVJ30, + BTMinerM53SPlusVJ40, + BTMinerM53SPlusVJ50, + BTMinerM53SPlusVK30, + BTMinerM53SVH20, + BTMinerM53SVH30, + BTMinerM53SVJ30, + BTMinerM53SVJ40, + BTMinerM53SVK30, + BTMinerM53VH30, + BTMinerM53VH40, + BTMinerM53VH50, + BTMinerM53VK30, + BTMinerM53VK60, + BTMinerM54SPlusPlusVK30, + BTMinerM54SPlusPlusVL30, + BTMinerM54SPlusPlusVL40, + BTMinerM56SPlusPlusVK10, + BTMinerM56SPlusPlusVK30, + BTMinerM56SPlusPlusVK40, + BTMinerM56SPlusPlusVK50, + BTMinerM56SPlusVJ30, + BTMinerM56SPlusVK30, + BTMinerM56SPlusVK40, + BTMinerM56SPlusVK50, + BTMinerM56SVH30, + BTMinerM56SVJ30, + BTMinerM56SVJ40, + BTMinerM56VH30, + BTMinerM59VH30, +) +from pyasic.miners.whatsminer.btminer.M6X import ( + BTMinerM60SPlusPlusVL30, + BTMinerM60SPlusPlusVL40, + BTMinerM60SPlusVK30, + BTMinerM60SPlusVK40, + BTMinerM60SPlusVK50, + BTMinerM60SPlusVK60, + BTMinerM60SPlusVK70, + BTMinerM60SPlusVL10, + BTMinerM60SPlusVL30, + BTMinerM60SPlusVL40, + BTMinerM60SPlusVL50, + BTMinerM60SPlusVL60, + BTMinerM60SVK10, + BTMinerM60SVK20, + BTMinerM60SVK30, + BTMinerM60SVK40, + BTMinerM60SVL10, + BTMinerM60SVL20, + BTMinerM60SVL30, + BTMinerM60SVL40, + BTMinerM60SVL50, + BTMinerM60SVL60, + BTMinerM60SVL70, + BTMinerM60VK6A, + BTMinerM60VK10, + BTMinerM60VK20, + BTMinerM60VK30, + BTMinerM60VK40, + BTMinerM60VL10, + BTMinerM60VL20, + BTMinerM60VL30, + BTMinerM60VL40, + BTMinerM60VL50, + BTMinerM61SPlusVL30, + BTMinerM61SVL10, + BTMinerM61SVL20, + BTMinerM61SVL30, + BTMinerM61VK10, + BTMinerM61VK20, + BTMinerM61VK30, + BTMinerM61VK40, + BTMinerM61VL10, + BTMinerM61VL30, + BTMinerM61VL40, + BTMinerM61VL50, + BTMinerM61VL60, + BTMinerM62SPlusVK30, + BTMinerM63SPlusPlusVL20, + BTMinerM63SPlusVK30, + BTMinerM63SPlusVL10, + BTMinerM63SPlusVL20, + BTMinerM63SPlusVL30, + BTMinerM63SPlusVL50, + BTMinerM63SVK10, + BTMinerM63SVK20, + BTMinerM63SVK30, + BTMinerM63SVK60, + BTMinerM63SVL10, + BTMinerM63SVL50, + BTMinerM63SVL60, + BTMinerM63VK10, + BTMinerM63VK20, + BTMinerM63VK30, + BTMinerM63VL10, + BTMinerM63VL30, + BTMinerM64SVL30, + BTMinerM64VL30, + BTMinerM64VL40, + BTMinerM65SPlusVK30, + BTMinerM65SVK20, + BTMinerM65SVL60, + BTMinerM66SPlusPlusVL20, + BTMinerM66SPlusVK30, + BTMinerM66SPlusVL10, + BTMinerM66SPlusVL20, + BTMinerM66SPlusVL30, + BTMinerM66SPlusVL40, + BTMinerM66SPlusVL60, + BTMinerM66SVK20, + BTMinerM66SVK30, + BTMinerM66SVK40, + BTMinerM66SVK50, + BTMinerM66SVK60, + BTMinerM66SVL10, + BTMinerM66SVL20, + BTMinerM66SVL30, + BTMinerM66SVL40, + BTMinerM66SVL50, + BTMinerM66VK20, + BTMinerM66VK30, + BTMinerM66VL20, + BTMinerM66VL30, + BTMinerM67SVK30, +) +from pyasic.miners.whatsminer.btminer.M7X import ( + BTMinerM70VM30, +) class MinerTypes(enum.Enum): @@ -713,7 +1367,9 @@ class MinerTypes(enum.Enum): } -async def concurrent_get_first_result(tasks: list, verification_func: Callable) -> Any: +async def concurrent_get_first_result( + tasks: list[Any], verification_func: Callable[..., Any] +) -> Any: res = None for fut in asyncio.as_completed(tasks): res = await fut @@ -741,7 +1397,7 @@ async def get_multiple_miners( return results async def get_miner_generator( - self, ips: list, limit: int = 200 + self, ips: list[str], limit: int = 200 ) -> AsyncGenerator[AnyMiner, None]: tasks = [] semaphore = asyncio.Semaphore(limit) @@ -832,7 +1488,7 @@ async def _get_miner_type(self, ip: str) -> MinerTypes | None: asyncio.create_task(self._get_miner_socket(ip)), ] - return await concurrent_get_first_result(tasks, lambda x: x is not None) + return await concurrent_get_first_result(tasks, lambda x: x is not None) # type: ignore[no-any-return] async def _get_miner_web(self, ip: str) -> MinerTypes | None: urls = [f"http://{ip}/", f"https://{ip}/"] @@ -1029,7 +1685,7 @@ async def send_web_command( ip: str, location: str, auth: httpx.DigestAuth | None = None, - ) -> dict | None: + ) -> dict[str, Any] | None: async with httpx.AsyncClient(transport=settings.transport()) as session: try: data = await session.get( @@ -1046,13 +1702,13 @@ async def send_web_command( json_data = data.json() except (json.JSONDecodeError, asyncio.TimeoutError): try: - return json.loads(data.text) + return json.loads(data.text) # type: ignore[no-any-return] except (json.JSONDecodeError, httpx.HTTPError): return None else: - return json_data + return json_data # type: ignore[no-any-return] - async def send_api_command(self, ip: str, command: str) -> dict | None: + async def send_api_command(self, ip: str, command: str) -> dict[str, Any] | None: data = b"" try: reader, writer = await asyncio.open_connection(ip, 4028) @@ -1090,9 +1746,11 @@ async def send_api_command(self, ip: str, command: str) -> dict | None: except json.JSONDecodeError: return {} - return data_dict + return data_dict # type: ignore[no-any-return] - async def send_btminer_v3_api_command(self, ip, command): + async def send_btminer_v3_api_command( + self, ip: str, command: str + ) -> dict[str, Any] | None: try: reader, writer = await asyncio.open_connection(ip, 4433) except (ConnectionError, OSError): @@ -1129,7 +1787,7 @@ async def send_btminer_v3_api_command(self, ip, command): except json.JSONDecodeError: return {} - return data + return data # type: ignore[no-any-return] @staticmethod async def _fix_api_data(data: bytes) -> str: @@ -1177,10 +1835,10 @@ def _select_miner_from_classes( miner_type = MinerTypes.HIVEON if miner_type is None: - return cast(AnyMiner, UnknownMiner(str(ip), version)) + return UnknownMiner(str(ip), version) # type: ignore[return-value] try: - return MINER_CLASSES[miner_type][str(miner_model).upper()](ip, version) + return MINER_CLASSES[miner_type][str(miner_model).upper()](ip, version) # type: ignore[no-any-return] except LookupError: if miner_type in MINER_CLASSES: if miner_model is not None: @@ -1188,8 +1846,8 @@ def _select_miner_from_classes( f"Partially supported miner found: {miner_model}, type: {miner_type}, please open an issue with miner data " f"and this model on GitHub (https://github.com/UpstreamData/pyasic/issues)." ) - return MINER_CLASSES[miner_type][None](ip, version) - return cast(AnyMiner, UnknownMiner(str(ip), version)) + return MINER_CLASSES[miner_type][None](ip, version) # type: ignore[no-any-return] + return UnknownMiner(str(ip), version) # type: ignore[return-value] async def get_miner_model_antminer(self, ip: str) -> str | None: tasks = [ @@ -1197,7 +1855,7 @@ async def get_miner_model_antminer(self, ip: str) -> str | None: asyncio.create_task(self._get_model_antminer_sock(ip)), ] - return await concurrent_get_first_result(tasks, lambda x: x is not None) + return await concurrent_get_first_result(tasks, lambda x: x is not None) # type: ignore[no-any-return] async def _get_model_antminer_web(self, ip: str) -> str | None: # last resort, this is slow @@ -1211,7 +1869,7 @@ async def _get_model_antminer_web(self, ip: str) -> str | None: if web_json_data is not None: try: miner_model = web_json_data["minertype"] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1226,7 +1884,7 @@ async def _get_model_antminer_sock(self, ip: str) -> str | None: split_miner_model = miner_model.split(" (") miner_model = split_miner_model[0] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1250,7 +1908,7 @@ async def get_miner_model_goldshell(self, ip: str) -> str | None: if json_data is not None: try: miner_model = json_data["model"].replace("-", " ") - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1261,20 +1919,23 @@ async def get_miner_model_whatsminer(self, ip: str) -> str | None: try: miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace("_", "") miner_model = miner_model[:-1] + "0" - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass else: sock_json_data_v3 = await self.send_btminer_v3_api_command( ip, "get.device.info" ) - try: - miner_model = sock_json_data_v3["msg"]["miner"]["type"].replace("_", "") - miner_model = miner_model[:-1] + "0" + if sock_json_data_v3 is not None: + try: + miner_model = sock_json_data_v3["msg"]["miner"]["type"].replace( + "_", "" + ) + miner_model = miner_model[:-1] + "0" - return miner_model - except (TypeError, LookupError): - pass + return miner_model # type: ignore[no-any-return] + except (TypeError, LookupError): + pass return None async def get_miner_version_whatsminer(self, ip: str) -> str | None: @@ -1282,7 +1943,7 @@ async def get_miner_version_whatsminer(self, ip: str) -> str | None: if sock_json_data is not None: try: version = sock_json_data["Msg"]["fw_ver"] - return version + return version # type: ignore[no-any-return] except LookupError: pass return None @@ -1297,7 +1958,7 @@ async def get_miner_model_avalonminer(self, ip: str) -> str | None: if miner_model in ["AVALONNANO", "AVALON0O", "AVALONMINER 15"]: subtype = sock_json_data["VERSION"][0]["MODEL"].upper() miner_model = f"AVALONMINER {subtype}" - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1327,7 +1988,7 @@ async def get_miner_model_innosilicon(self, ip: str) -> str | None: data={}, ) ).json() - return web_data["type"] + return web_data["type"] # type: ignore[no-any-return] except (httpx.HTTPError, LookupError): pass return None @@ -1354,7 +2015,7 @@ async def get_miner_model_braiins_os(self, ip: str) -> str | None: .replace("Bitmain ", "") .replace("S19XP", "S19 XP") ) - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1390,7 +2051,7 @@ async def get_miner_model_vnish(self, ip: str) -> str | None: if " AML" in miner_model: miner_model = miner_model.replace(" AML", "") - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1401,7 +2062,7 @@ async def get_miner_model_epic(self, ip: str) -> str | None: if sock_json_data is not None: try: miner_model = sock_json_data["Model"] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass else: @@ -1416,7 +2077,7 @@ async def get_miner_model_hiveon(self, ip: str) -> str | None: if sock_json_data is not None: try: miner_type = sock_json_data["VERSION"][0]["Type"] - return miner_type.replace(" HIVEON", "") + return miner_type.replace(" HIVEON", "") # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1429,7 +2090,7 @@ async def get_miner_model_luxos(self, ip: str) -> str | None: if " (" in miner_model: split_miner_model = miner_model.split(" (") miner_model = split_miner_model[0] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1438,7 +2099,7 @@ async def get_miner_model_auradine(self, ip: str) -> str | None: sock_json_data = await self.send_api_command(ip, "devdetails") if sock_json_data is not None: try: - return sock_json_data["DEVDETAILS"][0]["Model"] + return sock_json_data["DEVDETAILS"][0]["Model"] # type: ignore[no-any-return] except LookupError: pass return None @@ -1454,7 +2115,7 @@ async def get_miner_model_marathon(self, ip: str) -> str | None: miner_model = web_json_data["model"] if miner_model == "": return None - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1467,7 +2128,7 @@ async def get_miner_model_bitaxe(self, ip: str) -> str | None: miner_model = web_json_data["ASICModel"] if miner_model == "": return None - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1480,7 +2141,7 @@ async def get_miner_model_luckyminer(self, ip: str) -> str | None: miner_model = web_json_data["minerModel"] if miner_model == "": return None - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1514,7 +2175,7 @@ async def get_miner_model_iceriver(self, ip: str) -> str | None: miner_ver = split_ver[-2] else: miner_ver = split_ver[-1].replace("miner", "") - return miner_ver.upper() + return miner_ver.upper() # type: ignore[no-any-return] except httpx.HTTPError: pass return None @@ -1530,7 +2191,7 @@ async def get_miner_model_hammer(self, ip: str) -> str | None: if web_json_data is not None: try: miner_model = web_json_data["minertype"] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1546,7 +2207,7 @@ async def get_miner_model_volcminer(self, ip: str) -> str | None: if web_json_data is not None: try: miner_model = web_json_data["minertype"] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1562,7 +2223,7 @@ async def get_miner_model_elphapex(self, ip: str) -> str | None: if web_json_data is not None: try: miner_model = web_json_data["minertype"] - return miner_model + return miner_model # type: ignore[no-any-return] except (TypeError, LookupError): pass return None @@ -1571,7 +2232,7 @@ async def get_miner_model_mskminer(self, ip: str) -> str | None: sock_json_data = await self.send_api_command(ip, "version") if sock_json_data is not None: try: - return sock_json_data["VERSION"][0]["Type"].split(" ")[0] + return sock_json_data["VERSION"][0]["Type"].split(" ")[0] # type: ignore[no-any-return] except LookupError: pass return None diff --git a/pyasic/miners/goldshell/__init__.py b/pyasic/miners/goldshell/__init__.py index 75327f3ab..7dc101293 100644 --- a/pyasic/miners/goldshell/__init__.py +++ b/pyasic/miners/goldshell/__init__.py @@ -13,4 +13,24 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from .bfgminer import * +from .bfgminer import ( + GoldshellByte, + GoldshellCK5, + GoldshellHS5, + GoldshellKD5, + GoldshellKDBoxII, + GoldshellKDBoxPro, + GoldshellKDMax, + GoldshellMiniDoge, +) + +__all__ = [ + "GoldshellByte", + "GoldshellCK5", + "GoldshellHS5", + "GoldshellKD5", + "GoldshellKDBoxII", + "GoldshellKDBoxPro", + "GoldshellKDMax", + "GoldshellMiniDoge", +] diff --git a/pyasic/miners/goldshell/bfgminer/X5/__init__.py b/pyasic/miners/goldshell/bfgminer/X5/__init__.py index cb1bbc9cd..767bf09c4 100644 --- a/pyasic/miners/goldshell/bfgminer/X5/__init__.py +++ b/pyasic/miners/goldshell/bfgminer/X5/__init__.py @@ -16,3 +16,5 @@ from .CK5 import GoldshellCK5 from .HS5 import GoldshellHS5 from .KD5 import GoldshellKD5 + +__all__ = ["GoldshellCK5", "GoldshellHS5", "GoldshellKD5"] diff --git a/pyasic/miners/goldshell/bfgminer/XBox/__init__.py b/pyasic/miners/goldshell/bfgminer/XBox/__init__.py index 2f1c68491..61eb4c2db 100644 --- a/pyasic/miners/goldshell/bfgminer/XBox/__init__.py +++ b/pyasic/miners/goldshell/bfgminer/XBox/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .KDBox import GoldshellKDBoxII, GoldshellKDBoxPro + +__all__ = ["GoldshellKDBoxII", "GoldshellKDBoxPro"] diff --git a/pyasic/miners/goldshell/bfgminer/XMax/__init__.py b/pyasic/miners/goldshell/bfgminer/XMax/__init__.py index 1154b4667..b7d9b5404 100644 --- a/pyasic/miners/goldshell/bfgminer/XMax/__init__.py +++ b/pyasic/miners/goldshell/bfgminer/XMax/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .KDMax import GoldshellKDMax + +__all__ = ["GoldshellKDMax"] diff --git a/pyasic/miners/goldshell/bfgminer/__init__.py b/pyasic/miners/goldshell/bfgminer/__init__.py index e2d4daefc..533d6cec1 100644 --- a/pyasic/miners/goldshell/bfgminer/__init__.py +++ b/pyasic/miners/goldshell/bfgminer/__init__.py @@ -13,8 +13,19 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from .byte import * -from .mini_doge import * -from .X5 import * -from .XBox import * -from .XMax import * +from .byte import GoldshellByte +from .mini_doge import GoldshellMiniDoge +from .X5 import GoldshellCK5, GoldshellHS5, GoldshellKD5 +from .XBox import GoldshellKDBoxII, GoldshellKDBoxPro +from .XMax import GoldshellKDMax + +__all__ = [ + "GoldshellByte", + "GoldshellMiniDoge", + "GoldshellCK5", + "GoldshellHS5", + "GoldshellKD5", + "GoldshellKDBoxII", + "GoldshellKDBoxPro", + "GoldshellKDMax", +] diff --git a/pyasic/miners/goldshell/bfgminer/byte/__init__.py b/pyasic/miners/goldshell/bfgminer/byte/__init__.py index 2e5fdda01..a164e1d28 100644 --- a/pyasic/miners/goldshell/bfgminer/byte/__init__.py +++ b/pyasic/miners/goldshell/bfgminer/byte/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .byte import GoldshellByte + +__all__ = ["GoldshellByte"] diff --git a/pyasic/miners/goldshell/bfgminer/byte/byte.py b/pyasic/miners/goldshell/bfgminer/byte/byte.py index e02b1a71e..9c5ee72d7 100644 --- a/pyasic/miners/goldshell/bfgminer/byte/byte.py +++ b/pyasic/miners/goldshell/bfgminer/byte/byte.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.config import MinerConfig from pyasic.data import Fan, MinerData from pyasic.data.boards import HashBoard @@ -88,7 +90,7 @@ class GoldshellByte(GoldshellMiner, Byte): data_locations = GOLDSHELL_BYTE_DATA_LOC supports_shutdown = False supports_power_modes = False - web_devs: dict | None = None + web_devs: dict[str, Any] | None = None async def get_data( self, @@ -140,7 +142,9 @@ async def get_config(self) -> MinerConfig: self.config = MinerConfig.from_goldshell_byte(pools.get("groups", [])) return self.config - async def _get_api_ver(self, web_setting: dict | None = None) -> str | None: + async def _get_api_ver( + self, web_setting: dict[str, Any] | None = None + ) -> str | None: if web_setting is None: try: web_setting = await self.web.setting() @@ -159,7 +163,7 @@ async def _get_api_ver(self, web_setting: dict | None = None) -> str | None: return self.api_ver async def _get_expected_hashrate( - self, rpc_devs: dict | None = None + self, rpc_devs: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_devs is None: try: @@ -192,7 +196,7 @@ async def _get_expected_hashrate( return hash_rate async def _get_hashrate( - self, rpc_devs: dict | None = None + self, rpc_devs: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_devs is None: try: @@ -212,7 +216,9 @@ async def _get_hashrate( return hash_rate - async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: + async def _get_pools( + self, rpc_pools: dict[str, Any] | None = None + ) -> list[PoolMetrics]: if rpc_pools is None: try: rpc_pools = await self.rpc.pools() @@ -241,7 +247,9 @@ async def _get_pools(self, rpc_pools: dict | None = None) -> list[PoolMetrics]: return pools_data async def _get_hashboards( - self, rpc_devs: dict | None = None, rpc_devdetails: dict | None = None + self, + rpc_devs: dict[str, Any] | None = None, + rpc_devdetails: dict[str, Any] | None = None, ) -> list[HashBoard]: if rpc_devs is None: try: @@ -286,7 +294,7 @@ async def _get_hashboards( return hashboards - async def _get_fans(self, rpc_devs: dict | None = None) -> list[Fan]: + async def _get_fans(self, rpc_devs: dict[str, Any] | None = None) -> list[Fan]: if self.expected_fans is None: return [] @@ -313,7 +321,7 @@ async def _get_fans(self, rpc_devs: dict | None = None) -> list[Fan]: return fans - async def _get_uptime(self, web_devs: dict | None = None) -> int | None: + async def _get_uptime(self, web_devs: dict[str, Any] | None = None) -> int | None: if web_devs is None: try: web_devs = await self.web.devs() @@ -331,7 +339,7 @@ async def _get_uptime(self, web_devs: dict | None = None) -> int | None: return None - async def _get_wattage(self, web_devs: dict | None = None) -> int | None: + async def _get_wattage(self, web_devs: dict[str, Any] | None = None) -> int | None: if web_devs is None: try: web_devs = await self.web.devs() diff --git a/pyasic/miners/goldshell/bfgminer/mini_doge/__init__.py b/pyasic/miners/goldshell/bfgminer/mini_doge/__init__.py index 32a2bee70..0282673ec 100644 --- a/pyasic/miners/goldshell/bfgminer/mini_doge/__init__.py +++ b/pyasic/miners/goldshell/bfgminer/mini_doge/__init__.py @@ -14,3 +14,5 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .mini_doge import GoldshellMiniDoge + +__all__ = ["GoldshellMiniDoge"] diff --git a/pyasic/miners/goldshell/bfgminer/mini_doge/mini_doge.py b/pyasic/miners/goldshell/bfgminer/mini_doge/mini_doge.py index 575c2d691..791dcf1c4 100644 --- a/pyasic/miners/goldshell/bfgminer/mini_doge/mini_doge.py +++ b/pyasic/miners/goldshell/bfgminer/mini_doge/mini_doge.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.config import MinerConfig from pyasic.data.boards import HashBoard from pyasic.device.algorithm.hashrate.base import AlgoHashRateType @@ -90,7 +92,7 @@ async def get_config(self) -> MinerConfig | None: # type: ignore[override] return self.config async def _get_expected_hashrate( - self, rpc_devs: dict | None = None + self, rpc_devs: dict[str, Any] | None = None ) -> AlgoHashRateType | None: if rpc_devs is None: try: @@ -110,7 +112,9 @@ async def _get_expected_hashrate( return None async def _get_hashboards( - self, rpc_devs: dict | None = None, rpc_devdetails: dict | None = None + self, + rpc_devs: dict[str, Any] | None = None, + rpc_devdetails: dict[str, Any] | None = None, ) -> list[HashBoard]: if rpc_devs is None: try: @@ -162,7 +166,7 @@ async def _get_hashboards( return hashboards - async def _get_uptime(self, web_devs: dict | None = None) -> int | None: + async def _get_uptime(self, web_devs: dict[str, Any] | None = None) -> int | None: if web_devs is None: try: web_devs = await self.web.devs() diff --git a/pyasic/miners/hammer/__init__.py b/pyasic/miners/hammer/__init__.py index d78a4d0ed..ae00ef326 100644 --- a/pyasic/miners/hammer/__init__.py +++ b/pyasic/miners/hammer/__init__.py @@ -1 +1,5 @@ -from .blackminer import * +from .blackminer import HammerD10 + +__all__ = [ + "HammerD10", +] diff --git a/pyasic/miners/hammer/blackminer/DX/D10.py b/pyasic/miners/hammer/blackminer/DX/D10.py index 7c48badcb..de2bcc57c 100644 --- a/pyasic/miners/hammer/blackminer/DX/D10.py +++ b/pyasic/miners/hammer/blackminer/DX/D10.py @@ -1,7 +1,7 @@ from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit from pyasic.device.algorithm.scrypt import ScryptHashRate from pyasic.miners.backends import BlackMiner -from pyasic.miners.device.models import D10 +from pyasic.miners.device.models.hammer.DX.D10 import D10 class HammerD10(BlackMiner, D10): diff --git a/pyasic/miners/hammer/blackminer/DX/__init__.py b/pyasic/miners/hammer/blackminer/DX/__init__.py index ad8f43f88..969cc9cdd 100644 --- a/pyasic/miners/hammer/blackminer/DX/__init__.py +++ b/pyasic/miners/hammer/blackminer/DX/__init__.py @@ -1 +1,3 @@ from .D10 import HammerD10 + +__all__ = ["HammerD10"] diff --git a/pyasic/miners/hammer/blackminer/__init__.py b/pyasic/miners/hammer/blackminer/__init__.py index d9f82972e..e0ecd3ca1 100644 --- a/pyasic/miners/hammer/blackminer/__init__.py +++ b/pyasic/miners/hammer/blackminer/__init__.py @@ -1 +1,3 @@ -from .DX import * +from .DX import HammerD10 + +__all__ = ["HammerD10"] diff --git a/pyasic/miners/iceriver/iceminer/ALX/AL3.py b/pyasic/miners/iceriver/iceminer/ALX/AL3.py index 953a65f68..46ddbad67 100644 --- a/pyasic/miners/iceriver/iceminer/ALX/AL3.py +++ b/pyasic/miners/iceriver/iceminer/ALX/AL3.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.iceriver import IceRiver -from pyasic.miners.device.models.iceriver import AL3 +from pyasic.miners.device.models.iceriver.ALX.AL3 import AL3 class IceRiverAL3(IceRiver, AL3): diff --git a/pyasic/miners/iceriver/iceminer/ALX/__init__.py b/pyasic/miners/iceriver/iceminer/ALX/__init__.py index 875297855..60e3f7d1a 100644 --- a/pyasic/miners/iceriver/iceminer/ALX/__init__.py +++ b/pyasic/miners/iceriver/iceminer/ALX/__init__.py @@ -1 +1,5 @@ from .AL3 import IceRiverAL3 + +__all__ = [ + "IceRiverAL3", +] diff --git a/pyasic/miners/iceriver/iceminer/KSX/KS0.py b/pyasic/miners/iceriver/iceminer/KSX/KS0.py index 8d1b17693..808efaac2 100644 --- a/pyasic/miners/iceriver/iceminer/KSX/KS0.py +++ b/pyasic/miners/iceriver/iceminer/KSX/KS0.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.iceriver import IceRiver -from pyasic.miners.device.models import KS0 +from pyasic.miners.device.models.iceriver.KSX.KS0 import KS0 class IceRiverKS0(IceRiver, KS0): diff --git a/pyasic/miners/iceriver/iceminer/KSX/KS1.py b/pyasic/miners/iceriver/iceminer/KSX/KS1.py index 3beb895f4..db8cc838e 100644 --- a/pyasic/miners/iceriver/iceminer/KSX/KS1.py +++ b/pyasic/miners/iceriver/iceminer/KSX/KS1.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.iceriver import IceRiver -from pyasic.miners.device.models import KS1 +from pyasic.miners.device.models.iceriver.KSX.KS1 import KS1 class IceRiverKS1(IceRiver, KS1): diff --git a/pyasic/miners/iceriver/iceminer/KSX/KS2.py b/pyasic/miners/iceriver/iceminer/KSX/KS2.py index 629f80951..99d7465ea 100644 --- a/pyasic/miners/iceriver/iceminer/KSX/KS2.py +++ b/pyasic/miners/iceriver/iceminer/KSX/KS2.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.iceriver import IceRiver -from pyasic.miners.device.models import KS2 +from pyasic.miners.device.models.iceriver.KSX.KS2 import KS2 class IceRiverKS2(IceRiver, KS2): diff --git a/pyasic/miners/iceriver/iceminer/KSX/KS3.py b/pyasic/miners/iceriver/iceminer/KSX/KS3.py index 24df961ae..2cf7c5ef4 100644 --- a/pyasic/miners/iceriver/iceminer/KSX/KS3.py +++ b/pyasic/miners/iceriver/iceminer/KSX/KS3.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.iceriver import IceRiver -from pyasic.miners.device.models.iceriver import KS3, KS3L, KS3M +from pyasic.miners.device.models.iceriver.KSX.KS3 import KS3, KS3L, KS3M class IceRiverKS3(IceRiver, KS3): diff --git a/pyasic/miners/iceriver/iceminer/KSX/KS5.py b/pyasic/miners/iceriver/iceminer/KSX/KS5.py index 5bb2fc1e7..4947f503e 100644 --- a/pyasic/miners/iceriver/iceminer/KSX/KS5.py +++ b/pyasic/miners/iceriver/iceminer/KSX/KS5.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.iceriver import IceRiver -from pyasic.miners.device.models.iceriver import KS5, KS5L, KS5M +from pyasic.miners.device.models.iceriver.KSX.KS5 import KS5, KS5L, KS5M class IceRiverKS5(IceRiver, KS5): diff --git a/pyasic/miners/iceriver/iceminer/KSX/__init__.py b/pyasic/miners/iceriver/iceminer/KSX/__init__.py index be5d3333e..3be26e340 100644 --- a/pyasic/miners/iceriver/iceminer/KSX/__init__.py +++ b/pyasic/miners/iceriver/iceminer/KSX/__init__.py @@ -3,3 +3,15 @@ from .KS2 import IceRiverKS2 from .KS3 import IceRiverKS3, IceRiverKS3L, IceRiverKS3M from .KS5 import IceRiverKS5, IceRiverKS5L, IceRiverKS5M + +__all__ = [ + "IceRiverKS0", + "IceRiverKS1", + "IceRiverKS2", + "IceRiverKS3", + "IceRiverKS3L", + "IceRiverKS3M", + "IceRiverKS5", + "IceRiverKS5L", + "IceRiverKS5M", +] diff --git a/pyasic/miners/innosilicon/__init__.py b/pyasic/miners/innosilicon/__init__.py index 5d1891a7a..0785fda95 100644 --- a/pyasic/miners/innosilicon/__init__.py +++ b/pyasic/miners/innosilicon/__init__.py @@ -14,4 +14,16 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .cgminer import * +from .cgminer import ( + InnosiliconA10X, + InnosiliconA11, + InnosiliconA11MX, + InnosiliconT3HPlus, +) + +__all__ = [ + "InnosiliconA10X", + "InnosiliconA11", + "InnosiliconA11MX", + "InnosiliconT3HPlus", +] diff --git a/pyasic/miners/innosilicon/cgminer/A10X/__init__.py b/pyasic/miners/innosilicon/cgminer/A10X/__init__.py index e18c86d73..0e21a91bb 100644 --- a/pyasic/miners/innosilicon/cgminer/A10X/__init__.py +++ b/pyasic/miners/innosilicon/cgminer/A10X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .A10X import InnosiliconA10X + +__all__ = ["InnosiliconA10X"] diff --git a/pyasic/miners/innosilicon/cgminer/A11X/__init__.py b/pyasic/miners/innosilicon/cgminer/A11X/__init__.py index e967f018d..4022e7230 100644 --- a/pyasic/miners/innosilicon/cgminer/A11X/__init__.py +++ b/pyasic/miners/innosilicon/cgminer/A11X/__init__.py @@ -1,2 +1,4 @@ from .A11 import InnosiliconA11 from .A11M import InnosiliconA11MX + +__all__ = ["InnosiliconA11", "InnosiliconA11MX"] diff --git a/pyasic/miners/innosilicon/cgminer/T3X/T3H.py b/pyasic/miners/innosilicon/cgminer/T3X/T3H.py index e47e51c48..cd86ad862 100644 --- a/pyasic/miners/innosilicon/cgminer/T3X/T3H.py +++ b/pyasic/miners/innosilicon/cgminer/T3X/T3H.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends.innosilicon import Innosilicon -from pyasic.miners.device.models import T3HPlus +from pyasic.miners.device.models.innosilicon.T3X.T3H import T3HPlus class InnosiliconT3HPlus(Innosilicon, T3HPlus): diff --git a/pyasic/miners/innosilicon/cgminer/T3X/__init__.py b/pyasic/miners/innosilicon/cgminer/T3X/__init__.py index ebfb03378..205676ee7 100644 --- a/pyasic/miners/innosilicon/cgminer/T3X/__init__.py +++ b/pyasic/miners/innosilicon/cgminer/T3X/__init__.py @@ -15,3 +15,5 @@ # ------------------------------------------------------------------------------ from .T3H import InnosiliconT3HPlus + +__all__ = ["InnosiliconT3HPlus"] diff --git a/pyasic/miners/innosilicon/cgminer/__init__.py b/pyasic/miners/innosilicon/cgminer/__init__.py index 186ea0758..d4f1cc23d 100644 --- a/pyasic/miners/innosilicon/cgminer/__init__.py +++ b/pyasic/miners/innosilicon/cgminer/__init__.py @@ -14,6 +14,13 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .A10X import * -from .A11X import * -from .T3X import * +from .A10X import InnosiliconA10X +from .A11X import InnosiliconA11, InnosiliconA11MX +from .T3X import InnosiliconT3HPlus + +__all__ = [ + "InnosiliconA10X", + "InnosiliconA11", + "InnosiliconA11MX", + "InnosiliconT3HPlus", +] diff --git a/pyasic/miners/listener.py b/pyasic/miners/listener.py index 56f8561d1..1b53c2b7b 100644 --- a/pyasic/miners/listener.py +++ b/pyasic/miners/listener.py @@ -15,15 +15,17 @@ # ------------------------------------------------------------------------------ import asyncio +from collections.abc import AsyncGenerator +from typing import Any -class MinerListenerProtocol(asyncio.Protocol): - def __init__(self): - self.responses = {} - self.transport = None - self.new_miner = None +class MinerListenerProtocol(asyncio.DatagramProtocol): + def __init__(self) -> None: + self.responses: dict[str, Any] = {} + self.transport: asyncio.DatagramTransport | None = None + self.new_miner: dict[str, str] | None = None - async def get_new_miner(self): + async def get_new_miner(self) -> dict[str, str] | None: try: while self.new_miner is None: await asyncio.sleep(0) @@ -31,10 +33,10 @@ async def get_new_miner(self): finally: self.new_miner = None - def connection_made(self, transport): + def connection_made(self, transport: asyncio.DatagramTransport) -> None: # type: ignore[override] self.transport = transport - def datagram_received(self, data, _addr): + def datagram_received(self, data: bytes, _addr: tuple[str, int]) -> None: if data == b"OK\x00\x00\x00\x00\x00\x00\x00\x00": return m = data.decode() @@ -49,17 +51,17 @@ def datagram_received(self, data, _addr): self.new_miner = {"IP": ip, "MAC": mac.upper()} - def connection_lost(self, _): + def connection_lost(self, _: Exception | None) -> None: pass class MinerListener: - def __init__(self, bind_addr: str = "0.0.0.0"): + def __init__(self, bind_addr: str = "0.0.0.0") -> None: self.found_miners: list[dict[str, str]] = [] self.stop = asyncio.Event() self.bind_addr = bind_addr - async def listen(self): + async def listen(self) -> AsyncGenerator[dict[str, str], None]: loop = asyncio.get_running_loop() transport_14235, protocol_14235 = await loop.create_datagram_endpoint( @@ -77,11 +79,13 @@ async def listen(self): await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) for t in tasks: if t.done(): - yield t.result() + result = t.result() + if result is not None: + yield result finally: transport_14235.close() transport_8888.close() - async def cancel(self): - self.stop = True + async def cancel(self) -> None: + self.stop.set() diff --git a/pyasic/miners/luckyminer/__init__.py b/pyasic/miners/luckyminer/__init__.py index 6068242d7..8ec04029f 100644 --- a/pyasic/miners/luckyminer/__init__.py +++ b/pyasic/miners/luckyminer/__init__.py @@ -1 +1,3 @@ -from .espminer import * +from .espminer import LuckyMinerLV07, LuckyMinerLV08 + +__all__ = ["LuckyMinerLV07", "LuckyMinerLV08"] diff --git a/pyasic/miners/luckyminer/espminer/LV/LV07.py b/pyasic/miners/luckyminer/espminer/LV/LV07.py index 4ad63fcec..45b36dddd 100644 --- a/pyasic/miners/luckyminer/espminer/LV/LV07.py +++ b/pyasic/miners/luckyminer/espminer/LV/LV07.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.luckyminer import LuckyMiner -from pyasic.miners.device.models.luckyminer import LV07 +from pyasic.miners.device.models.luckyminer.LV.LV07 import LV07 class LuckyMinerLV07(LuckyMiner, LV07): diff --git a/pyasic/miners/luckyminer/espminer/LV/LV08.py b/pyasic/miners/luckyminer/espminer/LV/LV08.py index c83f91d8f..ebd65033d 100644 --- a/pyasic/miners/luckyminer/espminer/LV/LV08.py +++ b/pyasic/miners/luckyminer/espminer/LV/LV08.py @@ -1,5 +1,5 @@ from pyasic.miners.backends.luckyminer import LuckyMiner -from pyasic.miners.device.models.luckyminer import LV08 +from pyasic.miners.device.models.luckyminer.LV.LV08 import LV08 class LuckyMinerLV08(LuckyMiner, LV08): diff --git a/pyasic/miners/luckyminer/espminer/LV/__init__.py b/pyasic/miners/luckyminer/espminer/LV/__init__.py index ee0a251aa..dde3fd51c 100644 --- a/pyasic/miners/luckyminer/espminer/LV/__init__.py +++ b/pyasic/miners/luckyminer/espminer/LV/__init__.py @@ -1,2 +1,4 @@ from .LV07 import LuckyMinerLV07 from .LV08 import LuckyMinerLV08 + +__all__ = ["LuckyMinerLV07", "LuckyMinerLV08"] diff --git a/pyasic/miners/luckyminer/espminer/__init__.py b/pyasic/miners/luckyminer/espminer/__init__.py index abeadffc0..f0c7f319d 100644 --- a/pyasic/miners/luckyminer/espminer/__init__.py +++ b/pyasic/miners/luckyminer/espminer/__init__.py @@ -1 +1,3 @@ -from .LV import * +from .LV import LuckyMinerLV07, LuckyMinerLV08 + +__all__ = ["LuckyMinerLV07", "LuckyMinerLV08"] diff --git a/pyasic/miners/volcminer/__init__.py b/pyasic/miners/volcminer/__init__.py index d78a4d0ed..db5ae228c 100644 --- a/pyasic/miners/volcminer/__init__.py +++ b/pyasic/miners/volcminer/__init__.py @@ -1 +1,3 @@ -from .blackminer import * +from .blackminer import VolcMinerD1 + +__all__ = ["VolcMinerD1"] diff --git a/pyasic/miners/volcminer/blackminer/DX/D1.py b/pyasic/miners/volcminer/blackminer/DX/D1.py index b3b7e4b11..91879e943 100644 --- a/pyasic/miners/volcminer/blackminer/DX/D1.py +++ b/pyasic/miners/volcminer/blackminer/DX/D1.py @@ -1,7 +1,7 @@ from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit from pyasic.device.algorithm.scrypt import ScryptHashRate from pyasic.miners.backends import BlackMiner -from pyasic.miners.device.models import D1 +from pyasic.miners.device.models.volcminer.DX.D1 import D1 class VolcMinerD1(BlackMiner, D1): diff --git a/pyasic/miners/volcminer/blackminer/DX/__init__.py b/pyasic/miners/volcminer/blackminer/DX/__init__.py index 477660e4b..79b8172c7 100644 --- a/pyasic/miners/volcminer/blackminer/DX/__init__.py +++ b/pyasic/miners/volcminer/blackminer/DX/__init__.py @@ -1 +1,3 @@ from .D1 import VolcMinerD1 + +__all__ = ["VolcMinerD1"] diff --git a/pyasic/miners/volcminer/blackminer/__init__.py b/pyasic/miners/volcminer/blackminer/__init__.py index d9f82972e..86cd049ac 100644 --- a/pyasic/miners/volcminer/blackminer/__init__.py +++ b/pyasic/miners/volcminer/blackminer/__init__.py @@ -1 +1,3 @@ -from .DX import * +from .DX import VolcMinerD1 + +__all__ = ["VolcMinerD1"] diff --git a/pyasic/miners/whatsminer/btminer/M2X/M20.py b/pyasic/miners/whatsminer/btminer/M2X/M20.py index 432787d4e..a72510a0f 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M20.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M20.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M20V10 +from pyasic.miners.device.models.whatsminer.M2X import M20V10 class BTMinerM20V10(M2X, M20V10): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M20P.py b/pyasic/miners/whatsminer/btminer/M2X/M20P.py index cc39c78b7..591b1a17f 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M20P.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M20P.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M20PV10 +from pyasic.miners.device.models.whatsminer.M2X import M20PV10 class BTMinerM20PV10(M2X, M20PV10): pass -from pyasic.miners.device.models import M20PV30 +from pyasic.miners.device.models.whatsminer.M2X import M20PV30 class BTMinerM20PV30(M2X, M20PV30): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M20S.py b/pyasic/miners/whatsminer/btminer/M2X/M20S.py index 136905f6d..2f596db47 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M20S.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M20S.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M20SV10 +from pyasic.miners.device.models.whatsminer.M2X import M20SV10 class BTMinerM20SV10(M2X, M20SV10): pass -from pyasic.miners.device.models import M20SV20 +from pyasic.miners.device.models.whatsminer.M2X import M20SV20 class BTMinerM20SV20(M2X, M20SV20): pass -from pyasic.miners.device.models import M20SV30 +from pyasic.miners.device.models.whatsminer.M2X import M20SV30 class BTMinerM20SV30(M2X, M20SV30): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M20S_Plus.py b/pyasic/miners/whatsminer/btminer/M2X/M20S_Plus.py index 732e439cb..bca0e8721 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M20S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M20S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M20SPlusV30 +from pyasic.miners.device.models.whatsminer.M2X import M20SPlusV30 class BTMinerM20SPlusV30(M2X, M20SPlusV30): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M21.py b/pyasic/miners/whatsminer/btminer/M2X/M21.py index f9de7777c..1dafca606 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M21.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M21.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M21V10 +from pyasic.miners.device.models.whatsminer.M2X import M21V10 class BTMinerM21V10(M2X, M21V10): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M21S.py b/pyasic/miners/whatsminer/btminer/M2X/M21S.py index c2699aa47..d60ad724e 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M21S.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M21S.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M21SV20 +from pyasic.miners.device.models.whatsminer.M2X import M21SV20 class BTMinerM21SV20(M2X, M21SV20): pass -from pyasic.miners.device.models import M21SV60 +from pyasic.miners.device.models.whatsminer.M2X import M21SV60 class BTMinerM21SV60(M2X, M21SV60): pass -from pyasic.miners.device.models import M21SV70 +from pyasic.miners.device.models.whatsminer.M2X import M21SV70 class BTMinerM21SV70(M2X, M21SV70): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M21S_Plus.py b/pyasic/miners/whatsminer/btminer/M2X/M21S_Plus.py index 0af320296..f2de876fe 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M21S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M21S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M21SPlusV20 +from pyasic.miners.device.models.whatsminer.M2X import M21SPlusV20 class BTMinerM21SPlusV20(M2X, M21SPlusV20): diff --git a/pyasic/miners/whatsminer/btminer/M2X/M29.py b/pyasic/miners/whatsminer/btminer/M2X/M29.py index 2819b1f0d..f56b0155c 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/M29.py +++ b/pyasic/miners/whatsminer/btminer/M2X/M29.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M2X -from pyasic.miners.device.models import M29V10 +from pyasic.miners.device.models.whatsminer.M2X import M29V10 class BTMinerM29V10(M2X, M29V10): diff --git a/pyasic/miners/whatsminer/btminer/M2X/__init__.py b/pyasic/miners/whatsminer/btminer/M2X/__init__.py index f889a0ec4..684dfeb9a 100644 --- a/pyasic/miners/whatsminer/btminer/M2X/__init__.py +++ b/pyasic/miners/whatsminer/btminer/M2X/__init__.py @@ -6,3 +6,19 @@ from .M21S import BTMinerM21SV20, BTMinerM21SV60, BTMinerM21SV70 from .M21S_Plus import BTMinerM21SPlusV20 from .M29 import BTMinerM29V10 + +__all__ = [ + "BTMinerM20V10", + "BTMinerM20PV10", + "BTMinerM20PV30", + "BTMinerM20SV10", + "BTMinerM20SV20", + "BTMinerM20SV30", + "BTMinerM20SPlusV30", + "BTMinerM21V10", + "BTMinerM21SV20", + "BTMinerM21SV60", + "BTMinerM21SV70", + "BTMinerM21SPlusV20", + "BTMinerM29V10", +] diff --git a/pyasic/miners/whatsminer/btminer/M3X/M30.py b/pyasic/miners/whatsminer/btminer/M3X/M30.py index 1fb5be01f..25bbd9395 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M30.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M30.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M30V10 +from pyasic.miners.device.models.whatsminer.M3X import M30V10 class BTMinerM30V10(M3X, M30V10): pass -from pyasic.miners.device.models import M30V20 +from pyasic.miners.device.models.whatsminer.M3X import M30V20 class BTMinerM30V20(M3X, M30V20): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M30K.py b/pyasic/miners/whatsminer/btminer/M3X/M30K.py index 31c02edf4..1a44c66c8 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M30K.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M30K.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M30KV10 +from pyasic.miners.device.models.whatsminer.M3X import M30KV10 class BTMinerM30KV10(M3X, M30KV10): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M30L.py b/pyasic/miners/whatsminer/btminer/M3X/M30L.py index 9f54e890a..1b39c4b7f 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M30L.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M30L.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M30LV10 +from pyasic.miners.device.models.whatsminer.M3X import M30LV10 class BTMinerM30LV10(M3X, M30LV10): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M30S.py b/pyasic/miners/whatsminer/btminer/M3X/M30S.py index 2e01a6336..c653b8fb4 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M30S.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M30S.py @@ -1,208 +1,208 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M30SV10 +from pyasic.miners.device.models.whatsminer.M3X import M30SV10 class BTMinerM30SV10(M3X, M30SV10): pass -from pyasic.miners.device.models import M30SV20 +from pyasic.miners.device.models.whatsminer.M3X import M30SV20 class BTMinerM30SV20(M3X, M30SV20): pass -from pyasic.miners.device.models import M30SV30 +from pyasic.miners.device.models.whatsminer.M3X import M30SV30 class BTMinerM30SV30(M3X, M30SV30): pass -from pyasic.miners.device.models import M30SV40 +from pyasic.miners.device.models.whatsminer.M3X import M30SV40 class BTMinerM30SV40(M3X, M30SV40): pass -from pyasic.miners.device.models import M30SV50 +from pyasic.miners.device.models.whatsminer.M3X import M30SV50 class BTMinerM30SV50(M3X, M30SV50): pass -from pyasic.miners.device.models import M30SV60 +from pyasic.miners.device.models.whatsminer.M3X import M30SV60 class BTMinerM30SV60(M3X, M30SV60): pass -from pyasic.miners.device.models import M30SV70 +from pyasic.miners.device.models.whatsminer.M3X import M30SV70 class BTMinerM30SV70(M3X, M30SV70): pass -from pyasic.miners.device.models import M30SV80 +from pyasic.miners.device.models.whatsminer.M3X import M30SV80 class BTMinerM30SV80(M3X, M30SV80): pass -from pyasic.miners.device.models import M30SVE10 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE10 class BTMinerM30SVE10(M3X, M30SVE10): pass -from pyasic.miners.device.models import M30SVE20 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE20 class BTMinerM30SVE20(M3X, M30SVE20): pass -from pyasic.miners.device.models import M30SVE30 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE30 class BTMinerM30SVE30(M3X, M30SVE30): pass -from pyasic.miners.device.models import M30SVE40 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE40 class BTMinerM30SVE40(M3X, M30SVE40): pass -from pyasic.miners.device.models import M30SVE50 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE50 class BTMinerM30SVE50(M3X, M30SVE50): pass -from pyasic.miners.device.models import M30SVE60 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE60 class BTMinerM30SVE60(M3X, M30SVE60): pass -from pyasic.miners.device.models import M30SVE70 +from pyasic.miners.device.models.whatsminer.M3X import M30SVE70 class BTMinerM30SVE70(M3X, M30SVE70): pass -from pyasic.miners.device.models import M30SVF10 +from pyasic.miners.device.models.whatsminer.M3X import M30SVF10 class BTMinerM30SVF10(M3X, M30SVF10): pass -from pyasic.miners.device.models import M30SVF20 +from pyasic.miners.device.models.whatsminer.M3X import M30SVF20 class BTMinerM30SVF20(M3X, M30SVF20): pass -from pyasic.miners.device.models import M30SVF30 +from pyasic.miners.device.models.whatsminer.M3X import M30SVF30 class BTMinerM30SVF30(M3X, M30SVF30): pass -from pyasic.miners.device.models import M30SVG10 +from pyasic.miners.device.models.whatsminer.M3X import M30SVG10 class BTMinerM30SVG10(M3X, M30SVG10): pass -from pyasic.miners.device.models import M30SVG20 +from pyasic.miners.device.models.whatsminer.M3X import M30SVG20 class BTMinerM30SVG20(M3X, M30SVG20): pass -from pyasic.miners.device.models import M30SVG30 +from pyasic.miners.device.models.whatsminer.M3X import M30SVG30 class BTMinerM30SVG30(M3X, M30SVG30): pass -from pyasic.miners.device.models import M30SVG40 +from pyasic.miners.device.models.whatsminer.M3X import M30SVG40 class BTMinerM30SVG40(M3X, M30SVG40): pass -from pyasic.miners.device.models import M30SVH10 +from pyasic.miners.device.models.whatsminer.M3X import M30SVH10 class BTMinerM30SVH10(M3X, M30SVH10): pass -from pyasic.miners.device.models import M30SVH20 +from pyasic.miners.device.models.whatsminer.M3X import M30SVH20 class BTMinerM30SVH20(M3X, M30SVH20): pass -from pyasic.miners.device.models import M30SVH30 +from pyasic.miners.device.models.whatsminer.M3X import M30SVH30 class BTMinerM30SVH30(M3X, M30SVH30): pass -from pyasic.miners.device.models import M30SVH40 +from pyasic.miners.device.models.whatsminer.M3X import M30SVH40 class BTMinerM30SVH40(M3X, M30SVH40): pass -from pyasic.miners.device.models import M30SVH50 +from pyasic.miners.device.models.whatsminer.M3X import M30SVH50 class BTMinerM30SVH50(M3X, M30SVH50): pass -from pyasic.miners.device.models import M30SVH60 +from pyasic.miners.device.models.whatsminer.M3X import M30SVH60 class BTMinerM30SVH60(M3X, M30SVH60): pass -from pyasic.miners.device.models import M30SVI20 +from pyasic.miners.device.models.whatsminer.M3X import M30SVI20 class BTMinerM30SVI20(M3X, M30SVI20): pass -from pyasic.miners.device.models import M30SVJ30 +from pyasic.miners.device.models.whatsminer.M3X import M30SVJ30 class BTMinerM30SVJ30(M3X, M30SVJ30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus.py index f637a888d..d6eebaccd 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus.py @@ -1,243 +1,243 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M30SPlusV10 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV10 class BTMinerM30SPlusV10(M3X, M30SPlusV10): pass -from pyasic.miners.device.models import M30SPlusV100 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV100 class BTMinerM30SPlusV100(M3X, M30SPlusV100): pass -from pyasic.miners.device.models import M30SPlusV20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV20 class BTMinerM30SPlusV20(M3X, M30SPlusV20): pass -from pyasic.miners.device.models import M30SPlusV30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV30 class BTMinerM30SPlusV30(M3X, M30SPlusV30): pass -from pyasic.miners.device.models import M30SPlusV40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV40 class BTMinerM30SPlusV40(M3X, M30SPlusV40): pass -from pyasic.miners.device.models import M30SPlusV50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV50 class BTMinerM30SPlusV50(M3X, M30SPlusV50): pass -from pyasic.miners.device.models import M30SPlusV60 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV60 class BTMinerM30SPlusV60(M3X, M30SPlusV60): pass -from pyasic.miners.device.models import M30SPlusV70 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV70 class BTMinerM30SPlusV70(M3X, M30SPlusV70): pass -from pyasic.miners.device.models import M30SPlusV80 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV80 class BTMinerM30SPlusV80(M3X, M30SPlusV80): pass -from pyasic.miners.device.models import M30SPlusV90 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusV90 class BTMinerM30SPlusV90(M3X, M30SPlusV90): pass -from pyasic.miners.device.models import M30SPlusVE100 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE100 class BTMinerM30SPlusVE100(M3X, M30SPlusVE100): pass -from pyasic.miners.device.models import M30SPlusVE30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE30 class BTMinerM30SPlusVE30(M3X, M30SPlusVE30): pass -from pyasic.miners.device.models import M30SPlusVE40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE40 class BTMinerM30SPlusVE40(M3X, M30SPlusVE40): pass -from pyasic.miners.device.models import M30SPlusVE50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE50 class BTMinerM30SPlusVE50(M3X, M30SPlusVE50): pass -from pyasic.miners.device.models import M30SPlusVE60 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE60 class BTMinerM30SPlusVE60(M3X, M30SPlusVE60): pass -from pyasic.miners.device.models import M30SPlusVE70 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE70 class BTMinerM30SPlusVE70(M3X, M30SPlusVE70): pass -from pyasic.miners.device.models import M30SPlusVE80 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE80 class BTMinerM30SPlusVE80(M3X, M30SPlusVE80): pass -from pyasic.miners.device.models import M30SPlusVE90 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVE90 class BTMinerM30SPlusVE90(M3X, M30SPlusVE90): pass -from pyasic.miners.device.models import M30SPlusVF20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVF20 class BTMinerM30SPlusVF20(M3X, M30SPlusVF20): pass -from pyasic.miners.device.models import M30SPlusVF30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVF30 class BTMinerM30SPlusVF30(M3X, M30SPlusVF30): pass -from pyasic.miners.device.models import M30SPlusVG20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVG20 class BTMinerM30SPlusVG20(M3X, M30SPlusVG20): pass -from pyasic.miners.device.models import M30SPlusVG30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVG30 class BTMinerM30SPlusVG30(M3X, M30SPlusVG30): pass -from pyasic.miners.device.models import M30SPlusVG40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVG40 class BTMinerM30SPlusVG40(M3X, M30SPlusVG40): pass -from pyasic.miners.device.models import M30SPlusVG50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVG50 class BTMinerM30SPlusVG50(M3X, M30SPlusVG50): pass -from pyasic.miners.device.models import M30SPlusVG60 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVG60 class BTMinerM30SPlusVG60(M3X, M30SPlusVG60): pass -from pyasic.miners.device.models import M30SPlusVH10 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH10 class BTMinerM30SPlusVH10(M3X, M30SPlusVH10): pass -from pyasic.miners.device.models import M30SPlusVH20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH20 class BTMinerM30SPlusVH20(M3X, M30SPlusVH20): pass -from pyasic.miners.device.models import M30SPlusVH30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH30 class BTMinerM30SPlusVH30(M3X, M30SPlusVH30): pass -from pyasic.miners.device.models import M30SPlusVH40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH40 class BTMinerM30SPlusVH40(M3X, M30SPlusVH40): pass -from pyasic.miners.device.models import M30SPlusVH50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH50 class BTMinerM30SPlusVH50(M3X, M30SPlusVH50): pass -from pyasic.miners.device.models import M30SPlusVH60 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH60 class BTMinerM30SPlusVH60(M3X, M30SPlusVH60): pass -from pyasic.miners.device.models import M30SPlusVH70 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVH70 class BTMinerM30SPlusVH70(M3X, M30SPlusVH70): pass -from pyasic.miners.device.models import M30SPlusVI30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVI30 class BTMinerM30SPlusVI30(M3X, M30SPlusVI30): pass -from pyasic.miners.device.models import M30SPlusVJ30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVJ30 class BTMinerM30SPlusVJ30(M3X, M30SPlusVJ30): pass -from pyasic.miners.device.models import M30SPlusVJ40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusVJ40 class BTMinerM30SPlusVJ40(M3X, M30SPlusVJ40): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus_Plus.py index cf5b43121..4ad697d3b 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M30S_Plus_Plus.py @@ -1,194 +1,194 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M30SPlusPlusV10 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusV10 class BTMinerM30SPlusPlusV10(M3X, M30SPlusPlusV10): pass -from pyasic.miners.device.models import M30SPlusPlusV20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusV20 class BTMinerM30SPlusPlusV20(M3X, M30SPlusPlusV20): pass -from pyasic.miners.device.models import M30SPlusPlusVE30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVE30 class BTMinerM30SPlusPlusVE30(M3X, M30SPlusPlusVE30): pass -from pyasic.miners.device.models import M30SPlusPlusVE40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVE40 class BTMinerM30SPlusPlusVE40(M3X, M30SPlusPlusVE40): pass -from pyasic.miners.device.models import M30SPlusPlusVE50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVE50 class BTMinerM30SPlusPlusVE50(M3X, M30SPlusPlusVE50): pass -from pyasic.miners.device.models import M30SPlusPlusVF40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVF40 class BTMinerM30SPlusPlusVF40(M3X, M30SPlusPlusVF40): pass -from pyasic.miners.device.models import M30SPlusPlusVG30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVG30 class BTMinerM30SPlusPlusVG30(M3X, M30SPlusPlusVG30): pass -from pyasic.miners.device.models import M30SPlusPlusVG40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVG40 class BTMinerM30SPlusPlusVG40(M3X, M30SPlusPlusVG40): pass -from pyasic.miners.device.models import M30SPlusPlusVG50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVG50 class BTMinerM30SPlusPlusVG50(M3X, M30SPlusPlusVG50): pass -from pyasic.miners.device.models import M30SPlusPlusVH10 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH10 class BTMinerM30SPlusPlusVH10(M3X, M30SPlusPlusVH10): pass -from pyasic.miners.device.models import M30SPlusPlusVH100 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH100 class BTMinerM30SPlusPlusVH100(M3X, M30SPlusPlusVH100): pass -from pyasic.miners.device.models import M30SPlusPlusVH110 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH110 class BTMinerM30SPlusPlusVH110(M3X, M30SPlusPlusVH110): pass -from pyasic.miners.device.models import M30SPlusPlusVH20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH20 class BTMinerM30SPlusPlusVH20(M3X, M30SPlusPlusVH20): pass -from pyasic.miners.device.models import M30SPlusPlusVH30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH30 class BTMinerM30SPlusPlusVH30(M3X, M30SPlusPlusVH30): pass -from pyasic.miners.device.models import M30SPlusPlusVH40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH40 class BTMinerM30SPlusPlusVH40(M3X, M30SPlusPlusVH40): pass -from pyasic.miners.device.models import M30SPlusPlusVH50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH50 class BTMinerM30SPlusPlusVH50(M3X, M30SPlusPlusVH50): pass -from pyasic.miners.device.models import M30SPlusPlusVH60 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH60 class BTMinerM30SPlusPlusVH60(M3X, M30SPlusPlusVH60): pass -from pyasic.miners.device.models import M30SPlusPlusVH70 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH70 class BTMinerM30SPlusPlusVH70(M3X, M30SPlusPlusVH70): pass -from pyasic.miners.device.models import M30SPlusPlusVH80 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH80 class BTMinerM30SPlusPlusVH80(M3X, M30SPlusPlusVH80): pass -from pyasic.miners.device.models import M30SPlusPlusVH90 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVH90 class BTMinerM30SPlusPlusVH90(M3X, M30SPlusPlusVH90): pass -from pyasic.miners.device.models import M30SPlusPlusVI30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVI30 class BTMinerM30SPlusPlusVI30(M3X, M30SPlusPlusVI30): pass -from pyasic.miners.device.models import M30SPlusPlusVJ20 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVJ20 class BTMinerM30SPlusPlusVJ20(M3X, M30SPlusPlusVJ20): pass -from pyasic.miners.device.models import M30SPlusPlusVJ30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVJ30 class BTMinerM30SPlusPlusVJ30(M3X, M30SPlusPlusVJ30): pass -from pyasic.miners.device.models import M30SPlusPlusVJ50 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVJ50 class BTMinerM30SPlusPlusVJ50(M3X, M30SPlusPlusVJ50): pass -from pyasic.miners.device.models import M30SPlusPlusVJ60 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVJ60 class BTMinerM30SPlusPlusVJ60(M3X, M30SPlusPlusVJ60): pass -from pyasic.miners.device.models import M30SPlusPlusVJ70 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVJ70 class BTMinerM30SPlusPlusVJ70(M3X, M30SPlusPlusVJ70): pass -from pyasic.miners.device.models import M30SPlusPlusVK30 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVK30 class BTMinerM30SPlusPlusVK30(M3X, M30SPlusPlusVK30): pass -from pyasic.miners.device.models import M30SPlusPlusVK40 +from pyasic.miners.device.models.whatsminer.M3X import M30SPlusPlusVK40 class BTMinerM30SPlusPlusVK40(M3X, M30SPlusPlusVK40): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M31.py b/pyasic/miners/whatsminer/btminer/M3X/M31.py index e20574d8e..25b2d28a2 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M31.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M31.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M31V10 +from pyasic.miners.device.models.whatsminer.M3X import M31V10 class BTMinerM31V10(M3X, M31V10): pass -from pyasic.miners.device.models import M31V20 +from pyasic.miners.device.models.whatsminer.M3X import M31V20 class BTMinerM31V20(M3X, M31V20): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M31H.py b/pyasic/miners/whatsminer/btminer/M3X/M31H.py index d07588f7e..3402910c3 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M31H.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M31H.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M31HV10 +from pyasic.miners.device.models.whatsminer.M3X import M31HV10 class BTMinerM31HV10(M3X, M31HV10): pass -from pyasic.miners.device.models import M31HV40 +from pyasic.miners.device.models.whatsminer.M3X import M31HV40 class BTMinerM31HV40(M3X, M31HV40): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M31L.py b/pyasic/miners/whatsminer/btminer/M3X/M31L.py index 7df8fe311..77d2c2eca 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M31L.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M31L.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M31LV10 +from pyasic.miners.device.models.whatsminer.M3X import M31LV10 class BTMinerM31LV10(M3X, M31LV10): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M31S.py b/pyasic/miners/whatsminer/btminer/M3X/M31S.py index 906dc8866..703bdfe2e 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M31S.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M31S.py @@ -1,82 +1,82 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M31SV10 +from pyasic.miners.device.models.whatsminer.M3X import M31SV10 class BTMinerM31SV10(M3X, M31SV10): pass -from pyasic.miners.device.models import M31SV20 +from pyasic.miners.device.models.whatsminer.M3X import M31SV20 class BTMinerM31SV20(M3X, M31SV20): pass -from pyasic.miners.device.models import M31SV30 +from pyasic.miners.device.models.whatsminer.M3X import M31SV30 class BTMinerM31SV30(M3X, M31SV30): pass -from pyasic.miners.device.models import M31SV40 +from pyasic.miners.device.models.whatsminer.M3X import M31SV40 class BTMinerM31SV40(M3X, M31SV40): pass -from pyasic.miners.device.models import M31SV50 +from pyasic.miners.device.models.whatsminer.M3X import M31SV50 class BTMinerM31SV50(M3X, M31SV50): pass -from pyasic.miners.device.models import M31SV60 +from pyasic.miners.device.models.whatsminer.M3X import M31SV60 class BTMinerM31SV60(M3X, M31SV60): pass -from pyasic.miners.device.models import M31SV70 +from pyasic.miners.device.models.whatsminer.M3X import M31SV70 class BTMinerM31SV70(M3X, M31SV70): pass -from pyasic.miners.device.models import M31SV80 +from pyasic.miners.device.models.whatsminer.M3X import M31SV80 class BTMinerM31SV80(M3X, M31SV80): pass -from pyasic.miners.device.models import M31SV90 +from pyasic.miners.device.models.whatsminer.M3X import M31SV90 class BTMinerM31SV90(M3X, M31SV90): pass -from pyasic.miners.device.models import M31SVE10 +from pyasic.miners.device.models.whatsminer.M3X import M31SVE10 class BTMinerM31SVE10(M3X, M31SVE10): pass -from pyasic.miners.device.models import M31SVE20 +from pyasic.miners.device.models.whatsminer.M3X import M31SVE20 class BTMinerM31SVE20(M3X, M31SVE20): pass -from pyasic.miners.device.models import M31SVE30 +from pyasic.miners.device.models.whatsminer.M3X import M31SVE30 class BTMinerM31SVE30(M3X, M31SVE30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M31SE.py b/pyasic/miners/whatsminer/btminer/M3X/M31SE.py index aa42e0069..f8b99676b 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M31SE.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M31SE.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M31SEV10 +from pyasic.miners.device.models.whatsminer.M3X import M31SEV10 class BTMinerM31SEV10(M3X, M31SEV10): pass -from pyasic.miners.device.models import M31SEV20 +from pyasic.miners.device.models.whatsminer.M3X import M31SEV20 class BTMinerM31SEV20(M3X, M31SEV20): pass -from pyasic.miners.device.models import M31SEV30 +from pyasic.miners.device.models.whatsminer.M3X import M31SEV30 class BTMinerM31SEV30(M3X, M31SEV30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M31S_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M31S_Plus.py index 34365c3e7..d2f4b2bd4 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M31S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M31S_Plus.py @@ -1,138 +1,138 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M31SPlusV10 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV10 class BTMinerM31SPlusV10(M3X, M31SPlusV10): pass -from pyasic.miners.device.models import M31SPlusV100 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV100 class BTMinerM31SPlusV100(M3X, M31SPlusV100): pass -from pyasic.miners.device.models import M31SPlusV20 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV20 class BTMinerM31SPlusV20(M3X, M31SPlusV20): pass -from pyasic.miners.device.models import M31SPlusV30 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV30 class BTMinerM31SPlusV30(M3X, M31SPlusV30): pass -from pyasic.miners.device.models import M31SPlusV40 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV40 class BTMinerM31SPlusV40(M3X, M31SPlusV40): pass -from pyasic.miners.device.models import M31SPlusV50 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV50 class BTMinerM31SPlusV50(M3X, M31SPlusV50): pass -from pyasic.miners.device.models import M31SPlusV60 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV60 class BTMinerM31SPlusV60(M3X, M31SPlusV60): pass -from pyasic.miners.device.models import M31SPlusV80 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV80 class BTMinerM31SPlusV80(M3X, M31SPlusV80): pass -from pyasic.miners.device.models import M31SPlusV90 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusV90 class BTMinerM31SPlusV90(M3X, M31SPlusV90): pass -from pyasic.miners.device.models import M31SPlusVE10 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE10 class BTMinerM31SPlusVE10(M3X, M31SPlusVE10): pass -from pyasic.miners.device.models import M31SPlusVE20 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE20 class BTMinerM31SPlusVE20(M3X, M31SPlusVE20): pass -from pyasic.miners.device.models import M31SPlusVE30 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE30 class BTMinerM31SPlusVE30(M3X, M31SPlusVE30): pass -from pyasic.miners.device.models import M31SPlusVE40 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE40 class BTMinerM31SPlusVE40(M3X, M31SPlusVE40): pass -from pyasic.miners.device.models import M31SPlusVE50 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE50 class BTMinerM31SPlusVE50(M3X, M31SPlusVE50): pass -from pyasic.miners.device.models import M31SPlusVE60 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE60 class BTMinerM31SPlusVE60(M3X, M31SPlusVE60): pass -from pyasic.miners.device.models import M31SPlusVE80 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVE80 class BTMinerM31SPlusVE80(M3X, M31SPlusVE80): pass -from pyasic.miners.device.models import M31SPlusVF20 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVF20 class BTMinerM31SPlusVF20(M3X, M31SPlusVF20): pass -from pyasic.miners.device.models import M31SPlusVF30 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVF30 class BTMinerM31SPlusVF30(M3X, M31SPlusVF30): pass -from pyasic.miners.device.models import M31SPlusVG20 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVG20 class BTMinerM31SPlusVG20(M3X, M31SPlusVG20): pass -from pyasic.miners.device.models import M31SPlusVG30 +from pyasic.miners.device.models.whatsminer.M3X import M31SPlusVG30 class BTMinerM31SPlusVG30(M3X, M31SPlusVG30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M32.py b/pyasic/miners/whatsminer/btminer/M3X/M32.py index cc1060cc5..f709e40a2 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M32.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M32.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M32V10 +from pyasic.miners.device.models.whatsminer.M3X import M32V10 class BTMinerM32V10(M3X, M32V10): pass -from pyasic.miners.device.models import M32V20 +from pyasic.miners.device.models.whatsminer.M3X import M32V20 class BTMinerM32V20(M3X, M32V20): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M32S.py b/pyasic/miners/whatsminer/btminer/M3X/M32S.py index 3f56290d7..395a72ce5 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M32S.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M32S.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M32S +from pyasic.miners.device.models.whatsminer.M3X import M32S class BTMinerM32S(M3X, M32S): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M33.py b/pyasic/miners/whatsminer/btminer/M3X/M33.py index 0c1f1f3e8..6590de7bb 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M33.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M33.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M33V10 +from pyasic.miners.device.models.whatsminer.M3X import M33V10 class BTMinerM33V10(M3X, M33V10): pass -from pyasic.miners.device.models import M33V20 +from pyasic.miners.device.models.whatsminer.M3X import M33V20 class BTMinerM33V20(M3X, M33V20): pass -from pyasic.miners.device.models import M33V30 +from pyasic.miners.device.models.whatsminer.M3X import M33V30 class BTMinerM33V30(M3X, M33V30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M33S.py b/pyasic/miners/whatsminer/btminer/M3X/M33S.py index 0fb51dc33..a61701049 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M33S.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M33S.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M33SVG30 +from pyasic.miners.device.models.whatsminer.M3X import M33SVG30 class BTMinerM33SVG30(M3X, M33SVG30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus.py index 31caa84fa..35db2192a 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus.py @@ -1,26 +1,26 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M33SPlusVG20 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusVG20 class BTMinerM33SPlusVG20(M3X, M33SPlusVG20): pass -from pyasic.miners.device.models import M33SPlusVG30 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusVG30 class BTMinerM33SPlusVG30(M3X, M33SPlusVG30): pass -from pyasic.miners.device.models import M33SPlusVH20 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusVH20 class BTMinerM33SPlusVH20(M3X, M33SPlusVH20): pass -from pyasic.miners.device.models import M33SPlusVH30 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusVH30 class BTMinerM33SPlusVH30(M3X, M33SPlusVH30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus_Plus.py index 787cd032f..18b9e50dc 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M33S_Plus_Plus.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M33SPlusPlusVG40 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusPlusVG40 class BTMinerM33SPlusPlusVG40(M3X, M33SPlusPlusVG40): pass -from pyasic.miners.device.models import M33SPlusPlusVH20 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusPlusVH20 class BTMinerM33SPlusPlusVH20(M3X, M33SPlusPlusVH20): pass -from pyasic.miners.device.models import M33SPlusPlusVH30 +from pyasic.miners.device.models.whatsminer.M3X import M33SPlusPlusVH30 class BTMinerM33SPlusPlusVH30(M3X, M33SPlusPlusVH30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M34S_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M34S_Plus.py index d07e816a8..5f7b3e854 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M34S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M34S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M34SPlusVE10 +from pyasic.miners.device.models.whatsminer.M3X import M34SPlusVE10 class BTMinerM34SPlusVE10(M3X, M34SPlusVE10): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M36S.py b/pyasic/miners/whatsminer/btminer/M3X/M36S.py index ebd9143f3..817cada4e 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M36S.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M36S.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M36SVE10 +from pyasic.miners.device.models.whatsminer.M3X import M36SVE10 class BTMinerM36SVE10(M3X, M36SVE10): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus.py index 0fec19db0..028b7e77e 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M36SPlusVG30 +from pyasic.miners.device.models.whatsminer.M3X import M36SPlusVG30 class BTMinerM36SPlusVG30(M3X, M36SPlusVG30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus_Plus.py index 2e1351f3c..0d7cbb664 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M36S_Plus_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M36SPlusPlusVH30 +from pyasic.miners.device.models.whatsminer.M3X import M36SPlusPlusVH30 class BTMinerM36SPlusPlusVH30(M3X, M36SPlusPlusVH30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/M39.py b/pyasic/miners/whatsminer/btminer/M3X/M39.py index a2cec46ec..94e74e707 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/M39.py +++ b/pyasic/miners/whatsminer/btminer/M3X/M39.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M3X -from pyasic.miners.device.models import M39V10 +from pyasic.miners.device.models.whatsminer.M3X import M39V10 class BTMinerM39V10(M3X, M39V10): pass -from pyasic.miners.device.models import M39V20 +from pyasic.miners.device.models.whatsminer.M3X import M39V20 class BTMinerM39V20(M3X, M39V20): pass -from pyasic.miners.device.models import M39V30 +from pyasic.miners.device.models.whatsminer.M3X import M39V30 class BTMinerM39V30(M3X, M39V30): diff --git a/pyasic/miners/whatsminer/btminer/M3X/__init__.py b/pyasic/miners/whatsminer/btminer/M3X/__init__.py index bc73316af..2f5f17ff3 100644 --- a/pyasic/miners/whatsminer/btminer/M3X/__init__.py +++ b/pyasic/miners/whatsminer/btminer/M3X/__init__.py @@ -159,3 +159,163 @@ from .M36S_Plus import BTMinerM36SPlusVG30 from .M36S_Plus_Plus import BTMinerM36SPlusPlusVH30 from .M39 import BTMinerM39V10, BTMinerM39V20, BTMinerM39V30 + +__all__ = [ + "BTMinerM30V10", + "BTMinerM30V20", + "BTMinerM30KV10", + "BTMinerM30LV10", + "BTMinerM30SV10", + "BTMinerM30SV20", + "BTMinerM30SV30", + "BTMinerM30SV40", + "BTMinerM30SV50", + "BTMinerM30SV60", + "BTMinerM30SV70", + "BTMinerM30SV80", + "BTMinerM30SVE10", + "BTMinerM30SVE20", + "BTMinerM30SVE30", + "BTMinerM30SVE40", + "BTMinerM30SVE50", + "BTMinerM30SVE60", + "BTMinerM30SVE70", + "BTMinerM30SVF10", + "BTMinerM30SVF20", + "BTMinerM30SVF30", + "BTMinerM30SVG10", + "BTMinerM30SVG20", + "BTMinerM30SVG30", + "BTMinerM30SVG40", + "BTMinerM30SVH10", + "BTMinerM30SVH20", + "BTMinerM30SVH30", + "BTMinerM30SVH40", + "BTMinerM30SVH50", + "BTMinerM30SVH60", + "BTMinerM30SVI20", + "BTMinerM30SVJ30", + "BTMinerM30SPlusV10", + "BTMinerM30SPlusV20", + "BTMinerM30SPlusV30", + "BTMinerM30SPlusV40", + "BTMinerM30SPlusV50", + "BTMinerM30SPlusV60", + "BTMinerM30SPlusV70", + "BTMinerM30SPlusV80", + "BTMinerM30SPlusV90", + "BTMinerM30SPlusV100", + "BTMinerM30SPlusVE30", + "BTMinerM30SPlusVE40", + "BTMinerM30SPlusVE50", + "BTMinerM30SPlusVE60", + "BTMinerM30SPlusVE70", + "BTMinerM30SPlusVE80", + "BTMinerM30SPlusVE90", + "BTMinerM30SPlusVE100", + "BTMinerM30SPlusVF20", + "BTMinerM30SPlusVF30", + "BTMinerM30SPlusVG20", + "BTMinerM30SPlusVG30", + "BTMinerM30SPlusVG40", + "BTMinerM30SPlusVG50", + "BTMinerM30SPlusVG60", + "BTMinerM30SPlusVH10", + "BTMinerM30SPlusVH20", + "BTMinerM30SPlusVH30", + "BTMinerM30SPlusVH40", + "BTMinerM30SPlusVH50", + "BTMinerM30SPlusVH60", + "BTMinerM30SPlusVH70", + "BTMinerM30SPlusVI30", + "BTMinerM30SPlusVJ30", + "BTMinerM30SPlusVJ40", + "BTMinerM30SPlusPlusV10", + "BTMinerM30SPlusPlusV20", + "BTMinerM30SPlusPlusVE30", + "BTMinerM30SPlusPlusVE40", + "BTMinerM30SPlusPlusVE50", + "BTMinerM30SPlusPlusVF40", + "BTMinerM30SPlusPlusVG30", + "BTMinerM30SPlusPlusVG40", + "BTMinerM30SPlusPlusVG50", + "BTMinerM30SPlusPlusVH10", + "BTMinerM30SPlusPlusVH20", + "BTMinerM30SPlusPlusVH30", + "BTMinerM30SPlusPlusVH40", + "BTMinerM30SPlusPlusVH50", + "BTMinerM30SPlusPlusVH60", + "BTMinerM30SPlusPlusVH70", + "BTMinerM30SPlusPlusVH80", + "BTMinerM30SPlusPlusVH90", + "BTMinerM30SPlusPlusVH100", + "BTMinerM30SPlusPlusVH110", + "BTMinerM30SPlusPlusVI30", + "BTMinerM30SPlusPlusVJ20", + "BTMinerM30SPlusPlusVJ30", + "BTMinerM30SPlusPlusVJ50", + "BTMinerM30SPlusPlusVJ60", + "BTMinerM30SPlusPlusVJ70", + "BTMinerM30SPlusPlusVK30", + "BTMinerM30SPlusPlusVK40", + "BTMinerM31V10", + "BTMinerM31V20", + "BTMinerM31HV10", + "BTMinerM31HV40", + "BTMinerM31LV10", + "BTMinerM31SV10", + "BTMinerM31SV20", + "BTMinerM31SV30", + "BTMinerM31SV40", + "BTMinerM31SV50", + "BTMinerM31SV60", + "BTMinerM31SV70", + "BTMinerM31SV80", + "BTMinerM31SV90", + "BTMinerM31SVE10", + "BTMinerM31SVE20", + "BTMinerM31SVE30", + "BTMinerM31SPlusV10", + "BTMinerM31SPlusV20", + "BTMinerM31SPlusV30", + "BTMinerM31SPlusV40", + "BTMinerM31SPlusV50", + "BTMinerM31SPlusV60", + "BTMinerM31SPlusV80", + "BTMinerM31SPlusV90", + "BTMinerM31SPlusV100", + "BTMinerM31SPlusVE10", + "BTMinerM31SPlusVE20", + "BTMinerM31SPlusVE30", + "BTMinerM31SPlusVE40", + "BTMinerM31SPlusVE50", + "BTMinerM31SPlusVE60", + "BTMinerM31SPlusVE80", + "BTMinerM31SPlusVF20", + "BTMinerM31SPlusVF30", + "BTMinerM31SPlusVG20", + "BTMinerM31SPlusVG30", + "BTMinerM31SEV10", + "BTMinerM31SEV20", + "BTMinerM31SEV30", + "BTMinerM32V10", + "BTMinerM32V20", + "BTMinerM33V10", + "BTMinerM33V20", + "BTMinerM33V30", + "BTMinerM33SVG30", + "BTMinerM33SPlusVG20", + "BTMinerM33SPlusVG30", + "BTMinerM33SPlusVH20", + "BTMinerM33SPlusVH30", + "BTMinerM33SPlusPlusVG40", + "BTMinerM33SPlusPlusVH20", + "BTMinerM33SPlusPlusVH30", + "BTMinerM34SPlusVE10", + "BTMinerM36SVE10", + "BTMinerM36SPlusVG30", + "BTMinerM36SPlusPlusVH30", + "BTMinerM39V10", + "BTMinerM39V20", + "BTMinerM39V30", +] diff --git a/pyasic/miners/whatsminer/btminer/M5X/M50.py b/pyasic/miners/whatsminer/btminer/M5X/M50.py index a4434e36a..4d5498359 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M50.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M50.py @@ -1,124 +1,124 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M50VE30 +from pyasic.miners.device.models.whatsminer.M5X import M50VE30 class BTMinerM50VE30(M5X, M50VE30): pass -from pyasic.miners.device.models import M50VG30 +from pyasic.miners.device.models.whatsminer.M5X import M50VG30 class BTMinerM50VG30(M5X, M50VG30): pass -from pyasic.miners.device.models import M50VH10 +from pyasic.miners.device.models.whatsminer.M5X import M50VH10 class BTMinerM50VH10(M5X, M50VH10): pass -from pyasic.miners.device.models import M50VH20 +from pyasic.miners.device.models.whatsminer.M5X import M50VH20 class BTMinerM50VH20(M5X, M50VH20): pass -from pyasic.miners.device.models import M50VH30 +from pyasic.miners.device.models.whatsminer.M5X import M50VH30 class BTMinerM50VH30(M5X, M50VH30): pass -from pyasic.miners.device.models import M50VH40 +from pyasic.miners.device.models.whatsminer.M5X import M50VH40 class BTMinerM50VH40(M5X, M50VH40): pass -from pyasic.miners.device.models import M50VH50 +from pyasic.miners.device.models.whatsminer.M5X import M50VH50 class BTMinerM50VH50(M5X, M50VH50): pass -from pyasic.miners.device.models import M50VH60 +from pyasic.miners.device.models.whatsminer.M5X import M50VH60 class BTMinerM50VH60(M5X, M50VH60): pass -from pyasic.miners.device.models import M50VH70 +from pyasic.miners.device.models.whatsminer.M5X import M50VH70 class BTMinerM50VH70(M5X, M50VH70): pass -from pyasic.miners.device.models import M50VH80 +from pyasic.miners.device.models.whatsminer.M5X import M50VH80 class BTMinerM50VH80(M5X, M50VH80): pass -from pyasic.miners.device.models import M50VH90 +from pyasic.miners.device.models.whatsminer.M5X import M50VH90 class BTMinerM50VH90(M5X, M50VH90): pass -from pyasic.miners.device.models import M50VJ10 +from pyasic.miners.device.models.whatsminer.M5X import M50VJ10 class BTMinerM50VJ10(M5X, M50VJ10): pass -from pyasic.miners.device.models import M50VJ20 +from pyasic.miners.device.models.whatsminer.M5X import M50VJ20 class BTMinerM50VJ20(M5X, M50VJ20): pass -from pyasic.miners.device.models import M50VJ30 +from pyasic.miners.device.models.whatsminer.M5X import M50VJ30 class BTMinerM50VJ30(M5X, M50VJ30): pass -from pyasic.miners.device.models import M50VJ40 +from pyasic.miners.device.models.whatsminer.M5X import M50VJ40 class BTMinerM50VJ40(M5X, M50VJ40): pass -from pyasic.miners.device.models import M50VJ60 +from pyasic.miners.device.models.whatsminer.M5X import M50VJ60 class BTMinerM50VJ60(M5X, M50VJ60): pass -from pyasic.miners.device.models import M50VK40 +from pyasic.miners.device.models.whatsminer.M5X import M50VK40 class BTMinerM50VK40(M5X, M50VK40): pass -from pyasic.miners.device.models import M50VK50 +from pyasic.miners.device.models.whatsminer.M5X import M50VK50 class BTMinerM50VK50(M5X, M50VK50): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M50S.py b/pyasic/miners/whatsminer/btminer/M5X/M50S.py index e9d6f3d33..e3b464f57 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M50S.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M50S.py @@ -1,131 +1,131 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M50SVH10 +from pyasic.miners.device.models.whatsminer.M5X import M50SVH10 class BTMinerM50SVH10(M5X, M50SVH10): pass -from pyasic.miners.device.models import M50SVH20 +from pyasic.miners.device.models.whatsminer.M5X import M50SVH20 class BTMinerM50SVH20(M5X, M50SVH20): pass -from pyasic.miners.device.models import M50SVH30 +from pyasic.miners.device.models.whatsminer.M5X import M50SVH30 class BTMinerM50SVH30(M5X, M50SVH30): pass -from pyasic.miners.device.models import M50SVH40 +from pyasic.miners.device.models.whatsminer.M5X import M50SVH40 class BTMinerM50SVH40(M5X, M50SVH40): pass -from pyasic.miners.device.models import M50SVH50 +from pyasic.miners.device.models.whatsminer.M5X import M50SVH50 class BTMinerM50SVH50(M5X, M50SVH50): pass -from pyasic.miners.device.models import M50SVJ10 +from pyasic.miners.device.models.whatsminer.M5X import M50SVJ10 class BTMinerM50SVJ10(M5X, M50SVJ10): pass -from pyasic.miners.device.models import M50SVJ20 +from pyasic.miners.device.models.whatsminer.M5X import M50SVJ20 class BTMinerM50SVJ20(M5X, M50SVJ20): pass -from pyasic.miners.device.models import M50SVJ30 +from pyasic.miners.device.models.whatsminer.M5X import M50SVJ30 class BTMinerM50SVJ30(M5X, M50SVJ30): pass -from pyasic.miners.device.models import M50SVJ40 +from pyasic.miners.device.models.whatsminer.M5X import M50SVJ40 class BTMinerM50SVJ40(M5X, M50SVJ40): pass -from pyasic.miners.device.models import M50SVJ50 +from pyasic.miners.device.models.whatsminer.M5X import M50SVJ50 class BTMinerM50SVJ50(M5X, M50SVJ50): pass -from pyasic.miners.device.models import M50SVK10 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK10 class BTMinerM50SVK10(M5X, M50SVK10): pass -from pyasic.miners.device.models import M50SVK20 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK20 class BTMinerM50SVK20(M5X, M50SVK20): pass -from pyasic.miners.device.models import M50SVK30 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK30 class BTMinerM50SVK30(M5X, M50SVK30): pass -from pyasic.miners.device.models import M50SVK50 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK50 class BTMinerM50SVK50(M5X, M50SVK50): pass -from pyasic.miners.device.models import M50SVK60 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK60 class BTMinerM50SVK60(M5X, M50SVK60): pass -from pyasic.miners.device.models import M50SVK70 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK70 class BTMinerM50SVK70(M5X, M50SVK70): pass -from pyasic.miners.device.models import M50SVK80 +from pyasic.miners.device.models.whatsminer.M5X import M50SVK80 class BTMinerM50SVK80(M5X, M50SVK80): pass -from pyasic.miners.device.models import M50SVL20 +from pyasic.miners.device.models.whatsminer.M5X import M50SVL20 class BTMinerM50SVL20(M5X, M50SVL20): pass -from pyasic.miners.device.models import M50SVL30 +from pyasic.miners.device.models.whatsminer.M5X import M50SVL30 class BTMinerM50SVL30(M5X, M50SVL30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus.py index 5debb9787..d2f69253d 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus.py @@ -1,75 +1,75 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M50SPlusVH30 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVH30 class BTMinerM50SPlusVH30(M5X, M50SPlusVH30): pass -from pyasic.miners.device.models import M50SPlusVH40 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVH40 class BTMinerM50SPlusVH40(M5X, M50SPlusVH40): pass -from pyasic.miners.device.models import M50SPlusVJ30 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVJ30 class BTMinerM50SPlusVJ30(M5X, M50SPlusVJ30): pass -from pyasic.miners.device.models import M50SPlusVJ40 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVJ40 class BTMinerM50SPlusVJ40(M5X, M50SPlusVJ40): pass -from pyasic.miners.device.models import M50SPlusVJ60 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVJ60 class BTMinerM50SPlusVJ60(M5X, M50SPlusVJ60): pass -from pyasic.miners.device.models import M50SPlusVK10 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVK10 class BTMinerM50SPlusVK10(M5X, M50SPlusVK10): pass -from pyasic.miners.device.models import M50SPlusVK20 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVK20 class BTMinerM50SPlusVK20(M5X, M50SPlusVK20): pass -from pyasic.miners.device.models import M50SPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVK30 class BTMinerM50SPlusVK30(M5X, M50SPlusVK30): pass -from pyasic.miners.device.models import M50SPlusVL10 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVL10 class BTMinerM50SPlusVL10(M5X, M50SPlusVL10): pass -from pyasic.miners.device.models import M50SPlusVL20 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVL20 class BTMinerM50SPlusVL20(M5X, M50SPlusVL20): pass -from pyasic.miners.device.models import M50SPlusVL30 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusVL30 class BTMinerM50SPlusVL30(M5X, M50SPlusVL30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus_Plus.py index 72251059d..9de89e9ac 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M50S_Plus_Plus.py @@ -1,75 +1,75 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M50SPlusPlusVK10 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVK10 class BTMinerM50SPlusPlusVK10(M5X, M50SPlusPlusVK10): pass -from pyasic.miners.device.models import M50SPlusPlusVK20 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVK20 class BTMinerM50SPlusPlusVK20(M5X, M50SPlusPlusVK20): pass -from pyasic.miners.device.models import M50SPlusPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVK30 class BTMinerM50SPlusPlusVK30(M5X, M50SPlusPlusVK30): pass -from pyasic.miners.device.models import M50SPlusPlusVK40 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVK40 class BTMinerM50SPlusPlusVK40(M5X, M50SPlusPlusVK40): pass -from pyasic.miners.device.models import M50SPlusPlusVK50 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVK50 class BTMinerM50SPlusPlusVK50(M5X, M50SPlusPlusVK50): pass -from pyasic.miners.device.models import M50SPlusPlusVK60 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVK60 class BTMinerM50SPlusPlusVK60(M5X, M50SPlusPlusVK60): pass -from pyasic.miners.device.models import M50SPlusPlusVL20 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVL20 class BTMinerM50SPlusPlusVL20(M5X, M50SPlusPlusVL20): pass -from pyasic.miners.device.models import M50SPlusPlusVL30 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVL30 class BTMinerM50SPlusPlusVL30(M5X, M50SPlusPlusVL30): pass -from pyasic.miners.device.models import M50SPlusPlusVL40 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVL40 class BTMinerM50SPlusPlusVL40(M5X, M50SPlusPlusVL40): pass -from pyasic.miners.device.models import M50SPlusPlusVL50 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVL50 class BTMinerM50SPlusPlusVL50(M5X, M50SPlusPlusVL50): pass -from pyasic.miners.device.models import M50SPlusPlusVL60 +from pyasic.miners.device.models.whatsminer.M5X import M50SPlusPlusVL60 class BTMinerM50SPlusPlusVL60(M5X, M50SPlusPlusVL60): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M52S.py b/pyasic/miners/whatsminer/btminer/M5X/M52S.py index 2f4cd89ef..edf93fdd3 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M52S.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M52S.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M52SVK30 +from pyasic.miners.device.models.whatsminer.M5X import M52SVK30 class BTMinerM52SVK30(M5X, M52SVK30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M52S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M52S_Plus_Plus.py index dd471843f..3e252fe71 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M52S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M52S_Plus_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M52SPlusPlusVL10 +from pyasic.miners.device.models.whatsminer.M5X import M52SPlusPlusVL10 class BTMinerM52SPlusPlusVL10(M5X, M52SPlusPlusVL10): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M53.py b/pyasic/miners/whatsminer/btminer/M5X/M53.py index c1471799e..82f1f0eb4 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M53.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M53.py @@ -1,33 +1,33 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M53VH30 +from pyasic.miners.device.models.whatsminer.M5X import M53VH30 class BTMinerM53VH30(M5X, M53VH30): pass -from pyasic.miners.device.models import M53VH40 +from pyasic.miners.device.models.whatsminer.M5X import M53VH40 class BTMinerM53VH40(M5X, M53VH40): pass -from pyasic.miners.device.models import M53VH50 +from pyasic.miners.device.models.whatsminer.M5X import M53VH50 class BTMinerM53VH50(M5X, M53VH50): pass -from pyasic.miners.device.models import M53VK30 +from pyasic.miners.device.models.whatsminer.M5X import M53VK30 class BTMinerM53VK30(M5X, M53VK30): pass -from pyasic.miners.device.models import M53VK60 +from pyasic.miners.device.models.whatsminer.M5X import M53VK60 class BTMinerM53VK60(M5X, M53VK60): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M53H.py b/pyasic/miners/whatsminer/btminer/M5X/M53H.py index eff24c2cc..dd29c1c40 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M53H.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M53H.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M53HVH10 +from pyasic.miners.device.models.whatsminer.M5X import M53HVH10 class BTMinerM53HVH10(M5X, M53HVH10): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M53S.py b/pyasic/miners/whatsminer/btminer/M5X/M53S.py index b3fd6ff12..dc7413196 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M53S.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M53S.py @@ -1,33 +1,33 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M53SVH20 +from pyasic.miners.device.models.whatsminer.M5X import M53SVH20 class BTMinerM53SVH20(M5X, M53SVH20): pass -from pyasic.miners.device.models import M53SVH30 +from pyasic.miners.device.models.whatsminer.M5X import M53SVH30 class BTMinerM53SVH30(M5X, M53SVH30): pass -from pyasic.miners.device.models import M53SVJ30 +from pyasic.miners.device.models.whatsminer.M5X import M53SVJ30 class BTMinerM53SVJ30(M5X, M53SVJ30): pass -from pyasic.miners.device.models import M53SVJ40 +from pyasic.miners.device.models.whatsminer.M5X import M53SVJ40 class BTMinerM53SVJ40(M5X, M53SVJ40): pass -from pyasic.miners.device.models import M53SVK30 +from pyasic.miners.device.models.whatsminer.M5X import M53SVK30 class BTMinerM53SVK30(M5X, M53SVK30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus.py index c3f44f190..6506c3d04 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus.py @@ -1,26 +1,26 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M53SPlusVJ30 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusVJ30 class BTMinerM53SPlusVJ30(M5X, M53SPlusVJ30): pass -from pyasic.miners.device.models import M53SPlusVJ40 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusVJ40 class BTMinerM53SPlusVJ40(M5X, M53SPlusVJ40): pass -from pyasic.miners.device.models import M53SPlusVJ50 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusVJ50 class BTMinerM53SPlusVJ50(M5X, M53SPlusVJ50): pass -from pyasic.miners.device.models import M53SPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusVK30 class BTMinerM53SPlusVK30(M5X, M53SPlusVK30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus_Plus.py index 2198e8859..27c30f76a 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M53S_Plus_Plus.py @@ -1,40 +1,40 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M53SPlusPlusVK10 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusPlusVK10 class BTMinerM53SPlusPlusVK10(M5X, M53SPlusPlusVK10): pass -from pyasic.miners.device.models import M53SPlusPlusVK20 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusPlusVK20 class BTMinerM53SPlusPlusVK20(M5X, M53SPlusPlusVK20): pass -from pyasic.miners.device.models import M53SPlusPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusPlusVK30 class BTMinerM53SPlusPlusVK30(M5X, M53SPlusPlusVK30): pass -from pyasic.miners.device.models import M53SPlusPlusVK50 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusPlusVK50 class BTMinerM53SPlusPlusVK50(M5X, M53SPlusPlusVK50): pass -from pyasic.miners.device.models import M53SPlusPlusVL10 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusPlusVL10 class BTMinerM53SPlusPlusVL10(M5X, M53SPlusPlusVL10): pass -from pyasic.miners.device.models import M53SPlusPlusVL30 +from pyasic.miners.device.models.whatsminer.M5X import M53SPlusPlusVL30 class BTMinerM53SPlusPlusVL30(M5X, M53SPlusPlusVL30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M54S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M54S_Plus_Plus.py index c8e4f5b53..427089bd3 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M54S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M54S_Plus_Plus.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M54SPlusPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M54SPlusPlusVK30 class BTMinerM54SPlusPlusVK30(M5X, M54SPlusPlusVK30): pass -from pyasic.miners.device.models import M54SPlusPlusVL30 +from pyasic.miners.device.models.whatsminer.M5X import M54SPlusPlusVL30 class BTMinerM54SPlusPlusVL30(M5X, M54SPlusPlusVL30): pass -from pyasic.miners.device.models import M54SPlusPlusVL40 +from pyasic.miners.device.models.whatsminer.M5X import M54SPlusPlusVL40 class BTMinerM54SPlusPlusVL40(M5X, M54SPlusPlusVL40): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M56.py b/pyasic/miners/whatsminer/btminer/M5X/M56.py index 224f9fe74..ae1c03569 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M56.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M56.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M56VH30 +from pyasic.miners.device.models.whatsminer.M5X import M56VH30 class BTMinerM56VH30(M5X, M56VH30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M56S.py b/pyasic/miners/whatsminer/btminer/M5X/M56S.py index 973c26680..18b99d495 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M56S.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M56S.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M56SVH30 +from pyasic.miners.device.models.whatsminer.M5X import M56SVH30 class BTMinerM56SVH30(M5X, M56SVH30): pass -from pyasic.miners.device.models import M56SVJ30 +from pyasic.miners.device.models.whatsminer.M5X import M56SVJ30 class BTMinerM56SVJ30(M5X, M56SVJ30): pass -from pyasic.miners.device.models import M56SVJ40 +from pyasic.miners.device.models.whatsminer.M5X import M56SVJ40 class BTMinerM56SVJ40(M5X, M56SVJ40): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus.py index cc76b1eab..950a60764 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus.py @@ -1,26 +1,26 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M56SPlusVJ30 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusVJ30 class BTMinerM56SPlusVJ30(M5X, M56SPlusVJ30): pass -from pyasic.miners.device.models import M56SPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusVK30 class BTMinerM56SPlusVK30(M5X, M56SPlusVK30): pass -from pyasic.miners.device.models import M56SPlusVK40 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusVK40 class BTMinerM56SPlusVK40(M5X, M56SPlusVK40): pass -from pyasic.miners.device.models import M56SPlusVK50 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusVK50 class BTMinerM56SPlusVK50(M5X, M56SPlusVK50): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus_Plus.py index d7df313a0..c4aa01625 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M56S_Plus_Plus.py @@ -1,26 +1,26 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M56SPlusPlusVK10 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusPlusVK10 class BTMinerM56SPlusPlusVK10(M5X, M56SPlusPlusVK10): pass -from pyasic.miners.device.models import M56SPlusPlusVK30 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusPlusVK30 class BTMinerM56SPlusPlusVK30(M5X, M56SPlusPlusVK30): pass -from pyasic.miners.device.models import M56SPlusPlusVK40 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusPlusVK40 class BTMinerM56SPlusPlusVK40(M5X, M56SPlusPlusVK40): pass -from pyasic.miners.device.models import M56SPlusPlusVK50 +from pyasic.miners.device.models.whatsminer.M5X import M56SPlusPlusVK50 class BTMinerM56SPlusPlusVK50(M5X, M56SPlusPlusVK50): diff --git a/pyasic/miners/whatsminer/btminer/M5X/M59.py b/pyasic/miners/whatsminer/btminer/M5X/M59.py index 97cd175ad..8f4e5f1be 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/M59.py +++ b/pyasic/miners/whatsminer/btminer/M5X/M59.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M5X -from pyasic.miners.device.models import M59VH30 +from pyasic.miners.device.models.whatsminer.M5X import M59VH30 class BTMinerM59VH30(M5X, M59VH30): diff --git a/pyasic/miners/whatsminer/btminer/M5X/__init__.py b/pyasic/miners/whatsminer/btminer/M5X/__init__.py index a0911642f..d10f5e1bc 100644 --- a/pyasic/miners/whatsminer/btminer/M5X/__init__.py +++ b/pyasic/miners/whatsminer/btminer/M5X/__init__.py @@ -116,3 +116,104 @@ BTMinerM56SPlusPlusVK50, ) from .M59 import BTMinerM59VH30 + +__all__ = [ + "BTMinerM50VE30", + "BTMinerM50VG30", + "BTMinerM50VH10", + "BTMinerM50VH20", + "BTMinerM50VH30", + "BTMinerM50VH40", + "BTMinerM50VH50", + "BTMinerM50VH60", + "BTMinerM50VH70", + "BTMinerM50VH80", + "BTMinerM50VH90", + "BTMinerM50VJ10", + "BTMinerM50VJ20", + "BTMinerM50VJ30", + "BTMinerM50VJ40", + "BTMinerM50VJ60", + "BTMinerM50VK40", + "BTMinerM50VK50", + "BTMinerM50SVH10", + "BTMinerM50SVH20", + "BTMinerM50SVH30", + "BTMinerM50SVH40", + "BTMinerM50SVH50", + "BTMinerM50SVJ10", + "BTMinerM50SVJ20", + "BTMinerM50SVJ30", + "BTMinerM50SVJ40", + "BTMinerM50SVJ50", + "BTMinerM50SVK10", + "BTMinerM50SVK20", + "BTMinerM50SVK30", + "BTMinerM50SVK50", + "BTMinerM50SVK60", + "BTMinerM50SVK70", + "BTMinerM50SVK80", + "BTMinerM50SVL20", + "BTMinerM50SVL30", + "BTMinerM50SPlusVH30", + "BTMinerM50SPlusVH40", + "BTMinerM50SPlusVJ30", + "BTMinerM50SPlusVJ40", + "BTMinerM50SPlusVJ60", + "BTMinerM50SPlusVK10", + "BTMinerM50SPlusVK20", + "BTMinerM50SPlusVK30", + "BTMinerM50SPlusVL10", + "BTMinerM50SPlusVL20", + "BTMinerM50SPlusVL30", + "BTMinerM50SPlusPlusVK10", + "BTMinerM50SPlusPlusVK20", + "BTMinerM50SPlusPlusVK30", + "BTMinerM50SPlusPlusVK40", + "BTMinerM50SPlusPlusVK50", + "BTMinerM50SPlusPlusVK60", + "BTMinerM50SPlusPlusVL20", + "BTMinerM50SPlusPlusVL30", + "BTMinerM50SPlusPlusVL40", + "BTMinerM50SPlusPlusVL50", + "BTMinerM50SPlusPlusVL60", + "BTMinerM52SVK30", + "BTMinerM52SPlusPlusVL10", + "BTMinerM53VH30", + "BTMinerM53VH40", + "BTMinerM53VH50", + "BTMinerM53VK30", + "BTMinerM53VK60", + "BTMinerM53HVH10", + "BTMinerM53SVH20", + "BTMinerM53SVH30", + "BTMinerM53SVJ30", + "BTMinerM53SVJ40", + "BTMinerM53SVK30", + "BTMinerM53SPlusVJ30", + "BTMinerM53SPlusVJ40", + "BTMinerM53SPlusVJ50", + "BTMinerM53SPlusVK30", + "BTMinerM53SPlusPlusVK10", + "BTMinerM53SPlusPlusVK20", + "BTMinerM53SPlusPlusVK30", + "BTMinerM53SPlusPlusVK50", + "BTMinerM53SPlusPlusVL10", + "BTMinerM53SPlusPlusVL30", + "BTMinerM54SPlusPlusVK30", + "BTMinerM54SPlusPlusVL30", + "BTMinerM54SPlusPlusVL40", + "BTMinerM56VH30", + "BTMinerM56SVH30", + "BTMinerM56SVJ30", + "BTMinerM56SVJ40", + "BTMinerM56SPlusVJ30", + "BTMinerM56SPlusVK30", + "BTMinerM56SPlusVK40", + "BTMinerM56SPlusVK50", + "BTMinerM56SPlusPlusVK10", + "BTMinerM56SPlusPlusVK30", + "BTMinerM56SPlusPlusVK40", + "BTMinerM56SPlusPlusVK50", + "BTMinerM59VH30", +] diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60.py b/pyasic/miners/whatsminer/btminer/M6X/M60.py index e7b84a7ce..fcd8d5ff3 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M60.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M60.py @@ -1,68 +1,68 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M60VK10 +from pyasic.miners.device.models.whatsminer.M6X import M60VK10 class BTMinerM60VK10(M6X, M60VK10): pass -from pyasic.miners.device.models import M60VK20 +from pyasic.miners.device.models.whatsminer.M6X import M60VK20 class BTMinerM60VK20(M6X, M60VK20): pass -from pyasic.miners.device.models import M60VK30 +from pyasic.miners.device.models.whatsminer.M6X import M60VK30 class BTMinerM60VK30(M6X, M60VK30): pass -from pyasic.miners.device.models import M60VK40 +from pyasic.miners.device.models.whatsminer.M6X import M60VK40 class BTMinerM60VK40(M6X, M60VK40): pass -from pyasic.miners.device.models import M60VK6A +from pyasic.miners.device.models.whatsminer.M6X import M60VK6A class BTMinerM60VK6A(M6X, M60VK6A): pass -from pyasic.miners.device.models import M60VL10 +from pyasic.miners.device.models.whatsminer.M6X import M60VL10 class BTMinerM60VL10(M6X, M60VL10): pass -from pyasic.miners.device.models import M60VL20 +from pyasic.miners.device.models.whatsminer.M6X import M60VL20 class BTMinerM60VL20(M6X, M60VL20): pass -from pyasic.miners.device.models import M60VL30 +from pyasic.miners.device.models.whatsminer.M6X import M60VL30 class BTMinerM60VL30(M6X, M60VL30): pass -from pyasic.miners.device.models import M60VL40 +from pyasic.miners.device.models.whatsminer.M6X import M60VL40 class BTMinerM60VL40(M6X, M60VL40): pass -from pyasic.miners.device.models import M60VL50 +from pyasic.miners.device.models.whatsminer.M6X import M60VL50 class BTMinerM60VL50(M6X, M60VL50): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60S.py b/pyasic/miners/whatsminer/btminer/M6X/M60S.py index 8f8a96bf0..b8d2acc23 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M60S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M60S.py @@ -1,75 +1,75 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M60SVK10 +from pyasic.miners.device.models.whatsminer.M6X import M60SVK10 class BTMinerM60SVK10(M6X, M60SVK10): pass -from pyasic.miners.device.models import M60SVK20 +from pyasic.miners.device.models.whatsminer.M6X import M60SVK20 class BTMinerM60SVK20(M6X, M60SVK20): pass -from pyasic.miners.device.models import M60SVK30 +from pyasic.miners.device.models.whatsminer.M6X import M60SVK30 class BTMinerM60SVK30(M6X, M60SVK30): pass -from pyasic.miners.device.models import M60SVK40 +from pyasic.miners.device.models.whatsminer.M6X import M60SVK40 class BTMinerM60SVK40(M6X, M60SVK40): pass -from pyasic.miners.device.models import M60SVL10 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL10 class BTMinerM60SVL10(M6X, M60SVL10): pass -from pyasic.miners.device.models import M60SVL20 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL20 class BTMinerM60SVL20(M6X, M60SVL20): pass -from pyasic.miners.device.models import M60SVL30 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL30 class BTMinerM60SVL30(M6X, M60SVL30): pass -from pyasic.miners.device.models import M60SVL40 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL40 class BTMinerM60SVL40(M6X, M60SVL40): pass -from pyasic.miners.device.models import M60SVL50 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL50 class BTMinerM60SVL50(M6X, M60SVL50): pass -from pyasic.miners.device.models import M60SVL60 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL60 class BTMinerM60SVL60(M6X, M60SVL60): pass -from pyasic.miners.device.models import M60SVL70 +from pyasic.miners.device.models.whatsminer.M6X import M60SVL70 class BTMinerM60SVL70(M6X, M60SVL70): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus.py index 8bee85ec8..992a7e4b6 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus.py @@ -1,68 +1,68 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M60SPlusVK30 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVK30 class BTMinerM60SPlusVK30(M6X, M60SPlusVK30): pass -from pyasic.miners.device.models import M60SPlusVK40 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVK40 class BTMinerM60SPlusVK40(M6X, M60SPlusVK40): pass -from pyasic.miners.device.models import M60SPlusVK50 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVK50 class BTMinerM60SPlusVK50(M6X, M60SPlusVK50): pass -from pyasic.miners.device.models import M60SPlusVK60 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVK60 class BTMinerM60SPlusVK60(M6X, M60SPlusVK60): pass -from pyasic.miners.device.models import M60SPlusVK70 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVK70 class BTMinerM60SPlusVK70(M6X, M60SPlusVK70): pass -from pyasic.miners.device.models import M60SPlusVL10 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVL10 class BTMinerM60SPlusVL10(M6X, M60SPlusVL10): pass -from pyasic.miners.device.models import M60SPlusVL30 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVL30 class BTMinerM60SPlusVL30(M6X, M60SPlusVL30): pass -from pyasic.miners.device.models import M60SPlusVL40 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVL40 class BTMinerM60SPlusVL40(M6X, M60SPlusVL40): pass -from pyasic.miners.device.models import M60SPlusVL50 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVL50 class BTMinerM60SPlusVL50(M6X, M60SPlusVL50): pass -from pyasic.miners.device.models import M60SPlusVL60 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusVL60 class BTMinerM60SPlusVL60(M6X, M60SPlusVL60): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus_Plus.py index 8103f17a2..4cf380057 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M60S_Plus_Plus.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M60SPlusPlusVL30 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusPlusVL30 class BTMinerM60SPlusPlusVL30(M6X, M60SPlusPlusVL30): pass -from pyasic.miners.device.models import M60SPlusPlusVL40 +from pyasic.miners.device.models.whatsminer.M6X import M60SPlusPlusVL40 class BTMinerM60SPlusPlusVL40(M6X, M60SPlusPlusVL40): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M61.py b/pyasic/miners/whatsminer/btminer/M6X/M61.py index a49158d58..d6ca78775 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M61.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M61.py @@ -1,61 +1,61 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M61VK10 +from pyasic.miners.device.models.whatsminer.M6X import M61VK10 class BTMinerM61VK10(M6X, M61VK10): pass -from pyasic.miners.device.models import M61VK20 +from pyasic.miners.device.models.whatsminer.M6X import M61VK20 class BTMinerM61VK20(M6X, M61VK20): pass -from pyasic.miners.device.models import M61VK30 +from pyasic.miners.device.models.whatsminer.M6X import M61VK30 class BTMinerM61VK30(M6X, M61VK30): pass -from pyasic.miners.device.models import M61VK40 +from pyasic.miners.device.models.whatsminer.M6X import M61VK40 class BTMinerM61VK40(M6X, M61VK40): pass -from pyasic.miners.device.models import M61VL10 +from pyasic.miners.device.models.whatsminer.M6X import M61VL10 class BTMinerM61VL10(M6X, M61VL10): pass -from pyasic.miners.device.models import M61VL30 +from pyasic.miners.device.models.whatsminer.M6X import M61VL30 class BTMinerM61VL30(M6X, M61VL30): pass -from pyasic.miners.device.models import M61VL40 +from pyasic.miners.device.models.whatsminer.M6X import M61VL40 class BTMinerM61VL40(M6X, M61VL40): pass -from pyasic.miners.device.models import M61VL50 +from pyasic.miners.device.models.whatsminer.M6X import M61VL50 class BTMinerM61VL50(M6X, M61VL50): pass -from pyasic.miners.device.models import M61VL60 +from pyasic.miners.device.models.whatsminer.M6X import M61VL60 class BTMinerM61VL60(M6X, M61VL60): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M61S.py b/pyasic/miners/whatsminer/btminer/M6X/M61S.py index ca597fe53..a0b413cf4 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M61S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M61S.py @@ -1,19 +1,19 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M61SVL10 +from pyasic.miners.device.models.whatsminer.M6X import M61SVL10 class BTMinerM61SVL10(M6X, M61SVL10): pass -from pyasic.miners.device.models import M61SVL20 +from pyasic.miners.device.models.whatsminer.M6X import M61SVL20 class BTMinerM61SVL20(M6X, M61SVL20): pass -from pyasic.miners.device.models import M61SVL30 +from pyasic.miners.device.models.whatsminer.M6X import M61SVL30 class BTMinerM61SVL30(M6X, M61SVL30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M61S_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M61S_Plus.py index 8b2ea8e87..83f71173a 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M61S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M61S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M61SPlusVL30 +from pyasic.miners.device.models.whatsminer.M6X import M61SPlusVL30 class BTMinerM61SPlusVL30(M6X, M61SPlusVL30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M62S_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M62S_Plus.py index 0ec4a46b2..dec268bd5 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M62S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M62S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M62SPlusVK30 +from pyasic.miners.device.models.whatsminer.M6X import M62SPlusVK30 class BTMinerM62SPlusVK30(M6X, M62SPlusVK30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M63.py b/pyasic/miners/whatsminer/btminer/M6X/M63.py index 4eca2d6d5..9a27ebe63 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M63.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M63.py @@ -1,33 +1,33 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M63VK10 +from pyasic.miners.device.models.whatsminer.M6X import M63VK10 class BTMinerM63VK10(M6X, M63VK10): pass -from pyasic.miners.device.models import M63VK20 +from pyasic.miners.device.models.whatsminer.M6X import M63VK20 class BTMinerM63VK20(M6X, M63VK20): pass -from pyasic.miners.device.models import M63VK30 +from pyasic.miners.device.models.whatsminer.M6X import M63VK30 class BTMinerM63VK30(M6X, M63VK30): pass -from pyasic.miners.device.models import M63VL10 +from pyasic.miners.device.models.whatsminer.M6X import M63VL10 class BTMinerM63VL10(M6X, M63VL10): pass -from pyasic.miners.device.models import M63VL30 +from pyasic.miners.device.models.whatsminer.M6X import M63VL30 class BTMinerM63VL30(M6X, M63VL30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M63S.py b/pyasic/miners/whatsminer/btminer/M6X/M63S.py index ab52adb7a..f777ea593 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M63S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M63S.py @@ -1,47 +1,47 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M63SVK10 +from pyasic.miners.device.models.whatsminer.M6X import M63SVK10 class BTMinerM63SVK10(M6X, M63SVK10): pass -from pyasic.miners.device.models import M63SVK20 +from pyasic.miners.device.models.whatsminer.M6X import M63SVK20 class BTMinerM63SVK20(M6X, M63SVK20): pass -from pyasic.miners.device.models import M63SVK30 +from pyasic.miners.device.models.whatsminer.M6X import M63SVK30 class BTMinerM63SVK30(M6X, M63SVK30): pass -from pyasic.miners.device.models import M63SVK60 +from pyasic.miners.device.models.whatsminer.M6X import M63SVK60 class BTMinerM63SVK60(M6X, M63SVK60): pass -from pyasic.miners.device.models import M63SVL10 +from pyasic.miners.device.models.whatsminer.M6X import M63SVL10 class BTMinerM63SVL10(M6X, M63SVL10): pass -from pyasic.miners.device.models import M63SVL50 +from pyasic.miners.device.models.whatsminer.M6X import M63SVL50 class BTMinerM63SVL50(M6X, M63SVL50): pass -from pyasic.miners.device.models import M63SVL60 +from pyasic.miners.device.models.whatsminer.M6X import M63SVL60 class BTMinerM63SVL60(M6X, M63SVL60): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus.py index 0bb2c2ac3..e62e065c6 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus.py @@ -1,33 +1,33 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M63SPlusVK30 +from pyasic.miners.device.models.whatsminer.M6X import M63SPlusVK30 class BTMinerM63SPlusVK30(M6X, M63SPlusVK30): pass -from pyasic.miners.device.models import M63SPlusVL10 +from pyasic.miners.device.models.whatsminer.M6X import M63SPlusVL10 class BTMinerM63SPlusVL10(M6X, M63SPlusVL10): pass -from pyasic.miners.device.models import M63SPlusVL20 +from pyasic.miners.device.models.whatsminer.M6X import M63SPlusVL20 class BTMinerM63SPlusVL20(M6X, M63SPlusVL20): pass -from pyasic.miners.device.models import M63SPlusVL30 +from pyasic.miners.device.models.whatsminer.M6X import M63SPlusVL30 class BTMinerM63SPlusVL30(M6X, M63SPlusVL30): pass -from pyasic.miners.device.models import M63SPlusVL50 +from pyasic.miners.device.models.whatsminer.M6X import M63SPlusVL50 class BTMinerM63SPlusVL50(M6X, M63SPlusVL50): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus_Plus.py index 64bc45df1..dec39f54a 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M63S_Plus_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M63SPlusPlusVL20 +from pyasic.miners.device.models.whatsminer.M6X import M63SPlusPlusVL20 class BTMinerM63SPlusPlusVL20(M6X, M63SPlusPlusVL20): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M64.py b/pyasic/miners/whatsminer/btminer/M6X/M64.py index 8cf823157..8b6d7bce4 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M64.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M64.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M64VL30 +from pyasic.miners.device.models.whatsminer.M6X import M64VL30 class BTMinerM64VL30(M6X, M64VL30): pass -from pyasic.miners.device.models import M64VL40 +from pyasic.miners.device.models.whatsminer.M6X import M64VL40 class BTMinerM64VL40(M6X, M64VL40): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M64S.py b/pyasic/miners/whatsminer/btminer/M6X/M64S.py index af58d56a6..9215ce853 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M64S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M64S.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M64SVL30 +from pyasic.miners.device.models.whatsminer.M6X import M64SVL30 class BTMinerM64SVL30(M6X, M64SVL30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M65S.py b/pyasic/miners/whatsminer/btminer/M6X/M65S.py index f287677ba..f1da39754 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M65S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M65S.py @@ -1,12 +1,12 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M65SVK20 +from pyasic.miners.device.models.whatsminer.M6X import M65SVK20 class BTMinerM65SVK20(M6X, M65SVK20): pass -from pyasic.miners.device.models import M65SVL60 +from pyasic.miners.device.models.whatsminer.M6X import M65SVL60 class BTMinerM65SVL60(M6X, M65SVL60): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M65S_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M65S_Plus.py index 637d82f3c..dcb2d440e 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M65S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M65S_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M65SPlusVK30 +from pyasic.miners.device.models.whatsminer.M6X import M65SPlusVK30 class BTMinerM65SPlusVK30(M6X, M65SPlusVK30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66.py b/pyasic/miners/whatsminer/btminer/M6X/M66.py index f7520bcc0..d0a923069 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M66.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M66.py @@ -1,26 +1,26 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M66VK20 +from pyasic.miners.device.models.whatsminer.M6X import M66VK20 class BTMinerM66VK20(M6X, M66VK20): pass -from pyasic.miners.device.models import M66VK30 +from pyasic.miners.device.models.whatsminer.M6X import M66VK30 class BTMinerM66VK30(M6X, M66VK30): pass -from pyasic.miners.device.models import M66VL20 +from pyasic.miners.device.models.whatsminer.M6X import M66VL20 class BTMinerM66VL20(M6X, M66VL20): pass -from pyasic.miners.device.models import M66VL30 +from pyasic.miners.device.models.whatsminer.M6X import M66VL30 class BTMinerM66VL30(M6X, M66VL30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66S.py b/pyasic/miners/whatsminer/btminer/M6X/M66S.py index 7971f4316..ebf923a06 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M66S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M66S.py @@ -1,68 +1,68 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M66SVK20 +from pyasic.miners.device.models.whatsminer.M6X import M66SVK20 class BTMinerM66SVK20(M6X, M66SVK20): pass -from pyasic.miners.device.models import M66SVK30 +from pyasic.miners.device.models.whatsminer.M6X import M66SVK30 class BTMinerM66SVK30(M6X, M66SVK30): pass -from pyasic.miners.device.models import M66SVK40 +from pyasic.miners.device.models.whatsminer.M6X import M66SVK40 class BTMinerM66SVK40(M6X, M66SVK40): pass -from pyasic.miners.device.models import M66SVK50 +from pyasic.miners.device.models.whatsminer.M6X import M66SVK50 class BTMinerM66SVK50(M6X, M66SVK50): pass -from pyasic.miners.device.models import M66SVK60 +from pyasic.miners.device.models.whatsminer.M6X import M66SVK60 class BTMinerM66SVK60(M6X, M66SVK60): pass -from pyasic.miners.device.models import M66SVL10 +from pyasic.miners.device.models.whatsminer.M6X import M66SVL10 class BTMinerM66SVL10(M6X, M66SVL10): pass -from pyasic.miners.device.models import M66SVL20 +from pyasic.miners.device.models.whatsminer.M6X import M66SVL20 class BTMinerM66SVL20(M6X, M66SVL20): pass -from pyasic.miners.device.models import M66SVL30 +from pyasic.miners.device.models.whatsminer.M6X import M66SVL30 class BTMinerM66SVL30(M6X, M66SVL30): pass -from pyasic.miners.device.models import M66SVL40 +from pyasic.miners.device.models.whatsminer.M6X import M66SVL40 class BTMinerM66SVL40(M6X, M66SVL40): pass -from pyasic.miners.device.models import M66SVL50 +from pyasic.miners.device.models.whatsminer.M6X import M66SVL50 class BTMinerM66SVL50(M6X, M66SVL50): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus.py index 8e6620416..199a80993 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus.py @@ -1,40 +1,40 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M66SPlusVK30 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusVK30 class BTMinerM66SPlusVK30(M6X, M66SPlusVK30): pass -from pyasic.miners.device.models import M66SPlusVL10 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusVL10 class BTMinerM66SPlusVL10(M6X, M66SPlusVL10): pass -from pyasic.miners.device.models import M66SPlusVL20 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusVL20 class BTMinerM66SPlusVL20(M6X, M66SPlusVL20): pass -from pyasic.miners.device.models import M66SPlusVL30 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusVL30 class BTMinerM66SPlusVL30(M6X, M66SPlusVL30): pass -from pyasic.miners.device.models import M66SPlusVL40 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusVL40 class BTMinerM66SPlusVL40(M6X, M66SPlusVL40): pass -from pyasic.miners.device.models import M66SPlusVL60 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusVL60 class BTMinerM66SPlusVL60(M6X, M66SPlusVL60): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus_Plus.py b/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus_Plus.py index ec3e658a1..dba309590 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus_Plus.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M66S_Plus_Plus.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M66SPlusPlusVL20 +from pyasic.miners.device.models.whatsminer.M6X import M66SPlusPlusVL20 class BTMinerM66SPlusPlusVL20(M6X, M66SPlusPlusVL20): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M67S.py b/pyasic/miners/whatsminer/btminer/M6X/M67S.py index ab7ddb133..a9b1edfc6 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M67S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M67S.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M6X -from pyasic.miners.device.models import M67SVK30 +from pyasic.miners.device.models.whatsminer.M6X import M67SVK30 class BTMinerM67SVK30(M6X, M67SVK30): diff --git a/pyasic/miners/whatsminer/btminer/M6X/__init__.py b/pyasic/miners/whatsminer/btminer/M6X/__init__.py index 6e46d20d3..4eecf1fae 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/__init__.py +++ b/pyasic/miners/whatsminer/btminer/M6X/__init__.py @@ -101,3 +101,99 @@ ) from .M66S_Plus_Plus import BTMinerM66SPlusPlusVL20 from .M67S import BTMinerM67SVK30 + +__all__ = [ + "BTMinerM60VK6A", + "BTMinerM60VK10", + "BTMinerM60VK20", + "BTMinerM60VK30", + "BTMinerM60VK40", + "BTMinerM60VL10", + "BTMinerM60VL20", + "BTMinerM60VL30", + "BTMinerM60VL40", + "BTMinerM60VL50", + "BTMinerM60SVK10", + "BTMinerM60SVK20", + "BTMinerM60SVK30", + "BTMinerM60SVK40", + "BTMinerM60SVL10", + "BTMinerM60SVL20", + "BTMinerM60SVL30", + "BTMinerM60SVL40", + "BTMinerM60SVL50", + "BTMinerM60SVL60", + "BTMinerM60SVL70", + "BTMinerM60SPlusVK30", + "BTMinerM60SPlusVK40", + "BTMinerM60SPlusVK50", + "BTMinerM60SPlusVK60", + "BTMinerM60SPlusVK70", + "BTMinerM60SPlusVL10", + "BTMinerM60SPlusVL30", + "BTMinerM60SPlusVL40", + "BTMinerM60SPlusVL50", + "BTMinerM60SPlusVL60", + "BTMinerM60SPlusPlusVL30", + "BTMinerM60SPlusPlusVL40", + "BTMinerM61VK10", + "BTMinerM61VK20", + "BTMinerM61VK30", + "BTMinerM61VK40", + "BTMinerM61VL10", + "BTMinerM61VL30", + "BTMinerM61VL40", + "BTMinerM61VL50", + "BTMinerM61VL60", + "BTMinerM61SVL10", + "BTMinerM61SVL20", + "BTMinerM61SVL30", + "BTMinerM61SPlusVL30", + "BTMinerM62SPlusVK30", + "BTMinerM63VK10", + "BTMinerM63VK20", + "BTMinerM63VK30", + "BTMinerM63VL10", + "BTMinerM63VL30", + "BTMinerM63SVK10", + "BTMinerM63SVK20", + "BTMinerM63SVK30", + "BTMinerM63SVK60", + "BTMinerM63SVL10", + "BTMinerM63SVL50", + "BTMinerM63SVL60", + "BTMinerM63SPlusVK30", + "BTMinerM63SPlusVL10", + "BTMinerM63SPlusVL20", + "BTMinerM63SPlusVL30", + "BTMinerM63SPlusVL50", + "BTMinerM63SPlusPlusVL20", + "BTMinerM64VL30", + "BTMinerM64VL40", + "BTMinerM64SVL30", + "BTMinerM65SVK20", + "BTMinerM65SVL60", + "BTMinerM65SPlusVK30", + "BTMinerM66VK20", + "BTMinerM66VK30", + "BTMinerM66VL20", + "BTMinerM66VL30", + "BTMinerM66SVK20", + "BTMinerM66SVK30", + "BTMinerM66SVK40", + "BTMinerM66SVK50", + "BTMinerM66SVK60", + "BTMinerM66SVL10", + "BTMinerM66SVL20", + "BTMinerM66SVL30", + "BTMinerM66SVL40", + "BTMinerM66SVL50", + "BTMinerM66SPlusVK30", + "BTMinerM66SPlusVL10", + "BTMinerM66SPlusVL20", + "BTMinerM66SPlusVL30", + "BTMinerM66SPlusVL40", + "BTMinerM66SPlusVL60", + "BTMinerM66SPlusPlusVL20", + "BTMinerM67SVK30", +] diff --git a/pyasic/miners/whatsminer/btminer/M7X/M70.py b/pyasic/miners/whatsminer/btminer/M7X/M70.py index e10f74b95..8e40d09ea 100644 --- a/pyasic/miners/whatsminer/btminer/M7X/M70.py +++ b/pyasic/miners/whatsminer/btminer/M7X/M70.py @@ -1,5 +1,5 @@ from pyasic.miners.backends import M7X -from pyasic.miners.device.models import M70VM30 +from pyasic.miners.device.models.whatsminer.M7X import M70VM30 class BTMinerM70VM30(M7X, M70VM30): diff --git a/pyasic/miners/whatsminer/btminer/M7X/__init__.py b/pyasic/miners/whatsminer/btminer/M7X/__init__.py index 22f818161..9bb0de854 100644 --- a/pyasic/miners/whatsminer/btminer/M7X/__init__.py +++ b/pyasic/miners/whatsminer/btminer/M7X/__init__.py @@ -1 +1,5 @@ from .M70 import BTMinerM70VM30 + +__all__ = [ + "BTMinerM70VM30", +] diff --git a/pyasic/misc/__init__.py b/pyasic/misc/__init__.py index 79e20d225..9c9259238 100644 --- a/pyasic/misc/__init__.py +++ b/pyasic/misc/__init__.py @@ -15,15 +15,19 @@ # ------------------------------------------------------------------------------ from __future__ import annotations +import functools +from collections.abc import Callable from copy import deepcopy +from typing import Any from pyasic.errors import APIError -def api_min_version(version: str): - def decorator(func): +def api_min_version(version: str) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: # handle the inner function that the decorator is wrapping - async def inner(*args, **kwargs): + @functools.wraps(func) + async def inner(*args: Any, **kwargs: Any) -> Any: api_ver = args[0].api_ver if not api_ver == "0.0.0" and isinstance(api_ver, str): @@ -73,7 +77,7 @@ async def inner(*args, **kwargs): return decorator -def merge_dicts(a: dict, b: dict) -> dict: +def merge_dicts(a: dict[str, Any], b: dict[str, Any]) -> dict[str, Any]: result = deepcopy(a) for b_key, b_val in b.items(): a_val = result.get(b_key) @@ -84,7 +88,7 @@ def merge_dicts(a: dict, b: dict) -> dict: return result -def validate_command_output(data: dict) -> tuple[bool, str | None]: +def validate_command_output(data: dict[str, Any]) -> tuple[bool, str | None]: if "STATUS" in data.keys(): status = data["STATUS"] if isinstance(status, str): diff --git a/pyasic/network/__init__.py b/pyasic/network/__init__.py index f832d44a5..e06e24d16 100644 --- a/pyasic/network/__init__.py +++ b/pyasic/network/__init__.py @@ -21,7 +21,8 @@ from typing import cast from pyasic import settings -from pyasic.miners.factory import AnyMiner, miner_factory +from pyasic.miners.base import AnyMiner +from pyasic.miners.factory import miner_factory class MinerNetwork: @@ -31,7 +32,7 @@ class MinerNetwork: hosts: A list of `ipaddress.IPv4Address` to be used when scanning. """ - def __init__(self, hosts: list[ipaddress.IPv4Address]): + def __init__(self, hosts: list[ipaddress.IPv4Address]) -> None: self.hosts = hosts semaphore_limit = settings.get("network_scan_semaphore", 255) if semaphore_limit is None: diff --git a/pyasic/rpc/__init__.py b/pyasic/rpc/__init__.py index 93dd66317..17f14a79c 100644 --- a/pyasic/rpc/__init__.py +++ b/pyasic/rpc/__init__.py @@ -22,3 +22,15 @@ from .gcminer import GCMinerRPCAPI from .luxminer import LUXMinerRPCAPI from .unknown import UnknownRPCAPI + +__all__ = [ + "BFGMinerRPCAPI", + "BMMinerRPCAPI", + "BOSMinerRPCAPI", + "BTMinerRPCAPI", + "CCMinerRPCAPI", + "CGMinerRPCAPI", + "GCMinerRPCAPI", + "LUXMinerRPCAPI", + "UnknownRPCAPI", +] diff --git a/pyasic/rpc/antminer.py b/pyasic/rpc/antminer.py index 7105a07c5..1f90c105b 100644 --- a/pyasic/rpc/antminer.py +++ b/pyasic/rpc/antminer.py @@ -1,36 +1,38 @@ +from typing import Any + from pyasic.rpc.bmminer import BMMinerRPCAPI class AntminerRPCAPI(BMMinerRPCAPI): - async def stats(self, new_api: bool = False) -> dict: + async def stats(self, new_api: bool = False) -> dict[str, Any]: if new_api: return await self.send_command("stats", new_api=True) return await super().stats() - async def rate(self): + async def rate(self) -> dict[str, Any]: return await self.send_command("rate", new_api=True) - async def pools(self, new_api: bool = False): + async def pools(self, new_api: bool = False) -> dict[str, Any]: if new_api: return await self.send_command("pools", new_api=True) return await self.send_command("pools") - async def reload(self): + async def reload(self) -> dict[str, Any]: return await self.send_command("reload", new_api=True) - async def summary(self, new_api: bool = False): + async def summary(self, new_api: bool = False) -> dict[str, Any]: if new_api: return await self.send_command("summary", new_api=True) return await self.send_command("summary") - async def warning(self): + async def warning(self) -> dict[str, Any]: return await self.send_command("warning", new_api=True) - async def new_api_pools(self): + async def new_api_pools(self) -> dict[str, Any]: return await self.pools(new_api=True) - async def new_api_stats(self): + async def new_api_stats(self) -> dict[str, Any]: return await self.stats(new_api=True) - async def new_api_summary(self): + async def new_api_summary(self) -> dict[str, Any]: return await self.summary(new_api=True) diff --git a/pyasic/rpc/avalonminer.py b/pyasic/rpc/avalonminer.py index 0dff3c174..ec4b42e34 100644 --- a/pyasic/rpc/avalonminer.py +++ b/pyasic/rpc/avalonminer.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.cgminer import CGMinerRPCAPI @@ -23,5 +25,5 @@ class AvalonMinerRPCAPI(CGMinerRPCAPI): Each method corresponds to an API command in AvalonMiner. """ - async def litestats(self): + async def litestats(self) -> dict[str, Any]: return await self.send_command("litestats") diff --git a/pyasic/rpc/base.py b/pyasic/rpc/base.py index eff331b21..ba84e167f 100644 --- a/pyasic/rpc/base.py +++ b/pyasic/rpc/base.py @@ -20,6 +20,7 @@ import logging import re import warnings +from typing import Any from pyasic.errors import APIError, APIWarning from pyasic.misc import validate_command_output @@ -36,7 +37,7 @@ def __init__(self, ip: str, port: int = 4028, api_ver: str = "0.0.0") -> None: self.pwd: str | None = None - def __new__(cls, *args, **kwargs): + def __new__(cls, *args: Any, **kwargs: Any) -> "BaseMinerRPCAPI": if cls is BaseMinerRPCAPI: raise TypeError(f"Only children of '{cls.__name__}' may be instantiated") return object.__new__(cls) @@ -50,8 +51,8 @@ async def send_command( parameters: str | int | bool | None = None, ignore_errors: bool = False, allow_warning: bool = True, - **kwargs, - ) -> dict: + **kwargs: Any, + ) -> dict[str, Any]: """Send an API command to the miner and return the result. Parameters: @@ -102,10 +103,14 @@ async def send_command( return api_data # Privileged command handler, only used by whatsminers, defined here for consistency. - async def send_privileged_command(self, *args, **kwargs) -> dict: + async def send_privileged_command( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any]: return await self.send_command(*args, **kwargs) - async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict: + async def multicommand( + self, *commands: str, allow_warning: bool = True + ) -> dict[str, Any]: """Creates and sends multiple commands as one command to the miner. Parameters: @@ -126,9 +131,9 @@ async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict return data async def _send_split_multicommand( - self, *commands, allow_warning: bool = True - ) -> dict: - tasks = {} + self, *commands: str, allow_warning: bool = True + ) -> dict[str, Any]: + tasks: dict[str, asyncio.Task[dict[str, Any]]] = {} # send all commands individually for cmd in commands: tasks[cmd] = asyncio.create_task( @@ -139,7 +144,7 @@ async def _send_split_multicommand( *[tasks[cmd] for cmd in tasks], return_exceptions=True ) - data = {} + data: dict[str, Any] = {} for cmd, result in zip(tasks.keys(), results): if not isinstance(result, (APIError, Exception)): if result is None or result == {}: @@ -149,10 +154,10 @@ async def _send_split_multicommand( return data @property - def commands(self) -> list: + def commands(self) -> list[str]: return self.get_commands() - def get_commands(self) -> list: + def get_commands(self) -> list[str]: """Get a list of command accessible to a specific type of API on the miner. Returns: @@ -179,7 +184,7 @@ def get_commands(self) -> list: ] ] - def _check_commands(self, *commands) -> list: + def _check_commands(self, *commands: str) -> list[str]: allowed_commands = self.commands return_commands = [] @@ -250,7 +255,7 @@ async def _read_bytes(self, reader: asyncio.StreamReader, timeout: int) -> bytes return ret_data @staticmethod - def _load_api_data(data: bytes) -> dict: + def _load_api_data(data: bytes) -> dict[str, Any]: # some json from the API returns with a null byte (\x00) on the end if data.endswith(b"\x00"): # handle the null byte @@ -289,4 +294,9 @@ def _load_api_data(data: bytes) -> dict: parsed_data = json.loads(str_data) except json.decoder.JSONDecodeError as e: raise APIError(f"Decode Error {e}: {str_data}") + # Ensure we return a dict as declared in the type annotation + if not isinstance(parsed_data, dict): + raise APIError( + f"Expected dict from JSON, got {type(parsed_data)}: {parsed_data}" + ) return parsed_data diff --git a/pyasic/rpc/bfgminer.py b/pyasic/rpc/bfgminer.py index 366b822fb..8cbf6e274 100644 --- a/pyasic/rpc/bfgminer.py +++ b/pyasic/rpc/bfgminer.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.cgminer import CGMinerRPCAPI @@ -31,7 +33,7 @@ class BFGMinerRPCAPI(CGMinerRPCAPI): rely on it to send the command for them. """ - async def procs(self) -> dict: + async def procs(self) -> dict[str, Any]: """Get data on each processor with their details.
Expand @@ -42,7 +44,7 @@ async def procs(self) -> dict: """ return await self.send_command("procs") - async def devscan(self, info: str = "") -> dict: + async def devscan(self, info: str = "") -> dict[str, Any]: """Get data on each processor with their details.
Expand @@ -56,7 +58,7 @@ async def devscan(self, info: str = "") -> dict: """ return await self.send_command("devscan", parameters=info) - async def proc(self, n: int = 0) -> dict: + async def proc(self, n: int = 0) -> dict[str, Any]: """Get data processor n.
Expand @@ -70,7 +72,7 @@ async def proc(self, n: int = 0) -> dict: """ return await self.send_command("proc", parameters=n) - async def proccount(self) -> dict: + async def proccount(self) -> dict[str, Any]: """Get data fon all processors.
Expand @@ -81,7 +83,7 @@ async def proccount(self) -> dict: """ return await self.send_command("proccount") - async def pgarestart(self, n: int) -> dict: + async def pgarestart(self, n: int) -> dict[str, Any]: """Restart PGA n.
Expand @@ -95,7 +97,7 @@ async def pgarestart(self, n: int) -> dict: """ return await self.send_command("pgadisable", parameters=n) - async def procenable(self, n: int) -> dict: + async def procenable(self, n: int) -> dict[str, Any]: """Enable processor n.
Expand @@ -109,7 +111,7 @@ async def procenable(self, n: int) -> dict: """ return await self.send_command("procenable", parameters=n) - async def procdisable(self, n: int) -> dict: + async def procdisable(self, n: int) -> dict[str, Any]: """Disable processor n.
Expand @@ -123,7 +125,7 @@ async def procdisable(self, n: int) -> dict: """ return await self.send_command("procdisable", parameters=n) - async def procrestart(self, n: int) -> dict: + async def procrestart(self, n: int) -> dict[str, Any]: """Restart processor n.
Expand @@ -137,7 +139,7 @@ async def procrestart(self, n: int) -> dict: """ return await self.send_command("procdisable", parameters=n) - async def procidentify(self, n: int) -> dict: + async def procidentify(self, n: int) -> dict[str, Any]: """Identify processor n.
Expand @@ -151,7 +153,7 @@ async def procidentify(self, n: int) -> dict: """ return await self.send_command("procidentify", parameters=n) - async def procset(self, n: int, opt: str, val: int | None = None) -> dict: + async def procset(self, n: int, opt: str, val: int | None = None) -> dict[str, Any]: """Set processor option opt to val on processor n.
Expand diff --git a/pyasic/rpc/bosminer.py b/pyasic/rpc/bosminer.py index 11829067c..4384bd83d 100644 --- a/pyasic/rpc/bosminer.py +++ b/pyasic/rpc/bosminer.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.base import BaseMinerRPCAPI @@ -31,7 +33,7 @@ class BOSMinerRPCAPI(BaseMinerRPCAPI): rely on it to send the command for them. """ - async def asccount(self) -> dict: + async def asccount(self) -> dict[str, Any]: """Get data on the number of ASC devices and their info.
Expand @@ -42,7 +44,7 @@ async def asccount(self) -> dict: """ return await self.send_command("asccount") - async def asc(self, n: int) -> dict: + async def asc(self, n: int) -> dict[str, Any]: """Get data for ASC device n.
Expand @@ -56,7 +58,7 @@ async def asc(self, n: int) -> dict: """ return await self.send_command("asc", parameters=n) - async def devdetails(self) -> dict: + async def devdetails(self) -> dict[str, Any]: """Get data on all devices with their static details.
Expand @@ -67,7 +69,7 @@ async def devdetails(self) -> dict: """ return await self.send_command("devdetails") - async def devs(self) -> dict: + async def devs(self) -> dict[str, Any]: """Get data on each PGA/ASC with their details.
Expand @@ -78,7 +80,7 @@ async def devs(self) -> dict: """ return await self.send_command("devs") - async def edevs(self, old: bool = False) -> dict: + async def edevs(self, old: bool = False) -> dict[str, Any]: """Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
Expand @@ -95,7 +97,7 @@ async def edevs(self, old: bool = False) -> dict: else: return await self.send_command("edevs") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Get pool information.
@@ -107,7 +109,7 @@ async def pools(self) -> dict: """ return await self.send_command("pools") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get the status summary of the miner.
@@ -119,7 +121,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Get stats of each device/pool with more than 1 getwork.
@@ -131,7 +133,7 @@ async def stats(self) -> dict: """ return await self.send_command("stats") - async def version(self) -> dict: + async def version(self) -> dict[str, Any]: """Get miner version info.
@@ -143,7 +145,7 @@ async def version(self) -> dict: """ return await self.send_command("version") - async def estats(self, old: bool = False) -> dict: + async def estats(self, old: bool = False) -> dict[str, Any]: """Get stats of each device/pool with more than 1 getwork, ignoring zombie devices.
Expand @@ -160,7 +162,7 @@ async def estats(self, old: bool = False) -> dict: else: return await self.send_command("estats") - async def check(self, command: str) -> dict: + async def check(self, command: str) -> dict[str, Any]: """Check if the command `command` exists in BOSMiner.
Expand @@ -176,7 +178,7 @@ async def check(self, command: str) -> dict: """ return await self.send_command("check", parameters=command) - async def coin(self) -> dict: + async def coin(self) -> dict[str, Any]: """Get information on the current coin.
Expand @@ -192,7 +194,7 @@ async def coin(self) -> dict: """ return await self.send_command("coin") - async def lcd(self) -> dict: + async def lcd(self) -> dict[str, Any]: """Get a general all-in-one status summary of the miner.
Expand @@ -203,7 +205,7 @@ async def lcd(self) -> dict: """ return await self.send_command("lcd") - async def fans(self) -> dict: + async def fans(self) -> dict[str, Any]: """Get fan data.
Expand @@ -214,7 +216,7 @@ async def fans(self) -> dict: """ return await self.send_command("fans") - async def tempctrl(self) -> dict: + async def tempctrl(self) -> dict[str, Any]: """Get temperature control data.
Expand @@ -225,7 +227,7 @@ async def tempctrl(self) -> dict: """ return await self.send_command("tempctrl") - async def temps(self) -> dict: + async def temps(self) -> dict[str, Any]: """Get temperature data.
Expand @@ -236,7 +238,7 @@ async def temps(self) -> dict: """ return await self.send_command("temps") - async def tunerstatus(self) -> dict: + async def tunerstatus(self) -> dict[str, Any]: """Get tuner status data
Expand @@ -247,7 +249,7 @@ async def tunerstatus(self) -> dict: """ return await self.send_command("tunerstatus") - async def pause(self) -> dict: + async def pause(self) -> dict[str, Any]: """Pause mining.
Expand @@ -258,7 +260,7 @@ async def pause(self) -> dict: """ return await self.send_command("pause") - async def resume(self) -> dict: + async def resume(self) -> dict[str, Any]: """Resume mining.
Expand diff --git a/pyasic/rpc/btminer.py b/pyasic/rpc/btminer.py index 1dd9365d1..67ba649a0 100644 --- a/pyasic/rpc/btminer.py +++ b/pyasic/rpc/btminer.py @@ -26,12 +26,13 @@ import typing import warnings from collections.abc import AsyncGenerator -from typing import Any, Literal +from enum import IntEnum +from typing import Annotated, Any, Literal import httpx from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from passlib.handlers.md5_crypt import md5_crypt -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field from pyasic import settings from pyasic.errors import APIError, APIWarning @@ -47,14 +48,22 @@ # or add it as the Whatsminer_pwd in the settings.toml file. +class BTMinerAPICode(IntEnum): + MSG_CMDOK = 0 + MSG_CMDERR = -1 + MSG_INVCMD = -2 + MSG_INVJSON = -3 + MSG_ACCDENY = -4 + MSG_OUT_OF_MEMORY = -5 + + class TokenResponse(BaseModel): + model_config = ConfigDict(extra="allow") + salt: str time: str newsalt: str - class Config: - extra = "allow" - class TokenData(BaseModel): host_sign: str @@ -62,32 +71,22 @@ class TokenData(BaseModel): timestamp: datetime.datetime = Field(default_factory=datetime.datetime.now) -class BTMinerPrivilegedCommand(BaseModel): - cmd: str - token: str - - class Config: - extra = "allow" - - class BTMinerV3Command(BaseModel): + model_config = ConfigDict(extra="forbid") + cmd: str param: Any | None = None - class Config: - extra = "forbid" - class BTMinerV3PrivilegedCommand(BaseModel): + model_config = ConfigDict(extra="forbid") + cmd: str param: Any | None = None ts: int account: str token: str - class Config: - extra = "forbid" - PrePowerOnMessage = ( Literal["wait for adjust temp"] @@ -96,6 +95,254 @@ class Config: ) +class BTMinerV3Pool(BaseModel): + model_config = ConfigDict(extra="allow") + + id: int | None = None + url: str | None = None + status: str | None = None + account: str | None = None + stratum_active: bool | None = Field(None, alias="stratum-active") + reject_rate: float | None = Field(None, alias="reject-rate") + last_share_time: float | None = Field(None, alias="last-share-time") + + +class BTMinerV3Summary(BaseModel): + model_config = ConfigDict(extra="allow") + + elapsed: int + bootup_time: int = Field(alias="bootup-time") + freq_avg: float = Field(alias="freq-avg") + target_freq: int | None = Field(None, alias="target-freq") + factory_hash: float = Field(alias="factory-hash") + hash_average: float = Field(alias="hash-average") + hash_1min: float = Field(alias="hash-1min") + hash_15min: float = Field(alias="hash-15min") + hash_realtime: float = Field(alias="hash-realtime") + power_rate: float = Field(alias="power-rate") + power_5min: float = Field(alias="power-5min") + power_realtime: float = Field(alias="power-realtime") + environment_temperature: float = Field(alias="environment-temperature") + board_temperature: list[float] = Field(alias="board-temperature") + chip_temp_min: float = Field(alias="chip-temp-min") + chip_temp_avg: float = Field(alias="chip-temp-avg") + chip_temp_max: float = Field(alias="chip-temp-max") + power_limit: int = Field(alias="power-limit") + up_freq_finish: int = Field(alias="up-freq-finish") + fan_speed_in: int = Field(alias="fan-speed-in") + fan_speed_out: int = Field(alias="fan-speed-out") + + +class BTMinerV3EdevItem(BaseModel): + model_config = ConfigDict(extra="allow") + + id: int + slot: int + hash_average: float = Field(alias="hash-average") + factory_hash: float = Field(alias="factory-hash") + freq: int + effective_chips: int = Field(alias="effective-chips") + chip_temp_min: float = Field(alias="chip-temp-min") + chip_temp_avg: float = Field(alias="chip-temp-avg") + chip_temp_max: float = Field(alias="chip-temp-max") + + +class BTMinerV3MinerStatusMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + pools: list[BTMinerV3Pool] = Field(default_factory=list) + summary: BTMinerV3Summary | None = None + edevs: list[BTMinerV3EdevItem] = Field(default_factory=list) + + +class BTMinerV3Network(BaseModel): + model_config = ConfigDict(extra="allow") + + mac: str | None = None + hostname: str | None = None + + +class BTMinerV3System(BaseModel): + model_config = ConfigDict(extra="allow") + + api: str | None = None + fwversion: str | None = None + ledstatus: str | None = None + + +class BTMinerV3Hardware(BaseModel): + model_config = ConfigDict(extra="allow") + + boards: int = 3 + + +class BTMinerV3Power(BaseModel): + model_config = ConfigDict(extra="allow") + + fanspeed: int | None = None + + +class BTMinerV3Miner(BaseModel): + model_config = ConfigDict(extra="allow") + + power_limit_set: str | None = Field(None, alias="power-limit-set") + pcbsn0: str | None = None + pcbsn1: str | None = None + pcbsn2: str | None = None + + +class BTMinerV3DeviceInfoMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + network: BTMinerV3Network = Field(default_factory=BTMinerV3Network) + system: BTMinerV3System = Field(default_factory=BTMinerV3System) + hardware: BTMinerV3Hardware = Field(default_factory=BTMinerV3Hardware) + power: BTMinerV3Power = Field(default_factory=BTMinerV3Power) + miner: BTMinerV3Miner = Field(default_factory=BTMinerV3Miner) + + +class BTMinerV3MinerStatusSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3MinerStatusMsg + desc: Literal["get.miner.status"] + + +class BTMinerV3DeviceInfoSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3DeviceInfoMsg + desc: Literal["get.device.info"] + + +class BTMinerV3SystemSettingMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + +class BTMinerV3SystemSettingSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3SystemSettingMsg + desc: Literal["get.system.setting"] + + +class BTMinerV3MinerHistoryMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + Data: str + + +class BTMinerV3MinerHistorySuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3MinerHistoryMsg + desc: Literal["get.miner.history"] + + +class BTMinerV3MinerSettingMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + +class BTMinerV3MinerSettingSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3MinerSettingMsg + desc: Literal["get.miner.setting"] + + +class BTMinerV3LogDownloadMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + logsize: str + + +class BTMinerV3LogDownloadSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3LogDownloadMsg + desc: Literal["get.log.download"] + + +class BTMinerV3FanSettingMsg(BaseModel): + model_config = ConfigDict(extra="allow") + + +class BTMinerV3FanSettingSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: BTMinerV3FanSettingMsg + desc: Literal["get.fan.setting"] + + +class BTMinerV3SetCommandSuccessResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[BTMinerAPICode.MSG_CMDOK] + when: int + msg: Literal["ok"] + desc: Literal[ + "set.system.reboot", + "set.system.led", + "set.miner.service", + "set.miner.report", + "set.miner.power_limit", + ] + + +BTMinerV3SuccessResponse = Annotated[ + BTMinerV3MinerStatusSuccessResponse + | BTMinerV3DeviceInfoSuccessResponse + | BTMinerV3SystemSettingSuccessResponse + | BTMinerV3MinerHistorySuccessResponse + | BTMinerV3MinerSettingSuccessResponse + | BTMinerV3LogDownloadSuccessResponse + | BTMinerV3FanSettingSuccessResponse + | BTMinerV3SetCommandSuccessResponse, + Field(discriminator="desc"), +] + + +class BTMinerV3ErrorResponse(BaseModel): + model_config = ConfigDict(extra="allow") + + code: Literal[ + BTMinerAPICode.MSG_CMDERR, + BTMinerAPICode.MSG_INVCMD, + BTMinerAPICode.MSG_INVJSON, + BTMinerAPICode.MSG_ACCDENY, + BTMinerAPICode.MSG_OUT_OF_MEMORY, + ] + when: int + msg: str + desc: str + + +BTMinerV3ResponseData = Annotated[ + BTMinerV3SuccessResponse | BTMinerV3ErrorResponse, + Field(discriminator="code"), +] + + +class BTMinerV3Response(BaseModel): + model_config = ConfigDict(extra="allow") + + data: BTMinerV3ResponseData + + def _crypt(word: str, salt: str) -> str: r"""Encrypts a word with a salt, using a standard salt format. @@ -138,7 +385,9 @@ def _add_to_16(string: str) -> bytes: return str.encode(string) # return bytes -def parse_btminer_priviledge_data(token_data: TokenData, data: dict) -> dict: +def parse_btminer_priviledge_data( + token_data: TokenData, data: dict[str, Any] +) -> dict[str, Any]: """Parses data returned from the BTMiner privileged API. Parses data from the BTMiner privileged API using the token @@ -161,7 +410,7 @@ def parse_btminer_priviledge_data(token_data: TokenData, data: dict) -> dict: aes = Cipher(algorithms.AES(aeskey), modes.ECB()) decryptor = aes.decryptor() # decode the message with the decryptor - ret_msg = json.loads( + ret_msg: dict[str, Any] = json.loads( decryptor.update(base64.decodebytes(bytes(enc_data, encoding="utf8"))) .rstrip(b"\0") .decode("utf8") @@ -169,7 +418,7 @@ def parse_btminer_priviledge_data(token_data: TokenData, data: dict) -> dict: return ret_msg -def create_privileged_cmd(token_data: TokenData, command: dict) -> bytes: +def create_privileged_cmd(token_data: TokenData, command: dict[str, Any]) -> bytes: """Create a privileged command to send to the BTMiner API. Creates a privileged command using the token from the API and the @@ -236,7 +485,9 @@ def __init__(self, ip: str, port: int = 4028, api_ver: str = "0.0.0") -> None: self.pwd: str = settings.get("default_whatsminer_rpc_password", "admin") self.token: TokenData | None = None - async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict: + async def multicommand( + self, *commands: str, allow_warning: bool = True + ) -> dict[str, Any]: """Creates and sends multiple commands as one command to the miner. Parameters: @@ -276,7 +527,7 @@ async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict except APIError: return {} - data = {} + data: dict[str, Any] = {} for item in all_data: data.update(item) @@ -288,8 +539,8 @@ async def send_privileged_command( command: str, ignore_errors: bool = False, timeout: int = 10, - **kwargs, - ) -> dict: + **kwargs: Any, + ) -> dict[str, Any]: try: return await self._send_privileged_command( command=command, ignore_errors=ignore_errors, timeout=timeout, **kwargs @@ -312,8 +563,8 @@ async def _send_privileged_command( command: str, ignore_errors: bool = False, timeout: int = 10, - **kwargs, - ) -> dict: + **kwargs: Any, + ) -> dict[str, Any]: logging.debug( f"{self} - (Send Privileged Command) - {command} " + f"with args {kwargs}" if len(kwargs) > 0 @@ -398,7 +649,7 @@ async def get_token(self) -> TokenData: logging.debug(f"{self} - (Get Token) - Gathered token data: {self.token}") return self.token - async def open_api(self): + async def open_api(self) -> bool: async with httpx.AsyncClient() as c: stage1_req = ( await c.post( @@ -444,7 +695,7 @@ async def update_pools( pool_3: str | None = None, worker_3: str | None = None, passwd_3: str | None = None, - ) -> dict: + ) -> dict[str, Any]: """Update the pools of the miner using the API.
Expand @@ -480,7 +731,7 @@ async def update_pools( passwd3=passwd_3, ) - async def restart(self) -> dict: + async def restart(self) -> dict[str, Any]: """Restart BTMiner using the API.
Expand @@ -494,7 +745,7 @@ async def restart(self) -> dict: """ return await self.send_privileged_command("restart_btminer") - async def power_off(self, respbefore: bool = True) -> dict: + async def power_off(self, respbefore: bool = True) -> dict[str, Any]: """Power off the miner using the API.
Expand @@ -512,7 +763,7 @@ async def power_off(self, respbefore: bool = True) -> dict: return await self.send_privileged_command("power_off", respbefore="true") return await self.send_privileged_command("power_off", respbefore="false") - async def power_on(self) -> dict: + async def power_on(self) -> dict[str, Any]: """Power on the miner using the API.
Expand @@ -526,7 +777,7 @@ async def power_on(self) -> dict: """ return await self.send_privileged_command("power_on") - async def reset_led(self) -> dict: + async def reset_led(self) -> dict[str, Any]: """Reset the LED on the miner using the API.
Expand @@ -547,7 +798,7 @@ async def set_led( period: int = 60, duration: int = 20, start: int = 0, - ) -> dict: + ) -> dict[str, Any]: """Set the LED on the miner using the API.
Expand @@ -571,7 +822,7 @@ async def set_led( "set_led", color=color, period=period, duration=duration, start=start ) - async def set_low_power(self) -> dict: + async def set_low_power(self) -> dict[str, Any]: """Set low power mode on the miner using the API.
Expand @@ -585,7 +836,7 @@ async def set_low_power(self) -> dict: """ return await self.send_privileged_command("set_low_power") - async def set_high_power(self) -> dict: + async def set_high_power(self) -> dict[str, Any]: """Set low power mode on the miner using the API.
Expand @@ -599,7 +850,7 @@ async def set_high_power(self) -> dict: """ return await self.send_privileged_command("set_high_power") - async def set_normal_power(self) -> dict: + async def set_normal_power(self) -> dict[str, Any]: """Set low power mode on the miner using the API.
Expand @@ -636,7 +887,7 @@ async def update_firmware(self, firmware: bytes) -> bool: await self._send_bytes(file_size + firmware) return True - async def reboot(self, timeout: int = 10) -> dict: + async def reboot(self, timeout: int = 10) -> dict[str, Any]: """Reboot the miner using the API.
Expand @@ -654,7 +905,7 @@ async def reboot(self, timeout: int = 10) -> dict: else: return d - async def factory_reset(self) -> dict: + async def factory_reset(self) -> dict[str, Any]: """Reset the miner to factory defaults.
Expand @@ -665,7 +916,7 @@ async def factory_reset(self) -> dict: """ return await self.send_privileged_command("factory_reset") - async def update_pwd(self, old_pwd: str, new_pwd: str) -> dict: + async def update_pwd(self, old_pwd: str, new_pwd: str) -> dict[str, Any]: """Update the admin user's password.
@@ -706,7 +957,7 @@ async def net_config( dns: str | None = None, host: str | None = None, dhcp: bool = True, - ): + ) -> dict[str, Any]: if dhcp: return await self.send_privileged_command("net_config", param="dhcp") if None in [ip, mask, gate, dns, host]: @@ -715,7 +966,7 @@ async def net_config( "net_config", ip=ip, mask=mask, gate=gate, dns=dns, host=host ) - async def set_target_freq(self, percent: int) -> dict: + async def set_target_freq(self, percent: int) -> dict[str, Any]: """Update the target frequency.
@@ -741,7 +992,7 @@ async def set_target_freq(self, percent: int) -> dict: "set_target_freq", percent=str(percent) ) - async def enable_fast_boot(self) -> dict: + async def enable_fast_boot(self) -> dict[str, Any]: """Turn on fast boot.
@@ -756,7 +1007,7 @@ async def enable_fast_boot(self) -> dict: """ return await self.send_privileged_command("enable_btminer_fast_boot") - async def disable_fast_boot(self) -> dict: + async def disable_fast_boot(self) -> dict[str, Any]: """Turn off fast boot.
@@ -771,7 +1022,7 @@ async def disable_fast_boot(self) -> dict: """ return await self.send_privileged_command("disable_btminer_fast_boot") - async def enable_web_pools(self) -> dict: + async def enable_web_pools(self) -> dict[str, Any]: """Turn on web pool updates.
@@ -786,7 +1037,7 @@ async def enable_web_pools(self) -> dict: """ return await self.send_privileged_command("enable_web_pools") - async def disable_web_pools(self) -> dict: + async def disable_web_pools(self) -> dict[str, Any]: """Turn off web pool updates.
@@ -801,7 +1052,7 @@ async def disable_web_pools(self) -> dict: """ return await self.send_privileged_command("disable_web_pools") - async def set_hostname(self, hostname: str) -> dict: + async def set_hostname(self, hostname: str) -> dict[str, Any]: """Set the hostname of the miner.
@@ -818,7 +1069,7 @@ async def set_hostname(self, hostname: str) -> dict: """ return await self.send_privileged_command("set_hostname", hostname=hostname) - async def set_power_pct(self, percent: int) -> dict: + async def set_power_pct(self, percent: int) -> dict[str, Any]: """Set the power percentage of the miner based on current power. Used for temporary adjustment.
@@ -842,7 +1093,9 @@ async def set_power_pct(self, percent: int) -> dict: ) return await self.send_privileged_command("set_power_pct", percent=str(percent)) - async def pre_power_on(self, complete: bool, msg: PrePowerOnMessage) -> dict: + async def pre_power_on( + self, complete: bool, msg: PrePowerOnMessage + ) -> dict[str, Any]: """Configure or check status of pre power on.
@@ -878,7 +1131,7 @@ async def pre_power_on(self, complete: bool, msg: PrePowerOnMessage) -> dict: ### ADDED IN V2.0.5 Whatsminer API ### @api_min_version("2.0.5") - async def set_power_pct_v2(self, percent: int) -> dict: + async def set_power_pct_v2(self, percent: int) -> dict[str, Any]: """Set the power percentage of the miner based on current power. Used for temporary adjustment. Added in API v2.0.5.
@@ -905,7 +1158,7 @@ async def set_power_pct_v2(self, percent: int) -> dict: ) @api_min_version("2.0.5") - async def set_temp_offset(self, temp_offset: int) -> dict: + async def set_temp_offset(self, temp_offset: int) -> dict[str, Any]: """Set the offset of miner hash board target temperature.
@@ -933,7 +1186,7 @@ async def set_temp_offset(self, temp_offset: int) -> dict: ) @api_min_version("2.0.5") - async def adjust_power_limit(self, power_limit: int) -> dict: + async def adjust_power_limit(self, power_limit: int) -> dict[str, Any]: """Set the upper limit of the miner's power. Cannot be higher than the ordinary power of the machine.
@@ -955,7 +1208,7 @@ async def adjust_power_limit(self, power_limit: int) -> dict: ) @api_min_version("2.0.5") - async def adjust_upfreq_speed(self, upfreq_speed: int) -> dict: + async def adjust_upfreq_speed(self, upfreq_speed: int) -> dict[str, Any]: """Set the upfreq speed, 0 is the normal speed, 9 is the fastest speed.
@@ -983,7 +1236,7 @@ async def adjust_upfreq_speed(self, upfreq_speed: int) -> dict: ) @api_min_version("2.0.5") - async def set_poweroff_cool(self, poweroff_cool: bool) -> dict: + async def set_poweroff_cool(self, poweroff_cool: bool) -> dict[str, Any]: """Set whether to cool the machine when mining is stopped.
@@ -1004,7 +1257,7 @@ async def set_poweroff_cool(self, poweroff_cool: bool) -> dict: ) @api_min_version("2.0.5") - async def set_fan_zero_speed(self, fan_zero_speed: bool) -> dict: + async def set_fan_zero_speed(self, fan_zero_speed: bool) -> dict[str, Any]: """Sets whether the fan speed supports the lowest 0 speed.
@@ -1026,7 +1279,7 @@ async def set_fan_zero_speed(self, fan_zero_speed: bool) -> dict: #### END privileged COMMANDS #### - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get the summary status from the miner.
Expand @@ -1037,7 +1290,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Get the pool status from the miner.
Expand @@ -1048,7 +1301,7 @@ async def pools(self) -> dict: """ return await self.send_command("pools") - async def devs(self) -> dict: + async def devs(self) -> dict[str, Any]: """Get data on each PGA/ASC with their details.
Expand @@ -1059,7 +1312,7 @@ async def devs(self) -> dict: """ return await self.send_command("devs") - async def edevs(self) -> dict: + async def edevs(self) -> dict[str, Any]: """Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
Expand @@ -1070,7 +1323,7 @@ async def edevs(self) -> dict: """ return await self.send_command("edevs") - async def devdetails(self) -> dict: + async def devdetails(self) -> dict[str, Any]: """Get data on all devices with their static details.
Expand @@ -1081,7 +1334,7 @@ async def devdetails(self) -> dict: """ return await self.send_command("devdetails") - async def get_psu(self) -> dict: + async def get_psu(self) -> dict[str, Any]: """Get data on the PSU and power information.
Expand @@ -1092,7 +1345,7 @@ async def get_psu(self) -> dict: """ return await self.send_command("get_psu") - async def version(self) -> dict: + async def version(self) -> dict[str, Any]: """Get version data for the miner. Wraps `self.get_version()`.
Expand @@ -1107,7 +1360,7 @@ async def version(self) -> dict: """ return await self.get_version() - async def get_version(self) -> dict: + async def get_version(self) -> dict[str, Any]: """Get version data for the miner.
Expand @@ -1118,7 +1371,7 @@ async def get_version(self) -> dict: """ return await self.send_command("get_version") - async def status(self) -> dict: + async def status(self) -> dict[str, Any]: """Get BTMiner status and firmware version.
Expand @@ -1129,7 +1382,7 @@ async def status(self) -> dict: """ return await self.send_command("status") - async def get_miner_info(self) -> dict: + async def get_miner_info(self) -> dict[str, Any]: """Get general miner info.
Expand @@ -1141,7 +1394,7 @@ async def get_miner_info(self) -> dict: return await self.send_command("get_miner_info", allow_warning=False) @api_min_version("2.0.1") - async def get_error_code(self) -> dict: + async def get_error_code(self) -> dict[str, Any]: """Get a list of error codes from the miner.
@@ -1156,13 +1409,15 @@ async def get_error_code(self) -> dict: class BTMinerV3RPCAPI(BaseMinerRPCAPI): - def __init__(self, ip: str, port: int = 4433, api_ver: str = "0.0.0"): + def __init__(self, ip: str, port: int = 4433, api_ver: str = "0.0.0") -> None: super().__init__(ip, port, api_ver=api_ver) self.salt: str | None = None self.pwd: str = "super" - async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict: + async def multicommand( + self, *commands: str, allow_warning: bool = True + ) -> dict[str, Any]: """Creates and sends multiple commands as one command to the miner. Parameters: @@ -1171,9 +1426,40 @@ async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict """ checked_commands = self._check_commands(*commands) - data = await self._send_split_multicommand(*checked_commands) - data["multicommand"] = True - return data + result: dict[str, Any] = {"multicommand": True} + + for command in checked_commands: + try: + if ":" in command: + cmd, param = command.split(":", 1) + else: + cmd, param = command, None + + response = await self.api_request(cmd, parameters=param) + + if response.desc == "get.device.info": + result[command] = [response.msg] + elif response.desc == "get.miner.status": + if param == "summary": + result[command] = [response.msg.summary] + elif param == "edevs": + result[command] = [response.msg.edevs] + elif param == "pools": + result[command] = [response.msg.pools] + else: + result[command] = [response.msg] + elif response.desc == "get.system.setting": + result[command] = [response.msg] + elif response.desc == "get.miner.setting": + result[command] = [response.msg] + elif response.desc == "get.fan.setting": + result[command] = [response.msg] + else: + result[command] = [response.msg] + except APIError: + continue + + return result async def send_command( self, @@ -1181,8 +1467,8 @@ async def send_command( parameters: Any = None, ignore_errors: bool = False, allow_warning: bool = True, - **kwargs, - ) -> dict: + **kwargs: Any, + ) -> dict[str, Any]: if ":" in command: parameters = command.split(":")[1] command = command.split(":")[0] @@ -1209,7 +1495,26 @@ async def send_command( cmd_dict = cmd.model_dump() ser = json.dumps(cmd_dict).encode("utf-8") header = struct.pack(" BTMinerV3SuccessResponse: + data = await self.send_command(command, parameters=parameters, **kwargs) + response = BTMinerV3Response.model_validate({"data": data}) + + if response.data.code != BTMinerAPICode.MSG_CMDOK: + logging.error( + f"BTMiner API error: code={response.data.code.name}, msg={response.data.msg}" + ) + raise APIError( + f"BTMiner API error: {response.data.code.name} - {response.data.msg}" + ) + return response.data async def _send_bytes( self, @@ -1253,7 +1558,7 @@ async def _send_bytes( return ret_data - def _check_commands(self, *commands) -> list: + def _check_commands(self, *commands: str) -> list[str]: return_commands = [] for command in commands: @@ -1290,13 +1595,13 @@ async def get_salt(self) -> str: return self.salt @typing.no_type_check - async def get_miner_report(self) -> AsyncGenerator[dict, None]: + async def get_miner_report(self) -> AsyncGenerator[dict[str, Any], None]: if self.writer is None: await self.connect() result = asyncio.Queue() - async def callback(data: dict): + async def callback(data: dict[str, Any]) -> None: await result.put(data) cb_fn = callback @@ -1310,167 +1615,251 @@ async def callback(data: dict): finally: self.cmd_callbacks["get.miner.report"].remove(cb_fn) - async def get_system_setting(self) -> dict | None: - return await self.send_command("get.system.setting") - - async def get_miner_status_summary(self) -> dict | None: - return await self.send_command("get.miner.status", parameters="summary") - - async def get_miner_status_edevs(self) -> dict | None: - return await self.send_command("get.miner.status", parameters="edevs") - - async def get_miner_status_pools(self) -> dict | None: - return await self.send_command("get.miner.status", parameters="pools") + async def get_system_setting(self) -> BTMinerV3SystemSettingMsg: + response = await self.api_request("get.system.setting") + if not response.desc == "get.system.setting": + raise APIError(f'Expected "get.system.setting" but got "{response.desc}"') + return response.msg - async def get_miner_history(self) -> dict | None: - data = await self.send_command( + async def get_miner_status_summary( + self, + ) -> BTMinerV3Summary: + response = await self.api_request("get.miner.status", parameters="summary") + if not response.desc == "get.miner.status": + raise APIError(f'Expected "get.miner.status" but got "{response.desc}"') + if response.msg.summary is None: + raise APIError("No summary data returned") + return response.msg.summary + + async def get_miner_status_edevs(self) -> list[BTMinerV3EdevItem]: + response = await self.api_request("get.miner.status", parameters="edevs") + if not response.desc == "get.miner.status": + raise APIError(f'Expected "get.miner.status" but got "{response.desc}"') + return response.msg.edevs + + async def get_miner_status_pools( + self, + ) -> list[BTMinerV3Pool]: + response = await self.api_request("get.miner.status", parameters="pools") + if not response.desc == "get.miner.status": + raise APIError(f'Expected "get.miner.status" but got "{response.desc}"') + return response.msg.pools + + async def get_miner_history(self) -> dict[int, list[str]]: + response = await self.api_request( "get.miner.history", parameters={ "start": "1", "stop": str(datetime.datetime.now().timestamp()), }, ) - ret = {} - result = data.get("msg") - if result is not None: - unparsed = result["Data"].strip() - for item in unparsed.split(" "): - list_item = item.split(",") - timestamp = int(list_item.pop(0)) - ret[timestamp] = list_item + if not response.desc == "get.miner.history": + raise APIError(f'Expected "get.miner.history" but got "{response.desc}"') + + ret: dict[int, list[str]] = {} + unparsed = response.msg.Data.strip() + for item in unparsed.split(" "): + list_item = item.split(",") + timestamp = int(list_item.pop(0)) + ret[timestamp] = list_item return ret - async def get_psu_command(self): - return await self.send_command("get.psu.command") - - async def get_miner_setting(self) -> dict | None: - return await self.send_command("get.miner.setting") - - async def get_device_info(self) -> dict | None: - return await self.send_command("get.device.info") - - async def get_log_download(self) -> dict | None: - return await self.send_command("get.log.download") - - async def get_fan_setting(self) -> dict | None: - return await self.send_command("get.fan.setting") - - async def set_system_reboot(self) -> dict | None: - return await self.send_command("set.system.reboot") - - async def set_system_factory_reset(self, *args, **kwargs) -> dict | None: + async def get_miner_setting(self) -> BTMinerV3MinerSettingMsg: + response = await self.api_request("get.miner.setting") + if not response.desc == "get.miner.setting": + raise APIError(f'Expected "get.miner.setting" but got "{response.desc}"') + return response.msg + + async def get_device_info(self) -> BTMinerV3DeviceInfoMsg: + response = await self.api_request("get.device.info") + if not response.desc == "get.device.info": + raise APIError(f'Expected "get.device.info" but got "{response.desc}"') + return response.msg + + async def get_log_download(self) -> BTMinerV3LogDownloadMsg: + response = await self.api_request("get.log.download") + if not response.desc == "get.log.download": + raise APIError(f'Expected "get.log.download" but got "{response.desc}"') + return response.msg + + async def get_fan_setting(self) -> BTMinerV3FanSettingMsg: + response = await self.api_request("get.fan.setting") + if not response.desc == "get.fan.setting": + raise APIError(f'Expected "get.fan.setting" but got "{response.desc}"') + return response.msg + + async def set_system_reboot(self) -> None: + response = await self.api_request("set.system.reboot") + if response.desc != "set.system.reboot": + raise APIError(f'Expected "set.system.reboot" but got "{response.desc}"') + + async def set_system_factory_reset( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.factory_reset") - async def set_system_update_firmware(self, *args, **kwargs) -> dict | None: + async def set_system_update_firmware( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.update_firmware") - async def set_system_net_config(self, *args, **kwargs) -> dict | None: + async def set_system_net_config( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.net_config") - async def set_system_led(self, leds: list | None = None) -> dict | None: + async def set_system_led(self, leds: list[Any] | None = None) -> None: if leds is None: - return await self.send_command("set.system.led", parameters="auto") + response = await self.api_request("set.system.led", parameters="auto") else: - return await self.send_command("set.system.led", parameters=leds) + response = await self.api_request("set.system.led", parameters=leds) + if response.desc != "set.system.led": + raise APIError(f'Expected "set.system.led" but got "{response.desc}"') - async def set_system_time_randomized(self, *args, **kwargs) -> dict | None: + async def set_system_time_randomized( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.time_randomized") - async def set_system_timezone(self, *args, **kwargs) -> dict | None: + async def set_system_timezone( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.timezone") - async def set_system_hostname(self, *args, **kwargs) -> dict | None: + async def set_system_hostname( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.hostname") - async def set_system_webpools(self, *args, **kwargs) -> dict | None: + async def set_system_webpools( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.webpools") - async def set_miner_target_freq(self, *args, **kwargs) -> dict | None: + async def set_miner_target_freq( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.target_freq") - async def set_miner_heat_mode(self, *args, **kwargs) -> dict | None: + async def set_miner_heat_mode( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.heat_mode") - async def set_system_ntp_server(self, *args, **kwargs) -> dict | None: + async def set_system_ntp_server( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.system.ntp_server") - async def set_miner_service(self, value: str) -> dict | None: - return await self.send_command("set.miner.service", parameters=value) + async def set_miner_service(self, value: str) -> None: + response = await self.api_request("set.miner.service", parameters=value) + if response.desc != "set.miner.service": + raise APIError(f'Expected "set.miner.service" but got "{response.desc}"') - async def set_miner_power_mode(self, *args, **kwargs) -> dict | None: + async def set_miner_power_mode( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.power_mode") - async def set_miner_cointype(self, *args, **kwargs) -> dict | None: + async def set_miner_cointype( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.cointype") - async def set_miner_pools(self, *args, **kwargs) -> dict | None: + async def set_miner_pools(self, *args: Any, **kwargs: Any) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.pools") - async def set_miner_fastboot(self, *args, **kwargs) -> dict | None: + async def set_miner_fastboot( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.fastboot") - async def set_miner_power_percent(self, *args, **kwargs) -> dict | None: + async def set_miner_power_percent( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.power_percent") - async def set_miner_pre_power_on(self, *args, **kwargs) -> dict | None: + async def set_miner_pre_power_on( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.pre_power_on") - async def set_miner_restore_setting(self, *args, **kwargs) -> dict | None: + async def set_miner_restore_setting( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.restore_setting") - async def set_miner_report(self, frequency: int = 1) -> dict | None: - return await self.send_command( + async def set_miner_report(self, frequency: int = 1) -> None: + response = await self.api_request( "set.miner.report", parameters={"gap": frequency} ) + if response.desc != "set.miner.report": + raise APIError(f'Expected "set.miner.report" but got "{response.desc}"') - async def set_miner_power_limit(self, power: int) -> dict | None: - return await self.send_command("set.miner.power_limit", parameters=power) + async def set_miner_power_limit(self, power: int) -> None: + response = await self.api_request("set.miner.power_limit", parameters=power) + if response.desc != "set.miner.power_limit": + raise APIError( + f'Expected "set.miner.power_limit" but got "{response.desc}"' + ) - async def set_miner_upfreq_speed(self, *args, **kwargs) -> dict | None: + async def set_miner_upfreq_speed( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.miner.upfreq_speed") - async def set_log_upload(self, *args, **kwargs) -> dict | None: + async def set_log_upload(self, *args: Any, **kwargs: Any) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.log.upload") - async def set_user_change_passwd(self, *args, **kwargs) -> dict | None: + async def set_user_change_passwd( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.user.change_passwd") - async def set_user_permission(self, *args, **kwargs) -> dict | None: + async def set_user_permission( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.user.permission") - async def set_fan_temp_offset(self, *args, **kwargs) -> dict | None: + async def set_fan_temp_offset( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.fan.temp_offset") - async def set_fan_poweroff_cool(self, *args, **kwargs) -> dict | None: + async def set_fan_poweroff_cool( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.fan.poweroff_cool") - async def set_fan_zero_speed(self, *args, **kwargs) -> dict | None: + async def set_fan_zero_speed( + self, *args: Any, **kwargs: Any + ) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.fan.zero_speed") - async def set_shell_debug(self, *args, **kwargs) -> dict | None: + async def set_shell_debug(self, *args: Any, **kwargs: Any) -> dict[str, Any] | None: raise NotImplementedError return await self.send_command("set.shell.debug") diff --git a/pyasic/rpc/ccminer.py b/pyasic/rpc/ccminer.py index fa118876e..257bccdf4 100644 --- a/pyasic/rpc/ccminer.py +++ b/pyasic/rpc/ccminer.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.bmminer import BMMinerRPCAPI @@ -29,6 +31,6 @@ class CCMinerRPCAPI(BMMinerRPCAPI): rely on it to send the command for them. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.port = 8359 diff --git a/pyasic/rpc/cgminer.py b/pyasic/rpc/cgminer.py index d88a9557e..feb68b226 100644 --- a/pyasic/rpc/cgminer.py +++ b/pyasic/rpc/cgminer.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.base import BaseMinerRPCAPI @@ -31,7 +33,7 @@ class CGMinerRPCAPI(BaseMinerRPCAPI): rely on it to send the command for them. """ - async def version(self) -> dict: + async def version(self) -> dict[str, Any]: """Get miner version info.
Expand @@ -42,7 +44,7 @@ async def version(self) -> dict: """ return await self.send_command("version") - async def config(self) -> dict: + async def config(self) -> dict[str, Any]: """Get some basic configuration info.
Expand @@ -64,7 +66,7 @@ async def config(self) -> dict: """ return await self.send_command("config") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get the status summary of the miner.
Expand @@ -75,7 +77,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Get pool information.
Expand @@ -86,7 +88,7 @@ async def pools(self) -> dict: """ return await self.send_command("pools") - async def devs(self) -> dict: + async def devs(self) -> dict[str, Any]: """Get data on each PGA/ASC with their details.
Expand @@ -97,7 +99,7 @@ async def devs(self) -> dict: """ return await self.send_command("devs") - async def edevs(self, old: bool = False) -> dict: + async def edevs(self, old: bool = False) -> dict[str, Any]: """Get data on each PGA/ASC with their details, ignoring blacklisted and zombie devices.
Expand @@ -114,7 +116,7 @@ async def edevs(self, old: bool = False) -> dict: else: return await self.send_command("edevs") - async def pga(self, n: int) -> dict: + async def pga(self, n: int) -> dict[str, Any]: """Get data from PGA n.
Expand @@ -128,7 +130,7 @@ async def pga(self, n: int) -> dict: """ return await self.send_command("pga", parameters=n) - async def pgacount(self) -> dict: + async def pgacount(self) -> dict[str, Any]: """Get data fon all PGAs.
Expand @@ -139,7 +141,7 @@ async def pgacount(self) -> dict: """ return await self.send_command("pgacount") - async def switchpool(self, n: int) -> dict: + async def switchpool(self, n: int) -> dict[str, Any]: """Switch pools to pool n.
Expand @@ -153,7 +155,7 @@ async def switchpool(self, n: int) -> dict: """ return await self.send_command("switchpool", parameters=n) - async def enablepool(self, n: int) -> dict: + async def enablepool(self, n: int) -> dict[str, Any]: """Enable pool n.
Expand @@ -167,7 +169,7 @@ async def enablepool(self, n: int) -> dict: """ return await self.send_command("enablepool", parameters=n) - async def addpool(self, url: str, username: str, password: str) -> dict: + async def addpool(self, url: str, username: str, password: str) -> dict[str, Any]: """Add a pool to the miner.
Expand @@ -185,7 +187,7 @@ async def addpool(self, url: str, username: str, password: str) -> dict: "addpool", parameters=f"{url},{username},{password}" ) - async def poolpriority(self, *n: int) -> dict: + async def poolpriority(self, *n: int) -> dict[str, Any]: """Set pool priority.
Expand @@ -200,7 +202,7 @@ async def poolpriority(self, *n: int) -> dict: pools = f"{','.join([str(item) for item in n])}" return await self.send_command("poolpriority", parameters=pools) - async def poolquota(self, n: int, q: int) -> dict: + async def poolquota(self, n: int, q: int) -> dict[str, Any]: """Set pool quota.
Expand @@ -215,7 +217,7 @@ async def poolquota(self, n: int, q: int) -> dict: """ return await self.send_command("poolquota", parameters=f"{n},{q}") - async def disablepool(self, n: int) -> dict: + async def disablepool(self, n: int) -> dict[str, Any]: """Disable a pool.
Expand @@ -229,7 +231,7 @@ async def disablepool(self, n: int) -> dict: """ return await self.send_command("disablepool", parameters=n) - async def removepool(self, n: int) -> dict: + async def removepool(self, n: int) -> dict[str, Any]: """Remove a pool.
Expand @@ -243,7 +245,7 @@ async def removepool(self, n: int) -> dict: """ return await self.send_command("removepool", parameters=n) - async def save(self, filename: str | None = None) -> dict: + async def save(self, filename: str | None = None) -> dict[str, Any]: """Save the config.
Expand @@ -260,7 +262,7 @@ async def save(self, filename: str | None = None) -> dict: else: return await self.send_command("save") - async def quit(self) -> dict: + async def quit(self) -> dict[str, Any]: """Quit CGMiner.
Expand @@ -271,7 +273,7 @@ async def quit(self) -> dict: """ return await self.send_command("quit") - async def notify(self) -> dict: + async def notify(self) -> dict[str, Any]: """Notify the user of past errors.
Expand @@ -282,7 +284,7 @@ async def notify(self) -> dict: """ return await self.send_command("notify") - async def privileged(self) -> dict: + async def privileged(self) -> dict[str, Any]: """Check if you have privileged access.
Expand @@ -293,7 +295,7 @@ async def privileged(self) -> dict: """ return await self.send_command("privileged") - async def pgaenable(self, n: int) -> dict: + async def pgaenable(self, n: int) -> dict[str, Any]: """Enable PGA n.
Expand @@ -307,7 +309,7 @@ async def pgaenable(self, n: int) -> dict: """ return await self.send_command("pgaenable", parameters=n) - async def pgadisable(self, n: int) -> dict: + async def pgadisable(self, n: int) -> dict[str, Any]: """Disable PGA n.
Expand @@ -321,7 +323,7 @@ async def pgadisable(self, n: int) -> dict: """ return await self.send_command("pgadisable", parameters=n) - async def pgaidentify(self, n: int) -> dict: + async def pgaidentify(self, n: int) -> dict[str, Any]: """Identify PGA n.
Expand @@ -335,7 +337,7 @@ async def pgaidentify(self, n: int) -> dict: """ return await self.send_command("pgaidentify", parameters=n) - async def devdetails(self) -> dict: + async def devdetails(self) -> dict[str, Any]: """Get data on all devices with their static details.
Expand @@ -346,7 +348,7 @@ async def devdetails(self) -> dict: """ return await self.send_command("devdetails") - async def restart(self) -> dict: + async def restart(self) -> dict[str, Any]: """Restart CGMiner using the API.
Expand @@ -357,7 +359,7 @@ async def restart(self) -> dict: """ return await self.send_command("restart") - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Get stats of each device/pool with more than 1 getwork.
Expand @@ -368,7 +370,7 @@ async def stats(self) -> dict: """ return await self.send_command("stats") - async def estats(self, old: bool = False) -> dict: + async def estats(self, old: bool = False) -> dict[str, Any]: """Get stats of each device/pool with more than 1 getwork, ignoring zombie devices.
Expand @@ -385,7 +387,7 @@ async def estats(self, old: bool = False) -> dict: else: return await self.send_command("estats") - async def check(self, command: str) -> dict: + async def check(self, command: str) -> dict[str, Any]: """Check if the command command exists in CGMiner.
Expand @@ -401,7 +403,7 @@ async def check(self, command: str) -> dict: """ return await self.send_command("check", parameters=command) - async def failover_only(self, failover: bool) -> dict: + async def failover_only(self, failover: bool) -> dict[str, Any]: """Set failover-only.
Expand @@ -415,7 +417,7 @@ async def failover_only(self, failover: bool) -> dict: """ return await self.send_command("failover-only", parameters=failover) - async def coin(self) -> dict: + async def coin(self) -> dict[str, Any]: """Get information on the current coin.
Expand @@ -431,7 +433,7 @@ async def coin(self) -> dict: """ return await self.send_command("coin") - async def debug(self, setting: str) -> dict: + async def debug(self, setting: str) -> dict[str, Any]: """Set a debug setting.
Expand @@ -454,7 +456,7 @@ async def debug(self, setting: str) -> dict: """ return await self.send_command("debug", parameters=setting) - async def setconfig(self, name: str, n: int) -> dict: + async def setconfig(self, name: str, n: int) -> dict[str, Any]: """Set config of name to value n.
Expand @@ -473,7 +475,7 @@ async def setconfig(self, name: str, n: int) -> dict: """ return await self.send_command("setconfig", parameters=f"{name},{n}") - async def usbstats(self) -> dict: + async def usbstats(self) -> dict[str, Any]: """Get stats of all USB devices except ztex.
Expand @@ -484,7 +486,7 @@ async def usbstats(self) -> dict: """ return await self.send_command("usbstats") - async def pgaset(self, n: int, opt: str, val: int | None = None) -> dict: + async def pgaset(self, n: int, opt: str, val: int | None = None) -> dict[str, Any]: """Set PGA option opt to val on PGA n.
Expand @@ -513,7 +515,7 @@ async def pgaset(self, n: int, opt: str, val: int | None = None) -> dict: else: return await self.send_command("pgaset", parameters=f"{n},{opt}") - async def zero(self, which: str, summary: bool) -> dict: + async def zero(self, which: str, summary: bool) -> dict[str, Any]: """Zero a device.
Expand @@ -530,7 +532,7 @@ async def zero(self, which: str, summary: bool) -> dict: """ return await self.send_command("zero", parameters=f"{which},{summary}") - async def hotplug(self, n: int) -> dict: + async def hotplug(self, n: int) -> dict[str, Any]: """Enable hotplug.
Expand @@ -544,7 +546,7 @@ async def hotplug(self, n: int) -> dict: """ return await self.send_command("hotplug", parameters=n) - async def asc(self, n: int) -> dict: + async def asc(self, n: int) -> dict[str, Any]: """Get data for ASC device n.
Expand @@ -558,7 +560,7 @@ async def asc(self, n: int) -> dict: """ return await self.send_command("asc", parameters=n) - async def ascenable(self, n: int) -> dict: + async def ascenable(self, n: int) -> dict[str, Any]: """Enable ASC device n.
Expand @@ -572,7 +574,7 @@ async def ascenable(self, n: int) -> dict: """ return await self.send_command("ascenable", parameters=n) - async def ascdisable(self, n: int) -> dict: + async def ascdisable(self, n: int) -> dict[str, Any]: """Disable ASC device n.
Expand @@ -586,7 +588,7 @@ async def ascdisable(self, n: int) -> dict: """ return await self.send_command("ascdisable", parameters=n) - async def ascidentify(self, n: int) -> dict: + async def ascidentify(self, n: int) -> dict[str, Any]: """Identify ASC device n.
Expand @@ -600,7 +602,7 @@ async def ascidentify(self, n: int) -> dict: """ return await self.send_command("ascidentify", parameters=n) - async def asccount(self) -> dict: + async def asccount(self) -> dict[str, Any]: """Get data on the number of ASC devices and their info.
Expand @@ -611,7 +613,9 @@ async def asccount(self) -> dict: """ return await self.send_command("asccount") - async def ascset(self, n: int, opt: str, val: int | str | None = None) -> dict: + async def ascset( + self, n: int, opt: str, val: int | str | None = None + ) -> dict[str, Any]: """Set ASC n option opt to value val.
Expand @@ -667,7 +671,7 @@ async def ascset(self, n: int, opt: str, val: int | str | None = None) -> dict: else: return await self.send_command("ascset", parameters=f"{n},{opt}") - async def lcd(self) -> dict: + async def lcd(self) -> dict[str, Any]: """Get a general all-in-one status summary of the miner.
Expand @@ -678,7 +682,7 @@ async def lcd(self) -> dict: """ return await self.send_command("lcd") - async def lockstats(self) -> dict: + async def lockstats(self) -> dict[str, Any]: """Write lockstats to STDERR.
Expand diff --git a/pyasic/rpc/gcminer.py b/pyasic/rpc/gcminer.py index 378a29463..c8b0f3d72 100644 --- a/pyasic/rpc/gcminer.py +++ b/pyasic/rpc/gcminer.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.base import BaseMinerRPCAPI @@ -31,7 +33,7 @@ class GCMinerRPCAPI(BaseMinerRPCAPI): rely on it to send the command for them. """ - async def asc(self, n: int) -> dict: + async def asc(self, n: int) -> dict[str, Any]: """Get data for ASC device n.
Expand @@ -45,7 +47,7 @@ async def asc(self, n: int) -> dict: """ return await self.send_command("asc", parameters=n) - async def asccount(self) -> dict: + async def asccount(self) -> dict[str, Any]: """Get data on the number of ASC devices and their info.
Expand @@ -56,7 +58,7 @@ async def asccount(self) -> dict: """ return await self.send_command("asccount") - async def check(self, command: str) -> dict: + async def check(self, command: str) -> dict[str, Any]: """Check if the command `command` exists in LUXMiner.
Expand @@ -72,7 +74,7 @@ async def check(self, command: str) -> dict: """ return await self.send_command("check", parameters=command) - async def coin(self) -> dict: + async def coin(self) -> dict[str, Any]: """Get information on the current coin.
Expand @@ -88,7 +90,7 @@ async def coin(self) -> dict: """ return await self.send_command("coin") - async def config(self) -> dict: + async def config(self) -> dict[str, Any]: """Get some basic configuration info.
Expand @@ -99,7 +101,7 @@ async def config(self) -> dict: """ return await self.send_command("config") - async def devdetails(self) -> dict: + async def devdetails(self) -> dict[str, Any]: """Get data on all devices with their static details.
Expand @@ -110,7 +112,7 @@ async def devdetails(self) -> dict: """ return await self.send_command("devdetails") - async def devs(self) -> dict: + async def devs(self) -> dict[str, Any]: """Get data on each PGA/ASC with their details.
Expand @@ -121,11 +123,11 @@ async def devs(self) -> dict: """ return await self.send_command("devs") - async def edevs(self) -> dict: + async def edevs(self) -> dict[str, Any]: """Alias for devs""" return await self.send_command("edevs") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Get pool information.
@@ -137,7 +139,7 @@ async def pools(self) -> dict: """ return await self.send_command("pools") - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Get stats of each device/pool with more than 1 getwork.
@@ -149,11 +151,11 @@ async def stats(self) -> dict: """ return await self.send_command("stats") - async def estats(self) -> dict: + async def estats(self) -> dict[str, Any]: """Alias for stats""" return await self.send_command("estats") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get the status summary of the miner.
@@ -165,7 +167,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def version(self) -> dict: + async def version(self) -> dict[str, Any]: """Get miner version info.
diff --git a/pyasic/rpc/luxminer.py b/pyasic/rpc/luxminer.py index 0a1014c82..8986139e7 100644 --- a/pyasic/rpc/luxminer.py +++ b/pyasic/rpc/luxminer.py @@ -13,9 +13,9 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from typing import Literal +from typing import Any, Literal -from pyasic import APIError +from pyasic.errors import APIError from pyasic.rpc.base import BaseMinerRPCAPI @@ -33,11 +33,13 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI): rely on it to send the command for them. """ - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.session_token = None - async def send_privileged_command(self, command: str, *args, **kwargs) -> dict: + async def send_privileged_command( + self, command: str, *args: Any, **kwargs: Any + ) -> dict[str, Any]: if self.session_token is None: await self.auth() return await self.send_command( @@ -50,9 +52,9 @@ async def send_privileged_command(self, command: str, *args, **kwargs) -> dict: async def send_command( self, command: str, - *args, - **kwargs, - ) -> dict: + *args: Any, + **kwargs: Any, + ) -> dict[str, Any]: if kwargs.get("parameters") is not None and len(args) == 0: return await super().send_command(command, **kwargs) return await super().send_command(command, parameters=",".join(args), **kwargs) @@ -74,7 +76,7 @@ async def auth(self) -> str | None: pass return None - async def addgroup(self, name: str, quota: int) -> dict: + async def addgroup(self, name: str, quota: int) -> dict[str, Any]: """Add a pool group.
Expand @@ -91,7 +93,7 @@ async def addgroup(self, name: str, quota: int) -> dict: async def addpool( self, url: str, user: str, pwd: str = "", group_id: str | None = None - ) -> dict: + ) -> dict[str, Any]: """Add a pool.
Expand @@ -111,7 +113,7 @@ async def addpool( pool_data.append(group_id) return await self.send_command("addpool", *pool_data) - async def asc(self, n: int) -> dict: + async def asc(self, n: int) -> dict[str, Any]: """Get data for ASC device n.
Expand @@ -125,7 +127,7 @@ async def asc(self, n: int) -> dict: """ return await self.send_command("asc", n) - async def asccount(self) -> dict: + async def asccount(self) -> dict[str, Any]: """Get data on the number of ASC devices and their info.
Expand @@ -136,7 +138,7 @@ async def asccount(self) -> dict: """ return await self.send_command("asccount") - async def atm(self) -> dict: + async def atm(self) -> dict[str, Any]: """Get data for Advanced Thermal Management (ATM) configuration.
Expand @@ -169,7 +171,7 @@ async def atmset( min_profile: str | None = None, max_profile: str | None = None, prevent_oc: bool | None = None, - ) -> dict: + ) -> dict[str, Any]: """Sets the ATM configuration.
Expand @@ -210,7 +212,7 @@ async def atmset( atmset_data.append(f"prevent_oc={str(prevent_oc).lower()}") return await self.send_privileged_command("atmset", *atmset_data) - async def check(self, command: str) -> dict: + async def check(self, command: str) -> dict[str, Any]: """Check if the command `command` exists in LUXMiner.
Expand @@ -226,7 +228,7 @@ async def check(self, command: str) -> dict: """ return await self.send_command("check", command) - async def coin(self) -> dict: + async def coin(self) -> dict[str, Any]: """Get information on the current coin.
Expand @@ -242,7 +244,7 @@ async def coin(self) -> dict: """ return await self.send_command("coin") - async def config(self) -> dict: + async def config(self) -> dict[str, Any]: """Get some basic configuration info.
Expand @@ -253,7 +255,7 @@ async def config(self) -> dict: """ return await self.send_command("config") - async def curtail(self) -> dict: + async def curtail(self) -> dict[str, Any]: """Put the miner into sleep mode.
Expand @@ -264,7 +266,7 @@ async def curtail(self) -> dict: """ return await self.send_privileged_command("curtail", "sleep") - async def sleep(self) -> dict: + async def sleep(self) -> dict[str, Any]: """Put the miner into sleep mode.
Expand @@ -275,7 +277,7 @@ async def sleep(self) -> dict: """ return await self.send_privileged_command("curtail", "sleep") - async def wakeup(self) -> dict: + async def wakeup(self) -> dict[str, Any]: """Wake the miner up from sleep mode.
Expand @@ -286,7 +288,7 @@ async def wakeup(self) -> dict: """ return await self.send_privileged_command("curtail", "wakeup") - async def devdetails(self) -> dict: + async def devdetails(self) -> dict[str, Any]: """Get data on all devices with their static details.
Expand @@ -297,7 +299,7 @@ async def devdetails(self) -> dict: """ return await self.send_command("devdetails") - async def devs(self) -> dict: + async def devs(self) -> dict[str, Any]: """Get data on each PGA/ASC with their details.
Expand @@ -308,7 +310,7 @@ async def devs(self) -> dict: """ return await self.send_command("devs") - async def disablepool(self, n: int) -> dict: + async def disablepool(self, n: int) -> dict[str, Any]: """Disable a pool.
Expand @@ -322,11 +324,11 @@ async def disablepool(self, n: int) -> dict: """ return await self.send_command("disablepool", n) - async def edevs(self) -> dict: + async def edevs(self) -> dict[str, Any]: """Alias for devs""" return await self.devs() - async def enablepool(self, n: int) -> dict: + async def enablepool(self, n: int) -> dict[str, Any]: """Enable pool n.
Expand @@ -340,11 +342,11 @@ async def enablepool(self, n: int) -> dict: """ return await self.send_command("enablepool", n) - async def estats(self) -> dict: + async def estats(self) -> dict[str, Any]: """Alias for stats""" return await self.stats() - async def fans(self) -> dict: + async def fans(self) -> dict[str, Any]: """Get fan data.
Expand @@ -360,7 +362,7 @@ async def fanset( speed: int | None = None, min_fans: int | None = None, power_off_speed: int | None = None, - ) -> dict: + ) -> dict[str, Any]: """Set fan control.
Expand @@ -382,7 +384,9 @@ async def fanset( fanset_data.append(f"power_off_speed={power_off_speed}") return await self.send_privileged_command("fanset", *fanset_data) - async def frequencyget(self, board_n: int, chip_n: int | None = None) -> dict: + async def frequencyget( + self, board_n: int, chip_n: int | None = None + ) -> dict[str, Any]: """Get frequency data for a board and chips.
Expand @@ -400,7 +404,7 @@ async def frequencyget(self, board_n: int, chip_n: int | None = None) -> dict: frequencyget_data.append(str(chip_n)) return await self.send_command("frequencyget", *frequencyget_data) - async def frequencyset(self, board_n: int, freq: int) -> dict: + async def frequencyset(self, board_n: int, freq: int) -> dict[str, Any]: """Set frequency.
Expand @@ -415,7 +419,7 @@ async def frequencyset(self, board_n: int, freq: int) -> dict: """ return await self.send_privileged_command("frequencyset", board_n, freq) - async def frequencystop(self, board_n: int) -> dict: + async def frequencystop(self, board_n: int) -> dict[str, Any]: """Stop set frequency.
Expand @@ -429,7 +433,7 @@ async def frequencystop(self, board_n: int) -> dict: """ return await self.send_privileged_command("frequencystop", board_n) - async def groupquota(self, group_n: int, quota: int) -> dict: + async def groupquota(self, group_n: int, quota: int) -> dict[str, Any]: """Set a group's quota.
Expand @@ -444,7 +448,7 @@ async def groupquota(self, group_n: int, quota: int) -> dict: """ return await self.send_command("groupquota", group_n, quota) - async def groups(self) -> dict: + async def groups(self) -> dict[str, Any]: """Get pool group data.
Expand @@ -455,7 +459,9 @@ async def groups(self) -> dict: """ return await self.send_command("groups") - async def healthchipget(self, board_n: int, chip_n: int | None = None) -> dict: + async def healthchipget( + self, board_n: int, chip_n: int | None = None + ) -> dict[str, Any]: """Get chip health.
Expand @@ -473,7 +479,9 @@ async def healthchipget(self, board_n: int, chip_n: int | None = None) -> dict: healthchipget_data.append(str(chip_n)) return await self.send_command("healthchipget", *healthchipget_data) - async def healthchipset(self, board_n: int, chip_n: int | None = None) -> dict: + async def healthchipset( + self, board_n: int, chip_n: int | None = None + ) -> dict[str, Any]: """Select the next chip to have its health checked.
Expand @@ -491,7 +499,7 @@ async def healthchipset(self, board_n: int, chip_n: int | None = None) -> dict: healthchipset_data.append(str(chip_n)) return await self.send_privileged_command("healthchipset", *healthchipset_data) - async def healthctrl(self) -> dict: + async def healthctrl(self) -> dict[str, Any]: """Get health check config.
Expand @@ -502,7 +510,9 @@ async def healthctrl(self) -> dict: """ return await self.send_command("healthctrl") - async def healthctrlset(self, num_readings: int, amplified_factor: float) -> dict: + async def healthctrlset( + self, num_readings: int, amplified_factor: float + ) -> dict[str, Any]: """Set health control config.
Expand @@ -519,7 +529,7 @@ async def healthctrlset(self, num_readings: int, amplified_factor: float) -> dic "healthctrlset", num_readings, amplified_factor ) - async def kill(self) -> dict: + async def kill(self) -> dict[str, Any]: """Forced session kill. Use logoff instead.
Expand @@ -530,7 +540,7 @@ async def kill(self) -> dict: """ return await self.send_command("kill") - async def lcd(self) -> dict: + async def lcd(self) -> dict[str, Any]: """Get a general all-in-one status summary of the miner. Always zeros on LUXMiner.
Expand @@ -545,7 +555,7 @@ async def ledset( self, color: Literal["red"], state: Literal["on", "off", "blink"], - ) -> dict: + ) -> dict[str, Any]: """Set led.
Expand @@ -560,7 +570,7 @@ async def ledset( """ return await self.send_privileged_command("ledset", color, state) - async def limits(self) -> dict: + async def limits(self) -> dict[str, Any]: """Get max and min values of config parameters.
Expand @@ -571,7 +581,7 @@ async def limits(self) -> dict: """ return await self.send_command("limits") - async def logoff(self) -> dict: + async def logoff(self) -> dict[str, Any]: """Log off of a session.
Expand @@ -584,7 +594,7 @@ async def logoff(self) -> dict: self.session_token = None return res - async def logon(self) -> dict: + async def logon(self) -> dict[str, Any]: """Get or create a session.
Expand @@ -595,7 +605,7 @@ async def logon(self) -> dict: """ return await self.send_command("logon") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Get pool information.
@@ -607,7 +617,7 @@ async def pools(self) -> dict: """ return await self.send_command("pools") - async def power(self) -> dict: + async def power(self) -> dict[str, Any]: """Get the estimated power usage in watts.
Expand @@ -618,7 +628,7 @@ async def power(self) -> dict: """ return await self.send_command("power") - async def profiles(self) -> dict: + async def profiles(self) -> dict[str, Any]: """Get the available profiles.
Expand @@ -629,7 +639,7 @@ async def profiles(self) -> dict: """ return await self.send_command("profiles") - async def profileset(self, profile: str) -> dict: + async def profileset(self, profile: str) -> dict[str, Any]: """Set active profile for the system.
Expand @@ -643,7 +653,7 @@ async def profileset(self, profile: str) -> dict: """ return await self.send_privileged_command("profileset", profile) - async def reboot(self, board_n: int, delay_s: int | None = None) -> dict: + async def reboot(self, board_n: int, delay_s: int | None = None) -> dict[str, Any]: """Reboot a board.
Expand @@ -661,7 +671,7 @@ async def reboot(self, board_n: int, delay_s: int | None = None) -> dict: reboot_data.append(str(delay_s)) return await self.send_privileged_command("reboot", *reboot_data) - async def rebootdevice(self) -> dict: + async def rebootdevice(self) -> dict[str, Any]: """Reboot the miner.
Expand @@ -672,7 +682,7 @@ async def rebootdevice(self) -> dict: """ return await self.send_privileged_command("rebootdevice") - async def removegroup(self, group_id: str) -> dict: + async def removegroup(self, group_id: str) -> dict[str, Any]: """Remove a pool group.
Expand @@ -686,7 +696,7 @@ async def removegroup(self, group_id: str) -> dict: """ return await self.send_command("removegroup", parameters=group_id) - async def resetminer(self) -> dict: + async def resetminer(self) -> dict[str, Any]: """Restart the mining process.
Expand @@ -697,7 +707,7 @@ async def resetminer(self) -> dict: """ return await self.send_privileged_command("resetminer") - async def removepool(self, pool_id: int) -> dict: + async def removepool(self, pool_id: int) -> dict[str, Any]: """Remove a pool.
Expand @@ -711,7 +721,7 @@ async def removepool(self, pool_id: int) -> dict: """ return await self.send_command("removepool", parameters=str(pool_id)) - async def session(self) -> dict: + async def session(self) -> dict[str, Any]: """Get the current session.
Expand @@ -727,7 +737,7 @@ async def tempctrlset( target: int | None = None, hot: int | None = None, dangerous: int | None = None, - ) -> dict: + ) -> dict[str, Any]: """Set temp control values.
Expand @@ -745,7 +755,7 @@ async def tempctrlset( "tempctrlset", target or "", hot or "", dangerous or "" ) - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Get stats of each device/pool with more than 1 getwork.
@@ -757,7 +767,7 @@ async def stats(self) -> dict: """ return await self.send_command("stats") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get the status summary of the miner.
@@ -769,7 +779,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def switchpool(self, pool_id: int) -> dict: + async def switchpool(self, pool_id: int) -> dict[str, Any]: """Switch to a pool.
Expand @@ -783,7 +793,7 @@ async def switchpool(self, pool_id: int) -> dict: """ return await self.send_command("switchpool", pool_id) - async def tempctrl(self) -> dict: + async def tempctrl(self) -> dict[str, Any]: """Get temperature control data.
Expand @@ -794,7 +804,7 @@ async def tempctrl(self) -> dict: """ return await self.send_command("tempctrl") - async def temps(self) -> dict: + async def temps(self) -> dict[str, Any]: """Get temperature data.
Expand @@ -805,7 +815,7 @@ async def temps(self) -> dict: """ return await self.send_command("temps") - async def version(self) -> dict: + async def version(self) -> dict[str, Any]: """Get miner version info.
@@ -817,7 +827,7 @@ async def version(self) -> dict: """ return await self.send_command("version") - async def voltageget(self, board_n: int) -> dict: + async def voltageget(self, board_n: int) -> dict[str, Any]: """Get voltage data for a board.
Expand @@ -831,7 +841,7 @@ async def voltageget(self, board_n: int) -> dict: """ return await self.send_command("frequencyget", board_n) - async def voltageset(self, board_n: int, voltage: float) -> dict: + async def voltageset(self, board_n: int, voltage: float) -> dict[str, Any]: """Set voltage values.
Expand @@ -846,7 +856,7 @@ async def voltageset(self, board_n: int, voltage: float) -> dict: """ return await self.send_privileged_command("voltageset", board_n, voltage) - async def updaterun(self) -> dict: + async def updaterun(self) -> dict[str, Any]: """ Send the 'updaterun' command to the miner. diff --git a/pyasic/rpc/marathon.py b/pyasic/rpc/marathon.py index 44bb43d1d..7a69483c5 100644 --- a/pyasic/rpc/marathon.py +++ b/pyasic/rpc/marathon.py @@ -1,3 +1,5 @@ +from typing import Any + from pyasic.rpc.base import BaseMinerRPCAPI @@ -17,17 +19,17 @@ class MaraRPCAPI(BaseMinerRPCAPI): rely on it to send the command for them. """ - async def summary(self): + async def summary(self) -> dict[str, Any]: return await self.send_command("summary") - async def devs(self): + async def devs(self) -> dict[str, Any]: return await self.send_command("devs") - async def pools(self): + async def pools(self) -> dict[str, Any]: return await self.send_command("pools") - async def stats(self): + async def stats(self) -> dict[str, Any]: return await self.send_command("stats") - async def version(self): + async def version(self) -> dict[str, Any]: return await self.send_command("version") diff --git a/pyasic/rpc/unknown.py b/pyasic/rpc/unknown.py index 3c38c6408..c7cd818f1 100644 --- a/pyasic/rpc/unknown.py +++ b/pyasic/rpc/unknown.py @@ -14,6 +14,8 @@ # limitations under the License. - # ------------------------------------------------------------------------------ +from typing import Any + from pyasic.rpc.cgminer import CGMinerRPCAPI @@ -25,27 +27,27 @@ class UnknownRPCAPI(CGMinerRPCAPI): with as many APIs as possible. """ - async def switchpool(self, n: int) -> dict: + async def switchpool(self, n: int) -> dict[str, Any]: # BOS has not implemented this yet, they will in the future raise NotImplementedError # return await self.send_command("switchpool", parameters=n) - async def enablepool(self, n: int) -> dict: + async def enablepool(self, n: int) -> dict[str, Any]: # BOS has not implemented this yet, they will in the future raise NotImplementedError # return await self.send_command("enablepool", parameters=n) - async def disablepool(self, n: int) -> dict: + async def disablepool(self, n: int) -> dict[str, Any]: # BOS has not implemented this yet, they will in the future raise NotImplementedError # return await self.send_command("disablepool", parameters=n) - async def addpool(self, url: str, username: str, password: str) -> dict: + async def addpool(self, url: str, username: str, password: str) -> dict[str, Any]: # BOS has not implemented this yet, they will in the future raise NotImplementedError # return await self.send_command("addpool", parameters=f"{url},{username},{password}") - async def removepool(self, n: int) -> dict: + async def removepool(self, n: int) -> dict[str, Any]: # BOS has not implemented this yet, they will in the future raise NotImplementedError # return await self.send_command("removepool", parameters=n) diff --git a/pyasic/settings/__init__.py b/pyasic/settings/__init__.py index cdbb38978..d3bffe82e 100644 --- a/pyasic/settings/__init__.py +++ b/pyasic/settings/__init__.py @@ -52,19 +52,19 @@ class Config: extra = "allow" -_settings = Settings() +_settings: Settings = Settings() -ssl_cxt = httpx.create_ssl_context() +ssl_cxt: SSLContext = httpx.create_ssl_context() # this function returns an AsyncHTTPTransport instance to perform asynchronous HTTP requests # using those options. -def transport(verify: str | bool | SSLContext = ssl_cxt): +def transport(verify: str | bool | SSLContext = ssl_cxt) -> AsyncHTTPTransport: return AsyncHTTPTransport(verify=verify) -def get(key: str, other: Any = None) -> Any: +def get(key: str, other: Any | None = None) -> Any: try: return getattr(_settings, key) except AttributeError: diff --git a/pyasic/ssh/__init__.py b/pyasic/ssh/__init__.py index 0146ba5fa..f408fcb90 100644 --- a/pyasic/ssh/__init__.py +++ b/pyasic/ssh/__init__.py @@ -15,3 +15,8 @@ # ------------------------------------------------------------------------------ from .antminer import AntminerModernSSH from .braiins_os import BOSMinerSSH + +__all__ = [ + "AntminerModernSSH", + "BOSMinerSSH", +] diff --git a/pyasic/ssh/antminer.py b/pyasic/ssh/antminer.py index d901b67ea..6272d41c6 100644 --- a/pyasic/ssh/antminer.py +++ b/pyasic/ssh/antminer.py @@ -10,7 +10,7 @@ class AntminerModernSSH(BaseSSH): ip (str): The IP address of the Antminer device. """ - def __init__(self, ip: str): + def __init__(self, ip: str) -> None: super().__init__(ip) self.pwd = settings.get("default_antminer_ssh_password", "root") self.username = "miner" diff --git a/pyasic/ssh/braiins_os.py b/pyasic/ssh/braiins_os.py index 6dee3502f..e7ca27725 100644 --- a/pyasic/ssh/braiins_os.py +++ b/pyasic/ssh/braiins_os.py @@ -3,7 +3,7 @@ class BOSMinerSSH(BaseSSH): - def __init__(self, ip: str): + def __init__(self, ip: str) -> None: """ Initialize a BOSMinerSSH instance. @@ -13,7 +13,7 @@ def __init__(self, ip: str): super().__init__(ip) self.pwd = settings.get("default_bosminer_ssh_password", "root") - async def get_board_info(self): + async def get_board_info(self) -> str | None: """ Retrieve information about the BOSMiner board. @@ -22,7 +22,7 @@ async def get_board_info(self): """ return await self.send_command("bosminer model -d") - async def fault_light_on(self): + async def fault_light_on(self) -> str | None: """ Turn on the fault light of the BOSMiner device. @@ -31,7 +31,7 @@ async def fault_light_on(self): """ return await self.send_command("miner fault_light on") - async def fault_light_off(self): + async def fault_light_off(self) -> str | None: """ Turn off the fault light of the BOSMiner device. @@ -40,7 +40,7 @@ async def fault_light_off(self): """ return await self.send_command("miner fault_light off") - async def restart_bosminer(self): + async def restart_bosminer(self) -> str | None: """ Restart the BOSMiner service on the device. @@ -49,7 +49,7 @@ async def restart_bosminer(self): """ return await self.send_command("/etc/init.d/bosminer restart") - async def reboot(self): + async def reboot(self) -> str | None: """ Reboot the BOSMiner device. @@ -58,7 +58,7 @@ async def reboot(self): """ return await self.send_command("/sbin/reboot") - async def get_config_file(self): + async def get_config_file(self) -> str | None: """ Retrieve the configuration file of BOSMiner. @@ -67,7 +67,7 @@ async def get_config_file(self): """ return await self.send_command("cat /etc/bosminer.toml") - async def get_network_config(self): + async def get_network_config(self) -> str | None: """ Retrieve the network configuration of the BOSMiner device. @@ -76,7 +76,7 @@ async def get_network_config(self): """ return await self.send_command("cat /etc/config/network") - async def get_hostname(self): + async def get_hostname(self) -> str | None: """ Retrieve the hostname of the BOSMiner device. @@ -85,7 +85,7 @@ async def get_hostname(self): """ return await self.send_command("cat /proc/sys/kernel/hostname") - async def get_led_status(self): + async def get_led_status(self) -> str | None: """ Retrieve the status of the LED on the BOSMiner device. diff --git a/pyasic/web/__init__.py b/pyasic/web/__init__.py index 4ed088c93..fd4177cd3 100644 --- a/pyasic/web/__init__.py +++ b/pyasic/web/__init__.py @@ -13,13 +13,42 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ +from __future__ import annotations + from .antminer import AntminerModernWebAPI, AntminerOldWebAPI from .auradine import AuradineWebAPI +from .avalonminer import AvalonMinerWebAPI from .base import BaseWebAPI from .braiins_os import BOSerWebAPI, BOSMinerWebAPI +from .elphapex import ElphapexWebAPI from .epic import ePICWebAPI +from .espminer import ESPMinerWebAPI from .goldshell import GoldshellWebAPI from .hammer import HammerWebAPI +from .hiveon import HiveonWebAPI from .iceriver import IceRiverWebAPI from .innosilicon import InnosiliconWebAPI +from .marathon import MaraWebAPI +from .mskminer import MSKMinerWebAPI from .vnish import VNishWebAPI + +__all__ = [ + "BaseWebAPI", + "AntminerModernWebAPI", + "AntminerOldWebAPI", + "AuradineWebAPI", + "AvalonMinerWebAPI", + "BOSerWebAPI", + "BOSMinerWebAPI", + "ElphapexWebAPI", + "ePICWebAPI", + "ESPMinerWebAPI", + "GoldshellWebAPI", + "HammerWebAPI", + "HiveonWebAPI", + "IceRiverWebAPI", + "InnosiliconWebAPI", + "MaraWebAPI", + "MSKMinerWebAPI", + "VNishWebAPI", +] diff --git a/pyasic/web/antminer.py b/pyasic/web/antminer.py index 23d21a2fc..ed6cbb18f 100644 --- a/pyasic/web/antminer.py +++ b/pyasic/web/antminer.py @@ -46,7 +46,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Antminer device using HTTP digest authentication. Args: @@ -77,14 +77,15 @@ async def send_command( else: if data.status_code == 200: try: - return data.json() + result: dict[str, Any] = data.json() + return result except json.decoder.JSONDecodeError: return {"success": False, "message": "Failed to decode JSON"} return {"success": False, "message": "Unknown error occurred"} async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously. Args: @@ -111,7 +112,7 @@ async def multicommand( async def _handle_multicommand( self, client: httpx.AsyncClient, command: str - ) -> dict: + ) -> dict[str, Any]: """Helper function for handling individual commands in a multicommand execution. Args: @@ -137,7 +138,7 @@ async def _handle_multicommand( pass return {command: {}} - async def get_miner_conf(self) -> dict: + async def get_miner_conf(self) -> dict[str, Any]: """Retrieve the miner configuration from the Antminer device. Returns: @@ -145,7 +146,7 @@ async def get_miner_conf(self) -> dict: """ return await self.send_command("get_miner_conf") - async def set_miner_conf(self, conf: dict) -> dict: + async def set_miner_conf(self, conf: dict[str, Any]) -> dict[str, Any]: """Set the configuration for the miner. Args: @@ -156,7 +157,7 @@ async def set_miner_conf(self, conf: dict) -> dict: """ return await self.send_command("set_miner_conf", **conf) - async def blink(self, blink: bool) -> dict: + async def blink(self, blink: bool) -> dict[str, Any]: """Control the blinking of the LED on the miner device. Args: @@ -169,7 +170,7 @@ async def blink(self, blink: bool) -> dict: return await self.send_command("blink", blink="true") return await self.send_command("blink", blink="false") - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: """Reboot the miner device. Returns: @@ -177,7 +178,7 @@ async def reboot(self) -> dict: """ return await self.send_command("reboot") - async def get_system_info(self) -> dict: + async def get_system_info(self) -> dict[str, Any]: """Retrieve system information from the miner. Returns: @@ -185,7 +186,7 @@ async def get_system_info(self) -> dict: """ return await self.send_command("get_system_info") - async def get_network_info(self) -> dict: + async def get_network_info(self) -> dict[str, Any]: """Retrieve network configuration information from the miner. Returns: @@ -193,7 +194,7 @@ async def get_network_info(self) -> dict: """ return await self.send_command("get_network_info") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get a summary of the miner's status and performance. Returns: @@ -201,7 +202,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def get_blink_status(self) -> dict: + async def get_blink_status(self) -> dict[str, Any]: """Check the status of the LED blinking on the miner. Returns: @@ -217,7 +218,7 @@ async def set_network_conf( subnet_mask: str, hostname: str, protocol: int, - ) -> dict: + ) -> dict[str, Any]: """Set the network configuration of the miner. Args: @@ -260,7 +261,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Antminer device using HTTP digest authentication. Args: @@ -291,14 +292,15 @@ async def send_command( else: if data.status_code == 200: try: - return data.json() + result: dict[str, Any] = data.json() + return result except json.decoder.JSONDecodeError: pass raise APIError(f"Failed to send command to miner: {self}") async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously. Args: @@ -327,7 +329,7 @@ async def multicommand( pass return data - async def get_system_info(self) -> dict: + async def get_system_info(self) -> dict[str, Any]: """Retrieve system information from the miner. Returns: @@ -335,7 +337,7 @@ async def get_system_info(self) -> dict: """ return await self.send_command("get_system_info") - async def blink(self, blink: bool) -> dict: + async def blink(self, blink: bool) -> dict[str, Any]: """Control the blinking of the LED on the miner device. Args: @@ -348,7 +350,7 @@ async def blink(self, blink: bool) -> dict: return await self.send_command("blink", action="startBlink") return await self.send_command("blink", action="stopBlink") - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: """Reboot the miner device. Returns: @@ -356,7 +358,7 @@ async def reboot(self) -> dict: """ return await self.send_command("reboot") - async def get_blink_status(self) -> dict: + async def get_blink_status(self) -> dict[str, Any]: """Check the status of the LED blinking on the miner. Returns: @@ -364,7 +366,7 @@ async def get_blink_status(self) -> dict: """ return await self.send_command("blink", action="onPageLoaded") - async def get_miner_conf(self) -> dict: + async def get_miner_conf(self) -> dict[str, Any]: """Retrieve the miner configuration from the Antminer device. Returns: @@ -372,7 +374,7 @@ async def get_miner_conf(self) -> dict: """ return await self.send_command("get_miner_conf") - async def set_miner_conf(self, conf: dict) -> dict: + async def set_miner_conf(self, conf: dict[str, Any]) -> dict[str, Any]: """Set the configuration for the miner. Args: @@ -383,7 +385,7 @@ async def set_miner_conf(self, conf: dict) -> dict: """ return await self.send_command("set_miner_conf", **conf) - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Retrieve detailed statistical data of the mining operation. Returns: @@ -391,7 +393,7 @@ async def stats(self) -> dict: """ return await self.send_command("miner_stats") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get a summary of the miner's status and performance. Returns: @@ -399,7 +401,7 @@ async def summary(self) -> dict: """ return await self.send_command("miner_summary") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Retrieve current pool information associated with the miner. Returns: @@ -407,7 +409,9 @@ async def pools(self) -> dict: """ return await self.send_command("miner_pools") - async def update_firmware(self, file: Path, keep_settings: bool = True) -> dict: + async def update_firmware( + self, file: Path, keep_settings: bool = True + ) -> dict[str, Any]: """Perform a system update by uploading a firmware file and sending a command to initiate the update.""" async with aiofiles.open(file, "rb") as firmware: diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index 75375443a..17eccfab0 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -60,7 +60,7 @@ async def auth(self) -> str | None: except httpx.HTTPError: warnings.warn(f"Could not authenticate web token with miner: {self}") else: - json_auth = auth.json() + json_auth: dict[str, Any] = auth.json() try: self.token = json_auth["Token"][0]["Token"] except LookupError: @@ -74,7 +74,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Auradine miner, handling authentication and retries. Args: @@ -113,7 +113,7 @@ async def send_command( headers={"Token": self.token}, timeout=settings.get("api_function_timeout", 5), ) - json_data = response.json() + json_data: dict[str, Any] = response.json() validation = validate_command_output(json_data) if not validation[0]: if i == settings.get("get_data_retries", 1): @@ -128,7 +128,7 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously on the Auradine miner. Args: @@ -157,7 +157,7 @@ async def multicommand( return data - async def factory_reset(self) -> dict: + async def factory_reset(self) -> dict[str, Any]: """Perform a factory reset on the Auradine miner. Returns: @@ -165,7 +165,7 @@ async def factory_reset(self) -> dict: """ return await self.send_command("factory-reset", privileged=True) - async def get_fan(self) -> dict: + async def get_fan(self) -> dict[str, Any]: """Retrieve the current fan status from the Auradine miner. Returns: @@ -173,7 +173,7 @@ async def get_fan(self) -> dict: """ return await self.send_command("fan") - async def set_fan(self, fan: int, speed_pct: int) -> dict: + async def set_fan(self, fan: int, speed_pct: int) -> dict[str, Any]: """Set the speed of a specific fan on the Auradine miner. Args: @@ -187,7 +187,7 @@ async def set_fan(self, fan: int, speed_pct: int) -> dict: async def firmware_upgrade( self, url: str | None = None, version: str = "latest" - ) -> dict: + ) -> dict[str, Any]: """Upgrade the firmware of the Auradine miner. Args: @@ -201,7 +201,7 @@ async def firmware_upgrade( return await self.send_command("firmware-upgrade", url=url) return await self.send_command("firmware-upgrade", version=version) - async def get_frequency(self) -> dict: + async def get_frequency(self) -> dict[str, Any]: """Retrieve the current frequency settings of the Auradine miner. Returns: @@ -209,7 +209,7 @@ async def get_frequency(self) -> dict: """ return await self.send_command("frequency") - async def set_frequency(self, board: int, frequency: float) -> dict: + async def set_frequency(self, board: int, frequency: float) -> dict[str, Any]: """Set the frequency for a specific board on the Auradine miner. Args: @@ -221,7 +221,7 @@ async def set_frequency(self, board: int, frequency: float) -> dict: """ return await self.send_command("frequency", board=board, frequency=frequency) - async def ipreport(self) -> dict: + async def ipreport(self) -> dict[str, Any]: """Generate an IP report for the Auradine miner. Returns: @@ -229,7 +229,7 @@ async def ipreport(self) -> dict: """ return await self.send_command("ipreport") - async def get_led(self) -> dict: + async def get_led(self) -> dict[str, Any]: """Retrieve the current LED status from the Auradine miner. Returns: @@ -237,7 +237,7 @@ async def get_led(self) -> dict: """ return await self.send_command("led") - async def set_led(self, code: int) -> dict: + async def set_led(self, code: int) -> dict[str, Any]: """Set the LED code on the Auradine miner. Args: @@ -248,7 +248,9 @@ async def set_led(self, code: int) -> dict: """ return await self.send_command("led", code=code) - async def set_led_custom(self, code: int, led_1: int, led_2: int, msg: str) -> dict: + async def set_led_custom( + self, code: int, led_1: int, led_2: int, msg: str + ) -> dict[str, Any]: """Set custom LED configurations including messages. Args: @@ -264,7 +266,7 @@ async def set_led_custom(self, code: int, led_1: int, led_2: int, msg: str) -> d "led", code=code, led1=led_1, led2=led_2, msg=msg ) - async def get_mode(self) -> dict: + async def get_mode(self) -> dict[str, Any]: """Retrieve the current operational mode of the Auradine miner. Returns: @@ -272,7 +274,7 @@ async def get_mode(self) -> dict: """ return await self.send_command("mode") - async def set_mode(self, **kwargs: Any) -> dict: + async def set_mode(self, **kwargs: Any) -> dict[str, Any]: """Set the operational mode of the Auradine miner. Args: @@ -283,7 +285,7 @@ async def set_mode(self, **kwargs: Any) -> dict: """ return await self.send_command("mode", **kwargs) - async def get_network(self) -> dict: + async def get_network(self) -> dict[str, Any]: """Retrieve the network configuration settings of the Auradine miner. Returns: @@ -291,7 +293,7 @@ async def get_network(self) -> dict: """ return await self.send_command("network") - async def set_network(self, **kwargs: Any) -> dict: + async def set_network(self, **kwargs: Any) -> dict[str, Any]: """Set the network configuration of the Auradine miner. Args: @@ -302,7 +304,7 @@ async def set_network(self, **kwargs: Any) -> dict: """ return await self.send_command("network", **kwargs) - async def password(self, password: str) -> dict: + async def password(self, password: str) -> dict[str, Any]: """Change the password used for accessing the Auradine miner. Args: @@ -317,7 +319,7 @@ async def password(self, password: str) -> dict: self.pwd = password return res - async def get_psu(self) -> dict: + async def get_psu(self) -> dict[str, Any]: """Retrieve the status of the power supply unit (PSU) from the Auradine miner. Returns: @@ -325,7 +327,7 @@ async def get_psu(self) -> dict: """ return await self.send_command("psu") - async def set_psu(self, voltage: float) -> dict: + async def set_psu(self, voltage: float) -> dict[str, Any]: """Set the voltage for the power supply unit of the Auradine miner. Args: @@ -336,7 +338,7 @@ async def set_psu(self, voltage: float) -> dict: """ return await self.send_command("psu", voltage=voltage) - async def get_register(self) -> dict: + async def get_register(self) -> dict[str, Any]: """Retrieve registration information from the Auradine miner. Returns: @@ -344,7 +346,7 @@ async def get_register(self) -> dict: """ return await self.send_command("register") - async def set_register(self, company: str) -> dict: + async def set_register(self, company: str) -> dict[str, Any]: """Set the registration information for the Auradine miner. Args: @@ -355,7 +357,7 @@ async def set_register(self, company: str) -> dict: """ return await self.send_command("register", parameter=company) - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: """Reboot the Auradine miner. Returns: @@ -363,7 +365,7 @@ async def reboot(self) -> dict: """ return await self.send_command("restart", privileged=True) - async def restart_gcminer(self) -> dict: + async def restart_gcminer(self) -> dict[str, Any]: """Restart the GCMiner application on the Auradine miner. Returns: @@ -371,7 +373,7 @@ async def restart_gcminer(self) -> dict: """ return await self.send_command("restart", parameter="gcminer") - async def restart_api_server(self) -> dict: + async def restart_api_server(self) -> dict[str, Any]: """Restart the API server on the Auradine miner. Returns: @@ -379,7 +381,7 @@ async def restart_api_server(self) -> dict: """ return await self.send_command("restart", parameter="api-server") - async def temperature(self) -> dict: + async def temperature(self) -> dict[str, Any]: """Retrieve the current temperature readings from the Auradine miner. Returns: @@ -387,7 +389,7 @@ async def temperature(self) -> dict: """ return await self.send_command("temperature") - async def timedate(self, ntp: str, timezone: str) -> dict: + async def timedate(self, ntp: str, timezone: str) -> dict[str, Any]: """Set the time and date settings for the Auradine miner. Args: @@ -399,7 +401,7 @@ async def timedate(self, ntp: str, timezone: str) -> dict: """ return await self.send_command("timedate", ntp=ntp, timezone=timezone) - async def get_token(self) -> dict: + async def get_token(self) -> dict[str, Any]: """Retrieve the current authentication token for the Auradine miner. Returns: @@ -407,7 +409,7 @@ async def get_token(self) -> dict: """ return await self.send_command("token", user=self.username, password=self.pwd) - async def update_pools(self, pools: list[dict]) -> dict: + async def update_pools(self, pools: list[dict[str, Any]]) -> dict[str, Any]: """Update the mining pools configuration on the Auradine miner. Args: @@ -418,7 +420,7 @@ async def update_pools(self, pools: list[dict]) -> dict: """ return await self.send_command("updatepools", pools=pools) - async def voltage(self) -> dict: + async def voltage(self) -> dict[str, Any]: """Retrieve the voltage settings of the Auradine miner. Returns: @@ -426,7 +428,7 @@ async def voltage(self) -> dict: """ return await self.send_command("voltage") - async def get_ztp(self) -> dict: + async def get_ztp(self) -> dict[str, Any]: """Retrieve the zero-touch provisioning status from the Auradine miner. Returns: @@ -434,7 +436,7 @@ async def get_ztp(self) -> dict: """ return await self.send_command("ztp") - async def set_ztp(self, enable: bool) -> dict: + async def set_ztp(self, enable: bool) -> dict[str, Any]: """Enable or disable zero-touch provisioning (ZTP) on the Auradine miner. Args: diff --git a/pyasic/web/avalonminer.py b/pyasic/web/avalonminer.py index 6d0c46b43..4510ee053 100644 --- a/pyasic/web/avalonminer.py +++ b/pyasic/web/avalonminer.py @@ -44,7 +44,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Avalonminer device using HTTP digest authentication. Args: @@ -64,14 +64,15 @@ async def send_command( client.cookies.set("auth", cookie_data) resp = await client.get(url) raw_data = resp.text.replace("minerinfoCallback(", "").replace(");", "") - return json.loads(raw_data) + result: dict[str, Any] = json.loads(raw_data) + return result except (httpx.HTTPError, json.JSONDecodeError): pass return {} async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: async with httpx.AsyncClient(transport=settings.transport()) as client: cookie_data = ( "ff0000ff" + hashlib.sha256(self.pwd.encode()).hexdigest()[:24] @@ -92,18 +93,19 @@ async def multicommand( async def _handle_multicommand( self, client: httpx.AsyncClient, command: str - ) -> dict: + ) -> dict[str, Any]: try: url = f"http://{self.ip}:{self.port}/{command}.cgi" resp = await client.get(url) raw_data = resp.text.replace("minerinfoCallback(", "").replace(");", "") - return json.loads(raw_data) + result: dict[str, Any] = json.loads(raw_data) + return result except httpx.HTTPError: pass return {} - async def minerinfo(self): + async def minerinfo(self) -> dict[str, Any]: return await self.send_command("get_minerinfo") - async def home(self): + async def home(self) -> dict[str, Any]: return await self.send_command("get_home") diff --git a/pyasic/web/base.py b/pyasic/web/base.py index fabb93809..2aa6fc160 100644 --- a/pyasic/web/base.py +++ b/pyasic/web/base.py @@ -48,13 +48,13 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: pass @abstractmethod async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: pass def _check_commands(self, *commands: str) -> list[str]: diff --git a/pyasic/web/braiins_os/__init__.py b/pyasic/web/braiins_os/__init__.py index 95ba7d521..3ffe6c52f 100644 --- a/pyasic/web/braiins_os/__init__.py +++ b/pyasic/web/braiins_os/__init__.py @@ -1,2 +1,7 @@ from .boser import BOSerWebAPI from .bosminer import BOSMinerWebAPI + +__all__ = [ + "BOSerWebAPI", + "BOSMinerWebAPI", +] diff --git a/pyasic/web/braiins_os/better_monkey.py b/pyasic/web/braiins_os/better_monkey.py index 45a2ec7cc..654ff6838 100644 --- a/pyasic/web/braiins_os/better_monkey.py +++ b/pyasic/web/braiins_os/better_monkey.py @@ -6,7 +6,7 @@ # https://github.com/danielgtaylor/python-betterproto/pull/609 def to_pydict( - self, casing: Casing = Casing.CAMEL, include_default_values: bool = False + self: Message, casing: Casing = Casing.CAMEL, include_default_values: bool = False ) -> dict[str, Any]: """ Returns a python dict representation of this object. @@ -27,8 +27,8 @@ def to_pydict( The python dict representation of this object. """ output: dict[str, Any] = {} - defaults = self._betterproto.default_gen - for field_name, meta in self._betterproto.meta_by_field_name.items(): + defaults = self._betterproto.default_gen # type: ignore + for field_name, meta in self._betterproto.meta_by_field_name.items(): # type: ignore field_is_repeated = defaults[field_name] is list try: value = getattr(self, field_name) @@ -89,5 +89,5 @@ def to_pydict( return output -def patch(): - Message.to_pydict = to_pydict +def patch() -> None: + setattr(Message, "to_pydict", to_pydict) diff --git a/pyasic/web/braiins_os/boser.py b/pyasic/web/braiins_os/boser.py index e538733dc..331a55f98 100644 --- a/pyasic/web/braiins_os/boser.py +++ b/pyasic/web/braiins_os/boser.py @@ -13,13 +13,13 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from __future__ import annotations - import asyncio import logging -from datetime import timedelta -from typing import Any +from datetime import datetime, timedelta +from typing import Any, cast +import betterproto +import grpclib.const from grpclib import GRPCError, Status from grpclib.client import Channel @@ -30,8 +30,66 @@ patch() -from .proto.braiins.bos import * -from .proto.braiins.bos.v1 import * +from .proto.braiins.bos import ApiVersionRequest, ApiVersionServiceStub +from .proto.braiins.bos.v1 import ( + ActionsServiceStub, + AuthenticationServiceStub, + ConfigurationServiceStub, + CoolingServiceStub, + DecrementHashrateTargetRequest, + DecrementPowerTargetRequest, + DisableHashboardsRequest, + DpsPowerTarget, + DpsTarget, + EnableHashboardsRequest, + GetConstraintsRequest, + GetCoolingStateRequest, + GetHashboardsRequest, + GetLicenseStateRequest, + GetLocateDeviceStatusRequest, + GetMinerConfigurationRequest, + GetMinerDetailsRequest, + GetMinerStatsRequest, + GetMinerStatusRequest, + GetPerformanceModeRequest, + GetPoolGroupsRequest, + GetSupportArchiveRequest, + GetTunerStateRequest, + HashrateTargetMode, + Hours, + IncrementHashrateTargetRequest, + IncrementPowerTargetRequest, + LicenseServiceStub, + ListTargetProfilesRequest, + LoginRequest, + LoginResponse, + MinerServiceStub, + PauseMiningRequest, + PerformanceMode, + PerformanceServiceStub, + PoolGroupConfiguration, + PoolServiceStub, + Power, + PowerTargetMode, + RebootRequest, + RestartRequest, + ResumeMiningRequest, + SaveAction, + SetDefaultHashrateTargetRequest, + SetDefaultPowerTargetRequest, + SetDpsRequest, + SetHashrateTargetRequest, + SetImmersionModeRequest, + SetLocateDeviceStatusRequest, + SetPasswordRequest, + SetPerformanceModeRequest, + SetPoolGroupsRequest, + SetPowerTargetRequest, + StartRequest, + StopRequest, + TeraHashrate, + TunerPerformanceMode, +) class BOSMinerGRPCStub( @@ -57,10 +115,10 @@ def __init__(self, ip: str) -> None: self._auth_time: datetime | None = None @property - def commands(self) -> list: + def commands(self) -> list[str]: return self.get_commands() - def get_commands(self) -> list: + def get_commands(self) -> list[str]: return [ func for func in @@ -77,7 +135,7 @@ def get_commands(self) -> list: async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: result: dict[str, Any] = {"multicommand": True} tasks = {} for command in commands: @@ -103,7 +161,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: message: betterproto.Message = parameters["message"] metadata = [] if privileged: @@ -116,12 +174,18 @@ async def send_command( raise APIError(f"Command not found - {endpoint}") return {} try: - return (await endpoint(message, metadata=metadata)).to_pydict() + return cast( + dict[str, Any], + (await endpoint(message, metadata=metadata)).to_pydict(), + ) except GRPCError as e: if e.status == Status.UNAUTHENTICATED: await self._get_auth() metadata = [("authorization", await self.auth())] - return (await endpoint(message, metadata=metadata)).to_pydict() + return cast( + dict[str, Any], + (await endpoint(message, metadata=metadata)).to_pydict(), + ) raise e except (GRPCError, ConnectionError) as e: raise APIError(f"gRPC command failed - {endpoint}") from e @@ -155,59 +219,59 @@ async def _get_auth(self) -> str | None: return self.token return None - async def get_api_version(self) -> dict: + async def get_api_version(self) -> dict[str, Any]: return await self.send_command( "get_api_version", message=ApiVersionRequest(), privileged=False ) - async def start(self) -> dict: + async def start(self) -> dict[str, Any]: return await self.send_command("start", message=StartRequest(), privileged=True) - async def stop(self) -> dict: + async def stop(self) -> dict[str, Any]: return await self.send_command("stop", message=StopRequest(), privileged=True) - async def pause_mining(self) -> dict: + async def pause_mining(self) -> dict[str, Any]: return await self.send_command( "pause_mining", message=PauseMiningRequest(), privileged=True ) - async def resume_mining(self) -> dict: + async def resume_mining(self) -> dict[str, Any]: return await self.send_command( "resume_mining", message=ResumeMiningRequest(), privileged=True ) - async def restart(self) -> dict: + async def restart(self) -> dict[str, Any]: return await self.send_command( "restart", message=RestartRequest(), privileged=True ) - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: return await self.send_command( "reboot", message=RebootRequest(), privileged=True ) - async def set_locate_device_status(self, enable: bool) -> dict: + async def set_locate_device_status(self, enable: bool) -> dict[str, Any]: return await self.send_command( "set_locate_device_status", message=SetLocateDeviceStatusRequest(enable=enable), privileged=True, ) - async def get_locate_device_status(self) -> dict: + async def get_locate_device_status(self) -> dict[str, Any]: return await self.send_command( "get_locate_device_status", message=GetLocateDeviceStatusRequest(), privileged=True, ) - async def set_password(self, password: str | None = None) -> dict: + async def set_password(self, password: str | None = None) -> dict[str, Any]: return await self.send_command( "set_password", message=SetPasswordRequest(password=password), privileged=True, ) - async def get_cooling_state(self) -> dict: + async def get_cooling_state(self) -> dict[str, Any]: return await self.send_command( "get_cooling_state", message=GetCoolingStateRequest(), privileged=True ) @@ -216,7 +280,7 @@ async def set_immersion_mode( self, enable: bool, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_immersion_mode", message=SetImmersionModeRequest( @@ -225,19 +289,19 @@ async def set_immersion_mode( privileged=True, ) - async def get_tuner_state(self) -> dict: + async def get_tuner_state(self) -> dict[str, Any]: return await self.send_command( "get_tuner_state", message=GetTunerStateRequest(), privileged=True ) - async def list_target_profiles(self) -> dict: + async def list_target_profiles(self) -> dict[str, Any]: return await self.send_command( "list_target_profiles", message=ListTargetProfilesRequest(), privileged=True ) async def set_default_power_target( self, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY) - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_default_power_target", message=SetDefaultPowerTargetRequest(save_action=save_action), @@ -248,7 +312,7 @@ async def set_power_target( self, power_target: int, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_power_target", message=SetPowerTargetRequest( @@ -261,7 +325,7 @@ async def increment_power_target( self, power_target_increment: int, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "increment_power_target", message=IncrementPowerTargetRequest( @@ -275,7 +339,7 @@ async def decrement_power_target( self, power_target_decrement: int, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "decrement_power_target", message=DecrementPowerTargetRequest( @@ -287,7 +351,7 @@ async def decrement_power_target( async def set_default_hashrate_target( self, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY) - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_default_hashrate_target", message=SetDefaultHashrateTargetRequest(save_action=save_action), @@ -298,7 +362,7 @@ async def set_hashrate_target( self, hashrate_target: float, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_hashrate_target", message=SetHashrateTargetRequest( @@ -312,7 +376,7 @@ async def increment_hashrate_target( self, hashrate_target_increment: int, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "increment_hashrate_target", message=IncrementHashrateTargetRequest( @@ -328,7 +392,7 @@ async def decrement_hashrate_target( self, hashrate_target_decrement: int, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "decrement_hashrate_target", message=DecrementHashrateTargetRequest( @@ -347,7 +411,7 @@ async def set_dps( min_power_target: int, enable_shutdown: bool | None = None, shutdown_duration: int | None = None, - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_dps", message=SetDpsRequest( @@ -373,7 +437,7 @@ async def set_performance_mode( wattage_target: int | None = None, hashrate_target: int | None = None, save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: if wattage_target is not None and hashrate_target is not None: logging.error( "Cannot use both wattage_target and hashrate_target, using wattage_target." @@ -405,56 +469,56 @@ async def set_performance_mode( privileged=True, ) - async def get_active_performance_mode(self) -> dict: + async def get_active_performance_mode(self) -> dict[str, Any]: return await self.send_command( "get_active_performance_mode", message=GetPerformanceModeRequest(), privileged=True, ) - async def get_pool_groups(self) -> dict: + async def get_pool_groups(self) -> dict[str, Any]: return await self.send_command( "get_pool_groups", message=GetPoolGroupsRequest(), privileged=True ) - async def get_miner_configuration(self) -> dict: + async def get_miner_configuration(self) -> dict[str, Any]: return await self.send_command( "get_miner_configuration", message=GetMinerConfigurationRequest(), privileged=True, ) - async def get_constraints(self) -> dict: + async def get_constraints(self) -> dict[str, Any]: return await self.send_command( "get_constraints", message=GetConstraintsRequest(), privileged=True ) - async def get_license_state(self) -> dict: + async def get_license_state(self) -> dict[str, Any]: return await self.send_command( "get_license_state", message=GetLicenseStateRequest(), privileged=True ) - async def get_miner_status(self) -> dict: + async def get_miner_status(self) -> dict[str, Any]: return await self.send_command( "get_miner_status", message=GetMinerStatusRequest(), privileged=True ) - async def get_miner_details(self) -> dict: + async def get_miner_details(self) -> dict[str, Any]: return await self.send_command( "get_miner_details", message=GetMinerDetailsRequest(), privileged=True ) - async def get_miner_stats(self) -> dict: + async def get_miner_stats(self) -> dict[str, Any]: return await self.send_command( "get_miner_stats", message=GetMinerStatsRequest(), privileged=True ) - async def get_hashboards(self) -> dict: + async def get_hashboards(self) -> dict[str, Any]: return await self.send_command( "get_hashboards", message=GetHashboardsRequest(), privileged=True ) - async def get_support_archive(self) -> dict: + async def get_support_archive(self) -> dict[str, Any]: return await self.send_command( "get_support_archive", message=GetSupportArchiveRequest(), privileged=True ) @@ -463,7 +527,7 @@ async def enable_hashboards( self, hashboard_ids: list[str], save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "enable_hashboards", message=EnableHashboardsRequest( @@ -476,7 +540,7 @@ async def disable_hashboards( self, hashboard_ids: list[str], save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "disable_hashboards", message=DisableHashboardsRequest( @@ -489,7 +553,7 @@ async def set_pool_groups( self, pool_groups: list[PoolGroupConfiguration], save_action: SaveAction = SaveAction(SaveAction.SAVE_AND_APPLY), - ) -> dict: + ) -> dict[str, Any]: return await self.send_command( "set_pool_groups", message=SetPoolGroupsRequest( diff --git a/pyasic/web/braiins_os/bosminer.py b/pyasic/web/braiins_os/bosminer.py index 36d17c8bc..177028ca1 100644 --- a/pyasic/web/braiins_os/bosminer.py +++ b/pyasic/web/braiins_os/bosminer.py @@ -39,7 +39,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: try: async with httpx.AsyncClient(transport=settings.transport()) as client: await self.auth(client) @@ -48,7 +48,8 @@ async def send_command( headers={"User-Agent": "BTC Tools v0.1"}, ) if data.status_code == 200: - return data.json() + json_data: dict[str, Any] = data.json() + return json_data if ignore_errors: return {} raise APIError( @@ -61,7 +62,7 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: data = {} for command in commands: data[command] = await self.send_command( @@ -80,22 +81,22 @@ async def auth(self, session: httpx.AsyncClient) -> None: } await session.post(url, headers=headers, data=login) - async def get_net_conf(self) -> dict: + async def get_net_conf(self) -> dict[str, Any]: return await self.send_command("admin/network/iface_status/lan") - async def get_cfg_metadata(self) -> dict: + async def get_cfg_metadata(self) -> dict[str, Any]: return await self.send_command("admin/miner/cfg_metadata") - async def get_cfg_data(self) -> dict: + async def get_cfg_data(self) -> dict[str, Any]: return await self.send_command("admin/miner/cfg_data") - async def get_bos_info(self) -> dict: + async def get_bos_info(self) -> dict[str, Any]: return await self.send_command("bos/info") - async def get_overview(self) -> dict: + async def get_overview(self) -> dict[str, Any]: return await self.send_command( "admin/status/overview?status=1" ) # needs status=1 or it fails - async def get_api_status(self) -> dict: + async def get_api_status(self) -> dict[str, Any]: return await self.send_command("admin/miner/api_status") diff --git a/pyasic/web/elphapex.py b/pyasic/web/elphapex.py index b288e7414..3f7bd6793 100644 --- a/pyasic/web/elphapex.py +++ b/pyasic/web/elphapex.py @@ -43,7 +43,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Elphapex device using HTTP digest authentication. Args: @@ -74,14 +74,15 @@ async def send_command( else: if data.status_code == 200: try: - return data.json() + result: dict[str, Any] = data.json() + return result except json.decoder.JSONDecodeError: return {"success": False, "message": "Failed to decode JSON"} return {"success": False, "message": "Unknown error occurred"} async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously. Args: @@ -108,7 +109,7 @@ async def multicommand( async def _handle_multicommand( self, client: httpx.AsyncClient, command: str - ) -> dict: + ) -> dict[str, Any]: """Helper function for handling individual commands in a multicommand execution. Args: @@ -120,7 +121,7 @@ async def _handle_multicommand( """ auth = httpx.DigestAuth(self.username, self.pwd) - async def _send(): + async def _send() -> dict[str, Any] | None: try: url = f"http://{self.ip}/cgi-bin/{command}.cgi" ret = await client.get(url, auth=auth) @@ -129,7 +130,7 @@ async def _send(): else: if ret.status_code == 200: try: - json_data = ret.json() + json_data: dict[str, Any] = ret.json() if json_data.get("STATUS", {}).get("STATUS") not in ["S", "I"]: return None return {command: json_data} @@ -144,7 +145,7 @@ async def _send(): return res return {command: {}} - async def get_miner_conf(self) -> dict: + async def get_miner_conf(self) -> dict[str, Any]: """Retrieve the miner configuration from the Elphapex device. Returns: @@ -152,7 +153,7 @@ async def get_miner_conf(self) -> dict: """ return await self.send_command("get_miner_conf") - async def set_miner_conf(self, conf: dict) -> dict: + async def set_miner_conf(self, conf: dict[str, Any]) -> dict[str, Any]: """Set the configuration for the miner. Args: @@ -163,7 +164,7 @@ async def set_miner_conf(self, conf: dict) -> dict: """ return await self.send_command("set_miner_conf", **conf) - async def blink(self, blink: bool) -> dict: + async def blink(self, blink: bool) -> dict[str, Any]: """Control the blinking of the LED on the miner device. Args: @@ -176,7 +177,7 @@ async def blink(self, blink: bool) -> dict: return await self.send_command("blink", blink="true") return await self.send_command("blink", blink="false") - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: """Reboot the miner device. Returns: @@ -184,7 +185,7 @@ async def reboot(self) -> dict: """ return await self.send_command("reboot") - async def get_system_info(self) -> dict: + async def get_system_info(self) -> dict[str, Any]: """Retrieve system information from the miner. Returns: @@ -192,7 +193,7 @@ async def get_system_info(self) -> dict: """ return await self.send_command("get_system_info") - async def get_network_info(self) -> dict: + async def get_network_info(self) -> dict[str, Any]: """Retrieve network configuration information from the miner. Returns: @@ -200,7 +201,7 @@ async def get_network_info(self) -> dict: """ return await self.send_command("get_network_info") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get a summary of the miner's status and performance. Returns: @@ -208,7 +209,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Get miners stats. Returns: @@ -216,7 +217,7 @@ async def stats(self) -> dict: """ return await self.send_command("stats") - async def get_blink_status(self) -> dict: + async def get_blink_status(self) -> dict[str, Any]: """Check the status of the LED blinking on the miner. Returns: @@ -224,7 +225,7 @@ async def get_blink_status(self) -> dict: """ return await self.send_command("get_blink_status") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Check the status of the miner's pools. Returns: diff --git a/pyasic/web/epic.py b/pyasic/web/epic.py index 267a8a9ba..16f07d79a 100644 --- a/pyasic/web/epic.py +++ b/pyasic/web/epic.py @@ -43,7 +43,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: post = privileged or not parameters == {} async with httpx.AsyncClient(transport=settings.transport()) as client: @@ -79,7 +79,7 @@ async def send_command( f"Web command {command} failed with status code {response.status_code}" ) return {} - json_data = response.json() + json_data: dict[str, Any] = response.json() if json_data: # The API can return a fail status if the miner cannot return the requested data. Catch this and pass if not json_data.get("result", True) and not post: @@ -95,62 +95,64 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: data: dict[str, Any] = {k: None for k in commands} data["multicommand"] = True for command in commands: data[command] = await self.send_command(command) return data - async def restart_epic(self) -> dict: + async def restart_epic(self) -> dict[str, Any]: return await self.send_command("softreboot", privileged=True) - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: return await self.send_command("reboot", privileged=True) - async def set_shutdown_temp(self, params: int) -> dict: + async def set_shutdown_temp(self, params: int) -> dict[str, Any]: return await self.send_command("shutdowntemp", param=params) - async def set_critical_temp(self, params: int) -> dict: + async def set_critical_temp(self, params: int) -> dict[str, Any]: return await self.send_command("criticaltemp", param=params) - async def set_fan(self, params: dict) -> dict: + async def set_fan(self, params: dict[str, Any]) -> dict[str, Any]: return await self.send_command("fanspeed", param=params) - async def set_ptune_enable(self, params: bool) -> dict: + async def set_ptune_enable(self, params: bool) -> dict[str, Any]: return await self.send_command("perpetualtune", param=params) - async def set_ptune_algo(self, params: dict) -> dict: + async def set_ptune_algo(self, params: dict[str, Any]) -> dict[str, Any]: return await self.send_command("perpetualtune/algo", param=params) - async def set_pools(self, params: dict) -> dict: + async def set_pools(self, params: dict[str, Any]) -> dict[str, Any]: return await self.send_command("coin", param=params) - async def pause_mining(self) -> dict: + async def pause_mining(self) -> dict[str, Any]: return await self.send_command("miner", param="Stop") - async def resume_mining(self) -> dict: + async def resume_mining(self) -> dict[str, Any]: return await self.send_command("miner", param="Autostart") - async def stop_mining(self) -> dict: + async def stop_mining(self) -> dict[str, Any]: return await self.send_command("miner", param="Stop") - async def start_mining(self) -> dict: + async def start_mining(self) -> dict[str, Any]: return await self.send_command("miner", param="Autostart") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: return await self.send_command("summary") - async def hashrate(self) -> dict: + async def hashrate(self) -> dict[str, Any]: return await self.send_command("hashrate") - async def network(self) -> dict: + async def network(self) -> dict[str, Any]: return await self.send_command("network") - async def capabilities(self) -> dict: + async def capabilities(self) -> dict[str, Any]: return await self.send_command("capabilities") - async def system_update(self, file: Path | str, keep_settings: bool = True) -> None: + async def system_update( + self, file: Path | str, keep_settings: bool = True + ) -> dict[str, Any]: """Perform a system update by uploading a firmware file and sending a command to initiate the update.""" @@ -165,4 +167,4 @@ async def system_update(self, file: Path | str, keep_settings: bool = True) -> N with open(file, "rb") as f: files = {"update.zip": ("update.zip", f, "application/zip")} data = {"checksum": checksum, "keepsettings": str(keep_settings).lower()} - await self.send_command("systemupdate", files=files, data=data) + return await self.send_command("systemupdate", files=files, data=data) diff --git a/pyasic/web/espminer.py b/pyasic/web/espminer.py index 0bf933bf6..c4e80b2bd 100644 --- a/pyasic/web/espminer.py +++ b/pyasic/web/espminer.py @@ -6,7 +6,8 @@ import httpx -from pyasic import APIError, settings +from pyasic import settings +from pyasic.errors import APIError from pyasic.web.base import BaseWebAPI @@ -18,7 +19,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: url = f"http://{self.ip}:{self.port}/api/{command}" async with httpx.AsyncClient(transport=settings.transport()) as client: retries = settings.get("get_data_retries", 1) @@ -51,7 +52,8 @@ async def send_command( else: if data.status_code == 200: try: - return data.json() + result: dict[str, Any] = data.json() + return result except json.decoder.JSONDecodeError as e: response_text = data.text if data.text else "empty response" if attempt == retries - 1: @@ -62,7 +64,7 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously on the BitAxe miner. Args: @@ -94,17 +96,17 @@ async def multicommand( return data - async def system_info(self) -> dict: + async def system_info(self) -> dict[str, Any]: return await self.send_command("system/info") - async def swarm_info(self) -> dict: + async def swarm_info(self) -> dict[str, Any]: return await self.send_command("swarm/info") - async def restart(self) -> dict: + async def restart(self) -> dict[str, Any]: return await self.send_command("system/restart", post=True) - async def update_settings(self, **config: Any) -> dict: + async def update_settings(self, **config: Any) -> dict[str, Any]: return await self.send_command("system", patch=True, **config) - async def asic_info(self) -> dict: + async def asic_info(self) -> dict[str, Any]: return await self.send_command("system/asic") diff --git a/pyasic/web/goldshell.py b/pyasic/web/goldshell.py index abd1f9e04..12ae1a04f 100644 --- a/pyasic/web/goldshell.py +++ b/pyasic/web/goldshell.py @@ -39,21 +39,19 @@ async def auth(self) -> str | None: async with httpx.AsyncClient(transport=settings.transport()) as client: try: await client.get(f"http://{self.ip}:{self.port}/user/logout") - auth = ( - await client.get( - f"http://{self.ip}:{self.port}/user/login?username={self.username}&password={self.pwd}&cipher=false" - ) - ).json() + auth_response = await client.get( + f"http://{self.ip}:{self.port}/user/login?username={self.username}&password={self.pwd}&cipher=false" + ) + auth: dict[str, Any] = auth_response.json() except httpx.HTTPError: warnings.warn(f"Could not authenticate web token with miner: {self}") except json.JSONDecodeError: # try again with encrypted normal password try: - auth = ( - await client.get( - f"http://{self.ip}:{self.port}/user/login?username=admin&password=bbad7537f4c8b6ea31eea0b3d760e257&cipher=true" - ) - ).json() + auth_response = await client.get( + f"http://{self.ip}:{self.port}/user/login?username=admin&password=bbad7537f4c8b6ea31eea0b3d760e257&cipher=true" + ) + auth = auth_response.json() except (httpx.HTTPError, json.JSONDecodeError): warnings.warn( f"Could not authenticate web token with miner: {self}" @@ -71,7 +69,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: if self.token is None: await self.auth() async with httpx.AsyncClient(transport=settings.transport()) as client: @@ -95,7 +93,7 @@ async def send_command( headers={"Authorization": "Bearer " + self.token}, timeout=settings.get("api_function_timeout", 5), ) - json_data = response.json() + json_data: dict[str, Any] = response.json() return json_data except TypeError: await self.auth() @@ -116,7 +114,7 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: data: dict[str, Any] = {k: None for k in commands} data["multicommand"] = True await self.auth() @@ -136,7 +134,7 @@ async def multicommand( headers={"Authorization": "Bearer " + self.token}, timeout=settings.get("api_function_timeout", 5), ) - json_data = response.json() + json_data: dict[str, Any] = response.json() data[command] = json_data except httpx.HTTPError: pass @@ -146,31 +144,31 @@ async def multicommand( await self.auth() return data - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: return await self.send_command("pools") - async def newpool(self, url: str, user: str, password: str) -> dict: + async def newpool(self, url: str, user: str, password: str) -> dict[str, Any]: # looks dumb, but cant pass `pass` since it is a built in type poolpass: PoolPass = {"pass": password} return await self.send_command("newpool", url=url, user=user, **poolpass) async def delpool( self, url: str, user: str, password: str, dragid: int = 0 - ) -> dict: + ) -> dict[str, Any]: # looks dumb, but cant pass `pass` since it is a built in type poolpass: PoolPass = {"pass": password} return await self.send_command( "delpool", url=url, user=user, dragid=dragid, **poolpass ) - async def setting(self) -> dict: + async def setting(self) -> dict[str, Any]: return await self.send_command("setting") - async def set_setting(self, values: dict) -> None: - await self.send_command("setting", **values) + async def set_setting(self, values: dict[str, Any]) -> dict[str, Any]: + return await self.send_command("setting", **values) - async def status(self) -> dict: + async def status(self) -> dict[str, Any]: return await self.send_command("status") - async def devs(self) -> dict: + async def devs(self) -> dict[str, Any]: return await self.send_command("cgminer?cgminercmd=devs") diff --git a/pyasic/web/hammer.py b/pyasic/web/hammer.py index e4bc0972e..fd2454555 100644 --- a/pyasic/web/hammer.py +++ b/pyasic/web/hammer.py @@ -43,7 +43,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Hammer device using HTTP digest authentication. Args: @@ -74,14 +74,15 @@ async def send_command( else: if data.status_code == 200: try: - return data.json() + result: dict[str, Any] = data.json() + return result except json.decoder.JSONDecodeError: return {"success": False, "message": "Failed to decode JSON"} return {"success": False, "message": "Unknown error occurred"} async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously. Args: @@ -108,7 +109,7 @@ async def multicommand( async def _handle_multicommand( self, client: httpx.AsyncClient, command: str - ) -> dict: + ) -> dict[str, Any]: """Helper function for handling individual commands in a multicommand execution. Args: @@ -128,13 +129,13 @@ async def _handle_multicommand( else: if ret.status_code == 200: try: - json_data = ret.json() + json_data: dict[str, Any] = ret.json() return {command: json_data} except json.decoder.JSONDecodeError: pass return {command: {}} - async def get_miner_conf(self) -> dict: + async def get_miner_conf(self) -> dict[str, Any]: """Retrieve the miner configuration from the Hammer device. Returns: @@ -142,7 +143,7 @@ async def get_miner_conf(self) -> dict: """ return await self.send_command("get_miner_conf") - async def set_miner_conf(self, conf: dict) -> dict: + async def set_miner_conf(self, conf: dict[str, Any]) -> dict[str, Any]: """Set the configuration for the miner. Args: @@ -153,7 +154,7 @@ async def set_miner_conf(self, conf: dict) -> dict: """ return await self.send_command("set_miner_conf", **conf) - async def blink(self, blink: bool) -> dict: + async def blink(self, blink: bool) -> dict[str, Any]: """Control the blinking of the LED on the miner device. Args: @@ -166,7 +167,7 @@ async def blink(self, blink: bool) -> dict: return await self.send_command("blink", blink="true") return await self.send_command("blink", blink="false") - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: """Reboot the miner device. Returns: @@ -174,7 +175,7 @@ async def reboot(self) -> dict: """ return await self.send_command("reboot") - async def get_system_info(self) -> dict: + async def get_system_info(self) -> dict[str, Any]: """Retrieve system information from the miner. Returns: @@ -182,7 +183,7 @@ async def get_system_info(self) -> dict: """ return await self.send_command("get_system_info") - async def get_network_info(self) -> dict: + async def get_network_info(self) -> dict[str, Any]: """Retrieve network configuration information from the miner. Returns: @@ -190,7 +191,7 @@ async def get_network_info(self) -> dict: """ return await self.send_command("get_network_info") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get a summary of the miner's status and performance. Returns: @@ -198,7 +199,7 @@ async def summary(self) -> dict: """ return await self.send_command("summary") - async def get_blink_status(self) -> dict: + async def get_blink_status(self) -> dict[str, Any]: """Check the status of the LED blinking on the miner. Returns: @@ -214,7 +215,7 @@ async def set_network_conf( subnet_mask: str, hostname: str, protocol: int, - ) -> dict: + ) -> dict[str, Any]: """Set the network configuration of the miner. Args: diff --git a/pyasic/web/hiveon.py b/pyasic/web/hiveon.py index 6b470cde7..5584b381a 100644 --- a/pyasic/web/hiveon.py +++ b/pyasic/web/hiveon.py @@ -22,7 +22,8 @@ import aiofiles import httpx -from pyasic import APIError, settings +from pyasic import settings +from pyasic.errors import APIError from pyasic.web.base import BaseWebAPI @@ -44,7 +45,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: """Send a command to the Antminer device using HTTP digest authentication. Args: @@ -75,7 +76,8 @@ async def send_command( else: if response.status_code == 200: try: - return response.json() + result: dict[str, Any] = response.json() + return result except json.decoder.JSONDecodeError as e: response_text = response.text if response.text else "empty response" raise APIError( @@ -85,7 +87,7 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: """Execute multiple commands simultaneously. Args: @@ -96,7 +98,7 @@ async def multicommand( Returns: dict: A dictionary containing the results of all commands executed. """ - data = {k: None for k in commands} + data: dict[str, Any] = {k: None for k in commands} auth = httpx.DigestAuth(self.username, self.pwd) async with httpx.AsyncClient(transport=settings.transport()) as client: for command in commands: @@ -108,13 +110,13 @@ async def multicommand( else: if ret.status_code == 200: try: - json_data = ret.json() + json_data: dict[str, Any] = ret.json() data[command] = json_data except json.decoder.JSONDecodeError: pass return data - async def get_system_info(self) -> dict: + async def get_system_info(self) -> dict[str, Any]: """Retrieve system information from the miner. Returns: @@ -122,7 +124,7 @@ async def get_system_info(self) -> dict: """ return await self.send_command("get_system_info") - async def get_network_info(self) -> dict: + async def get_network_info(self) -> dict[str, Any]: """Retrieve system information from the miner. Returns: @@ -130,7 +132,7 @@ async def get_network_info(self) -> dict: """ return await self.send_command("get_network_info") - async def blink(self, blink: bool) -> dict: + async def blink(self, blink: bool) -> dict[str, Any]: """Control the blinking of the LED on the miner device. Args: @@ -143,7 +145,7 @@ async def blink(self, blink: bool) -> dict: return await self.send_command("blink", action="startBlink") return await self.send_command("blink", action="stopBlink") - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: """Reboot the miner device. Returns: @@ -151,7 +153,7 @@ async def reboot(self) -> dict: """ return await self.send_command("reboot") - async def get_blink_status(self) -> dict: + async def get_blink_status(self) -> dict[str, Any]: """Check the status of the LED blinking on the miner. Returns: @@ -159,7 +161,7 @@ async def get_blink_status(self) -> dict: """ return await self.send_command("blink", action="onPageLoaded") - async def get_miner_conf(self) -> dict: + async def get_miner_conf(self) -> dict[str, Any]: """Retrieve the miner configuration from the Antminer device. Returns: @@ -167,7 +169,7 @@ async def get_miner_conf(self) -> dict: """ return await self.send_command("get_miner_conf") - async def set_miner_conf(self, conf: dict) -> dict: + async def set_miner_conf(self, conf: dict[str, Any]) -> dict[str, Any]: """Set the configuration for the miner. Args: @@ -178,7 +180,7 @@ async def set_miner_conf(self, conf: dict) -> dict: """ return await self.send_command("set_miner_conf", **conf) - async def stats(self) -> dict: + async def stats(self) -> dict[str, Any]: """Retrieve detailed statistical data of the mining operation. Returns: @@ -186,7 +188,7 @@ async def stats(self) -> dict: """ return await self.send_command("miner_stats") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: """Get a summary of the miner's status and performance. Returns: @@ -194,7 +196,7 @@ async def summary(self) -> dict: """ return await self.send_command("miner_summary") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: """Retrieve current pool information associated with the miner. Returns: @@ -202,7 +204,9 @@ async def pools(self) -> dict: """ return await self.send_command("miner_pools") - async def update_firmware(self, file: Path, keep_settings: bool = True) -> dict: + async def update_firmware( + self, file: Path, keep_settings: bool = True + ) -> dict[str, Any]: """Perform a system update by uploading a firmware file and sending a command to initiate the update.""" async with aiofiles.open(file, "rb") as firmware: diff --git a/pyasic/web/iceriver.py b/pyasic/web/iceriver.py index 92766c69c..0c2ad8c26 100644 --- a/pyasic/web/iceriver.py +++ b/pyasic/web/iceriver.py @@ -34,7 +34,7 @@ def __init__(self, ip: str) -> None: async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: tasks = {c: asyncio.create_task(getattr(self, c)()) for c in commands} await asyncio.gather(*[t for t in tasks.values()]) return {t: tasks[t].result() for t in tasks} @@ -46,7 +46,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: async with httpx.AsyncClient(transport=settings.transport()) as client: try: # auth @@ -64,14 +64,15 @@ async def send_command( if not ignore_errors: raise APIError(f"Command failed: {command}") warnings.warn(f"Command failed: {command}") - return resp.json() + result: dict[str, Any] = resp.json() + return result except httpx.HTTPError: raise APIError(f"Command failed: {command}") - async def locate(self, enable: bool): + async def locate(self, enable: bool) -> dict[str, Any]: return await self.send_command( "userpanel", post="5", locate="1" if enable else "0" ) - async def userpanel(self): + async def userpanel(self) -> dict[str, Any]: return await self.send_command("userpanel", post="4") diff --git a/pyasic/web/innosilicon.py b/pyasic/web/innosilicon.py index 895b3540c..04ffd0881 100644 --- a/pyasic/web/innosilicon.py +++ b/pyasic/web/innosilicon.py @@ -43,7 +43,7 @@ async def auth(self) -> str | None: except httpx.HTTPError: warnings.warn(f"Could not authenticate web token with miner: {self}") else: - json_auth = auth.json() + json_auth: dict[str, Any] = auth.json() self.token = json_auth.get("jwt") return self.token @@ -54,7 +54,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: if self.token is None: await self.auth() async with httpx.AsyncClient(transport=settings.transport()) as client: @@ -71,7 +71,7 @@ async def send_command( timeout=settings.get("api_function_timeout", 5), json=parameters, ) - json_data = response.json() + json_data: dict[str, Any] = response.json() if ( not json_data.get("success") and "token" in json_data @@ -104,7 +104,7 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: data: dict[str, Any] = {k: None for k in commands} data["multicommand"] = True await self.auth() @@ -120,7 +120,7 @@ async def multicommand( headers={"Authorization": "Bearer " + self.token}, timeout=settings.get("api_function_timeout", 5), ) - json_data = response.json() + json_data: dict[str, Any] = response.json() data[command] = json_data except httpx.HTTPError: pass @@ -130,32 +130,32 @@ async def multicommand( await self.auth() return data - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: return await self.send_command("reboot") - async def restart_cgminer(self) -> dict: + async def restart_cgminer(self) -> dict[str, Any]: return await self.send_command("restartCgMiner") - async def update_pools(self, conf: dict) -> dict: + async def update_pools(self, conf: dict[str, Any]) -> dict[str, Any]: return await self.send_command("updatePools", **conf) - async def overview(self) -> dict: + async def overview(self) -> dict[str, Any]: return await self.send_command("overview") - async def type(self) -> dict: + async def type(self) -> dict[str, Any]: return await self.send_command("type") - async def get_all(self) -> dict: + async def get_all(self) -> dict[str, Any]: return await self.send_command("getAll") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: return await self.send_command("summary") - async def get_error_detail(self) -> dict: + async def get_error_detail(self) -> dict[str, Any]: return await self.send_command("getErrorDetail") - async def pools(self) -> dict: + async def pools(self) -> dict[str, Any]: return await self.send_command("pools") - async def poweroff(self) -> dict: + async def poweroff(self) -> dict[str, Any]: return await self.send_command("poweroff") diff --git a/pyasic/web/luckyminer.py b/pyasic/web/luckyminer.py index 8c4800fe5..4137aa9b4 100644 --- a/pyasic/web/luckyminer.py +++ b/pyasic/web/luckyminer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from .bitaxe import ESPMinerWebAPI +from .espminer import ESPMinerWebAPI class LuckyMinerWebAPI(ESPMinerWebAPI): diff --git a/pyasic/web/marathon.py b/pyasic/web/marathon.py index a5421e3aa..fee1cfe47 100644 --- a/pyasic/web/marathon.py +++ b/pyasic/web/marathon.py @@ -6,7 +6,8 @@ import httpx -from pyasic import APIError, settings +from pyasic import settings +from pyasic.errors import APIError from pyasic.web.base import BaseWebAPI @@ -18,7 +19,7 @@ def __init__(self, ip: str) -> None: async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: async with httpx.AsyncClient(transport=settings.transport()) as client: tasks = [ asyncio.create_task(self._handle_multicommand(client, command)) @@ -35,7 +36,7 @@ async def multicommand( async def _handle_multicommand( self, client: httpx.AsyncClient, command: str - ) -> dict: + ) -> dict[str, Any]: auth = httpx.DigestAuth(self.username, self.pwd) try: @@ -46,7 +47,7 @@ async def _handle_multicommand( else: if ret.status_code == 200: try: - json_data = ret.json() + json_data: dict[str, Any] = ret.json() return {command: json_data} except json.decoder.JSONDecodeError: pass @@ -59,7 +60,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: url = f"http://{self.ip}:{self.port}/kaonsu/v1/{command}" auth = httpx.DigestAuth(self.username, self.pwd) try: @@ -80,7 +81,8 @@ async def send_command( else: if response.status_code == 200: try: - return response.json() + result: dict[str, Any] = response.json() + return result except json.decoder.JSONDecodeError as e: response_text = response.text if response.text else "empty response" raise APIError( @@ -88,66 +90,66 @@ async def send_command( ) raise APIError(f"Failed to send command to miner API: {url}") - async def brief(self): + async def brief(self) -> dict[str, Any]: return await self.send_command("brief") - async def ping(self): + async def ping(self) -> dict[str, Any]: return await self.send_command("ping") - async def get_locate_miner(self): + async def get_locate_miner(self) -> dict[str, Any]: return await self.send_command("locate_miner") - async def set_locate_miner(self, blinking: bool): + async def set_locate_miner(self, blinking: bool) -> dict[str, Any]: return await self.send_command("locate_miner", blinking=blinking) - async def reboot(self): + async def reboot(self) -> dict[str, Any]: return await self.send_command("maintenance", type="reboot") - async def reset(self): + async def reset(self) -> dict[str, Any]: return await self.send_command("maintenance", type="reset") - async def reload(self): + async def reload(self) -> dict[str, Any]: return await self.send_command("maintenance", type="reload") - async def set_password(self, new_pwd: str): + async def set_password(self, new_pwd: str) -> dict[str, Any]: return await self.send_command( "maintenance", type="passwd", params={"curPwd": self.pwd, "confirmPwd": self.pwd, "newPwd": new_pwd}, ) - async def get_network_config(self): + async def get_network_config(self) -> dict[str, Any]: return await self.send_command("network_config") - async def set_network_config(self, **params): + async def set_network_config(self, **params: Any) -> dict[str, Any]: return await self.send_command("network_config", **params) - async def get_miner_config(self): + async def get_miner_config(self) -> dict[str, Any]: return await self.send_command("miner_config") - async def set_miner_config(self, **params): + async def set_miner_config(self, **params: Any) -> dict[str, Any]: return await self.send_command("miner_config", **params) - async def fans(self): + async def fans(self) -> dict[str, Any]: return await self.send_command("fans") - async def log(self): + async def log(self) -> dict[str, Any]: return await self.send_command("log") - async def overview(self): + async def overview(self) -> dict[str, Any]: return await self.send_command("overview") - async def connections(self): + async def connections(self) -> dict[str, Any]: return await self.send_command("connections") - async def controlboard_info(self): + async def controlboard_info(self) -> dict[str, Any]: return await self.send_command("controlboard_info") - async def event_chart(self): + async def event_chart(self) -> dict[str, Any]: return await self.send_command("event_chart") - async def hashboards(self): + async def hashboards(self) -> dict[str, Any]: return await self.send_command("hashboards") - async def pools(self): + async def pools(self) -> dict[str, Any]: return await self.send_command("pools") diff --git a/pyasic/web/mskminer.py b/pyasic/web/mskminer.py index f930ba297..33d4e4f78 100644 --- a/pyasic/web/mskminer.py +++ b/pyasic/web/mskminer.py @@ -34,7 +34,7 @@ def __init__(self, ip: str) -> None: async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: tasks = {c: asyncio.create_task(getattr(self, c)()) for c in commands} await asyncio.gather(*[t for t in tasks.values()]) return {t: tasks[t].result() for t in tasks} @@ -46,7 +46,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: async with httpx.AsyncClient(transport=settings.transport()) as client: try: # auth @@ -64,9 +64,10 @@ async def send_command( if not ignore_errors: raise APIError(f"Command failed: {command}") warnings.warn(f"Command failed: {command}") - return resp.json() + result: dict[str, Any] = resp.json() + return result except httpx.HTTPError: raise APIError(f"Command failed: {command}") - async def info_v1(self): + async def info_v1(self) -> dict[str, Any]: return await self.send_command("info_v1") diff --git a/pyasic/web/vnish.py b/pyasic/web/vnish.py index d123d980a..00108cf47 100644 --- a/pyasic/web/vnish.py +++ b/pyasic/web/vnish.py @@ -59,7 +59,7 @@ async def send_command( allow_warning: bool = True, privileged: bool = False, **parameters: Any, - ) -> dict: + ) -> dict[str, Any]: post = privileged or not parameters == {} if self.token is None: await self.auth() @@ -92,7 +92,7 @@ async def send_command( # refresh the token, retry await self.auth() continue - json_data = response.json() + json_data: dict[str, Any] = response.json() if json_data: return json_data return {"success": True} @@ -118,53 +118,53 @@ async def send_command( async def multicommand( self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True - ) -> dict: + ) -> dict[str, Any]: data: dict[str, Any] = {k: None for k in commands} data["multicommand"] = True for command in commands: data[command] = await self.send_command(command) return data - async def restart_vnish(self) -> dict: + async def restart_vnish(self) -> dict[str, Any]: return await self.send_command("mining/restart", privileged=True) - async def reboot(self) -> dict: + async def reboot(self) -> dict[str, Any]: return await self.send_command("system/reboot", privileged=True) - async def pause_mining(self) -> dict: + async def pause_mining(self) -> dict[str, Any]: return await self.send_command("mining/pause", privileged=True) - async def resume_mining(self) -> dict: + async def resume_mining(self) -> dict[str, Any]: return await self.send_command("mining/resume", privileged=True) - async def stop_mining(self) -> dict: + async def stop_mining(self) -> dict[str, Any]: return await self.send_command("mining/stop", privileged=True) - async def start_mining(self) -> dict: + async def start_mining(self) -> dict[str, Any]: return await self.send_command("mining/start", privileged=True) - async def info(self) -> dict: + async def info(self) -> dict[str, Any]: return await self.send_command("info") - async def summary(self) -> dict: + async def summary(self) -> dict[str, Any]: return await self.send_command("summary") - async def perf_summary(self) -> dict: + async def perf_summary(self) -> dict[str, Any]: return await self.send_command("perf-summary") - async def chips(self) -> dict: + async def chips(self) -> dict[str, Any]: return await self.send_command("chips") - async def layout(self) -> dict: + async def layout(self) -> dict[str, Any]: return await self.send_command("layout") - async def status(self) -> dict: + async def status(self) -> dict[str, Any]: return await self.send_command("status") - async def settings(self) -> dict: + async def settings(self) -> dict[str, Any]: return await self.send_command("settings") - async def set_power_limit(self, wattage: int) -> dict: + async def set_power_limit(self, wattage: int) -> dict[str, Any]: # Can only set power limit to tuned preset settings = await self.settings() settings["miner"]["overclock"]["preset"] = str(wattage) @@ -173,11 +173,11 @@ async def set_power_limit(self, wattage: int) -> dict: # response will always be {"restart_required":false,"reboot_required":false} even if unsuccessful return await self.send_command("settings", privileged=True, miner=miner) - async def autotune_presets(self) -> dict: + async def autotune_presets(self) -> dict[str, Any]: return await self.send_command("autotune/presets") - async def find_miner(self) -> dict: + async def find_miner(self) -> dict[str, Any]: return await self.send_command("find-miner", privileged=True) - async def post_settings(self, miner_settings: dict): + async def post_settings(self, miner_settings: dict[str, Any]) -> dict[str, Any]: return await self.send_command("settings", post=True, **miner_settings) diff --git a/pyproject.toml b/pyproject.toml index 4c41294db..ca7f609bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,7 @@ requires = ["poetry-core>=2.0.0"] build-backend = "poetry.core.masonry.api" [tool.mypy] -warn_unused_ignores = true +strict = true plugins = ["pydantic.mypy"] [[tool.mypy.overrides]] @@ -109,9 +109,7 @@ fixable = ["ALL"] ignore = [ "E402", "E501", - "F401", "F403", - "F405", "F601", ] diff --git a/tests/__init__.py b/tests/__init__.py index a843f6b70..27bcc2ae2 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -13,11 +13,35 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ +import unittest from tests.config_tests import TestConfig -from tests.miners_tests import * +from tests.miners_tests import MinersTest from tests.network_tests import NetworkTest -from tests.rpc_tests import * +from tests.rpc_tests import ( + TestAPIBase, + TestBFGMinerAPI, + TestBMMinerAPI, + TestBOSMinerAPI, + TestBTMinerAPI, + TestCGMinerAPI, + TestGCMinerRPCAPI, + TestLuxOSAPI, +) + +__all__ = [ + "TestConfig", + "NetworkTest", + "MinersTest", + "TestAPIBase", + "TestBFGMinerAPI", + "TestBMMinerAPI", + "TestBOSMinerAPI", + "TestBTMinerAPI", + "TestCGMinerAPI", + "TestGCMinerRPCAPI", + "TestLuxOSAPI", +] if __name__ == "__main__": # `coverage run --source pyasic -m unittest discover` will give code coverage data diff --git a/tests/config_tests/__init__.py b/tests/config_tests/__init__.py index ae773b323..6519800e6 100644 --- a/tests/config_tests/__init__.py +++ b/tests/config_tests/__init__.py @@ -27,7 +27,7 @@ class TestConfig(unittest.TestCase): - def setUp(self): + def setUp(self) -> None: self.cfg = MinerConfig( pools=PoolConfig.simple( [ @@ -50,12 +50,12 @@ def setUp(self): ), ) - def test_dict_deserialize_and_serialize(self): + def test_dict_deserialize_and_serialize(self) -> None: dict_config = self.cfg.as_dict() loaded_cfg = MinerConfig.from_dict(dict_config) self.assertEqual(loaded_cfg, self.cfg) - def test_dict_serialize_and_deserialize(self): + def test_dict_serialize_and_deserialize(self) -> None: dict_config = { "pools": { "groups": [ @@ -90,12 +90,12 @@ def test_dict_serialize_and_deserialize(self): dumped_config = loaded_config.as_dict() self.assertEqual(dumped_config, dict_config) - def test_bosminer_deserialize_and_serialize(self): + def test_bosminer_deserialize_and_serialize(self) -> None: bosminer_config = self.cfg.as_bosminer() loaded_config = MinerConfig.from_bosminer(bosminer_config) self.assertEqual(loaded_config, self.cfg) - def test_bosminer_serialize_and_deserialize(self): + def test_bosminer_serialize_and_deserialize(self) -> None: bosminer_config = { "temp_control": { "mode": "manual", @@ -134,7 +134,7 @@ def test_bosminer_serialize_and_deserialize(self): dumped_config = loaded_config.as_bosminer() self.assertEqual(dumped_config, bosminer_config) - def test_am_modern_serialize(self): + def test_am_modern_serialize(self) -> None: correct_config = { "bitmain-fan-ctrl": True, "bitmain-fan-pwm": "90", @@ -153,7 +153,7 @@ def test_am_modern_serialize(self): self.assertEqual(correct_config, self.cfg.as_am_modern()) - def test_am_old_serialize(self): + def test_am_old_serialize(self) -> None: correct_config = { "_ant_pool1url": "stratum+tcp://stratum.test.io:3333", "_ant_pool1user": "test.test", @@ -168,7 +168,7 @@ def test_am_old_serialize(self): self.assertEqual(correct_config, self.cfg.as_am_old()) - def test_wm_serialize(self): + def test_wm_serialize(self) -> None: correct_config = { "mode": "power_tuning", "power_tuning": {"wattage": 3000}, @@ -187,7 +187,7 @@ def test_wm_serialize(self): self.assertEqual(correct_config, self.cfg.as_wm()) - def test_goldshell_serialize(self): + def test_goldshell_serialize(self) -> None: correct_config = { "pools": [ { @@ -200,12 +200,12 @@ def test_goldshell_serialize(self): self.assertEqual(correct_config, self.cfg.as_goldshell()) - def test_avalon_serialize(self): + def test_avalon_serialize(self) -> None: correct_config = {"pools": "stratum+tcp://stratum.test.io:3333,test.test,123"} self.assertEqual(correct_config, self.cfg.as_avalon()) - def test_inno_serialize(self): + def test_inno_serialize(self) -> None: correct_config = { "Pool1": "stratum+tcp://stratum.test.io:3333", "UserName1": "test.test", diff --git a/tests/config_tests/fans.py b/tests/config_tests/fans.py index fa6ceaa9c..8243d6ee2 100644 --- a/tests/config_tests/fans.py +++ b/tests/config_tests/fans.py @@ -4,7 +4,7 @@ class TestFanConfig(unittest.TestCase): - def test_serialize_and_deserialize(self): + def test_serialize_and_deserialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of fan config", @@ -14,7 +14,7 @@ def test_serialize_and_deserialize(self): dict_conf = conf.as_dict() self.assertEqual(conf, FanModeConfig.from_dict(dict_conf)) - def test_bosminer_deserialize_and_serialize(self): + def test_bosminer_deserialize_and_serialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of bosminer fan config", @@ -24,7 +24,7 @@ def test_bosminer_deserialize_and_serialize(self): bos_conf = conf.as_bosminer() self.assertEqual(conf, FanModeConfig.from_bosminer(bos_conf)) - def test_am_modern_deserialize_and_serialize(self): + def test_am_modern_deserialize_and_serialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of antminer modern fan config", @@ -34,7 +34,7 @@ def test_am_modern_deserialize_and_serialize(self): am_conf = conf.as_am_modern() self.assertEqual(conf, FanModeConfig.from_am_modern(am_conf)) - def test_epic_deserialize_and_serialize(self): + def test_epic_deserialize_and_serialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of epic fan config", @@ -44,7 +44,7 @@ def test_epic_deserialize_and_serialize(self): epic_conf = conf.as_epic() self.assertEqual(conf, FanModeConfig.from_epic(epic_conf)) - def test_vnish_deserialize_and_serialize(self): + def test_vnish_deserialize_and_serialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of vnish fan config", @@ -54,7 +54,7 @@ def test_vnish_deserialize_and_serialize(self): vnish_conf = conf.as_vnish() self.assertEqual(conf, FanModeConfig.from_vnish(vnish_conf)) - def test_auradine_deserialize_and_serialize(self): + def test_auradine_deserialize_and_serialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of auradine fan config", @@ -64,7 +64,7 @@ def test_auradine_deserialize_and_serialize(self): aur_conf = conf.as_auradine() self.assertEqual(conf, FanModeConfig.from_auradine(aur_conf)) - def test_boser_deserialize_and_serialize(self): + def test_boser_deserialize_and_serialize(self) -> None: for fan_mode in FanModeConfig: with self.subTest( msg="Test serialization and deserialization of boser fan config", diff --git a/tests/miners_tests/__init__.py b/tests/miners_tests/__init__.py index a9fd29f73..ff8a26b05 100644 --- a/tests/miners_tests/__init__.py +++ b/tests/miners_tests/__init__.py @@ -22,9 +22,11 @@ from .backends_tests import * +__all__ = ["MinersTest"] + class MinersTest(unittest.TestCase): - def test_miner_type_creation(self): + def test_miner_type_creation(self) -> None: warnings.filterwarnings("ignore") for miner_type in MINER_CLASSES.keys(): for miner_model in MINER_CLASSES[miner_type].keys(): @@ -35,7 +37,7 @@ def test_miner_type_creation(self): ): MINER_CLASSES[miner_type][miner_model]("127.0.0.1") - def test_miner_has_hashboards(self): + def test_miner_has_hashboards(self) -> None: warnings.filterwarnings("ignore") for miner_type in MINER_CLASSES.keys(): for miner_model in MINER_CLASSES[miner_type].keys(): @@ -49,7 +51,7 @@ def test_miner_has_hashboards(self): miner = MINER_CLASSES[miner_type][miner_model]("127.0.0.1") self.assertTrue(miner.expected_hashboards is not None) - def test_miner_has_fans(self): + def test_miner_has_fans(self) -> None: warnings.filterwarnings("ignore") for miner_type in MINER_CLASSES.keys(): for miner_model in MINER_CLASSES[miner_type].keys(): @@ -63,7 +65,7 @@ def test_miner_has_fans(self): miner = MINER_CLASSES[miner_type][miner_model]("127.0.0.1") self.assertTrue(miner.expected_fans is not None) - def test_miner_has_algo(self): + def test_miner_has_algo(self) -> None: warnings.filterwarnings("ignore") for miner_type in MINER_CLASSES.keys(): for miner_model in MINER_CLASSES[miner_type].keys(): @@ -77,7 +79,7 @@ def test_miner_has_algo(self): miner = MINER_CLASSES[miner_type][miner_model]("127.0.0.1") self.assertTrue(miner.algo is not None) - def test_miner_data_map_keys(self): + def test_miner_data_map_keys(self) -> None: keys = sorted( [ "api_ver", @@ -115,7 +117,7 @@ def test_miner_data_map_keys(self): ) self.assertEqual(miner_keys, keys) - def test_data_locations_match_signatures_command(self): + def test_data_locations_match_signatures_command(self) -> None: warnings.filterwarnings("ignore") for miner_type in MINER_CLASSES.keys(): for miner_model in MINER_CLASSES[miner_type].keys(): diff --git a/tests/miners_tests/backends_tests/avalonminer_tests/__init__.py b/tests/miners_tests/backends_tests/avalonminer_tests/__init__.py index b9bf8b89c..98e97dd03 100644 --- a/tests/miners_tests/backends_tests/avalonminer_tests/__init__.py +++ b/tests/miners_tests/backends_tests/avalonminer_tests/__init__.py @@ -1 +1,3 @@ from .version_24102401_25462b2_9ddf522 import TestAvalonMiners + +__all__ = ["TestAvalonMiners"] diff --git a/tests/miners_tests/backends_tests/avalonminer_tests/version_24102401_25462b2_9ddf522.py b/tests/miners_tests/backends_tests/avalonminer_tests/version_24102401_25462b2_9ddf522.py index 1a56f27f9..e330dafbf 100644 --- a/tests/miners_tests/backends_tests/avalonminer_tests/version_24102401_25462b2_9ddf522.py +++ b/tests/miners_tests/backends_tests/avalonminer_tests/version_24102401_25462b2_9ddf522.py @@ -2,11 +2,11 @@ import unittest from dataclasses import fields -from unittest.mock import patch +from unittest.mock import MagicMock, patch from pyasic import APIError, MinerData from pyasic.data import Fan, HashBoard -from pyasic.device.algorithm import SHA256Unit +from pyasic.device.algorithm.hashrate.unit.sha256 import SHA256Unit from pyasic.miners.avalonminer import CGMinerAvalon1566 POOLS = [ @@ -470,11 +470,11 @@ class TestAvalonMiners(unittest.IsolatedAsyncioTestCase): @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_all_data_gathering(self, mock_send_bytes): + async def test_all_data_gathering(self, mock_send_bytes: MagicMock) -> None: mock_send_bytes.raises = APIError() for m_type in data: gathered_data = {} - miner = m_type("127.0.0.1") + miner = m_type("127.0.0.1") # type: ignore[abstract] for data_name in fields(miner.data_locations): if data_name.name == "config": # skip @@ -507,7 +507,9 @@ async def test_all_data_gathering(self, mock_send_bytes): self.assertEqual(result.mac, "12:34:56:78:90:12") self.assertEqual(result.api_ver, "3.7") self.assertEqual(result.fw_ver, "4.11.1") - self.assertEqual(round(result.hashrate.into(SHA256Unit.TH)), 184) + self.assertIsNotNone(result.hashrate) + if result.hashrate is not None: + self.assertEqual(round(result.hashrate.into(SHA256Unit.TH)), 184) self.assertEqual( result.fans, [Fan(speed=4275), Fan(speed=4282)], diff --git a/tests/miners_tests/backends_tests/elphapex_tests/__init__.py b/tests/miners_tests/backends_tests/elphapex_tests/__init__.py index 23eed6ef1..2d9756047 100644 --- a/tests/miners_tests/backends_tests/elphapex_tests/__init__.py +++ b/tests/miners_tests/backends_tests/elphapex_tests/__init__.py @@ -1 +1,3 @@ from .version_1_0_2 import TestElphapexMiners + +__all__ = ["TestElphapexMiners"] diff --git a/tests/miners_tests/backends_tests/elphapex_tests/version_1_0_2.py b/tests/miners_tests/backends_tests/elphapex_tests/version_1_0_2.py index f6847532c..ac2f37056 100644 --- a/tests/miners_tests/backends_tests/elphapex_tests/version_1_0_2.py +++ b/tests/miners_tests/backends_tests/elphapex_tests/version_1_0_2.py @@ -2,7 +2,7 @@ import unittest from dataclasses import fields -from unittest.mock import patch +from unittest.mock import MagicMock, patch from pyasic import APIError, MinerData from pyasic.data import Fan, HashBoard @@ -290,11 +290,11 @@ class TestElphapexMiners(unittest.IsolatedAsyncioTestCase): @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_all_data_gathering(self, mock_send_bytes): + async def test_all_data_gathering(self, mock_send_bytes: MagicMock) -> None: mock_send_bytes.raises = APIError() for m_type in data: gathered_data = {} - miner = m_type("127.0.0.1") + miner: ElphapexDG1Plus = m_type("127.0.0.1") # type: ignore[abstract] for data_name in fields(miner.data_locations): if data_name.name == "config": # skip @@ -328,7 +328,9 @@ async def test_all_data_gathering(self, mock_send_bytes): self.assertEqual(result.api_ver, "1.0.0") self.assertEqual(result.fw_ver, "1.0.2") self.assertEqual(result.hostname, "DG1+") - self.assertEqual(round(result.hashrate.into(ScryptUnit.MH)), 14199) + self.assertIsNotNone(result.hashrate) + if result.hashrate is not None: + self.assertEqual(round(result.hashrate.into(ScryptUnit.MH)), 14199) self.assertEqual( result.fans, [Fan(speed=5340), Fan(speed=5400), Fan(speed=5400), Fan(speed=5400)], diff --git a/tests/miners_tests/backends_tests/hammer_tests/__init__.py b/tests/miners_tests/backends_tests/hammer_tests/__init__.py index 70fa7f4ec..07707399d 100644 --- a/tests/miners_tests/backends_tests/hammer_tests/__init__.py +++ b/tests/miners_tests/backends_tests/hammer_tests/__init__.py @@ -1 +1,3 @@ from .version_2023_05_28 import TestHammerMiners + +__all__ = ["TestHammerMiners"] diff --git a/tests/miners_tests/backends_tests/hammer_tests/version_2023_05_28.py b/tests/miners_tests/backends_tests/hammer_tests/version_2023_05_28.py index 6bb02b131..81281d557 100644 --- a/tests/miners_tests/backends_tests/hammer_tests/version_2023_05_28.py +++ b/tests/miners_tests/backends_tests/hammer_tests/version_2023_05_28.py @@ -2,7 +2,7 @@ import unittest from dataclasses import fields -from unittest.mock import patch +from unittest.mock import MagicMock, patch from pyasic import APIError, MinerData from pyasic.data import Fan, HashBoard @@ -361,11 +361,11 @@ class TestHammerMiners(unittest.IsolatedAsyncioTestCase): @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_all_data_gathering(self, mock_send_bytes): + async def test_all_data_gathering(self, mock_send_bytes: MagicMock) -> None: mock_send_bytes.raises = APIError() for m_type in data: gathered_data = {} - miner = m_type("127.0.0.1") + miner: HammerD10 = m_type("127.0.0.1") # type: ignore[abstract] for data_name in fields(miner.data_locations): if data_name.name == "config": # skip @@ -399,6 +399,8 @@ async def test_all_data_gathering(self, mock_send_bytes): self.assertEqual(result.api_ver, "3.1") self.assertEqual(result.fw_ver, "2023-05-28 17-20-35 CST") self.assertEqual(result.hostname, "Hammer") - self.assertEqual(round(result.hashrate.into(ScryptUnit.MH)), 4686) + self.assertIsNotNone(result.hashrate) + if result.hashrate is not None: + self.assertEqual(round(result.hashrate.into(ScryptUnit.MH)), 4686) self.assertEqual(result.fans, [Fan(speed=4650), Fan(speed=4500)]) self.assertEqual(result.total_chips, result.expected_chips) diff --git a/tests/miners_tests/backends_tests/mskminer_tests/__init__.py b/tests/miners_tests/backends_tests/mskminer_tests/__init__.py index 592064762..b605f52cd 100644 --- a/tests/miners_tests/backends_tests/mskminer_tests/__init__.py +++ b/tests/miners_tests/backends_tests/mskminer_tests/__init__.py @@ -1 +1,3 @@ from .version_2_6_0_39 import TestMSKMiners + +__all__ = ["TestMSKMiners"] diff --git a/tests/miners_tests/backends_tests/mskminer_tests/version_2_6_0_39.py b/tests/miners_tests/backends_tests/mskminer_tests/version_2_6_0_39.py index 41456dd74..ce37fd561 100644 --- a/tests/miners_tests/backends_tests/mskminer_tests/version_2_6_0_39.py +++ b/tests/miners_tests/backends_tests/mskminer_tests/version_2_6_0_39.py @@ -2,11 +2,11 @@ import unittest from dataclasses import fields -from unittest.mock import patch +from unittest.mock import MagicMock, patch from pyasic import APIError, MinerData from pyasic.data import Fan, HashBoard -from pyasic.device.algorithm import SHA256Unit +from pyasic.device.algorithm.sha256 import SHA256Unit from pyasic.miners.antminer import MSKMinerS19NoPIC POOLS = [ @@ -455,7 +455,7 @@ class TestMSKMiners(unittest.IsolatedAsyncioTestCase): @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_all_data_gathering(self, mock_send_bytes): + async def test_all_data_gathering(self, mock_send_bytes: MagicMock) -> None: mock_send_bytes.raises = APIError() for m_type in data: gathered_data = {} @@ -492,7 +492,9 @@ async def test_all_data_gathering(self, mock_send_bytes): self.assertEqual(result.mac, "12:34:56:78:90:12") self.assertEqual(result.api_ver, "3.1") self.assertEqual(result.fw_ver, "10 Dec 2024 14:34:31 GMT") - self.assertEqual(round(result.hashrate.into(SHA256Unit.TH)), 100) + self.assertIsNotNone(result.hashrate) + if result.hashrate is not None: + self.assertEqual(round(result.hashrate.into(SHA256Unit.TH)), 100) self.assertEqual( result.fans, [Fan(speed=5010), Fan(speed=5160), Fan(speed=5070), Fan(speed=5040)], diff --git a/tests/network_tests/__init__.py b/tests/network_tests/__init__.py index 561db7e3b..10f0f69e6 100644 --- a/tests/network_tests/__init__.py +++ b/tests/network_tests/__init__.py @@ -21,7 +21,7 @@ class NetworkTest(unittest.TestCase): - def test_net_range(self): + def test_net_range(self) -> None: net_range_str = ["192.168.1.29", "192.168.1.40-43", "192.168.1.60"] net_range_list = [ "192.168.1.29", @@ -47,7 +47,7 @@ def test_net_range(self): self.assertEqual(net_1, correct_net) self.assertEqual(net_2, correct_net) - def test_net(self): + def test_net(self) -> None: net_1_str = "192.168.1.0" net_1_mask = "/29" @@ -67,7 +67,7 @@ def test_net(self): self.assertEqual(net_1, correct_net) self.assertEqual(net_2, correct_net) - def test_net_defaults(self): + def test_net_defaults(self) -> None: net = MinerNetwork.from_subnet("192.168.1.1/24") self.assertEqual( net.hosts, list(ipaddress.ip_network("192.168.1.0/24").hosts()) diff --git a/tests/rpc_tests/__init__.py b/tests/rpc_tests/__init__.py index 2cc23cf4b..0448e4188 100644 --- a/tests/rpc_tests/__init__.py +++ b/tests/rpc_tests/__init__.py @@ -16,7 +16,7 @@ import json import time import unittest -from unittest.mock import patch +from unittest.mock import MagicMock, patch from pyasic import APIError from pyasic.rpc.bfgminer import BFGMinerRPCAPI @@ -27,19 +27,39 @@ from pyasic.rpc.gcminer import GCMinerRPCAPI from pyasic.rpc.luxminer import LUXMinerRPCAPI +__all__ = [ + "TestAPIBase", + "TestBFGMinerAPI", + "TestBMMinerAPI", + "TestBOSMinerAPI", + "TestBTMinerAPI", + "TestCGMinerAPI", + "TestGCMinerRPCAPI", + "TestLuxOSAPI", +] + class TestAPIBase(unittest.IsolatedAsyncioTestCase): - def setUp(self): + def setUp(self) -> None: self.ip = "10.0.0.50" self.port = 4028 self.api_str = "" - self.api = None + self.api: ( + BFGMinerRPCAPI + | BMMinerRPCAPI + | BOSMinerRPCAPI + | BTMinerRPCAPI + | CGMinerRPCAPI + | GCMinerRPCAPI + | LUXMinerRPCAPI + | None + ) = None self.setUpData() - def setUpData(self): + def setUpData(self) -> None: pass - def get_error_value(self): + def get_error_value(self) -> bytes: return json.dumps( { "STATUS": [ @@ -49,7 +69,7 @@ def get_error_value(self): } ).encode("utf-8") - def get_success_value(self, command: str): + def get_success_value(self, command: str) -> bytes: if self.api_str == "BTMiner": if command == "status": return json.dumps( @@ -99,7 +119,9 @@ def get_success_value(self, command: str): ).encode("utf-8") @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_command_error_raises_api_error(self, mock_send_bytes): + async def test_command_error_raises_api_error( + self, mock_send_bytes: MagicMock + ) -> None: if self.api is None: return @@ -108,7 +130,9 @@ async def test_command_error_raises_api_error(self, mock_send_bytes): await self.api.send_command("summary") @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_command_error_ignored_by_flag(self, mock_send_bytes): + async def test_command_error_ignored_by_flag( + self, mock_send_bytes: MagicMock + ) -> None: if self.api is None: return @@ -123,7 +147,7 @@ async def test_command_error_ignored_by_flag(self, mock_send_bytes): ) @patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes") - async def test_all_read_command_success(self, mock_send_bytes): + async def test_all_read_command_success(self, mock_send_bytes: MagicMock) -> None: if self.api is None: return @@ -140,7 +164,7 @@ async def test_all_read_command_success(self, mock_send_bytes): # Use a list to track calls and return different values if self.api_str == "BTMiner": - def btminer_side_effect(data): + def btminer_side_effect(data: bytes) -> bytes: # Parse the command from the sent data try: # data is already bytes @@ -173,44 +197,44 @@ def btminer_side_effect(data): class TestBFGMinerAPI(TestAPIBase): - def setUpData(self): - self.api = BFGMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: BFGMinerRPCAPI = BFGMinerRPCAPI(self.ip) self.api_str = "BFGMiner" class TestBMMinerAPI(TestAPIBase): - def setUpData(self): - self.api = BMMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: BMMinerRPCAPI = BMMinerRPCAPI(self.ip) self.api_str = "BMMiner" class TestBOSMinerAPI(TestAPIBase): - def setUpData(self): - self.api = BOSMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: BOSMinerRPCAPI = BOSMinerRPCAPI(self.ip) self.api_str = "BOSMiner" class TestBTMinerAPI(TestAPIBase): - def setUpData(self): - self.api = BTMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: BTMinerRPCAPI = BTMinerRPCAPI(self.ip) self.api_str = "BTMiner" class TestCGMinerAPI(TestAPIBase): - def setUpData(self): - self.api = CGMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: CGMinerRPCAPI = CGMinerRPCAPI(self.ip) self.api_str = "CGMiner" class TestGCMinerRPCAPI(TestAPIBase): - def setUpData(self): - self.api = GCMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: GCMinerRPCAPI = GCMinerRPCAPI(self.ip) self.api_str = "GCMiner" class TestLuxOSAPI(TestAPIBase): - def setUpData(self): - self.api = LUXMinerRPCAPI(self.ip) + def setUpData(self) -> None: + self.api: LUXMinerRPCAPI = LUXMinerRPCAPI(self.ip) self.api_str = "LuxOS"