Compare commits

..

13 Commits

Author SHA1 Message Date
Brett Rowan
90c8986900 version: bump version number 2025-07-23 11:56:21 -06:00
Brett Rowan
5457ae6cd5 feature: add support for avalon Q 2025-07-23 11:55:57 -06:00
Brett Rowan
aa3d105fcb version: bump version number 2025-07-21 15:41:35 -06:00
Brett Rowan
77f59f6db6 feature: add support for Elphapex DG1 Home
Fixes: #311
2025-07-21 10:08:11 -06:00
Brett Rowan
3fa0d96fbb feature: add support for Iceriver AL3
Fixes: #317
2025-07-21 10:02:06 -06:00
Brett Rowan
e55477a8b8 feature: add support for S19j+
Fixes: #330
2025-07-21 09:51:51 -06:00
Brett Rowan
7d5744ae28 feature: add support for vnish XP Hydro
Fixes: #342
2025-07-21 09:48:11 -06:00
Brett Rowan
d4500be10c feature: add support for a couple alternate models
- Antminer S19j Pro+, ANTMINER
 - Antminer S19 Pro A, VNISH
 - Antminer S19 XP, VNISH
 - Antminer L9, VNISH
 - DG1, ELPHAPEX

 Fixes: #354
2025-07-21 09:35:00 -06:00
Brett Rowan
7ef2540133 feature: add support for Vnish S19 Hydro
Fixes: #355
2025-07-21 09:18:20 -06:00
Brett Rowan
1ea4f4e124 version: bump version number 2025-07-15 08:11:34 -06:00
bbemoll
a8a0e4a5fe Added support for Antminer S19XH Hydro running on Braiins OS (#353)
* Add pyasic files

* Add pyasic files

* Update pyproject.toml

* Update pyproject.toml

* Added support for Antminer S19XP Hydro running on Braiins OS

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

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

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2025-07-15 08:10:59 -06:00
Brett Rowan
5f2f6e01da version: bump version number 2025-07-10 10:56:42 -06:00
Brody
41b4c23d45 Fix API Issue with Bitaxe Miners (#352)
* added checks to identify which field is None.

* better logging

* if asicCount is None, try to find it in /system/asic

* ran pre-commit
2025-07-10 10:56:22 -06:00
51 changed files with 882 additions and 165 deletions

View File

@@ -157,6 +157,19 @@
show_root_heading: false
heading_level: 0
## S19j Pro+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jProPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j XP (Stock)
- [x] Shutdowns
@@ -170,6 +183,19 @@
show_root_heading: false
heading_level: 0
## S19j+ (Stock)
- [x] Shutdowns
- [x] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19jPlus
handler: python
options:
show_root_heading: false
heading_level: 0
## S19j No PIC (Stock)
- [x] Shutdowns
@@ -287,6 +313,19 @@
show_root_heading: false
heading_level: 0
## S19 XP Hydro (BOS+)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [ ] Presets
::: pyasic.miners.antminer.bosminer.X19.S19.BOSMinerS19XPHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19+ (BOS+)
- [x] Shutdowns
@@ -443,6 +482,19 @@
show_root_heading: false
heading_level: 0
## S19 Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19Hydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro (VNish)
- [x] Shutdowns
@@ -456,6 +508,19 @@
show_root_heading: false
heading_level: 0
## S19 Pro A (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19ProA
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 Pro Hydro (VNish)
- [x] Shutdowns
@@ -469,6 +534,32 @@
show_root_heading: false
heading_level: 0
## S19 XP (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19XP
handler: python
options:
show_root_heading: false
heading_level: 0
## S19 XP Hydro (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19XPHydro
handler: python
options:
show_root_heading: false
heading_level: 0
## S19a (VNish)
- [x] Shutdowns

View File

@@ -73,7 +73,7 @@
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
::: pyasic.miners.antminer.vnish.X3.L3.VNishL3Plus
handler: python
options:
show_root_heading: false

View File

@@ -47,7 +47,7 @@
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
::: pyasic.miners.antminer.vnish.X7.L7.VNishL7
handler: python
options:
show_root_heading: false

View File

@@ -105,6 +105,19 @@
show_root_heading: false
heading_level: 0
## L9 (VNish)
- [x] Shutdowns
- [ ] Power Modes
- [x] Setpoints
- [x] Presets
::: pyasic.miners.antminer.vnish.X9.L9.VNishL9
handler: python
options:
show_root_heading: false
heading_level: 0
## T9 (Hive)
- [ ] Shutdowns

View File

@@ -1,6 +1,19 @@
# pyasic
## DGX Models
## DG1 (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1
handler: python
options:
show_root_heading: false
heading_level: 0
## DG1+ (Stock)
- [ ] Shutdowns
@@ -14,3 +27,16 @@
show_root_heading: false
heading_level: 0
## DG1Home (Stock)
- [ ] Shutdowns
- [ ] Power Modes
- [ ] Setpoints
- [ ] Presets
::: pyasic.miners.elphapex.daoge.DGX.DG1.ElphapexDG1Home
handler: python
options:
show_root_heading: false
heading_level: 0

View File

@@ -86,6 +86,8 @@ details {
<li><a href="../antminer/X19#s19j-no-pic-stock">S19j No PIC (Stock)</a></li>
<li><a href="../antminer/X19#s19-pro_1-stock">S19 Pro+ (Stock)</a></li>
<li><a href="../antminer/X19#s19j-pro-stock">S19j Pro (Stock)</a></li>
<li><a href="../antminer/X19#s19j_1-stock">S19j+ (Stock)</a></li>
<li><a href="../antminer/X19#s19j-pro_1-stock">S19j Pro+ (Stock)</a></li>
<li><a href="../antminer/X19#s19-xp-stock">S19 XP (Stock)</a></li>
<li><a href="../antminer/X19#s19a-stock">S19a (Stock)</a></li>
<li><a href="../antminer/X19#s19a-pro-stock">S19a Pro (Stock)</a></li>
@@ -563,6 +565,12 @@ details {
<li><a href="../avalonminer/A15X#avalon-1566-stock">Avalon 1566 (Stock)</a></li>
</ul>
</details>
<details>
<summary>Q Series:</summary>
<ul>
<li><a href="../avalonminer/Q#avalon-q-home-stock">Avalon Q Home (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -656,6 +664,7 @@ details {
<li><a href="../antminer/X19#s19-xp-bos_1">S19 XP (BOS+)</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro-bos_1">S19 Pro+ Hydro (BOS+)</a></li>
<li><a href="../antminer/X19#t19-bos_1">T19 (BOS+)</a></li>
<li><a href="../antminer/X19#s19-xp-hydro-bos_1">S19 XP Hydro (BOS+)</a></li>
</ul>
</details>
<details>
@@ -694,6 +703,12 @@ details {
<li><a href="../antminer/X7#l7-vnish">L7 (VNish)</a></li>
</ul>
</details>
<details>
<summary>X9 Series:</summary>
<ul>
<li><a href="../antminer/X9#l9-vnish">L9 (VNish)</a></li>
</ul>
</details>
<details>
<summary>X17 Series:</summary>
<ul>
@@ -709,11 +724,15 @@ details {
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
<li><a href="../antminer/X19#s19i-vnish">S19i (VNish)</a></li>
<li><a href="../antminer/X19#s19-xp-vnish">S19 XP (VNish)</a></li>
<li><a href="../antminer/X19#s19-xp-hydro-vnish">S19 XP Hydro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19a-vnish">S19a (VNish)</a></li>
<li><a href="../antminer/X19#s19-hydro-vnish">S19 Hydro (VNish)</a></li>
<li><a href="../antminer/X19#s19a-pro-vnish">S19a Pro (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-a-vnish">S19 Pro A (VNish)</a></li>
<li><a href="../antminer/X19#s19-pro-hydro-vnish">S19 Pro Hydro (VNish)</a></li>
<li><a href="../antminer/X19#s19k-pro-vnish">S19k Pro (VNish)</a></li>
<li><a href="../antminer/X19#t19-vnish">T19 (VNish)</a></li>
@@ -922,6 +941,12 @@ details {
<li><a href="../iceriver/KSX#ks5m-stock">KS5M (Stock)</a></li>
</ul>
</details>
<details>
<summary>ALX Series:</summary>
<ul>
<li><a href="../iceriver/ALX#al3-stock">AL3 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -953,6 +978,8 @@ details {
<summary>DGX Series:</summary>
<ul>
<li><a href="../elphapex/DGX#dg1_1-stock">DG1+ (Stock)</a></li>
<li><a href="../elphapex/DGX#dg1-stock">DG1 (Stock)</a></li>
<li><a href="../elphapex/DGX#dg1home-stock">DG1Home (Stock)</a></li>
</ul>
</details>
</ul>

View File

@@ -1,5 +1,6 @@
from .base import MinerAlgoType
from .blake256 import Blake256Algo
from .blockflow import BlockFlowAlgo
from .eaglesong import EaglesongAlgo
from .equihash import EquihashAlgo
from .ethash import EtHashAlgo
@@ -24,3 +25,4 @@ class MinerAlgo:
EAGLESONG = EaglesongAlgo
ETHASH = EtHashAlgo
EQUIHASH = EquihashAlgo
BLOCKFLOW = BlockFlowAlgo

View File

@@ -0,0 +1,12 @@
from __future__ import annotations
from .base import MinerAlgoType
from .hashrate import BlockFlowHashRate
from .hashrate.unit import BlockFlowUnit
class BlockFlowAlgo(MinerAlgoType):
hashrate: type[BlockFlowHashRate] = BlockFlowHashRate
unit: type[BlockFlowUnit] = BlockFlowUnit
name = "BlockFlow"

View File

@@ -1,5 +1,6 @@
from .base import AlgoHashRateType
from .blake256 import Blake256HashRate
from .blockflow import BlockFlowHashRate
from .eaglesong import EaglesongHashRate
from .equihash import EquihashHashRate
from .ethash import EtHashHashRate
@@ -22,3 +23,4 @@ class AlgoHashRate:
EAGLESONG = EaglesongHashRate
ETHASH = EtHashHashRate
EQUIHASH = EquihashHashRate
BLOCKFLOW = BlockFlowHashRate

View File

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

View File

@@ -1,4 +1,5 @@
from .blake256 import Blake256Unit
from .blockflow import BlockFlowUnit
from .eaglesong import EaglesongUnit
from .equihash import EquihashUnit
from .ethash import EtHashUnit
@@ -21,3 +22,4 @@ class HashUnit:
EAGLESONG = EaglesongUnit
ETHASH = EtHashUnit
EQUIHASH = EquihashUnit
BLOCKFLOW = BlockFlowUnit

View File

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

View File

@@ -43,6 +43,7 @@ class AntminerModels(MinerModelType):
S19jNoPIC = "S19j No PIC"
S19ProPlus = "S19 Pro+"
S19jPro = "S19j Pro"
S19jPlus = "S19j+"
S19jProNoPIC = "S19j Pro No PIC"
S19jProPlus = "S19j Pro+"
S19jProPlusNoPIC = "S19j Pro+ No PIC"
@@ -54,6 +55,7 @@ class AntminerModels(MinerModelType):
S19ProPlusHydro = "S19 Pro+ Hydro"
S19KPro = "S19K Pro"
S19kPro = "S19k Pro"
S19ProA = "S19 Pro A"
S19kProNoPIC = "S19k Pro No PIC"
S19jXP = "S19j XP"
T19 = "T19"
@@ -63,6 +65,7 @@ class AntminerModels(MinerModelType):
S21Pro = "S21 Pro"
S21Hydro = "S21 Hydro"
T21 = "T21"
S19XPHydro = "S19 XP Hydro"
def __str__(self):
return self.value
@@ -453,6 +456,7 @@ class AvalonminerModels(MinerModelType):
Avalon1566 = "Avalon 1566"
AvalonNano3 = "Avalon Nano 3"
AvalonNano3s = "Avalon Nano 3s"
AvalonQHome = "Avalon Q Home"
def __str__(self):
return self.value
@@ -532,6 +536,7 @@ class IceRiverModels(MinerModelType):
KS5 = "KS5"
KS5L = "KS5L"
KS5M = "KS5M"
AL3 = "AL3"
def __str__(self):
return self.value
@@ -557,7 +562,9 @@ class BraiinsModels(MinerModelType):
class ElphapexModels(MinerModelType):
DG1 = "DG1"
DG1Plus = "DG1+"
DG1Home = "DG1Home"
class MinerModel:

View File

@@ -25,7 +25,9 @@ from pyasic.miners.device.models import (
S19i,
S19j,
S19jNoPIC,
S19jPlus,
S19jPro,
S19jProPlus,
S19jXP,
S19KPro,
S19Plus,
@@ -80,6 +82,10 @@ class BMMinerS19jPro(AntminerModern, S19jPro):
pass
class BMMinerS19jPlus(AntminerModern, S19jPlus):
pass
class BMMinerS19L(AntminerModern, S19L):
pass
@@ -102,3 +108,7 @@ class BMMinerS19KPro(AntminerModern, S19KPro):
class BMMinerS19jXP(AntminerModern, S19jXP):
pass
class BMMinerS19jProPlus(AntminerModern, S19jProPlus):
pass

View File

@@ -22,7 +22,9 @@ from .S19 import (
BMMinerS19i,
BMMinerS19j,
BMMinerS19jNoPIC,
BMMinerS19jPlus,
BMMinerS19jPro,
BMMinerS19jProPlus,
BMMinerS19jXP,
BMMinerS19KPro,
BMMinerS19L,

View File

@@ -30,6 +30,7 @@ from pyasic.miners.device.models import (
S19Plus,
S19Pro,
S19ProPlusHydro,
S19XPHydro,
)
@@ -87,3 +88,7 @@ class BOSMinerS19XP(BOSer, S19XP):
class BOSMinerS19ProPlusHydro(BOSer, S19ProPlusHydro):
pass
class BOSMinerS19XPHydro(BOSer, S19XPHydro):
pass

View File

@@ -29,5 +29,6 @@ from .S19 import (
BOSMinerS19Pro,
BOSMinerS19ProPlusHydro,
BOSMinerS19XP,
BOSMinerS19XPHydro,
)
from .T19 import BOSMinerT19

View File

@@ -20,13 +20,16 @@ from pyasic.miners.device.models import (
S19XP,
S19a,
S19aPro,
S19Hydro,
S19i,
S19j,
S19jPro,
S19kPro,
S19NoPIC,
S19Pro,
S19ProA,
S19ProHydro,
S19XPHydro,
)
@@ -42,10 +45,18 @@ class VNishS19Pro(VNish, S19Pro):
pass
class VNishS19Hydro(VNish, S19Hydro):
pass
class VNishS19XP(VNish, S19XP):
pass
class VNishS19XPHydro(VNish, S19XPHydro):
pass
class VNishS19a(VNish, S19a):
pass
@@ -54,6 +65,10 @@ class VNishS19aPro(VNish, S19aPro):
pass
class VNishS19ProA(VNish, S19ProA):
pass
class VNishS19i(VNish, S19i):
pass
@@ -72,3 +87,7 @@ class VNishS19ProHydro(VNish, S19ProHydro):
class VNishS19kPro(VNish, S19kPro):
pass
class VNishS19ProA(VNish, S19ProA):
pass

View File

@@ -18,13 +18,16 @@ from .S19 import (
VNishS19,
VNishS19a,
VNishS19aPro,
VNishS19Hydro,
VNishS19i,
VNishS19j,
VNishS19jPro,
VNishS19kPro,
VNishS19NoPIC,
VNishS19Pro,
VNishS19ProA,
VNishS19ProHydro,
VNishS19XP,
VNishS19XPHydro,
)
from .T19 import VNishT19

View File

@@ -18,5 +18,5 @@ from pyasic.miners.backends import VNish
from pyasic.miners.device.models import L3Plus
class VnishL3Plus(VNish, L3Plus):
class VNishL3Plus(VNish, L3Plus):
pass

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .L3 import VnishL3Plus
from .L3 import VNishL3Plus

View File

@@ -18,5 +18,5 @@ from pyasic.miners.backends import VNish
from pyasic.miners.device.models import L7
class VnishL7(VNish, L7):
class VNishL7(VNish, L7):
pass

View File

@@ -14,4 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .L7 import VnishL7
from .L7 import VNishL7

View File

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

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .L9 import VNishL9

View File

@@ -16,6 +16,7 @@
from .X3 import *
from .X7 import *
from .X9 import *
from .X17 import *
from .X19 import *
from .X21 import *

View File

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

View File

@@ -0,0 +1 @@
from .Q import CGMinerAvalonQHome

View File

@@ -22,3 +22,4 @@ from .A11X import *
from .A12X import *
from .A15X import *
from .nano import *
from .Q import *

View File

@@ -49,31 +49,31 @@ AVALON_NANO_DATA_LOC = DataLocations(
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"_get_env_temp",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
@@ -102,35 +102,35 @@ AVALON_NANO3S_DATA_LOC = DataLocations(
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"_get_env_temp",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
@@ -170,58 +170,58 @@ class CGMinerAvalonNano3s(AvalonMiner, AvalonNano3s):
data_locations = AVALON_NANO3S_DATA_LOC
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
async def _get_wattage(self, rpc_estats: dict = None) -> Optional[int]:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["PS"][6])
unparsed_estats = rpc_estats["STATS"][0]["MM ID0"]
parsed_estats = self.parse_estats(unparsed_estats)
return int(parsed_estats["PS"][6])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_hashrate(self, rpc_stats: dict = None) -> Optional[AlgoHashRate]:
if rpc_stats is None:
async def _get_hashrate(self, rpc_estats: dict = None) -> Optional[AlgoHashRate]:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
unparsed_estats = rpc_estats["STATS"][0]["MM ID0"]
parsed_estats = self.parse_estats(unparsed_estats)
return self.algo.hashrate(
rate=float(parsed_stats["GHSspd"][0]), unit=self.algo.unit.GH
rate=float(parsed_estats["GHSspd"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = await AvalonMiner._get_hashboards(self, rpc_stats)
async def _get_hashboards(self, rpc_estats: dict = None) -> List[HashBoard]:
hashboards = await AvalonMiner._get_hashboards(self, rpc_estats)
if rpc_stats is None:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
unparsed_estats = rpc_estats["STATS"][0]["MM ID0"]
parsed_estats = self.parse_estats(unparsed_estats)
except (IndexError, KeyError, ValueError, TypeError):
return hashboards
for board in range(len(hashboards)):
try:
board_hr = parsed_stats["GHSspd"][board]
board_hr = parsed_estats["GHSspd"][board]
hashboards[board].hashrate = self.algo.hashrate(
rate=float(board_hr), unit=self.algo.unit.GH
).into(self.algo.unit.default)

View File

@@ -13,8 +13,9 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
import copy
import re
import time
from typing import List, Optional
from pyasic.data import Fan, HashBoard
@@ -22,6 +23,7 @@ from pyasic.device.algorithm import AlgoHashRate
from pyasic.errors import APIError
from pyasic.miners.backends.cgminer import CGMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.rpc.avalonminer import AvalonMinerRPCAPI
AVALON_DATA_LOC = DataLocations(
**{
@@ -43,31 +45,31 @@ AVALON_DATA_LOC = DataLocations(
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.ENVIRONMENT_TEMP): DataFunction(
"_get_env_temp",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.WATTAGE_LIMIT): DataFunction(
"_get_wattage_limit",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light",
[RPCAPICommand("rpc_stats", "stats")],
[RPCAPICommand("rpc_estats", "estats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
@@ -84,6 +86,9 @@ AVALON_DATA_LOC = DataLocations(
class AvalonMiner(CGMiner):
"""Handler for Avalon Miners"""
_rpc_cls = AvalonMinerRPCAPI
rpc: AvalonMinerRPCAPI
data_locations = AVALON_DATA_LOC
async def fault_light_on(self) -> bool:
@@ -134,45 +139,94 @@ class AvalonMiner(CGMiner):
return False
return False
async def stop_mining(self) -> bool:
try:
# Shut off 5 seconds from now
timestamp = int(time.time()) + 5
data = await self.rpc.ascset(0, f"softoff", f"1:{timestamp}")
except APIError:
return False
if "success" in data["STATUS"][0]["Msg"]:
return True
return False
async def resume_mining(self) -> bool:
try:
# Shut off 5 seconds from now
timestamp = int(time.time()) + 5
data = await self.rpc.ascset(0, f"softon", f"1:{timestamp}")
except APIError:
return False
if "success" in data["STATUS"][0]["Msg"]:
return True
return False
@staticmethod
def parse_stats(stats):
_stats_items = re.findall(".+?\\[*?]", stats)
stats_items = []
stats_dict = {}
for item in _stats_items:
if ": " in item:
data = item.replace("]", "").split("[")
data_list = [i.split(": ") for i in data[1].strip().split(", ")]
data_dict = {}
try:
for key, val in [tuple(item) for item in data_list]:
data_dict[key] = val
except ValueError:
# --avalon args
for arg_item in data_list:
item_data = arg_item[0].split(" ")
for idx, val in enumerate(item_data):
if idx % 2 == 0 or idx == 0:
data_dict[val] = item_data[idx + 1]
def parse_estats(data):
# Deep copy to preserve original structure
new_data = copy.deepcopy(data)
raw_data = [data[0].strip(), data_dict]
def convert_value(val, key):
val = val.strip()
if key == "SYSTEMSTATU":
return val
if " " in val:
parts = val.split()
result = []
for part in parts:
if part.isdigit():
result.append(int(part))
else:
try:
result.append(float(part))
except ValueError:
result.append(part)
return result
else:
raw_data = [
value
for value in item.replace("[", " ")
.replace("]", " ")
.split(" ")[:-1]
if value != ""
]
if len(raw_data) == 1:
raw_data.append("")
if raw_data[0] == "":
raw_data = raw_data[1:]
if val.isdigit():
return int(val)
try:
return float(val)
except ValueError:
return val
stats_dict[raw_data[0]] = raw_data[1:]
stats_items.append(raw_data)
def parse_info_block(info_str):
pattern = re.compile(r"(\w+)\[([^\]]*)\]")
return {
key: convert_value(val, key) for key, val in pattern.findall(info_str)
}
return stats_dict
for stat in new_data.get("STATS", []):
keys_to_replace = {}
for key, value in stat.items():
if "MM" in key:
# Normalize key by removing suffix after colon
norm_key = key.split(":")[0]
mm_data = value
if not isinstance(mm_data, str):
continue
if mm_data.startswith("'STATS':"):
mm_data = mm_data[len("'STATS':") :]
keys_to_replace[norm_key] = parse_info_block(mm_data)
elif key == "HBinfo":
match = re.search(r"'(\w+)':\{(.+)\}", value)
if match:
hb_key = match.group(1)
hb_data = match.group(2)
keys_to_replace[key] = {hb_key: parse_info_block(hb_data)}
# Remove old keys and insert parsed versions
for k in list(stat.keys()):
if "MM" in k or k == "HBinfo":
del stat[k]
stat.update(keys_to_replace)
return new_data
##################################################
### DATA GATHERING FUNCTIONS (get_{some_data}) ###
@@ -211,7 +265,7 @@ class AvalonMiner(CGMiner):
except (KeyError, IndexError, ValueError, TypeError):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
async def _get_hashboards(self, rpc_estats: dict = None) -> List[HashBoard]:
if self.expected_hashboards is None:
return []
@@ -220,164 +274,202 @@ class AvalonMiner(CGMiner):
for i in range(self.expected_hashboards)
]
if rpc_stats is None:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
parsed_estats = self.parse_estats(rpc_estats)
except (IndexError, KeyError, ValueError, TypeError):
return hashboards
for board in range(self.expected_hashboards):
try:
hashboards[board].chip_temp = int(parsed_stats["MTmax"][board])
board_hr = parsed_estats["STATS"][0]["MM ID0"]["MGHS"]
if isinstance(board_hr, list):
hashboards[board].hashrate = self.algo.hashrate(
rate=float(board_hr[board]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
else:
hashboards[board].hashrate = self.algo.hashrate(
rate=float(board_hr), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except LookupError:
pass
try:
board_hr = parsed_stats["MGHS"][board]
hashboards[board].hashrate = self.algo.hashrate(
rate=float(board_hr), unit=self.algo.unit.GH
).into(self.algo.unit.default)
hashboards[board].chip_temp = int(
parsed_estats["STATS"][0]["MM ID0"]["MTmax"][board]
)
except LookupError:
pass
try:
hashboards[board].chip_temp = int(
parsed_estats["STATS"][0]["MM ID0"]["Tmax"]
)
except LookupError:
pass
try:
hashboards[board].temp = int(parsed_stats["MTavg"][board])
hashboards[board].temp = int(
parsed_estats["STATS"][0]["MM ID0"]["MTmax"][board]
)
except LookupError:
pass
try:
hashboards[board].temp = int(
parsed_estats["STATS"][0]["MM ID0"]["Tavg"]
)
except LookupError:
pass
try:
chip_data = parsed_stats[f"PVT_T{board}"]
hashboards[board].inlet_temp = int(
parsed_estats["STATS"][0]["MM ID0"]["MTavg"][board]
)
except LookupError:
try:
hashboards[board].inlet_temp = int(
parsed_estats["STATS"][0]["MM ID0"]["HBITemp"]
)
except LookupError:
pass
try:
hashboards[board].outlet_temp = int(
parsed_estats["STATS"][0]["MM ID0"]["MTmax"][board]
)
except LookupError:
try:
hashboards[board].outlet_temp = int(
parsed_estats["STATS"][0]["MM ID0"]["HBOTemp"]
)
except LookupError:
pass
try:
chip_data = parsed_estats["STATS"][0]["MM ID0"][f"PVT_T{board}"]
hashboards[board].missing = False
if chip_data:
hashboards[board].chips = len(
[item for item in chip_data if not item == "0"]
)
except LookupError:
pass
try:
chip_data = parsed_estats["STATS"][0]["HBinfo"][f"HB{board}"][
f"PVT_T{board}"
]
hashboards[board].missing = False
if chip_data:
hashboards[board].chips = len(
[item for item in chip_data if not item == "0"]
)
except LookupError:
pass
return hashboards
async def _get_expected_hashrate(
self, rpc_stats: dict = None
self, rpc_estats: dict = None
) -> Optional[AlgoHashRate]:
if rpc_stats is None:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
parsed_estats = self.parse_estats(rpc_estats)["STATS"][0]["MM ID0"]
return self.algo.hashrate(
rate=float(parsed_stats["GHSmm"][0]), unit=self.algo.unit.GH
rate=float(parsed_estats["GHSmm"]), unit=self.algo.unit.GH
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_env_temp(self, rpc_stats: dict = None) -> Optional[float]:
if rpc_stats is None:
async def _get_env_temp(self, rpc_estats: dict = None) -> Optional[float]:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return float(parsed_stats["Temp"][0])
parsed_estats = self.parse_estats(rpc_estats)["STATS"][0]["MM ID0"]
return float(parsed_estats["Temp"])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_wattage_limit(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
async def _get_wattage_limit(self, rpc_estats: dict = None) -> Optional[int]:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["MPO"][0])
parsed_estats = self.parse_estats(rpc_estats)["STATS"][0]["MM ID0"]
return int(parsed_estats["MPO"])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if rpc_stats is None:
async def _get_wattage(self, rpc_estats: dict = None) -> Optional[int]:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
return int(parsed_stats["WALLPOWER"][0])
parsed_estats = self.parse_estats(rpc_estats)["STATS"][0]["MM ID0"]
return int(parsed_estats["WALLPOWER"])
except (IndexError, KeyError, ValueError, TypeError):
pass
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
async def _get_fans(self, rpc_estats: dict = None) -> List[Fan]:
if self.expected_fans is None:
return []
if rpc_stats is None:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
fans_data = [Fan() for _ in range(self.expected_fans)]
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
parsed_estats = self.parse_estats(rpc_estats)["STATS"][0]["MM ID0"]
except LookupError:
return fans_data
for fan in range(self.expected_fans):
try:
fans_data[fan].speed = int(parsed_stats[f"Fan{fan + 1}"][0])
fans_data[fan].speed = int(parsed_estats[f"Fan{fan + 1}"])
except (IndexError, KeyError, ValueError, TypeError):
pass
return fans_data
async def _get_fault_light(self, rpc_stats: dict = None) -> Optional[bool]:
async def _get_fault_light(self, rpc_estats: dict = None) -> Optional[bool]:
if self.light:
return self.light
if rpc_stats is None:
if rpc_estats is None:
try:
rpc_stats = await self.rpc.stats()
rpc_estats = await self.rpc.estats()
except APIError:
pass
if rpc_stats is not None:
if rpc_estats is not None:
try:
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
parsed_stats = self.parse_stats(unparsed_stats)
led = int(parsed_stats["Led"][0])
parsed_estats = self.parse_estats(rpc_estats)["STATS"][0]["MM ID0"]
led = int(parsed_estats["Led"])
return True if led == 1 else False
except (IndexError, KeyError, ValueError, TypeError):
pass
try:
data = await self.rpc.ascset(0, "led", "1-255")
except APIError:
return False
try:
if data["STATUS"][0]["Msg"] == "ASC 0 set info: LED[1]":
return True
except LookupError:
pass
return False

View File

@@ -115,11 +115,18 @@ class ESPMiner(BaseMiner):
if web_system_info is not None:
try:
expected_hashrate = (
web_system_info.get("smallCoreCount")
* web_system_info.get("asicCount")
* web_system_info.get("frequency")
)
small_core_count = web_system_info.get("smallCoreCount")
asic_count = web_system_info.get("asicCount")
frequency = web_system_info.get("frequency")
if asic_count is None:
try:
asic_info = await self.web.asic_info()
asic_count = asic_info.get("asicCount")
except APIError:
pass
expected_hashrate = small_core_count * asic_count * frequency
return self.algo.hashrate(
rate=float(expected_hashrate), unit=self.algo.unit.MH

View File

@@ -126,6 +126,15 @@ class S19jPro(AntMinerMake):
algo = MinerAlgo.SHA256
class S19jPlus(AntMinerMake):
raw_model = MinerModel.ANTMINER.S19jPlus
expected_chips = 108
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jProNoPIC(AntMinerMake):
raw_model = MinerModel.ANTMINER.S19jProNoPIC
@@ -216,6 +225,15 @@ class S19KPro(AntMinerMake):
algo = MinerAlgo.SHA256
class S19ProA(AntMinerMake):
raw_model = MinerModel.ANTMINER.S19ProA
expected_chips = 114
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19jXP(AntMinerMake):
raw_model = MinerModel.ANTMINER.S19jXP
@@ -223,3 +241,12 @@ class S19jXP(AntMinerMake):
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.SHA256
class S19XPHydro(AntMinerMake):
raw_model = MinerModel.ANTMINER.S19XPHydro
expected_chips = 204
expected_fans = 0
expected_hashboards = 3
algo = MinerAlgo.SHA256

View File

@@ -24,6 +24,7 @@ from .S19 import (
S19i,
S19j,
S19jNoPIC,
S19jPlus,
S19jPro,
S19jProNoPIC,
S19jProPlus,
@@ -35,8 +36,10 @@ from .S19 import (
S19NoPIC,
S19Plus,
S19Pro,
S19ProA,
S19ProHydro,
S19ProPlus,
S19ProPlusHydro,
S19XPHydro,
)
from .T19 import T19

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import AvalonMinerMake
class AvalonQHome(AvalonMinerMake):
raw_model = MinerModel.AVALONMINER.AvalonQHome
expected_chips = 160
expected_fans = 2
expected_hashboards = 1
algo = MinerAlgo.SHA256

View File

@@ -0,0 +1,17 @@
# ------------------------------------------------------------------------------
# Copyright 2022 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from .Q import AvalonQHome

View File

@@ -22,3 +22,4 @@ from .A11X import *
from .A12X import *
from .A15X import *
from .nano import *
from .Q import *

View File

@@ -18,6 +18,15 @@ from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import ElphapexMake
class DG1(ElphapexMake):
raw_model = MinerModel.ELPHAPEX.DG1
expected_chips = 144
expected_hashboards = 4
expected_fans = 4
algo = MinerAlgo.SCRYPT
class DG1Plus(ElphapexMake):
raw_model = MinerModel.ELPHAPEX.DG1Plus
@@ -25,3 +34,12 @@ class DG1Plus(ElphapexMake):
expected_hashboards = 4
expected_fans = 4
algo = MinerAlgo.SCRYPT
class DG1Home(ElphapexMake):
raw_model = MinerModel.ELPHAPEX.DG1Home
expected_chips = 120
expected_hashboards = 4
expected_fans = 4
algo = MinerAlgo.SCRYPT

View File

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

View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2024 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.device.algorithm import MinerAlgo
from pyasic.device.models import MinerModel
from pyasic.miners.device.makes import IceRiverMake
class AL3(IceRiverMake):
raw_model = MinerModel.ICERIVER.AL3
expected_chips = 156
expected_fans = 4
expected_hashboards = 3
algo = MinerAlgo.BLOCKFLOW

View File

@@ -0,0 +1 @@
from .AL3 import AL3

View File

@@ -1 +1,2 @@
from .ALX import *
from .KSX import *

View File

@@ -1,6 +1,14 @@
from pyasic.miners.backends.elphapex import ElphapexMiner
from pyasic.miners.device.models import DG1Plus
from pyasic.miners.device.models import DG1, DG1Home, DG1Plus
class ElphapexDG1Plus(ElphapexMiner, DG1Plus):
pass
class ElphapexDG1(ElphapexMiner, DG1):
pass
class ElphapexDG1Home(ElphapexMiner, DG1Home):
pass

View File

@@ -1 +1 @@
from .DG1 import ElphapexDG1Plus
from .DG1 import ElphapexDG1, ElphapexDG1Home, ElphapexDG1Plus

View File

@@ -41,6 +41,7 @@ from pyasic.miners.elphapex import *
from pyasic.miners.goldshell import *
from pyasic.miners.hammer import *
from pyasic.miners.iceriver import *
from pyasic.miners.iceriver.iceminer.ALX import IceRiverAL3
from pyasic.miners.innosilicon import *
from pyasic.miners.luckyminer import *
from pyasic.miners.volcminer import *
@@ -108,6 +109,8 @@ MINER_CLASSES = {
"ANTMINER S19J88NOPIC": BMMinerS19jNoPIC,
"ANTMINER S19PRO+": BMMinerS19ProPlus,
"ANTMINER S19J PRO": BMMinerS19jPro,
"ANTMINER S19J+": BMMinerS19jPlus,
"ANTMINER S19J PRO+": BMMinerS19jProPlus,
"ANTMINER S19 XP": BMMinerS19XP,
"ANTMINER S19A": BMMinerS19a,
"ANTMINER S19A PRO": BMMinerS19aPro,
@@ -509,6 +512,7 @@ MINER_CLASSES = {
"AVALONMINER NANO3": CGMinerAvalonNano3,
"AVALON NANO3S": CGMinerAvalonNano3s,
"AVALONMINER 15-194": CGMinerAvalon1566,
"AVALON Q": CGMinerAvalonQHome,
},
MinerTypes.INNOSILICON: {
None: type("InnosiliconUnknown", (Innosilicon, InnosiliconMake), {}),
@@ -561,12 +565,14 @@ MINER_CLASSES = {
"ANTMINER T21": BOSMinerT21,
"BRAIINS MINI MINER BMM 100": BraiinsBMM100,
"BRAIINS MINI MINER BMM 101": BraiinsBMM101,
"ANTMINER S19 XP HYD.": BOSMinerS19XPHydro,
},
MinerTypes.VNISH: {
None: VNish,
"L3+": VnishL3Plus,
"ANTMINER L3+": VnishL3Plus,
"ANTMINER L7": VnishL7,
"L3+": VNishL3Plus,
"ANTMINER L3+": VNishL3Plus,
"ANTMINER L7": VNishL7,
"ANTMINER L9": VNishL9,
"ANTMINER S17+": VNishS17Plus,
"ANTMINER S17 PRO": VNishS17Pro,
"ANTMINER S19": VNishS19,
@@ -574,11 +580,15 @@ MINER_CLASSES = {
"ANTMINER S19 PRO": VNishS19Pro,
"ANTMINER S19J": VNishS19j,
"ANTMINER S19I": VNishS19i,
"ANTMINER S19 XP": VNishS19XP,
"ANTMINER S19 XP HYD.": VNishS19XPHydro,
"ANTMINER S19J PRO": VNishS19jPro,
"ANTMINER S19J PRO A": VNishS19jPro,
"ANTMINER S19J PRO BB": VNishS19jPro,
"ANTMINER S19A": VNishS19a,
"ANTMINER S19 HYD.": VNishS19Hydro,
"ANTMINER S19A PRO": VNishS19aPro,
"ANTMINER S19 PRO A": VNishS19ProA,
"ANTMINER S19 PRO HYD.": VNishS19ProHydro,
"ANTMINER S19K PRO": VNishS19kPro,
"ANTMINER T19": VNishT19,
@@ -677,6 +687,7 @@ MINER_CLASSES = {
"KS5": IceRiverKS5,
"KS5L": IceRiverKS5L,
"KS5M": IceRiverKS5M,
"10306": IceRiverAL3,
},
MinerTypes.HAMMER: {
None: type("HammerUnknown", (BlackMiner, HammerMake), {}),
@@ -689,6 +700,8 @@ MINER_CLASSES = {
MinerTypes.ELPHAPEX: {
None: type("ElphapexUnknown", (ElphapexMiner, ElphapexMake), {}),
"DG1+": ElphapexDG1Plus,
"DG1": ElphapexDG1,
"DG1-Home": ElphapexDG1Home,
},
}

View File

@@ -0,0 +1,6 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models.iceriver import AL3
class IceRiverAL3(IceRiver, AL3):
pass

View File

@@ -0,0 +1 @@
from .AL3 import IceRiverAL3

27
pyasic/rpc/avalonminer.py Normal file
View File

@@ -0,0 +1,27 @@
# ------------------------------------------------------------------------------
# Copyright 2025 Upstream Data Inc -
# -
# Licensed under the Apache License, Version 2.0 (the "License"); -
# you may not use this file except in compliance with the License. -
# You may obtain a copy of the License at -
# -
# http://www.apache.org/licenses/LICENSE-2.0 -
# -
# Unless required by applicable law or agreed to in writing, software -
# distributed under the License is distributed on an "AS IS" BASIS, -
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic.rpc.cgminer import CGMinerRPCAPI
class AvalonMinerRPCAPI(CGMinerRPCAPI):
"""An abstraction of the AvalonMiner API.
Each method corresponds to an API command in AvalonMiner.
"""
async def litestats(self):
return await self.send_command("litestats")

View File

@@ -96,3 +96,6 @@ class ESPMinerWebAPI(BaseWebAPI):
async def update_settings(self, **config):
return await self.send_command("system", patch=True, **config)
async def asic_info(self):
return await self.send_command("system/asic")

View File

@@ -1,6 +1,6 @@
[project]
name = "pyasic"
version = "0.72.9"
version = "0.74.0"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = [{name = "UpstreamData", email = "brett@upstreamdata.ca"}]

File diff suppressed because one or more lines are too long