Compare commits

..

25 Commits

Author SHA1 Message Date
Upstream Data
8b1cbed9ce version: bump version number. 2024-10-30 12:25:47 -06:00
Upstream Data
0194e13427 bug: update A11MX chip count 2024-10-30 12:25:32 -06:00
Upstream Data
82d71abf54 version: bump version number 2024-10-30 12:23:08 -06:00
Upstream Data
e71cfadf6e bug: add model parser into map for iceriver 2024-10-30 12:22:44 -06:00
Upstream Data
18931c4e98 version: bump version number 2024-10-30 12:18:53 -06:00
Upstream Data
8622c080aa bug: fix inno pool parsing 2024-10-30 12:18:27 -06:00
Upstream Data
cb71b2a593 docs: update supported miners 2024-10-30 10:44:37 -06:00
Upstream Data
ff5956da41 version: bump version number 2024-10-30 10:32:48 -06:00
Upstream Data
acdafc2efd bug: fix hashboard innosilicon model selection 2024-10-30 10:32:29 -06:00
Upstream Data
b8874092ad bug: fix hashboard count on A11MX 2024-10-30 10:07:59 -06:00
Upstream Data
ad28ba0b3e version: bump version number 2024-10-30 09:27:54 -06:00
Upstream Data
0d90b60eef feature: add the rest of the iceriver models 2024-10-30 09:27:12 -06:00
Upstream Data
7c18c9f69c feature: improve iceriver support and add support for KS3M 2024-10-30 09:21:12 -06:00
Upstream Data
975560f46f feature: add iceriver type parsing 2024-10-30 09:12:54 -06:00
Upstream Data
bfe9cbf7d9 bug: fix iceriver miners being identified as bitaxe 2024-10-30 08:54:27 -06:00
Upstream Data
ccb5eb73db version: bump version number 2024-10-30 08:49:25 -06:00
Upstream Data
d143667bd6 feature: add warning message when instantiating an unsupported miner type 2024-10-30 08:49:01 -06:00
Upstream Data
87d809abc0 bug: update KS3 fan count 2024-10-30 08:44:38 -06:00
Upstream Data
4dc5b1a541 feature: add chip count for WM M60VK20 2024-10-30 08:44:08 -06:00
Upstream Data
ddd3e867f9 feature: add stratum+ssl pool url scheme 2024-10-30 08:41:35 -06:00
Upstream Data
77480d3d69 feature: add support for antminer KS5 2024-10-30 08:39:05 -06:00
Upstream Data
0767c93002 feature: add support for antminer KS3 2024-10-30 08:33:59 -06:00
Upstream Data
e690e6dd3b version: bump version number 2024-10-29 16:16:13 -06:00
James Hilliard
d4665ed768 Update betterproto and regenerate protoc files
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2024-10-29 16:15:30 -06:00
James Hilliard
b90a92c0df Update betterproto and regenerate protoc files
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
2024-10-29 16:15:11 -06:00
42 changed files with 690 additions and 221 deletions

View File

@@ -29,6 +29,13 @@
show_root_heading: false
heading_level: 4
## T21 (BOS+)
::: pyasic.miners.antminer.bosminer.X21.T21.BOSMinerT21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21 (VNish)
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21
handler: python

View File

@@ -22,6 +22,20 @@
show_root_heading: false
heading_level: 4
## KA3 (Stock)
::: pyasic.miners.antminer.bmminer.X3.KA3.BMMinerKA3
handler: python
options:
show_root_heading: false
heading_level: 4
## KS3 (Stock)
::: pyasic.miners.antminer.bmminer.X3.KS3.BMMinerKS3
handler: python
options:
show_root_heading: false
heading_level: 4
## L3+ (VNish)
::: pyasic.miners.antminer.vnish.X3.L3.VnishL3Plus
handler: python

View File

