Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ea4f4e124 | ||
|
|
a8a0e4a5fe | ||
|
|
5f2f6e01da | ||
|
|
41b4c23d45 | ||
|
|
b4687f18fd | ||
|
|
2437421005 | ||
|
|
40ebc2773f | ||
|
|
b8ae238d23 | ||
|
|
2920639b70 | ||
|
|
bd9144b3de | ||
|
|
8f7a67d4dc |
@@ -23,7 +23,7 @@ Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with
|
||||
## Installation
|
||||
|
||||
It is recommended to install `pyasic` in a [virtual environment](https://realpython.com/python-virtual-environments-a-primer/#what-other-popular-options-exist-aside-from-venv) to isolate it from the rest of your system. Options include:
|
||||
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default
|
||||
- [pypoetry](https://python-poetry.org/): the reccommended way, since pyasic already uses it by default. Use version 2.0+
|
||||
|
||||
```
|
||||
poetry install
|
||||
|
||||
@@ -92,6 +92,19 @@
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21 Hydro (BOS+)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [ ] Presets
|
||||
|
||||
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Hydro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21 Pro (BOS+)
|
||||
|
||||
- [x] Shutdowns
|
||||
@@ -105,6 +118,32 @@
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21+ (BOS+)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [ ] Presets
|
||||
|
||||
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21Plus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21+ Hydro (BOS+)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [ ] Presets
|
||||
|
||||
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21PlusHydro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## T21 (BOS+)
|
||||
|
||||
- [x] Shutdowns
|
||||
@@ -131,6 +170,58 @@
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21 Hydro (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [x] Presets
|
||||
|
||||
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Hydro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21 Pro (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [x] Presets
|
||||
|
||||
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Pro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21+ (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [x] Presets
|
||||
|
||||
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21Plus
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S21+ Hydro (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [x] Presets
|
||||
|
||||
::: pyasic.miners.antminer.vnish.X21.S21.VNishS21PlusHydro
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## T21 (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
|
||||
@@ -663,6 +663,9 @@ details {
|
||||
<ul>
|
||||
<li><a href="../antminer/X21#s21-bos_1">S21 (BOS+)</a></li>
|
||||
<li><a href="../antminer/X21#s21-pro-bos_1">S21 Pro (BOS+)</a></li>
|
||||
<li><a href="../antminer/X21#s21_1-bos_1">S21+ (BOS+)</a></li>
|
||||
<li><a href="../antminer/X21#s21_1-hydro-bos_1">S21+ Hydro (BOS+)</a></li>
|
||||
<li><a href="../antminer/X21#s21-hydro-bos_1">S21 Hydro (BOS+)</a></li>
|
||||
<li><a href="../antminer/X21#t21-bos_1">T21 (BOS+)</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
@@ -721,6 +724,10 @@ details {
|
||||
<ul>
|
||||
<li><a href="../antminer/X21#t21-vnish">T21 (VNish)</a></li>
|
||||
<li><a href="../antminer/X21#s21-vnish">S21 (VNish)</a></li>
|
||||
<li><a href="../antminer/X21#s21_1-vnish">S21+ (VNish)</a></li>
|
||||
<li><a href="../antminer/X21#s21_1-hydro-vnish">S21+ Hydro (VNish)</a></li>
|
||||
<li><a href="../antminer/X21#s21-pro-vnish">S21 Pro (VNish)</a></li>
|
||||
<li><a href="../antminer/X21#s21-hydro-vnish">S21 Hydro (VNish)</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
|
||||
@@ -283,13 +283,17 @@ class MinerConfig(BaseModel):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_vnish(cls, web_settings: dict, web_presets: list[dict]) -> "MinerConfig":
|
||||
def from_vnish(
|
||||
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
|
||||
) -> "MinerConfig":
|
||||
"""Constructs a MinerConfig object from web settings for VNish miners."""
|
||||
return cls(
|
||||
pools=PoolConfig.from_vnish(web_settings),
|
||||
fan_mode=FanModeConfig.from_vnish(web_settings),
|
||||
temperature=TemperatureConfig.from_vnish(web_settings),
|
||||
mining_mode=MiningModeConfig.from_vnish(web_settings, web_presets),
|
||||
mining_mode=MiningModeConfig.from_vnish(
|
||||
web_settings, web_presets, web_perf_summary
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -412,12 +412,18 @@ class MiningModePreset(MinerConfigValue):
|
||||
|
||||
@classmethod
|
||||
def from_vnish(
|
||||
cls, web_overclock_settings: dict, web_presets: list[dict]
|
||||
cls,
|
||||
web_overclock_settings: dict,
|
||||
web_presets: list[dict],
|
||||
web_perf_summary: dict,
|
||||
) -> "MiningModePreset":
|
||||
active_preset = None
|
||||
for preset in web_presets:
|
||||
if preset["name"] == web_overclock_settings["preset"]:
|
||||
active_preset = preset
|
||||
active_preset = web_perf_summary.get("current_preset")
|
||||
|
||||
if active_preset is None:
|
||||
for preset in web_presets:
|
||||
if preset["name"] == web_overclock_settings["preset"]:
|
||||
active_preset = preset
|
||||
|
||||
return cls(
|
||||
active_preset=MiningPreset.from_vnish(active_preset),
|
||||
available_presets=[MiningPreset.from_vnish(p) for p in web_presets],
|
||||
@@ -703,7 +709,9 @@ class MiningModeConfig(MinerConfigOption):
|
||||
return cls.default()
|
||||
|
||||
@classmethod
|
||||
def from_vnish(cls, web_settings: dict, web_presets: list[dict]):
|
||||
def from_vnish(
|
||||
cls, web_settings: dict, web_presets: list[dict], web_perf_summary: dict
|
||||
):
|
||||
try:
|
||||
mode_settings = web_settings["miner"]["overclock"]
|
||||
except KeyError:
|
||||
@@ -712,7 +720,9 @@ class MiningModeConfig(MinerConfigOption):
|
||||
if mode_settings["preset"] == "disabled":
|
||||
return MiningModeManual.from_vnish(mode_settings)
|
||||
else:
|
||||
return MiningModePreset.from_vnish(mode_settings, web_presets)
|
||||
return MiningModePreset.from_vnish(
|
||||
mode_settings, web_presets, web_perf_summary
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_boser(cls, grpc_miner_conf: dict):
|
||||
|
||||
@@ -70,6 +70,7 @@ class MinerData(BaseModel):
|
||||
errors: A list of errors on the miner.
|
||||
fault_light: Whether the fault light is on as a boolean.
|
||||
efficiency: Efficiency of the miner in J/TH (Watts per TH/s). Calculated automatically.
|
||||
efficiency_fract: Same as efficiency, but is not rounded to integer. Calculated automatically.
|
||||
is_mining: Whether the miner is mining.
|
||||
pools: A list of PoolMetrics instances, each representing metrics for a pool.
|
||||
"""
|
||||
@@ -293,7 +294,7 @@ class MinerData(BaseModel):
|
||||
@computed_field # type: ignore[misc]
|
||||
@property
|
||||
def efficiency_fract(self) -> float | None:
|
||||
self._efficiency(2)
|
||||
return self._efficiency(2)
|
||||
|
||||
def _efficiency(self, ndigits: int) -> float | None:
|
||||
if self.hashrate is None or self.wattage is None:
|
||||
@@ -301,7 +302,7 @@ class MinerData(BaseModel):
|
||||
try:
|
||||
return round(self.wattage / float(self.hashrate), ndigits)
|
||||
except ZeroDivisionError:
|
||||
return 0
|
||||
return 0.0
|
||||
|
||||
@computed_field # type: ignore[misc]
|
||||
@property
|
||||
|
||||
@@ -63,6 +63,7 @@ class AntminerModels(MinerModelType):
|
||||
S21Pro = "S21 Pro"
|
||||
S21Hydro = "S21 Hydro"
|
||||
T21 = "T21"
|
||||
S19XPHydro = "S19 XP Hydro"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,5 +29,6 @@ from .S19 import (
|
||||
BOSMinerS19Pro,
|
||||
BOSMinerS19ProPlusHydro,
|
||||
BOSMinerS19XP,
|
||||
BOSMinerS19XPHydro,
|
||||
)
|
||||
from .T19 import BOSMinerT19
|
||||
|
||||
@@ -15,12 +15,40 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from pyasic.miners.backends import BOSer
|
||||
from pyasic.miners.device.models import S21, S21Pro
|
||||
from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class BOSMinerS21(BOSer, S21):
|
||||
pass
|
||||
|
||||
|
||||
class BOSMinerS21Plus(BOSer, S21Plus):
|
||||
pass
|
||||
|
||||
|
||||
class BOSMinerS21PlusHydro(BOSer, S21PlusHydro):
|
||||
pass
|
||||
|
||||
|
||||
class BOSMinerS21Pro(BOSer, S21Pro):
|
||||
pass
|
||||
|
||||
|
||||
class BOSMinerS21Hydro(BOSer, S21Hydro):
|
||||
pass
|
||||
|
||||
@@ -14,5 +14,11 @@
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .S21 import BOSMinerS21, BOSMinerS21Pro
|
||||
from .S21 import (
|
||||
BOSMinerS21,
|
||||
BOSMinerS21Hydro,
|
||||
BOSMinerS21Plus,
|
||||
BOSMinerS21PlusHydro,
|
||||
BOSMinerS21Pro,
|
||||
)
|
||||
from .T21 import BOSMinerT21
|
||||
|
||||
@@ -15,8 +15,24 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from pyasic.miners.backends import VNish
|
||||
from pyasic.miners.device.models import S21
|
||||
from pyasic.miners.device.models import S21, S21Hydro, S21Plus, S21PlusHydro, S21Pro
|
||||
|
||||
|
||||
class VNishS21(VNish, S21):
|
||||
pass
|
||||
|
||||
|
||||
class VNishS21Plus(VNish, S21Plus):
|
||||
pass
|
||||
|
||||
|
||||
class VNishS21PlusHydro(VNish, S21PlusHydro):
|
||||
pass
|
||||
|
||||
|
||||
class VNishS21Pro(VNish, S21Pro):
|
||||
pass
|
||||
|
||||
|
||||
class VNishS21Hydro(VNish, S21Hydro):
|
||||
pass
|
||||
|
||||
@@ -14,5 +14,11 @@
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .S21 import VNishS21
|
||||
from .S21 import (
|
||||
VNishS21,
|
||||
VNishS21Hydro,
|
||||
VNishS21Plus,
|
||||
VNishS21PlusHydro,
|
||||
VNishS21Pro,
|
||||
)
|
||||
from .T21 import VNishT21
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -293,9 +293,12 @@ class VNish(VNishFirmware, BMMiner):
|
||||
try:
|
||||
web_settings = await self.web.settings()
|
||||
web_presets = await self.web.autotune_presets()
|
||||
web_perf_summary = (await self.web.perf_summary()) or {}
|
||||
except APIError:
|
||||
return self.config
|
||||
self.config = MinerConfig.from_vnish(web_settings, web_presets)
|
||||
self.config = MinerConfig.from_vnish(
|
||||
web_settings, web_presets, web_perf_summary
|
||||
)
|
||||
return self.config
|
||||
|
||||
async def set_power_limit(self, wattage: int) -> bool:
|
||||
|
||||
@@ -223,3 +223,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
|
||||
|
||||
@@ -38,5 +38,6 @@ from .S19 import (
|
||||
S19ProHydro,
|
||||
S19ProPlus,
|
||||
S19ProPlusHydro,
|
||||
S19XPHydro,
|
||||
)
|
||||
from .T19 import T19
|
||||
|
||||
@@ -555,9 +555,13 @@ MINER_CLASSES = {
|
||||
"ANTMINER T19": BOSMinerT19,
|
||||
"ANTMINER S21": BOSMinerS21,
|
||||
"ANTMINER S21 PRO": BOSMinerS21Pro,
|
||||
"ANTMINER S21+": BOSMinerS21Plus,
|
||||
"ANTMINER S21+ HYD.": BOSMinerS21PlusHydro,
|
||||
"ANTMINER S21 HYD.": BOSMinerS21Hydro,
|
||||
"ANTMINER T21": BOSMinerT21,
|
||||
"BRAIINS MINI MINER BMM 100": BraiinsBMM100,
|
||||
"BRAIINS MINI MINER BMM 101": BraiinsBMM101,
|
||||
"ANTMINER S19 XP HYD.": BOSMinerS19XPHydro,
|
||||
},
|
||||
MinerTypes.VNISH: {
|
||||
None: VNish,
|
||||
@@ -581,6 +585,10 @@ MINER_CLASSES = {
|
||||
"ANTMINER T19": VNishT19,
|
||||
"ANTMINER T21": VNishT21,
|
||||
"ANTMINER S21": VNishS21,
|
||||
"ANTMINER S21+": VNishS21Plus,
|
||||
"ANTMINER S21+ HYD.": VNishS21PlusHydro,
|
||||
"ANTMINER S21 PRO": VNishS21Pro,
|
||||
"ANTMINER S21 HYD.": VNishS21Hydro,
|
||||
},
|
||||
MinerTypes.EPIC: {
|
||||
None: ePIC,
|
||||
|
||||
@@ -20,34 +20,36 @@ class ESPMinerWebAPI(BaseWebAPI):
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
url = f"http://{self.ip}:{self.port}/api/{command}"
|
||||
try:
|
||||
async with httpx.AsyncClient(
|
||||
transport=settings.transport(),
|
||||
) as client:
|
||||
if parameters.get("post", False):
|
||||
parameters.pop("post")
|
||||
data = await client.post(
|
||||
url,
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
json=parameters,
|
||||
)
|
||||
elif parameters.get("patch", False):
|
||||
parameters.pop("patch")
|
||||
data = await client.patch(
|
||||
url,
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
data = await client.get(url)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if data.status_code == 200:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
for _ in range(settings.get("get_data_retries", 1)):
|
||||
try:
|
||||
return data.json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
if parameters.get("post", False):
|
||||
parameters.pop("post")
|
||||
data = await client.post(
|
||||
url,
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
json=parameters,
|
||||
)
|
||||
elif parameters.get("patch", False):
|
||||
parameters.pop("patch")
|
||||
data = await client.patch(
|
||||
url,
|
||||
timeout=settings.get("api_function_timeout", 3),
|
||||
json=parameters,
|
||||
)
|
||||
else:
|
||||
data = await client.get(
|
||||
url,
|
||||
timeout=settings.get("api_function_timeout", 5),
|
||||
)
|
||||
except httpx.HTTPError:
|
||||
pass
|
||||
else:
|
||||
if data.status_code == 200:
|
||||
try:
|
||||
return data.json()
|
||||
except json.decoder.JSONDecodeError:
|
||||
pass
|
||||
|
||||
async def multicommand(
|
||||
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||
@@ -94,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")
|
||||
|
||||
@@ -58,7 +58,7 @@ class VNishWebAPI(BaseWebAPI):
|
||||
allow_warning: bool = True,
|
||||
privileged: bool = False,
|
||||
**parameters: Any,
|
||||
) -> dict:
|
||||
) -> dict | None:
|
||||
post = privileged or not parameters == {}
|
||||
if self.token is None:
|
||||
await self.auth()
|
||||
@@ -126,6 +126,9 @@ class VNishWebAPI(BaseWebAPI):
|
||||
async def summary(self) -> dict:
|
||||
return await self.send_command("summary")
|
||||
|
||||
async def perf_summary(self) -> dict:
|
||||
return await self.send_command("perf-summary")
|
||||
|
||||
async def chips(self) -> dict:
|
||||
return await self.send_command("chips")
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "pyasic"
|
||||
version = "0.72.8"
|
||||
version = "0.72.11"
|
||||
|
||||
description = "A simplified and standardized interface for Bitcoin ASICs."
|
||||
authors = [{name = "UpstreamData", email = "brett@upstreamdata.ca"}]
|
||||
|
||||
Reference in New Issue
Block a user