Compare commits

..

20 Commits

Author SHA1 Message Date
UpstreamData
ad374fe2fb version: bump version number. 2023-10-05 10:18:10 -06:00
UpstreamData
5ca39b6fe7 bug: update get_miner to work with latest whatsminer version. 2023-10-05 10:17:45 -06:00
UpstreamData
4bafde9da7 docs: update documentation. 2023-10-03 14:59:25 -06:00
UpstreamData
5a3107aecf version: bump version number. 2023-10-03 11:12:11 -06:00
UpstreamData
7e758720f0 bug: fix some issues with early version of whatsminers, and handle some possible errors with BOS. 2023-10-03 11:11:32 -06:00
UpstreamData
39e3e249f8 version: bump version number. 2023-10-02 13:14:21 -06:00
UpstreamData
118c5b056e refactor: improve settings handling to not use a dataclass, and not use singleton. 2023-10-02 13:13:31 -06:00
UpstreamData
2c3b5599fe version: bump version number. 2023-10-02 09:20:24 -06:00
UpstreamData
e421eaa324 feature: add support for M20P, and add chips for M20SV30. 2023-10-02 09:20:01 -06:00
UpstreamData
53f3fc5ee9 version: bump version number. 2023-09-28 15:47:49 -06:00
UpstreamData
1b36de4131 bug: fAdd new commands added in whatsminer API 2.0.5. 2023-09-28 15:47:20 -06:00
UpstreamData
6f0c6f6284 bug: fix whatsminer identification to work with backwards incompatible changes in API 2.0.5. 2023-09-28 15:42:12 -06:00
UpstreamData
b7dda5bf87 Update README.md 2023-09-26 11:50:56 -06:00
UpstreamData
53a3bbf531 version: bump version number. 2023-09-19 13:59:56 -06:00
UpstreamData
50586f1ce7 feature: add S19+. 2023-09-19 13:59:03 -06:00
UpstreamData
9f6235a0fc feature: add S19i. 2023-09-19 13:56:40 -06:00
UpstreamData
4d21f150ce version: bump version number. 2023-09-18 09:35:38 -06:00
UpstreamData
7c0dfc49dd bug: fix wrong fault light setting when setting fault light to off. 2023-09-18 09:35:19 -06:00
UpstreamData
269b13f6c1 version: bump version number. 2023-09-15 08:57:56 -06:00
Elias Kunnas
a9bb7d2e5a Fix btminer pre_power_on (#62) 2023-09-15 08:56:29 -06:00
31 changed files with 282 additions and 94 deletions

View File

@@ -1,5 +1,5 @@
# pyasic
*A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH.*
*A simplified and standardized interface for Bitcoin ASICs.*
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![pypi](https://img.shields.io/pypi/v/pyasic.svg)](https://pypi.org/project/pyasic/)

View File

@@ -263,7 +263,7 @@ Pyasic implements a few dataclasses as helpers to make data return types consist
[`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns.
You can call [`MinerData.asdict()`][pyasic.data.MinerData.asdict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
You can call [`MinerData.as_dict()`][pyasic.data.MinerData.as_dict] to get the dataclass as a dictionary, and there are many other helper functions contained in the class to convert to different data formats.
[`MinerData`][pyasic.data.MinerData] instances can also be added to each other to combine their data and can be divided by a number to divide all their data, allowing you to get average data from many miners by doing -
```python
@@ -288,3 +288,27 @@ It is the return from [`get_config()`](#get-config).
Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class.
In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you.
## Settings
`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the [`pyasic.settings`][pyasic.settings] module, used as follows:
```python
from pyasic import settings
settings.update("default_antminer_password", "my_pwd")
```
Here are of all the settings, and their default values:
```
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"factory_get_retries": 1,
"get_data_retries": 1,
"default_whatsminer_password": "admin",
"default_innosilicon_password": "admin",
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
"default_goldshell_password": "123456789",
```

View File

@@ -1,6 +1,14 @@
# pyasic
## Miner Factory
[`MinerFactory`][pyasic.miners.miner_factory.MinerFactory] is the way to create miner types in `pyasic`. The most important method is [`get_miner()`][pyasic.get_miner], which is mapped to [`pyasic.get_miner()`][pyasic.get_miner], and should be used from there.
The instance used for [`pyasic.get_miner()`][pyasic.get_miner] is `pyasic.miner_factory`.
[`MinerFactory`][pyasic.MinerFactory] also keeps a cache, which can be cleared if needed with `pyasic.miner_factory.clear_cached_miners()`.
Finally, there is functionality to get multiple miners without using `asyncio.gather()` explicitly. Use `pyasic.miner_factory.get_multiple_miners()` with a list of IPs as strings to get a list of miner instances. You can also get multiple miners with an `AsyncGenerator` by using `pyasic.miner_factory.get_miner_generator()`.
::: pyasic.miners.miner_factory.MinerFactory
handler: python
options:

23
docs/settings/settings.md Normal file
View File

@@ -0,0 +1,23 @@
# pyasic
## settings
::: pyasic.settings
handler: python
options:
show_root_heading: false
heading_level: 4
### get
::: pyasic.settings.get
handler: python
options:
show_root_heading: false
heading_level: 4
### update
::: pyasic.settings.update
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -53,7 +53,8 @@ nav:
- Goldshell X5: "miners/goldshell/X5.md"
- Goldshell XMax: "miners/goldshell/XMax.md"
- Base Miner: "miners/base_miner.md"
- Settings:
- Settings: "settings/settings.md"
plugins:
- mkdocstrings

View File

@@ -22,15 +22,15 @@ import hashlib
import json
import logging
import re
from typing import Union
from typing import Literal, Union
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from passlib.handlers.md5_crypt import md5_crypt
from pyasic import settings
from pyasic.API import BaseMinerAPI
from pyasic.errors import APIError
from pyasic.misc import api_min_version
from pyasic.settings import PyasicSettings
### IMPORTANT ###
# you need to change the password of the miners using the Whatsminer
@@ -40,6 +40,12 @@ from pyasic.settings import PyasicSettings
# you change the password, you can pass that to this class as pwd,
# or add it as the Whatsminer_pwd in the settings.toml file.
PrePowerOnMessage = Union[
Literal["wait for adjust temp"],
Literal["adjust complete"],
Literal["adjust continue"],
]
def _crypt(word: str, salt: str) -> str:
"""Encrypts a word with a salt, using a standard salt format.
@@ -186,7 +192,7 @@ class BTMinerAPI(BaseMinerAPI):
ip: str,
api_ver: str = "0.0.0",
port: int = 4028,
pwd: str = PyasicSettings().global_whatsminer_password,
pwd: str = settings.get("default_whatsminer_password", "admin"),
):
super().__init__(ip, port)
self.pwd = pwd
@@ -693,7 +699,7 @@ class BTMinerAPI(BaseMinerAPI):
)
return await self.send_privileged_command("set_power_pct", percent=str(percent))
async def pre_power_on(self, complete: bool, msg: str) -> dict:
async def pre_power_on(self, complete: bool, msg: PrePowerOnMessage) -> dict:
"""Configure or check status of pre power on.
<details>
@@ -713,7 +719,7 @@ class BTMinerAPI(BaseMinerAPI):
</details>
"""
if not msg == "wait for adjust temp" or "adjust complete" or "adjust continue":
if msg not in ("wait for adjust temp", "adjust complete", "adjust continue"):
raise APIError(
"Message is incorrect, please choose one of "
'["wait for adjust temp", '
@@ -729,6 +735,34 @@ class BTMinerAPI(BaseMinerAPI):
)
### ADDED IN V2.0.5 Whatsminer API ###
@api_min_version("2.0.5")
async def set_power_pct_v2(self, percent: int) -> dict:
"""Set the power percentage of the miner based on current power. Used for temporary adjustment. Added in API v2.0.5.
<details>
<summary>Expand</summary>
Set the power percentage of the miner, only works after changing
the password of the miner using the Whatsminer tool.
Parameters:
percent: The power percentage to set.
Returns:
A reply informing of the status of setting the power percentage.
</details>
"""
if not 0 < percent < 100:
raise APIError(
f"Power PCT % is outside of the allowed "
f"range. Please set a % between 0 and "
f"100"
)
return await self.send_privileged_command(
"set_power_pct_v2", percent=str(percent)
)
@api_min_version("2.0.5")
async def set_temp_offset(self, temp_offset: int) -> dict:
"""Set the offset of miner hash board target temperature.

View File

@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and -
# limitations under the License. -
# ------------------------------------------------------------------------------
from pyasic import settings
from pyasic.API.bmminer import BMMinerAPI
from pyasic.API.bosminer import BOSMinerAPI
from pyasic.API.btminer import BTMinerAPI
@@ -29,10 +30,9 @@ from pyasic.data import (
from pyasic.errors import APIError, APIWarning
from pyasic.miners import get_miner
from pyasic.miners.base import AnyMiner
from pyasic.miners.miner_factory import MinerFactory
from pyasic.miners.miner_factory import MinerFactory, miner_factory
from pyasic.miners.miner_listener import MinerListener
from pyasic.network import MinerNetwork
from pyasic.settings import PyasicSettings
__all__ = [
"BMMinerAPI",
@@ -51,7 +51,8 @@ __all__ = [
"get_miner",
"AnyMiner",
"MinerFactory",
"miner_factory",
"MinerListener",
"MinerNetwork",
"PyasicSettings",
"settings",
]

View File

@@ -16,31 +16,29 @@
import logging
from pyasic.settings import PyasicSettings
def init_logger():
if PyasicSettings().logfile:
logging.basicConfig(
filename="logfile.txt",
filemode="a",
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
datefmt="%x %X",
)
else:
logging.basicConfig(
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
datefmt="%x %X",
)
# if PyasicSettings().logfile:
# logging.basicConfig(
# filename="logfile.txt",
# filemode="a",
# format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
# datefmt="%x %X",
# )
# else:
logging.basicConfig(
format="%(pathname)s:%(lineno)d in %(funcName)s\n[%(levelname)s][%(asctime)s](%(name)s) - %(message)s",
datefmt="%x %X",
)
_logger = logging.getLogger()
if PyasicSettings().debug:
_logger.setLevel(logging.DEBUG)
logging.getLogger("asyncssh").setLevel(logging.DEBUG)
else:
_logger.setLevel(logging.WARNING)
logging.getLogger("asyncssh").setLevel(logging.WARNING)
# if PyasicSettings().debug:
# _logger.setLevel(logging.DEBUG)
# logging.getLogger("asyncssh").setLevel(logging.DEBUG)
# else:
_logger.setLevel(logging.WARNING)
logging.getLogger("asyncssh").setLevel(logging.WARNING)
return _logger

View File

@@ -21,9 +21,11 @@ from pyasic.miners.types import (
S19XP,
S19a,
S19aPro,
S19i,
S19j,
S19jNoPIC,
S19jPro,
S19Plus,
S19Pro,
S19ProPlus,
)
@@ -33,6 +35,14 @@ class BMMinerS19(AntminerModern, S19):
pass
class BMMinerS19Plus(AntminerModern, S19Plus):
pass
class BMMinerS19i(AntminerModern, S19i):
pass
class BMMinerS19Pro(AntminerModern, S19Pro):
pass

View File

@@ -18,10 +18,12 @@ from .S19 import (
BMMinerS19,
BMMinerS19a,
BMMinerS19aPro,
BMMinerS19i,
BMMinerS19j,
BMMinerS19jNoPIC,
BMMinerS19jPro,
BMMinerS19L,
BMMinerS19Plus,
BMMinerS19Pro,
BMMinerS19ProPlus,
BMMinerS19XP,

View File

@@ -109,7 +109,7 @@ class AntminerModern(BMMiner):
data = await self.web.blink(blink=False)
if data:
if data.get("code") == "B100":
self.light = True
self.light = False
return self.light
async def reboot(self) -> bool:

View File

@@ -239,14 +239,16 @@ class BMMiner(BaseMiner):
for i in range(board_offset, board_offset + 4):
try:
key = f'chain_acs{i}'
if boards[1].get(key, '') != '':
key = f"chain_acs{i}"
if boards[1].get(key, "") != "":
real_slots.append(i)
except LookupError:
pass
if len(real_slots) < 3:
real_slots = list(range(board_offset, board_offset + self.ideal_hashboards))
real_slots = list(
range(board_offset, board_offset + self.ideal_hashboards)
)
for i in real_slots:
hashboard = HashBoard(

View File

@@ -1043,7 +1043,7 @@ class BOSMiner(BaseMiner):
if data == "50":
self.light = True
return self.light
except TypeError:
except (TypeError, AttributeError):
return self.light
async def get_nominal_hashrate(self, api_devs: dict = None) -> Optional[float]:

View File

@@ -442,7 +442,8 @@ class BTMiner(BaseMiner):
if api_summary:
try:
return api_summary["SUMMARY"][0]["Power"]
wattage = api_summary["SUMMARY"][0]["Power"]
return wattage if not wattage == -1 else None
except (KeyError, IndexError):
pass

View File

@@ -20,7 +20,7 @@ import enum
import ipaddress
import json
import re
from typing import Callable, List, Optional, Tuple, Union
from typing import AsyncGenerator, Callable, List, Optional, Tuple, Union
import httpx
@@ -85,6 +85,8 @@ MINER_CLASSES = {
"ANTMINER S19L": BMMinerS19L,
"ANTMINER S19 PRO": BMMinerS19Pro,
"ANTMINER S19J": BMMinerS19j,
"ANTMINER S19I": BMMinerS19i,
"ANTMINER S19+": BMMinerS19Plus,
"ANTMINER S19J88NOPIC": BMMinerS19jNoPIC,
"ANTMINER S19PRO+": BMMinerS19ProPlus,
"ANTMINER S19J PRO": BMMinerS19jPro,
@@ -99,6 +101,8 @@ MINER_CLASSES = {
"M20SV10": BTMinerM20SV10,
"M20SV20": BTMinerM20SV20,
"M20SV30": BTMinerM20SV30,
"M20PV10": BTMinerM20PV10,
"M20PV30": BTMinerM20PV30,
"M20S+V30": BTMinerM20SPlusV30,
"M21V10": BTMinerM21V10,
"M21SV20": BTMinerM21SV20,
@@ -375,7 +379,9 @@ class MinerFactory:
def clear_cached_miners(self):
self.cache = {}
async def get_multiple_miners(self, ips: List[str], limit: int = 200):
async def get_multiple_miners(
self, ips: List[str], limit: int = 200
) -> List[AnyMiner]:
results = []
async for miner in self.get_miner_generator(ips, limit):
@@ -383,7 +389,7 @@ class MinerFactory:
return results
async def get_miner_generator(self, ips: list, limit: int = 200):
async def get_miner_generator(self, ips: list, limit: int = 200) -> AsyncGenerator:
tasks = []
semaphore = asyncio.Semaphore(limit)
@@ -391,13 +397,10 @@ class MinerFactory:
tasks.append(asyncio.create_task(self.get_miner(ip)))
for task in tasks:
await semaphore.acquire()
try:
async with semaphore:
result = await task
if result is not None:
yield result
finally:
semaphore.release()
async def get_miner(self, ip: str):
ip = str(ip)
@@ -751,7 +754,8 @@ class MinerFactory:
async def get_miner_model_whatsminer(self, ip: str):
sock_json_data = await self.send_api_command(ip, "devdetails")
try:
miner_model = sock_json_data["DEVDETAILS"][0]["Model"]
miner_model = sock_json_data["DEVDETAILS"][0]["Model"].replace("_", "")
miner_model = miner_model[:-1] + "0"
return miner_model
except (TypeError, LookupError):
@@ -777,11 +781,13 @@ class MinerFactory:
)
auth = auth_req.json()["jwt"]
web_data = (await session.post(
web_data = (
await session.post(
f"http://{ip}/api/type",
headers={"Authorization": "Bearer " + auth},
data={},
)).json()
)
).json()
return web_data["type"]
except (httpx.HTTPError, LookupError):
pass

View File

@@ -44,6 +44,24 @@ class S19Pro(AntMiner): # noqa - ignore ABC method implementation
self.fan_count = 4
class S19i(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19i"
self.nominal_chips = 80
self.fan_count = 4
class S19Plus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "S19+"
self.nominal_chips = 80
self.fan_count = 4
class S19ProPlus(AntMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)

View File

@@ -20,10 +20,12 @@ from .S19 import (
S19XP,
S19a,
S19aPro,
S19i,
S19j,
S19jNoPIC,
S19jPro,
S19NoPIC,
S19Plus,
S19Pro,
S19ProPlus,
)

View File

@@ -0,0 +1,35 @@
# ------------------------------------------------------------------------------
# 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.makes import WhatsMiner
class M20PV10(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20P V10"
self.nominal_chips = 156
self.fan_count = 2
class M20PV30(WhatsMiner): # noqa - ignore ABC method implementation
def __init__(self, ip: str, api_ver: str = "0.0.0"):
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20P V30"
self.nominal_chips = 148
self.fan_count = 2

View File

@@ -42,8 +42,5 @@ class M20SV30(WhatsMiner): # noqa - ignore ABC method implementation
super().__init__(ip, api_ver)
self.ip = ip
self.model = "M20S V30"
self.nominal_chips = 0
warnings.warn(
"Unknown chip count for miner type M20SV30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)."
)
self.nominal_chips = 140
self.fan_count = 2

View File

@@ -15,6 +15,7 @@
# ------------------------------------------------------------------------------
from .M20 import M20V10
from .M20P import M20PV10, M20PV30
from .M20S import M20SV10, M20SV20, M20SV30
from .M20S_Plus import M20SPlusV30
from .M21 import M21V10

View File

@@ -0,0 +1,26 @@
# ------------------------------------------------------------------------------
# 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 M2X
from pyasic.miners.types import M20PV10, M20PV30
class BTMinerM20PV10(M2X, M20PV10):
pass
class BTMinerM20PV30(M2X, M20PV30):
pass

View File

@@ -15,6 +15,7 @@
# ------------------------------------------------------------------------------
from .M20 import BTMinerM20V10
from .M20P import BTMinerM20PV10, BTMinerM20PV30
from .M20S import BTMinerM20SV10, BTMinerM20SV20, BTMinerM20SV30
from .M20S_Plus import BTMinerM20SPlusV30
from .M21 import BTMinerM21V10

View File

@@ -19,9 +19,9 @@ import ipaddress
import logging
from typing import AsyncIterator, List, Union
from pyasic import settings
from pyasic.miners.miner_factory import AnyMiner, miner_factory
from pyasic.network.net_range import MinerNetworkRange
from pyasic.settings import PyasicSettings
class MinerNetwork:
@@ -108,7 +108,7 @@ class MinerNetwork:
# clear cached miners
miner_factory.clear_cached_miners()
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
miners = await asyncio.gather(
*[self.ping_and_get_miner(host, limit) for host in local_network.hosts()]
)
@@ -136,7 +136,7 @@ class MinerNetwork:
local_network = self.get_network()
# create a list of scan tasks
limit = asyncio.Semaphore(PyasicSettings().network_scan_threads)
limit = asyncio.Semaphore(settings.get("network_scan_threads", 300))
miners = asyncio.as_completed(
[
loop.create_task(self.ping_and_get_miner(host, limit))
@@ -191,12 +191,12 @@ class MinerNetwork:
async def ping_miner(
ip: ipaddress.ip_address, port=4028
) -> Union[None, ipaddress.ip_address]:
for i in range(PyasicSettings().network_ping_retries):
for i in range(settings.get("network_ping_retries", 1)):
try:
connection_fut = asyncio.open_connection(str(ip), port)
# get the read and write streams from the connection
reader, writer = await asyncio.wait_for(
connection_fut, timeout=PyasicSettings().network_ping_timeout
connection_fut, timeout=settings.get("network_ping_timeout", 3)
)
# immediately close connection, we know connection happened
writer.close()
@@ -220,12 +220,12 @@ async def ping_miner(
async def ping_and_get_miner(
ip: ipaddress.ip_address, port=4028
) -> Union[None, AnyMiner]:
for i in range(PyasicSettings().network_ping_retries):
for i in range(settings.get("network_ping_retries", 1)):
try:
connection_fut = asyncio.open_connection(str(ip), port)
# get the read and write streams from the connection
reader, writer = await asyncio.wait_for(
connection_fut, timeout=PyasicSettings().network_ping_timeout
connection_fut, timeout=settings.get("network_ping_timeout", 3)
)
# immediately close connection, we know connection happened
writer.close()

View File

@@ -14,27 +14,26 @@
# limitations under the License. -
# ------------------------------------------------------------------------------
from dataclasses import dataclass
from typing import Any
from pyasic.misc import Singleton
_settings = { # defaults
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_threads": 300,
"factory_get_retries": 1,
"get_data_retries": 1,
"default_whatsminer_password": "admin",
"default_innosilicon_password": "admin",
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
"default_goldshell_password": "123456789",
}
@dataclass
class PyasicSettings(metaclass=Singleton):
network_ping_retries: int = 1
network_ping_timeout: int = 3
network_scan_threads: int = 300
def get(key: str, other: Any = None) -> Any:
return _settings.get(key, other)
miner_factory_get_version_retries: int = 1
miner_get_data_retries: int = 1
global_whatsminer_password = "admin"
global_innosilicon_password = "admin"
global_antminer_password = "root"
global_bosminer_password = "root"
global_vnish_password = "admin"
global_goldshell_password = "123456789"
debug: bool = False
logfile: bool = False
def update(key: str, val: Any) -> Any:
_settings[key] = val

View File

@@ -24,7 +24,7 @@ from pyasic.errors import APIWarning
class BaseWebAPI(ABC):
def __init__(self, ip: str) -> None:
# ip address of the miner
self.ip = ip # ipaddress.ip_address(ip)
self.ip = ip # ipaddress.ip_address(ip)
self.username = "root"
self.pwd = "root"

View File

@@ -19,14 +19,14 @@ from typing import Union
import httpx
from pyasic.settings import PyasicSettings
from pyasic import settings
from pyasic.web import BaseWebAPI
class AntminerModernWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.pwd = PyasicSettings().global_antminer_password
self.pwd = settings.get("default_antminer_password", "root")
async def send_command(
self,
@@ -137,7 +137,7 @@ class AntminerModernWebAPI(BaseWebAPI):
class AntminerOldWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.pwd = PyasicSettings().global_antminer_password
self.pwd = settings.get("default_antminer_password", "root")
async def send_command(
self,

View File

@@ -18,15 +18,14 @@ from typing import Union
import httpx
from pyasic import APIError
from pyasic.settings import PyasicSettings
from pyasic import APIError, settings
from pyasic.web import BaseWebAPI
class BOSMinerWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.pwd = PyasicSettings().global_bosminer_password
self.pwd = settings.get("default_bosminer_password", "root")
async def send_command(
self,

View File

@@ -19,7 +19,7 @@ from typing import Union
import httpx
from pyasic.settings import PyasicSettings
from pyasic import settings
from pyasic.web import BaseWebAPI
@@ -27,7 +27,7 @@ class GoldshellWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.username = "admin"
self.pwd = PyasicSettings().global_goldshell_password
self.pwd = settings.get("default_goldshell_password", "123456789")
self.jwt = None
async def auth(self):
@@ -72,7 +72,7 @@ class GoldshellWebAPI(BaseWebAPI):
if not self.jwt:
await self.auth()
async with httpx.AsyncClient() as client:
for i in range(PyasicSettings().miner_get_data_retries):
for i in range(settings.get("get_data_retries", 1)):
try:
if parameters:
response = await client.put(

View File

@@ -19,8 +19,8 @@ from typing import Union
import httpx
from pyasic import settings
from pyasic.errors import APIError
from pyasic.settings import PyasicSettings
from pyasic.web import BaseWebAPI
@@ -28,7 +28,7 @@ class InnosiliconWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.username = "admin"
self.pwd = PyasicSettings().global_innosilicon_password
self.pwd = settings.get("default_innosilicon_password", "admin")
self.jwt = None
async def auth(self):
@@ -55,7 +55,7 @@ class InnosiliconWebAPI(BaseWebAPI):
if not self.jwt:
await self.auth()
async with httpx.AsyncClient() as client:
for i in range(PyasicSettings().miner_get_data_retries):
for i in range(settings.get("get_data_retries", 1)):
try:
response = await client.post(
f"http://{self.ip}/api/{command}",

View File

@@ -19,7 +19,7 @@ from typing import Union
import httpx
from pyasic.settings import PyasicSettings
from pyasic import settings
from pyasic.web import BaseWebAPI
@@ -27,7 +27,7 @@ class VNishWebAPI(BaseWebAPI):
def __init__(self, ip: str) -> None:
super().__init__(ip)
self.username = "admin"
self.pwd = PyasicSettings().global_vnish_password
self.pwd = settings.get("default_vnish_password", "admin")
self.token = None
async def auth(self):
@@ -59,7 +59,7 @@ class VNishWebAPI(BaseWebAPI):
if not self.token:
await self.auth()
async with httpx.AsyncClient() as client:
for i in range(PyasicSettings().miner_get_data_retries):
for i in range(settings.get("get_data_retries", 1)):
try:
auth = self.token
if command.startswith("system"):

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.38.5"
version = "0.39.2"
description = "A set of modules for interfacing with many common types of ASIC bitcoin miners, using both their API and SSH."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"