Compare commits

..

19 Commits

Author SHA1 Message Date
Upstream Data
2815d2ba11 version: bump version number. 2024-04-08 12:15:38 -06:00
Upstream Data
1ff20fc6f0 feature: add bosminer S21 support. 2024-04-08 12:15:18 -06:00
Brett Rowan
797c847055 version: bump version number. 2024-04-07 17:17:17 -06:00
Brett Rowan
65c7f2f66f bug: fix vnish shutting-down handling. 2024-04-07 17:16:37 -06:00
Brett Rowan
445d621590 version: bump version number. 2024-04-07 17:04:10 -06:00
Brett Rowan
d39ecfd6b4 feature: add is_mining for vnish. 2024-04-07 17:03:28 -06:00
Brett Rowan
36663471fb version: bump version number. 2024-04-06 12:34:43 -06:00
Brett Rowan
80293ac52f bug: fix incorrect model for XP. 2024-04-06 12:34:14 -06:00
UpstreamData
70b45f40f5 docs: update dev setup. 2024-03-29 11:19:17 -06:00
UpstreamData
a511fabd9c Update README.md with dev docs. 2024-03-29 11:17:39 -06:00
Jim Burtoft
8bc8f6f178 Comment on some code I didn't understand initially (#118) 2024-03-28 21:31:37 -06:00
Brett Rowan
b790ad58a7 version: bump version number. 2024-03-23 20:27:42 -06:00
Brett Rowan
354ab793a2 bug: fix vnish MAC not working on some versions. 2024-03-23 20:25:26 -06:00
Upstream Data
59346d641f version: bump version number. 2024-03-22 15:12:35 -06:00
Upstream Data
11d770771b feature: fix vnish not having the shutdown flag. 2024-03-22 15:12:20 -06:00
Upstream Data
1b6db7ed45 feature: add antminer new API. 2024-03-22 13:42:10 -06:00
Upstream Data
55c4e10fae tests: Update tests, and fix some bugs. 2024-03-22 13:19:44 -06:00
Upstream Data
77c06dad61 version: bump version number. 2024-03-19 14:24:54 -06:00
Upstream Data
68d250d2f2 bug: fix K pro naming. 2024-03-19 14:24:31 -06:00
20 changed files with 197 additions and 30 deletions

View File

@@ -1,16 +1,16 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/psf/black
rev: 22.6.0
rev: 24.3.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.10.1
rev: 5.13.2
hooks:
- id: isort
name: isort (python)

View File