@@ -8,3 +8,10 @@
show_root_heading: false
heading_level: 4
## KS5 (Stock)
::: pyasic.miners.antminer.bmminer.X5.KS5.BMMinerKS5
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -1,6 +1,20 @@
# pyasic
## KSX Models
## KS0 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS0.IceRiverKS0
handler: python
options:
show_root_heading: false
heading_level: 4
## KS1 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS1.IceRiverKS1
handler: python
options:
show_root_heading: false
heading_level: 4
## KS2 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS2.IceRiverKS2
handler: python
@@ -8,3 +22,24 @@
show_root_heading: false
heading_level: 4
## KS3 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3
handler: python
options:
show_root_heading: false
heading_level: 4
## KS3L (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3L
handler: python
options:
show_root_heading: false
heading_level: 4
## KS3M (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS3.IceRiverKS3M
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -21,12 +21,15 @@ details {
<li><a href="../antminer/X3#d3-stock">D3 (Stock)</a></li>
<li><a href="../antminer/X3#hs3-stock">HS3 (Stock)</a></li>
<li><a href="../antminer/X3#l3_1-stock">L3+ (Stock)</a></li>
<li><a href="../antminer/X3#ka3-stock">KA3 (Stock)</a></li>
<li><a href="../antminer/X3#ks3-stock">KS3 (Stock)</a></li>
</ul>
</details>
<details>
<summary>X5 Series:</summary>
<ul>
<li><a href="../antminer/X5#dr5-stock">DR5 (Stock)</a></li>
<li><a href="../antminer/X5#ks5-stock">KS5 (Stock)</a></li>
</ul>
</details>
<details>
@@ -378,6 +381,12 @@ details {
<li><a href="../avalonminer/A12X#avalon-1246-stock">Avalon 1246 (Stock)</a></li>
</ul>
</details>
<details>
<summary>nano Series:</summary>
<ul>
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -395,6 +404,12 @@ details {
<li><a href="../innosilicon/A10X#a10x-stock">A10X (Stock)</a></li>
</ul>
</details>
<details>
<summary>A11X Series:</summary>
<ul>
<li><a href="../innosilicon/A11X#a11mx-stock">A11MX (Stock)</a></li>
</ul>
</details>
</ul>
</details>
<details>
@@ -470,6 +485,7 @@ details {
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
</ul>
</details>
</ul>
@@ -661,7 +677,12 @@ details {
<details>
<summary>KSX Series:</summary>
<ul>
<li><a href="../iceriver/KSX#ks0-stock">KS0 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks1-stock">KS1 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks2-stock">KS2 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3-stock">KS3 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3l-stock">KS3L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3m-stock">KS3M (Stock)</a></li>
</ul>
</details>
</ul>

14
poetry.lock generated
View File

@@ -59,21 +59,23 @@ pywin32 = ["pywin32 (>=227)"]
[[package]]
name = "betterproto"
version = "2.0.0b6"
version = "2.0.0b7"
description = "A better Protobuf / gRPC generator & library"
optional = false
python-versions = ">=3.7,<4.0"
python-versions = "<4.0,>=3.7"
files = [
{file = "betterproto-2.0.0b6-py3-none-any.whl", hash = "sha256:a0839ec165d110a69d0d116f4d0e2bec8d186af4db826257931f0831dab73fcf"},
{file = "betterproto-2.0.0b6.tar.gz", hash = "sha256:720ae92697000f6fcf049c69267d957f0871654c8b0d7458906607685daee784"},
{file = "betterproto-2.0.0b7-py3-none-any.whl", hash = "sha256:401ab8055e2f814e77b9c88a74d0e1ae3d1e8a969cced6aeb1b59f71ad63fbd2"},
{file = "betterproto-2.0.0b7.tar.gz", hash = "sha256:1b1458ca5278d519bcd62556a4c236f998a91d503f0f71c67b0b954747052af2"},
]
[package.dependencies]
grpclib = ">=0.4.1,<0.5.0"
python-dateutil = ">=2.8,<3.0"
typing-extensions = ">=4.7.1,<5.0.0"
[package.extras]
compiler = ["black (>=19.3b0)", "isort (>=5.11.5,<6.0.0)", "jinja2 (>=3.0.3)"]
compiler = ["black (>=23.1.0)", "isort (>=5.11.5,<6.0.0)", "jinja2 (>=3.0.3)"]
rust-codec = ["betterproto-rust-codec (==0.1.1)"]
[[package]]
name = "certifi"
@@ -1181,4 +1183,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
content-hash = "fdcee9d1f9de23d7c35ab73ca06127e70638929f5db176f6b789cbd7b76bf6b5"
content-hash = "d611b5e8b0c5611d6ee916cedfb7f07f20dfc90a675ebaed04188e8b3c96aabe"

View File

@@ -198,7 +198,7 @@ class MiningModePowerTune(MinerConfigValue):
def as_boser(self) -> dict:
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action=SaveAction.SAVE_AND_APPLY,
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
power_target=PowerTargetMode(
@@ -275,7 +275,7 @@ class MiningModeHashrateTune(MinerConfigValue):
def as_boser(self) -> dict:
cfg = {
"set_performance_mode": SetPerformanceModeRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action=SaveAction.SAVE_AND_APPLY,
mode=PerformanceMode(
tuner_mode=TunerPerformanceMode(
hashrate_target=HashrateTargetMode(

View File

@@ -467,7 +467,7 @@ class PoolConfig(MinerConfigValue):
def as_boser(self, user_suffix: str = None) -> dict:
return {
"set_pool_groups": SetPoolGroupsRequest(
save_action=SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action=SaveAction.SAVE_AND_APPLY,
pool_groups=[g.as_boser(user_suffix=user_suffix) for g in self.groups],
)
}

View File

@@ -7,6 +7,7 @@ from urllib.parse import urlparse
class Scheme(Enum):
STRATUM_V1 = "stratum+tcp"
STRATUM_V2 = "stratum2+tcp"
STRATUM_V1_SSL = "stratum+ssl"
@dataclass

View File

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

View File

@@ -6,7 +6,9 @@ class AntminerModels(str, Enum):
HS3 = "HS3"
L3Plus = "L3+"
KA3 = "KA3"
KS3 = "KS3"
DR5 = "DR5"
KS5 = "KS5"
L7 = "L7"
E9Pro = "E9Pro"
S9 = "S9"
@@ -343,7 +345,12 @@ class BitAxeModels(str, Enum):
class IceRiverModels(str, Enum):
KS0 = "KS0"
KS1 = "KS1"
KS2 = "KS2"
KS3 = "KS3"
KS3L = "KS3L"
KS3M = "KS3M"
def __str__(self):
return self.value

View File

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

View File

@@ -15,4 +15,5 @@
# ------------------------------------------------------------------------------
from .HS3 import BMMinerHS3
from .KA3 import BMMinerKA3
from .KS3 import BMMinerKS3
from .L3 import BMMinerL3Plus

View File

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

View File

@@ -0,0 +1,16 @@
# ------------------------------------------------------------------------------
# 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 .KS5 import BMMinerKS5

View File

@@ -14,6 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .X3 import *
from .X5 import *
from .X7 import *
from .X9 import *
from .X17 import *

View File

@@ -798,7 +798,7 @@ class BOSer(BraiinsOSFirmware):
async def set_power_limit(self, wattage: int) -> bool:
try:
result = await self.web.set_power_target(
wattage, save_action=SaveAction.SAVE_ACTION_SAVE_AND_FORCE_APPLY
wattage, save_action=SaveAction.SAVE_AND_FORCE_APPLY
)
except APIError:
return False

View File

@@ -165,13 +165,13 @@ class IceRiver(StockFirmware):
if web_userpanel is not None:
try:
for board in web_userpanel["boards"]:
idx = board["no"] - 1
idx = int(board["no"] - 1)
hb_list[idx].chip_temp = round(board["outtmp"])
hb_list[idx].temp = round(board["intmp"])
hb_list[idx].hashrate = AlgoHashRate.SHA256(
float(board["rtpow"].replace("G", "")), HashUnit.SHA256.GH
).into(self.algo.unit.default)
hb_list[idx].chips = board["chipnum"]
hb_list[idx].chips = int(board["chipnum"])
hb_list[idx].missing = False
except LookupError:
pass

View File

@@ -19,6 +19,7 @@ from pyasic.config import MinerConfig
from pyasic.data import AlgoHashRate, Fan, HashBoard, HashUnit
from pyasic.data.error_codes import MinerErrorData
from pyasic.data.error_codes.innosilicon import InnosiliconError
from pyasic.data.pools import PoolMetrics, PoolUrl
from pyasic.errors import APIError
from pyasic.miners.backends import CGMiner
from pyasic.miners.data import (
@@ -29,7 +30,6 @@ from pyasic.miners.data import (
WebAPICommand,
)
from pyasic.web.innosilicon import InnosiliconWebAPI
from pyasic.data.pools import PoolMetrics, PoolUrl
INNOSILICON_DATA_LOC = DataLocations(
**{
@@ -92,9 +92,8 @@ INNOSILICON_DATA_LOC = DataLocations(
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")]
)
"_get_pools", [RPCAPICommand("rpc_pools", "pools")]
),
}
)
@@ -116,7 +115,7 @@ class Innosilicon(CGMiner):
except APIError:
return self.config
self.config = MinerConfig.from_inno([pools])
self.config = MinerConfig.from_inno(pools["pools"])
return self.config
async def reboot(self) -> bool:

View File

@@ -51,4 +51,4 @@ class BitAxeMake(BaseMiner):
class IceRiverMake(BaseMiner):
make = MinerMake.BITAXE
make = MinerMake.ICERIVER

View File

@@ -0,0 +1,24 @@
# ------------------------------------------------------------------------------
# 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.models import MinerModel
from pyasic.miners.device.makes import AntMinerMake
class KS3(AntMinerMake):
raw_model = MinerModel.ANTMINER.KS3
expected_chips = 92
expected_fans = 2

View File

@@ -16,4 +16,5 @@
from .D3 import D3
from .HS3 import HS3
from .KA3 import KA3
from .KS3 import KS3
from .L3 import L3Plus

View File

@@ -0,0 +1,23 @@
# ------------------------------------------------------------------------------
# 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.models import MinerModel
from pyasic.miners.device.makes import AntMinerMake
class KS5(AntMinerMake):
raw_model = MinerModel.ANTMINER.KS5
expected_chips = 92

View File

@@ -14,3 +14,4 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from .DR5 import DR5
from .KS5 import KS5

View File

@@ -0,0 +1,23 @@
# ------------------------------------------------------------------------------
# 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.models import MinerModel
from pyasic.miners.device.makes import IceRiverMake
class KS0(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS0
expected_fans = 0

View File

@@ -0,0 +1,23 @@
# ------------------------------------------------------------------------------
# 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.models import MinerModel
from pyasic.miners.device.makes import IceRiverMake
class KS1(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS1
expected_fans = 4

View File

@@ -21,3 +21,4 @@ class KS2(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS2
expected_fans = 4
expected_chips = 18

View File

@@ -0,0 +1,36 @@
# ------------------------------------------------------------------------------
# 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.models import MinerModel
from pyasic.miners.device.makes import IceRiverMake
class KS3(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS3
expected_fans = 4
class KS3L(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS3L
expected_fans = 4
class KS3M(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS3M
expected_fans = 4
expected_chips = 18

View File

@@ -1 +1,4 @@
from .KS0 import KS0
from .KS1 import KS1
from .KS2 import KS2
from .KS3 import KS3, KS3L, KS3M

View File

@@ -19,3 +19,6 @@ from pyasic.miners.device.makes import InnosiliconMake
class A11MX(InnosiliconMake):
raw_model = MinerModel.INNOSILICON.A11MX
expected_hashboards = 4
expected_chips = 8

View File

@@ -24,6 +24,8 @@ class M60VK10(WhatsMinerMake):
class M60VK20(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M60VK20
expected_chips = 172
class M60VK30(WhatsMinerMake):
raw_model = MinerModel.WHATSMINER.M60VK30

View File

@@ -20,6 +20,7 @@ import enum
import ipaddress
import json
import re
import warnings
from typing import Any, AsyncGenerator, Callable
import anyio
@@ -67,7 +68,9 @@ MINER_CLASSES = {
"ANTMINER HS3": BMMinerHS3,
"ANTMINER L3+": BMMinerL3Plus,
"ANTMINER KA3": BMMinerKA3,
"ANTMINER KS3": BMMinerKS3,
"ANTMINER DR5": CGMinerDR5,
"ANTMINER KS5": BMMinerKS5,
"ANTMINER L7": BMMinerL7,
"ANTMINER E9 PRO": BMMinerE9Pro,
"ANTMINER S9": BMMinerS9,
@@ -459,7 +462,12 @@ MINER_CLASSES = {
},
MinerTypes.ICERIVER: {
None: type("IceRiverUnknown", (IceRiver, IceRiverMake), {}),
"KS0": IceRiverKS0,
"KS1": IceRiverKS1,
"KS2": IceRiverKS2,
"KS3": IceRiverKS3,
"KS3L": IceRiverKS3L,
"KS3M": IceRiverKS3M,
},
}
@@ -538,6 +546,7 @@ class MinerFactory:
MinerTypes.AURADINE: self.get_miner_model_auradine,
MinerTypes.MARATHON: self.get_miner_model_marathon,
MinerTypes.BITAXE: self.get_miner_model_bitaxe,
MinerTypes.ICERIVER: self.get_miner_model_iceriver,
}
fn = miner_model_fns.get(miner_type)
@@ -619,6 +628,8 @@ class MinerFactory:
return MinerTypes.WHATSMINER
if "Braiins OS" in web_text:
return MinerTypes.BRAIINS_OS
if "<TITLE>用户界面</TITLE>" in web_text:
return MinerTypes.ICERIVER
if "AxeOS" in web_text:
return MinerTypes.BITAXE
if "cloud-box" in web_text:
@@ -633,8 +644,6 @@ class MinerFactory:
return MinerTypes.INNOSILICON
if "Miner UI" in web_text:
return MinerTypes.AURADINE
if "<TITLE>用户界面</TITLE>" in web_text:
return MinerTypes.ICERIVER
async def _get_miner_socket(self, ip: str) -> MinerTypes | None:
commands = ["version", "devdetails"]
@@ -839,6 +848,10 @@ class MinerFactory:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
except LookupError:
if miner_type in MINER_CLASSES:
warnings.warn(
f"Partially supported miner found: {miner_model}, 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)
return UnknownMiner(str(ip))
@@ -929,10 +942,19 @@ class MinerFactory:
async with httpx.AsyncClient(transport=settings.transport()) as session:
auth_req = await session.post(
f"http://{ip}/api/auth",
data={"username": "admin", "password": "admin"},
data={
"username": "admin",
"password": settings.get(
"default_innosilicon_web_password", "admin"
),
},
)
auth = auth_req.json()["jwt"]
except (httpx.HTTPError, LookupError):
return
try:
async with httpx.AsyncClient(transport=settings.transport()) as session:
web_data = (
await session.post(
f"http://{ip}/api/type",
@@ -943,6 +965,18 @@ class MinerFactory:
return web_data["type"]
except (httpx.HTTPError, LookupError):
pass
try:
async with httpx.AsyncClient(transport=settings.transport()) as session:
web_data = (
await session.post(
f"http://{ip}/overview",
headers={"Authorization": "Bearer " + auth},
data={},
)
).json()
return web_data["type"]
except (httpx.HTTPError, LookupError):
pass
async def get_miner_model_braiins_os(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "devdetails")
@@ -1056,6 +1090,39 @@ class MinerFactory:
except (TypeError, LookupError):
pass
async def get_miner_model_iceriver(self, ip: str) -> str | None:
async with httpx.AsyncClient(transport=settings.transport()) as client:
try:
# auth
await client.post(
f"http://{ip}/user/loginpost",
params={
"post": "6",
"user": "admin",
"pwd": settings.get(
"default_iceriver_web_password", "12345678"
),
},
)
except httpx.HTTPError:
return None
try:
resp = await client.post(
f"http://{ip}:/user/userpanel", params={"post": "4"}
)
if not resp.status_code == 200:
return
result = resp.json()
software_ver = result["data"]["softver1"]
split_ver = software_ver.split("_")
if split_ver[-1] == "miner":
miner_ver = split_ver[-2]
else:
miner_ver = split_ver[-1].replace("miner", "")
return miner_ver.upper()
except httpx.HTTPError:
pass
miner_factory = MinerFactory()

View File

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

View File

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

View File

@@ -0,0 +1,14 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models.iceriver import KS3, KS3L, KS3M
class IceRiverKS3(IceRiver, KS3):
pass
class IceRiverKS3L(IceRiver, KS3L):
pass
class IceRiverKS3M(IceRiver, KS3M):
pass

View File

@@ -1 +1,4 @@
from .KS0 import IceRiverKS0
from .KS1 import IceRiverKS1
from .KS2 import IceRiverKS2
from .KS3 import IceRiverKS3, IceRiverKS3L, IceRiverKS3M

View File

@@ -0,0 +1,93 @@
from datetime import datetime, timedelta
from typing import Any, Dict
from betterproto import DATETIME_ZERO, TYPE_MAP, TYPE_MESSAGE, Casing, Message
# https://github.com/danielgtaylor/python-betterproto/pull/609
def to_pydict(
self, casing: Casing = Casing.CAMEL, include_default_values: bool = False
) -> Dict[str, Any]:
"""
Returns a python dict representation of this object.
Parameters
-----------
casing: :class:`Casing`
The casing to use for key values. Default is :attr:`Casing.CAMEL` for
compatibility purposes.
include_default_values: :class:`bool`
If ``True`` will include the default values of fields. Default is ``False``.
E.g. an ``int32`` field will be included with a value of ``0`` if this is
set to ``True``, otherwise this would be ignored.
Returns
--------
Dict[:class:`str`, Any]
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():
field_is_repeated = defaults[field_name] is list
try:
value = getattr(self, field_name)
except AttributeError:
value = self._get_field_default(field_name)
cased_name = casing(field_name).rstrip("_") # type: ignore
if meta.proto_type == TYPE_MESSAGE:
if isinstance(value, datetime):
if (
value != DATETIME_ZERO
or include_default_values
or self._include_default_value_for_oneof(
field_name=field_name, meta=meta
)
):
output[cased_name] = value
elif isinstance(value, timedelta):
if (
value != timedelta(0)
or include_default_values
or self._include_default_value_for_oneof(
field_name=field_name, meta=meta
)
):
output[cased_name] = value
elif meta.wraps:
if value is not None or include_default_values:
output[cased_name] = value
elif field_is_repeated:
# Convert each item.
value = [i.to_pydict(casing, include_default_values) for i in value]
if value or include_default_values:
output[cased_name] = value
elif value is None:
if include_default_values:
output[cased_name] = None
elif (
value._serialized_on_wire
or include_default_values
or self._include_default_value_for_oneof(
field_name=field_name, meta=meta
)
):
output[cased_name] = value.to_pydict(casing, include_default_values)
elif meta.proto_type == TYPE_MAP:
for k in value:
if hasattr(value[k], "to_pydict"):
value[k] = value[k].to_pydict(casing, include_default_values)
if value or include_default_values:
output[cased_name] = value
elif (
value != self._get_field_default(field_name)
or include_default_values
or self._include_default_value_for_oneof(field_name=field_name, meta=meta)
):
output[cased_name] = value
return output
def patch():
Message.to_pydict = to_pydict

View File

@@ -26,6 +26,9 @@ from grpclib.client import Channel
from pyasic import settings
from pyasic.errors import APIError
from pyasic.web.base import BaseWebAPI
from pyasic.web.braiins_os.better_monkey import patch
patch()
from .proto.braiins.bos import *
from .proto.braiins.bos.v1 import *
@@ -206,7 +209,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_immersion_mode(
self,
enable: bool,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_immersion_mode",
@@ -227,7 +230,7 @@ class BOSerWebAPI(BaseWebAPI):
)
async def set_default_power_target(
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
self, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
) -> dict:
return await self.send_command(
"set_default_power_target",
@@ -238,7 +241,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_power_target(
self,
power_target: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_power_target",
@@ -251,7 +254,7 @@ class BOSerWebAPI(BaseWebAPI):
async def increment_power_target(
self,
power_target_increment: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"increment_power_target",
@@ -265,7 +268,7 @@ class BOSerWebAPI(BaseWebAPI):
async def decrement_power_target(
self,
power_target_decrement: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"decrement_power_target",
@@ -277,7 +280,7 @@ class BOSerWebAPI(BaseWebAPI):
)
async def set_default_hashrate_target(
self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY
self, save_action: SaveAction = SaveAction.SAVE_AND_APPLY
) -> dict:
return await self.send_command(
"set_default_hashrate_target",
@@ -288,7 +291,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_hashrate_target(
self,
hashrate_target: float,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_hashrate_target",
@@ -302,7 +305,7 @@ class BOSerWebAPI(BaseWebAPI):
async def increment_hashrate_target(
self,
hashrate_target_increment: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"increment_hashrate_target",
@@ -318,7 +321,7 @@ class BOSerWebAPI(BaseWebAPI):
async def decrement_hashrate_target(
self,
hashrate_target_decrement: int,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"decrement_hashrate_target",
@@ -359,7 +362,7 @@ class BOSerWebAPI(BaseWebAPI):
self,
wattage_target: int = None,
hashrate_target: int = None,
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
if wattage_target is not None and hashrate_target is not None:
logging.error(
@@ -459,7 +462,7 @@ class BOSerWebAPI(BaseWebAPI):
async def enable_hashboards(
self,
hashboard_ids: List[str],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"enable_hashboards",
@@ -472,7 +475,7 @@ class BOSerWebAPI(BaseWebAPI):
async def disable_hashboards(
self,
hashboard_ids: List[str],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"disable_hashboards",
@@ -485,7 +488,7 @@ class BOSerWebAPI(BaseWebAPI):
async def set_pool_groups(
self,
pool_groups: List[PoolGroupConfiguration],
save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY,
save_action: SaveAction = SaveAction.SAVE_AND_APPLY,
) -> dict:
return await self.send_command(
"set_pool_groups",

View File

@@ -4,12 +4,17 @@
# This file has been @generated
from dataclasses import dataclass
from typing import TYPE_CHECKING, Dict, Optional
from typing import (
TYPE_CHECKING,
Dict,
Optional,
)
import betterproto
import grpclib
from betterproto.grpc.grpclib_server import ServiceBase
if TYPE_CHECKING:
import grpclib.server
from betterproto.grpc.grpclib_client import MetadataLike
@@ -18,7 +23,7 @@ if TYPE_CHECKING:
@dataclass(eq=False, repr=False)
class ApiVersion(betterproto.Message):
"""LATEST_API_VERSION=1.2.0"""
"""LATEST_API_VERSION=1.3.0"""
major: int = betterproto.uint64_field(1)
minor: int = betterproto.uint64_field(2)

View File

@@ -5,12 +5,19 @@
import warnings
from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, AsyncIterator, Dict, List, Optional
from typing import (
TYPE_CHECKING,
AsyncIterator,
Dict,
List,
Optional,
)
import betterproto
import grpclib
from betterproto.grpc.grpclib_server import ServiceBase
if TYPE_CHECKING:
import grpclib.server
from betterproto.grpc.grpclib_client import MetadataLike
@@ -20,125 +27,129 @@ if TYPE_CHECKING:
class SaveAction(betterproto.Enum):
"""Save action for different operations"""
SAVE_ACTION_UNSPECIFIED = 0
SAVE_ACTION_SAVE = 1
SAVE_ACTION_SAVE_AND_APPLY = 2
SAVE_ACTION_SAVE_AND_FORCE_APPLY = 3
UNSPECIFIED = 0
SAVE = 1
SAVE_AND_APPLY = 2
SAVE_AND_FORCE_APPLY = 3
class CoolingMode(betterproto.Enum):
COOLING_MODE_UNSPECIFIED = 0
COOLING_MODE_AUTO = 1
COOLING_MODE_MANUAL = 2
COOLING_MODE_DISABLED = 3
UNSPECIFIED = 0
AUTO = 1
MANUAL = 2
DISABLED = 3
class SensorLocation(betterproto.Enum):
SENSOR_LOCATION_UNSPECIFIED = 0
SENSOR_LOCATION_CHIP = 1
SENSOR_LOCATION_PCB = 2
UNSPECIFIED = 0
CHIP = 1
PCB = 2
class TunerMode(betterproto.Enum):
TUNER_MODE_UNSPECIFIED = 0
TUNER_MODE_POWER_TARGET = 1
TUNER_MODE_HASHRATE_TARGET = 2
UNSPECIFIED = 0
POWER_TARGET = 1
HASHRATE_TARGET = 2
class TunerState(betterproto.Enum):
TUNER_STATE_UNSPECIFIED = 0
TUNER_STATE_DISABLED = 1
TUNER_STATE_STABLE = 2
TUNER_STATE_TUNING = 3
TUNER_STATE_ERROR = 4
UNSPECIFIED = 0
DISABLED = 1
STABLE = 2
TUNING = 3
ERROR = 4
class LicenseType(betterproto.Enum):
LICENSE_TYPE_UNSPECIFIED = 0
LICENSE_TYPE_STANDARD = 1
LICENSE_TYPE_CUSTOM = 2
UNSPECIFIED = 0
STANDARD = 1
CUSTOM = 2
class Platform(betterproto.Enum):
"""Supported platforms"""
PLATFORM_UNSPECIFIED = 0
PLATFORM_AM1_S9 = 1
PLATFORM_AM2_S17 = 2
PLATFORM_AM3_BBB = 3
PLATFORM_AM3_AML = 4
PLATFORM_STM32MP157C_II1_AM2 = 5
PLATFORM_CVITEK_BM1_AM2 = 6
PLATFORM_ZYNQ_BM3_AM2 = 7
UNSPECIFIED = 0
AM1_S9 = 1
AM2_S17 = 2
AM3_BBB = 3
AM3_AML = 4
STM32MP157C_II1_AM2 = 5
CVITEK_BM1_AM2 = 6
ZYNQ_BM3_AM2 = 7
STM32MP157C_II2_BMM1 = 8
class BosMode(betterproto.Enum):
"""BOS modes enumeration"""
BOS_MODE_UNSPECIFIED = 0
BOS_MODE_UPGRADE = 1
BOS_MODE_RECOVERY = 2
BOS_MODE_SD = 3
BOS_MODE_NAND = 4
BOS_MODE_EMMC = 5
UNSPECIFIED = 0
UPGRADE = 1
RECOVERY = 2
SD = 3
NAND = 4
EMMC = 5
class MinerBrand(betterproto.Enum):
MINER_BRAND_UNSPECIFIED = 0
MINER_BRAND_ANTMINER = 1
MINER_BRAND_WHATSMINER = 2
UNSPECIFIED = 0
ANTMINER = 1
WHATSMINER = 2
class MinerModel(betterproto.Enum):
"""Deprecated: This enumeration is not longer maintained"""
MINER_MODEL_UNSPECIFIED = 0
MINER_MODEL_ANTMINER_S9 = 1
MINER_MODEL_ANTMINER_X17 = 2
MINER_MODEL_ANTMINER_S17 = 3
MINER_MODEL_ANTMINER_S17_PLUS = 4
MINER_MODEL_ANTMINER_S17_PRO = 5
MINER_MODEL_ANTMINER_S17E = 6
MINER_MODEL_ANTMINER_T17 = 7
MINER_MODEL_ANTMINER_T17E = 8
MINER_MODEL_ANTMINER_T17_PLUS = 9
MINER_MODEL_ANTMINER_X19 = 10
MINER_MODEL_ANTMINER_S19 = 11
MINER_MODEL_ANTMINER_S19_PRO = 12
MINER_MODEL_ANTMINER_S19_PLUS = 13
MINER_MODEL_ANTMINER_S19J = 14
MINER_MODEL_ANTMINER_S19J_PRO = 15
MINER_MODEL_ANTMINER_S19A = 16
MINER_MODEL_ANTMINER_S19A_PRO = 17
MINER_MODEL_ANTMINER_S19XP = 18
MINER_MODEL_ANTMINER_T19 = 19
MINER_MODEL_ANTMINER_S19J_PRO_PLUS = 20
UNSPECIFIED = 0
ANTMINER_S9 = 1
ANTMINER_X17 = 2
ANTMINER_S17 = 3
ANTMINER_S17_PLUS = 4
ANTMINER_S17_PRO = 5
ANTMINER_S17E = 6
ANTMINER_T17 = 7
ANTMINER_T17E = 8
ANTMINER_T17_PLUS = 9
ANTMINER_X19 = 10
ANTMINER_S19 = 11
ANTMINER_S19_PRO = 12
ANTMINER_S19_PLUS = 13
ANTMINER_S19J = 14
ANTMINER_S19J_PRO = 15
ANTMINER_S19A = 16
ANTMINER_S19A_PRO = 17
ANTMINER_S19XP = 18
ANTMINER_T19 = 19
ANTMINER_S19J_PRO_PLUS = 20
class MinerStatus(betterproto.Enum):
MINER_STATUS_UNSPECIFIED = 0
MINER_STATUS_NOT_STARTED = 1
MINER_STATUS_NORMAL = 2
MINER_STATUS_PAUSED = 3
MINER_STATUS_SUSPENDED = 4
MINER_STATUS_RESTRICTED = 5
UNSPECIFIED = 0
NOT_STARTED = 1
NORMAL = 2
PAUSED = 3
SUSPENDED = 4
RESTRICTED = 5
class SupportArchiveFormat(betterproto.Enum):
"""Enumeration for support archive format"""
SUPPORT_ARCHIVE_FORMAT_UNSPECIFIED = 0
SUPPORT_ARCHIVE_FORMAT_ZIP = 1
UNSPECIFIED = 0
ZIP = 1
"""Compressed zip format"""
SUPPORT_ARCHIVE_FORMAT_BOS = 2
BOS = 2
"""BOS custom format"""
ZIP_ENCRYPTED = 3
"""Compressed encrypted zip format"""
class NetworkProtocol(betterproto.Enum):
NETWORK_PROTOCOL_UNSPECIFIED = 0
NETWORK_PROTOCOL_DHCP = 1
NETWORK_PROTOCOL_STATIC = 2
UNSPECIFIED = 0
DHCP = 1
STATIC = 2
@dataclass(eq=False, repr=False)
@@ -268,8 +279,8 @@ class LoginResponse(betterproto.Message):
timeout_s: int = betterproto.uint32_field(2)
"""
Authentication token validity/timeout in seconds. Token validity refreshed
to this value with each request.
Authentication token validity/timeout in seconds.
Token validity refreshed to this value with each request.
"""
@@ -277,9 +288,7 @@ class LoginResponse(betterproto.Message):
class SetPasswordRequest(betterproto.Message):
"""Request for set password action."""
password: Optional[str] = betterproto.string_field(
1, optional=True, group="_password"
)
password: Optional[str] = betterproto.string_field(1, optional=True)
@dataclass(eq=False, repr=False)
@@ -335,8 +344,8 @@ class BasesPoints(betterproto.Message):
bsp: int = betterproto.uint32_field(1)
"""
A basis point is one hundredth of 1 percentage point. For example: 1bps =
0.01%, 250bps = 2.5%
A basis point is one hundredth of 1 percentage point.
For example: 1bps = 0.01%, 250bps = 2.5%
"""
@@ -409,9 +418,9 @@ class VoltageConstraints(betterproto.Message):
@dataclass(eq=False, repr=False)
class CoolingAutoMode(betterproto.Message):
"""
The temperature control modes. Miner software tries to regulate the fan
speed so that miner temperature is approximately at the target temperature.
The allowed temperature range is 0-200 degree Celsius.
The temperature control modes.
Miner software tries to regulate the fan speed so that miner temperature is approximately at the target temperature.
The allowed temperature range is 0-200 degree Celsius.
"""
target_temperature: "Temperature" = betterproto.message_field(1)
@@ -422,8 +431,7 @@ class CoolingAutoMode(betterproto.Message):
dangerous_temperature: "Temperature" = betterproto.message_field(3)
"""
Temperature threshold at which BOSMiner shuts down in order to prevent
overheating and damaging the miner.
Temperature threshold at which BOSMiner shuts down in order to prevent overheating and damaging the miner.
"""
@@ -433,12 +441,11 @@ class CoolingManualMode(betterproto.Message):
Fans are kept at a fixed, user-defined speed, no matter the temperature.
"""
fan_speed_ratio: Optional[float] = betterproto.double_field(
1, optional=True, group="_fan_speed_ratio"
)
fan_speed_ratio: Optional[float] = betterproto.double_field(1, optional=True)
"""
User defined fan speed expressed as a ratio between 0.0 and 1.0 where 0.0
means completely turned off and 1.0 means running at full speed possible
User defined fan speed expressed as a ratio between 0.0 and 1.0
where 0.0 means completely turned off and
1.0 means running at full speed possible
"""
hot_temperature: "Temperature" = betterproto.message_field(2)
@@ -446,8 +453,7 @@ class CoolingManualMode(betterproto.Message):
dangerous_temperature: "Temperature" = betterproto.message_field(3)
"""
Temperature threshold at which BOSMiner shuts down in order to prevent
overheating and damaging the miner.
Temperature threshold at which BOSMiner shuts down in order to prevent overheating and damaging the miner.
"""
@@ -455,20 +461,17 @@ class CoolingManualMode(betterproto.Message):
class CoolingDisabledMode(betterproto.Message):
"""Disable temperature control. May be dangerous."""
fan_speed_ratio: Optional[float] = betterproto.double_field(
1, optional=True, group="_fan_speed_ratio"
)
fan_speed_ratio: Optional[float] = betterproto.double_field(1, optional=True)
"""
User defined fan speed expressed as a ratio between 0.0 and 1.0 where 0.0
means completely turned off and 1.0 means running at full speed possible
User defined fan speed expressed as a ratio between 0.0 and 1.0
where 0.0 means completely turned off and
1.0 means running at full speed possible
"""
@dataclass(eq=False, repr=False)
class CoolingConfiguration(betterproto.Message):
minimum_required_fans: Optional[int] = betterproto.uint32_field(
1, optional=True, group="_minimum_required_fans"
)
minimum_required_fans: Optional[int] = betterproto.uint32_field(1, optional=True)
auto: "CoolingAutoMode" = betterproto.message_field(2, group="mode")
manual: "CoolingManualMode" = betterproto.message_field(3, group="mode")
disabled: "CoolingDisabledMode" = betterproto.message_field(4, group="mode")
@@ -488,23 +491,19 @@ class CoolingConstraints(betterproto.Message):
class FanState(betterproto.Message):
"""Structure which contain info about one specific miner fan."""
position: Optional[int] = betterproto.uint32_field(
1, optional=True, group="_position"
)
position: Optional[int] = betterproto.uint32_field(1, optional=True)
"""Fan positions/ID"""
rpm: int = betterproto.uint32_field(2)
"""Actual fan RPM (Revolutions/Rotation Per Minute)"""
target_speed_ratio: Optional[float] = betterproto.double_field(
3, optional=True, group="_target_speed_ratio"
)
target_speed_ratio: Optional[float] = betterproto.double_field(3, optional=True)
"""Actual fan speed ratio(PWM) in range 0.0 - 1.0"""
@dataclass(eq=False, repr=False)
class TemperatureSensor(betterproto.Message):
id: Optional[int] = betterproto.uint32_field(1, optional=True, group="_id")
id: Optional[int] = betterproto.uint32_field(1, optional=True)
"""Sensor id"""
location: "SensorLocation" = betterproto.enum_field(2)
@@ -523,7 +522,10 @@ class GetCoolingStateRequest(betterproto.Message):
@dataclass(eq=False, repr=False)
class GetCoolingStateResponse(betterproto.Message):
"""Response to get current fan states and temperature measurements"""
"""
Response to get current fan states and
temperature measurements
"""
fans: List["FanState"] = betterproto.message_field(1)
"""All Fans state"""
@@ -551,12 +553,10 @@ class SetImmersionModeResponse(betterproto.Message):
@dataclass(eq=False, repr=False)
class TunerConfiguration(betterproto.Message):
enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(1, optional=True)
"""Flag if tuner is enabled"""
tuner_mode: Optional["TunerMode"] = betterproto.enum_field(
2, optional=True, group="_tuner_mode"
)
tuner_mode: Optional["TunerMode"] = betterproto.enum_field(2, optional=True)
"""Tuner mode"""
power_target: "Power" = betterproto.message_field(3)
@@ -583,7 +583,7 @@ class TunerConstraints(betterproto.Message):
@dataclass(eq=False, repr=False)
class DpsConfiguration(betterproto.Message):
enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(1, optional=True)
"""Flag if Dynamic Performance Scaling is enabled"""
power_step: "Power" = betterproto.message_field(2)
@@ -598,9 +598,7 @@ class DpsConfiguration(betterproto.Message):
min_hashrate_target: "TeraHashrate" = betterproto.message_field(5)
"""Dynamic Performance Scaling minimal hashrate target"""
shutdown_enabled: Optional[bool] = betterproto.bool_field(
6, optional=True, group="_shutdown_enabled"
)
shutdown_enabled: Optional[bool] = betterproto.bool_field(6, optional=True)
"""Flag if shutdown for Dynamic Performance Scaling is enabled"""
shutdown_duration: "Hours" = betterproto.message_field(7)
@@ -879,17 +877,13 @@ class SetDpsRequest(betterproto.Message):
save_action: "SaveAction" = betterproto.enum_field(1)
"""Save action"""
enable: Optional[bool] = betterproto.bool_field(2, optional=True, group="_enable")
enable: Optional[bool] = betterproto.bool_field(2, optional=True)
"""Flag if Dynamic Performance Scaling should be enabled"""
enable_shutdown: Optional[bool] = betterproto.bool_field(
3, optional=True, group="_enable_shutdown"
)
enable_shutdown: Optional[bool] = betterproto.bool_field(3, optional=True)
"""Flag if shutdown for Dynamic Performance Scaling should be enabled"""
shutdown_duration: Optional["Hours"] = betterproto.message_field(
4, optional=True, group="_shutdown_duration"
)
shutdown_duration: Optional["Hours"] = betterproto.message_field(4, optional=True)
"""Dynamic Performance Scaling shutdown duration"""
target: "DpsTarget" = betterproto.message_field(5)
@@ -898,17 +892,13 @@ class SetDpsRequest(betterproto.Message):
@dataclass(eq=False, repr=False)
class SetDpsResponse(betterproto.Message):
enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(1, optional=True)
"""Flag if Dynamic Performance Scaling is enabled"""
shutdown_enabled: Optional[bool] = betterproto.bool_field(
2, optional=True, group="_shutdown_enabled"
)
shutdown_enabled: Optional[bool] = betterproto.bool_field(2, optional=True)
"""Flag if shutdown for Dynamic Performance Scaling should be enabled"""
shutdown_duration: Optional["Hours"] = betterproto.message_field(
3, optional=True, group="_shutdown_duration"
)
shutdown_duration: Optional["Hours"] = betterproto.message_field(3, optional=True)
"""Dynamic Performance Scaling shutdown duration"""
power_target: "DpsPowerTarget" = betterproto.message_field(4)
@@ -935,7 +925,7 @@ class HashboardConfig(betterproto.Message):
id: str = betterproto.string_field(1)
"""Hashboard id"""
enabled: Optional[bool] = betterproto.bool_field(2, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(2, optional=True)
"""Flag if HB si enabled"""
frequency: "Frequency" = betterproto.message_field(3)
@@ -1019,9 +1009,9 @@ class Quota(betterproto.Message):
@dataclass(eq=False, repr=False)
class FixedShareRatio(betterproto.Message):
"""
Structure for fixed share ratio load balance strategy Fixed share ratio is
value between 0.0 to 1.0 where 1.0 represents that all work is generated
from the group
Structure for fixed share ratio load balance strategy
Fixed share ratio is value between 0.0 to 1.0 where 1.0 represents that all work is
generated from the group
"""
value: float = betterproto.double_field(1)
@@ -1058,12 +1048,10 @@ class PoolConfiguration(betterproto.Message):
user: str = betterproto.string_field(3)
"""Pool connection user"""
password: Optional[str] = betterproto.string_field(
4, optional=True, group="_password"
)
password: Optional[str] = betterproto.string_field(4, optional=True)
"""Pool connection password if set"""
enabled: Optional[bool] = betterproto.bool_field(5, optional=True, group="_enabled")
enabled: Optional[bool] = betterproto.bool_field(5, optional=True)
"""Flag if pool connection is enabled"""
@@ -1130,9 +1118,7 @@ class PoolStats(betterproto.Message):
generated_work: int = betterproto.uint64_field(6)
"""Generated work"""
last_share_time: Optional[datetime] = betterproto.message_field(
7, optional=True, group="_last_share_time"
)
last_share_time: Optional[datetime] = betterproto.message_field(7, optional=True)
"""Last share time"""
@@ -1154,9 +1140,9 @@ class GetPoolGroupsResponse(betterproto.Message):
@dataclass(eq=False, repr=False)
class CreatePoolGroupRequest(betterproto.Message):
"""
Request for pool group create action group.uid must not be specified (it
will be generated) group.pools[].uid must not be specified (it will be
generated)
Request for pool group create action
group.uid must not be specified (it will be generated)
group.pools[].uid must not be specified (it will be generated)
"""
save_action: "SaveAction" = betterproto.enum_field(1)
@@ -1177,9 +1163,9 @@ class CreatePoolGroupResponse(betterproto.Message):
@dataclass(eq=False, repr=False)
class UpdatePoolGroupRequest(betterproto.Message):
"""
Request for pool group update action group.uid must be specified and
represents unique id of group which will be updated group.pools[].uid must
not be specified (it will be generated)
Request for pool group update action
group.uid must be specified and represents unique id of group which will be updated
group.pools[].uid must not be specified (it will be generated)
"""
save_action: "SaveAction" = betterproto.enum_field(1)
@@ -1221,16 +1207,15 @@ class SetPoolGroupsRequest(betterproto.Message):
save_action: "SaveAction" = betterproto.enum_field(1)
"""
Save action SAVE just update config but changes will not be applied
SAVE_AND_APPLY and SAVE_AND_FORCE_APPLY are equal for this method. Pools
config will be updated and changes will be applied that will trigger
restart.
Save action
SAVE just update config but changes will not be applied
SAVE_AND_APPLY and SAVE_AND_FORCE_APPLY are equal for this method. Pools config will be updated and changes will be applied that will trigger restart.
"""
pool_groups: List["PoolGroupConfiguration"] = betterproto.message_field(2)
"""
Pool groups configuration `uid` must not be specified (it will be
generated)
Pool groups configuration
`uid` must not be specified (it will be generated)
"""
@@ -1292,9 +1277,8 @@ class NoneLicense(betterproto.Message):
time_to_restricted: int = betterproto.uint32_field(1)
"""
BOS Initialization timeout - number of seconds elapsed since bosminer start
i.e., number of seconds BOS will start mining in restricted mode burning 5%
of hashrate For more, see Section 3.10 of
https://braiins.com/os/plus/license
i.e., number of seconds BOS will start mining in restricted mode burning 5% of hashrate
For more, see Section 3.10 of https://braiins.com/os/plus/license
"""
@@ -1313,9 +1297,8 @@ class ValidLicense(betterproto.Message):
time_to_restricted: int = betterproto.uint32_field(3)
"""
Current license expiration - number of seconds since the moment the license
was received i.e., number of seconds BOS will start mining in restricted
mode burning 15% of hashrate
Current license expiration - number of seconds since the moment the license was received
i.e., number of seconds BOS will start mining in restricted mode burning 15% of hashrate
"""
dev_fee: "BasesPoints" = betterproto.message_field(4)
@@ -1388,7 +1371,8 @@ class MinerIdentity(betterproto.Message):
brand: "MinerBrand" = betterproto.enum_field(1)
model: "MinerModel" = betterproto.enum_field(2)
"""
Deprecated: Use miner_model instead. This field is no longer supported.
Deprecated: Use miner_model instead.
This field is no longer supported.
"""
name: str = betterproto.string_field(3)
@@ -1564,7 +1548,7 @@ class Hashboard(betterproto.Message):
stats: "WorkSolverStats" = betterproto.message_field(8)
"""Hashboard stats"""
model: Optional[str] = betterproto.string_field(9, optional=True, group="_model")
model: Optional[str] = betterproto.string_field(9, optional=True)
"""Hashboard model"""
@@ -1644,9 +1628,7 @@ class GetNetworkConfigurationResponse(betterproto.Message):
class SetNetworkConfigurationRequest(betterproto.Message):
dhcp: "Dhcp" = betterproto.message_field(1, group="protocol")
static: "Static" = betterproto.message_field(2, group="protocol")
hostname: Optional[str] = betterproto.string_field(
3, optional=True, group="_hostname"
)
hostname: Optional[str] = betterproto.string_field(3, optional=True)
"""Hostname. Existing value will be preserved if this field is not set."""
@@ -1691,27 +1673,21 @@ class GetNetworkInfoRequest(betterproto.Message):
@dataclass(eq=False, repr=False)
class GetNetworkInfoResponse(betterproto.Message):
"""
Response message for GetCurrentNetworkConfiguration Represents the current
network configuration for the default network interface. Only IPv4 is
supported.
Response message for GetCurrentNetworkConfiguration
Represents the current network configuration for the default network interface.
Only IPv4 is supported.
"""
name: str = betterproto.string_field(1)
"""Name of the network interface"""
mac_address: Optional[str] = betterproto.string_field(
2, optional=True, group="_mac_address"
)
mac_address: Optional[str] = betterproto.string_field(2, optional=True)
"""MAC address of the network interface"""
hostname: Optional[str] = betterproto.string_field(
3, optional=True, group="_hostname"
)
hostname: Optional[str] = betterproto.string_field(3, optional=True)
"""Miner hostname"""
protocol: Optional["NetworkProtocol"] = betterproto.enum_field(
4, optional=True, group="_protocol"
)
protocol: Optional["NetworkProtocol"] = betterproto.enum_field(4, optional=True)
"""Network protocol"""
dns_servers: List[str] = betterproto.string_field(5)
@@ -1720,9 +1696,7 @@ class GetNetworkInfoResponse(betterproto.Message):
networks: List["IpNetwork"] = betterproto.message_field(6)
"""List of assigned IP addresses"""
default_gateway: Optional[str] = betterproto.string_field(
7, optional=True, group="_default_gateway"
)
default_gateway: Optional[str] = betterproto.string_field(7, optional=True)
"""Default gateway/route for the interface"""
@@ -2332,7 +2306,7 @@ class MinerServiceStub(betterproto.ServiceStub):
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> AsyncIterator["GetMinerStatusResponse"]:
) -> AsyncIterator[GetMinerStatusResponse]:
async for response in self._unary_stream(
"/braiins.bos.v1.MinerService/GetMinerStatus",
get_miner_status_request,
@@ -2418,7 +2392,7 @@ class MinerServiceStub(betterproto.ServiceStub):
timeout: Optional[float] = None,
deadline: Optional["Deadline"] = None,
metadata: Optional["MetadataLike"] = None
) -> AsyncIterator["GetSupportArchiveResponse"]:
) -> AsyncIterator[GetSupportArchiveResponse]:
async for response in self._unary_stream(
"/braiins.bos.v1.MinerService/GetSupportArchive",
get_support_archive_request,
@@ -3195,7 +3169,7 @@ class MinerServiceBase(ServiceBase):
async def get_miner_status(
self, get_miner_status_request: "GetMinerStatusRequest"
) -> AsyncIterator["GetMinerStatusResponse"]:
) -> AsyncIterator[GetMinerStatusResponse]:
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
yield GetMinerStatusResponse()
@@ -3221,7 +3195,7 @@ class MinerServiceBase(ServiceBase):
async def get_support_archive(
self, get_support_archive_request: "GetSupportArchiveRequest"
) -> AsyncIterator["GetSupportArchiveResponse"]:
) -> AsyncIterator[GetSupportArchiveResponse]:
raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED)
yield GetSupportArchiveResponse()

View File

@@ -127,6 +127,9 @@ class InnosiliconWebAPI(BaseWebAPI):
async def get_all(self) -> dict:
return await self.send_command("getAll")
async def summary(self) -> dict:
return await self.send_command("summary")
async def get_error_detail(self) -> dict:
return await self.send_command("getErrorDetail")

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.61.1"
version = "0.61.8"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"
@@ -15,7 +15,7 @@ passlib = ">=1.7.4"
pyaml = ">=23.12.0"
tomli = { version = ">=2.0.1", python = "<3.11" }
tomli-w = "^1.0.0"
betterproto = "2.0.0b6"
betterproto = "2.0.0b7"
aiofiles = ">=23.2.1"
[tool.poetry.group.dev]