@@ -23,6 +23,10 @@ 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
```
poetry install
```
- [venv](https://docs.python.org/3/library/venv.html): included in Python standard library but has fewer features than other options
- [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv): [pyenv](https://github.com/pyenv/pyenv) plugin for managing virtualenvs
```
@@ -36,6 +40,12 @@ It is recommended to install `pyasic` in a [virtual environment](https://realpyt
`python -m pip install .` or `poetry install`
##### Additional Developer Setup
```
poetry install --with dev
pre-commit install
```
---
## Getting started

View File

@@ -106,7 +106,7 @@
show_root_heading: false
heading_level: 4
## S19 Pro+ Hydro
## S19K Pro
::: pyasic.miners.antminer.bmminer.X19.S19.BMMinerS19KPro
handler: python
options:

View File

@@ -8,6 +8,13 @@
show_root_heading: false
heading_level: 4
## S21
::: pyasic.miners.antminer.bosminer.X21.S21.BOSMinerS21
handler: python
options:
show_root_heading: false
heading_level: 4
## S21 (ePIC)
::: pyasic.miners.antminer.epic.X21.S21.ePICS21
handler: python

View File

@@ -81,7 +81,7 @@ details {
<li><a href="../antminer/X19#s19-hydro">S19 Hydro</a></li>
<li><a href="../antminer/X19#s19-pro-hydro">S19 Pro Hydro</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro">S19 Pro+ Hydro</a></li>
<li><a href="../antminer/X19#s19-pro_1-hydro">S19 Pro+ Hydro</a></li>
<li><a href="../antminer/X19#s19k-pro">S19K Pro</a></li>
<li><a href="../antminer/X19#t19">T19</a></li>
</ul>
</details>
@@ -460,6 +460,12 @@ details {
<li><a href="../antminer/X19#t19">T19</a></li>
</ul>
</details>
<details>
<summary>X21 Series:</summary>
<ul>
<li><a href="../antminer/X21#s21">S21</a></li>
</ul>
</details>
</ul>
</details>
<details>

View File

@@ -96,7 +96,7 @@ class FanModeManual(MinerConfigValue):
return cls(**cls_conf)
def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)}
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
def as_bosminer(self) -> dict:
return {
@@ -120,7 +120,7 @@ class FanModeImmersion(MinerConfigValue):
return cls()
def as_am_modern(self) -> dict:
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"}
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
def as_bosminer(self) -> dict:
return {"temp_control": {"mode": "disabled"}}
@@ -156,7 +156,10 @@ class FanModeConfig(MinerConfigOption):
if web_conf.get("bitmain-fan-ctrl") is not None:
fan_manual = web_conf["bitmain-fan-ctrl"]
if fan_manual:
return cls.manual(speed=web_conf["bitmain-fan-pwm"])
speed = int(web_conf["bitmain-fan-pwm"])
if speed == 0:
return cls.immersion()
return cls.manual(speed=speed)
else:
return cls.normal()
else:

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 BOSer
from pyasic.miners.models import S21
class BOSMinerS21(BOSer, S21):
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 .S21 import BOSMinerS21

View File

@@ -17,3 +17,4 @@
from .X9 import *
from .X17 import *
from .X19 import *
from .X21 import *

View File

@@ -22,6 +22,7 @@ from pyasic.data.error_codes import MinerErrorData, X19Error
from pyasic.errors import APIError
from pyasic.miners.backends.bmminer import BMMiner
from pyasic.miners.backends.cgminer import CGMiner
from pyasic.miners.base import BaseMiner
from pyasic.miners.data import (
DataFunction,
DataLocations,
@@ -29,6 +30,7 @@ from pyasic.miners.data import (
RPCAPICommand,
WebAPICommand,
)
from pyasic.rpc.antminer import AntminerRPCAPI
from pyasic.ssh.antminer import AntminerModernSSH
from pyasic.web.antminer import AntminerModernWebAPI, AntminerOldWebAPI
@@ -88,6 +90,9 @@ class AntminerModern(BMMiner):
_web_cls = AntminerModernWebAPI
web: AntminerModernWebAPI
_rpc_cls = AntminerRPCAPI
web: AntminerRPCAPI
_ssh_cls = AntminerModernSSH
ssh: AntminerModernSSH
@@ -207,7 +212,7 @@ class AntminerModern(BMMiner):
]
try:
rpc_stats = await self.rpc.send_command("stats", new_api=True)
rpc_stats = await self.rpc.stats(new_api=True)
except APIError:
return hashboards

View File

@@ -74,6 +74,10 @@ VNISH_DATA_LOC = DataLocations(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.IS_MINING): DataFunction(
"_is_mining",
[WebAPICommand("web_summary", "summary")],
),
}
)
@@ -84,6 +88,8 @@ class VNish(BMMiner):
_web_cls = VNishWebAPI
web: VNishWebAPI
supports_shutdown = True
firmware = "VNish"
data_locations = VNISH_DATA_LOC
@@ -125,16 +131,6 @@ class VNish(BMMiner):
return False
async def _get_mac(self, web_summary: dict = None) -> str:
if web_summary is None:
web_info = await self.web.info()
if web_info is not None:
try:
mac = web_info["system"]["network_status"]["mac"]
return mac
except KeyError:
pass
if web_summary is not None:
try:
mac = web_summary["system"]["network_status"]["mac"]
@@ -142,6 +138,15 @@ class VNish(BMMiner):
except KeyError:
pass
web_info = await self.web.info()
if web_info is not None:
try:
mac = web_info["system"]["network_status"]["mac"]
return mac
except KeyError:
pass
async def _get_hostname(self, web_summary: dict = None) -> str:
if web_summary is None:
web_info = await self.web.info()
@@ -214,6 +219,20 @@ class VNish(BMMiner):
except LookupError:
return fw_ver
async def _is_mining(self, web_summary: dict = None) -> Optional[bool]:
if web_summary is None:
web_summary = await self.web.summary()
if web_summary is not None:
try:
is_mining = not web_summary["miner"]["miner_status"]["miner_state"] in [
"stopped",
"shutting-down",
]
return is_mining
except LookupError:
pass
async def get_config(self) -> MinerConfig:
try:
web_settings = await self.web.settings()

View File

@@ -372,8 +372,9 @@ MINER_CLASSES = {
"ANTMINER S19J PRO NOPIC": BOSMinerS19jPro,
"ANTMINER S19J PRO+": BOSMinerS19jProPlus,
"ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC,
"ANTMINER S19XP": BOSMinerS19XP,
"ANTMINER S19 XP": BOSMinerS19XP,
"ANTMINER T19": BOSMinerT19,
"ANTMINER S21": BOSMinerS21,
},
MinerTypes.VNISH: {
None: VNish,
@@ -915,10 +916,11 @@ class MinerFactory:
async def get_miner_model_braiins_os(self, ip: str) -> str | None:
sock_json_data = await self.send_api_command(ip, "devdetails")
try:
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace(
"Bitmain ", ""
miner_model = (
sock_json_data["DEVDETAILS"][0]["Model"]
.replace("Bitmain ", "")
.replace("S19XP", "S19 XP")
)
return miner_model
except (TypeError, LookupError):
pass
@@ -931,7 +933,9 @@ class MinerFactory:
)
if d.status_code == 200:
json_data = d.json()
miner_model = json_data["data"]["bosminer"]["info"]["modelName"]
miner_model = json_data["data"]["bosminer"]["info"][
"modelName"
].replace("S19XP", "S19 XP")
return miner_model
except (httpx.HTTPError, LookupError):
pass

View File

@@ -135,6 +135,6 @@ class S19ProPlusHydro(AntMinerMake):
class S19KPro(AntMinerMake):
raw_model = "S19 Pro+ Hydro"
raw_model = "S19K Pro"
expected_chips = 77
expected_fans = 4

36
pyasic/rpc/antminer.py Normal file
View File

@@ -0,0 +1,36 @@
from pyasic.rpc.bmminer import BMMinerRPCAPI
class AntminerRPCAPI(BMMinerRPCAPI):
async def stats(self, new_api: bool = False) -> dict:
if new_api:
return await self.send_command("stats", new_api=True)
return await super().stats()
async def rate(self):
return await self.send_command("rate", new_api=True)
async def pools(self, new_api: bool = False):
if new_api:
return await self.send_command("pools", new_api=True)
return await self.send_command("pools")
async def reload(self):
return await self.send_command("reload", new_api=True)
async def summary(self, new_api: bool = False):
if new_api:
return await self.send_command("summary", new_api=True)
return await self.send_command("summary")
async def warning(self):
return await self.send_command("warning", new_api=True)
async def new_api_pools(self):
return await self.pools(new_api=True)
async def new_api_stats(self):
return await self.stats(new_api=True)
async def new_api_summary(self):
return await self.summary(new_api=True)

View File

@@ -46,7 +46,9 @@ _settings = { # defaults
ssl_cxt = httpx.create_ssl_context()
#this function configures socket options like SO_LINGER and returns an AsyncHTTPTransport instance to perform asynchronous HTTP requests
#using those options.
#SO_LINGER controls what happens when you close a socket with unsent data - it allows specifying linger time for the data to be sent.
def transport(verify: Union[str, bool, SSLContext] = ssl_cxt):
l_onoff = 1
l_linger = get("so_linger_time", 1000)

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.54.6"
version = "0.54.13"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"

View File

@@ -14,7 +14,7 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from tests.api_tests import *
from tests.rpc_tests import *
from tests.config_tests import TestConfig
from tests.miners_tests import MinersTest
from tests.network_tests import NetworkTest

View File

@@ -128,9 +128,9 @@ class TestConfig(unittest.TestCase):
def test_am_modern_serialize(self):
correct_config = {
"bitmain-fan-ctrl": True,
"bitmain-fan-pwn": "90",
"bitmain-fan-pwm": "90",
"freq-level": "100",
"miner-mode": "0",
"miner-mode": 0,
"pools": [
{
"url": "stratum+tcp://stratum.test.io:3333",

View File

@@ -0,0 +1,35 @@
import unittest
from pyasic.config import FanModeConfig
class TestFanConfig(unittest.TestCase):
def test_serialize_and_deserialize(self):
for fan_mode in FanModeConfig:
with self.subTest(
msg=f"Test serialization and deserialization of fan config",
fan_mode=fan_mode,
):
conf = fan_mode()
dict_conf = conf.as_dict()
self.assertEqual(conf, FanModeConfig.from_dict(dict_conf))
def test_bosminer_deserialize_and_serialize(self):
for fan_mode in FanModeConfig:
with self.subTest(
msg=f"Test serialization and deserialization of bosminer fan config",
fan_mode=fan_mode,
):
conf = fan_mode()
bos_conf = conf.as_bosminer()
self.assertEqual(conf, FanModeConfig.from_bosminer(bos_conf))
def test_am_modern_deserialize_and_serialize(self):
for fan_mode in FanModeConfig:
with self.subTest(
msg=f"Test serialization and deserialization of antminer modern fan config",
fan_mode=fan_mode,
):
conf = fan_mode()
am_conf = conf.as_am_modern()
self.assertEqual(conf, FanModeConfig.from_am_modern(am_conf))