Compare commits

...

23 Commits

Author SHA1 Message Date
Upstream Data
689b34611e version: bump version number. 2024-05-14 08:48:38 -06:00
Brett Rowan
d55c3f45ef Merge pull request #143 from jpcomps/master
feat: add additional ePIC UMC features, add Output Voltage to MinerData
2024-05-14 08:47:00 -06:00
John-Paul Compagnone
5ac533616f missed chiptune 2024-05-14 10:46:26 -04:00
John-Paul Compagnone
96ea5f5d16 remove passing self 2024-05-14 10:41:18 -04:00
John-Paul Compagnone
87526f5efc change target as well to get 2024-05-13 23:54:41 -04:00
John-Paul Compagnone
d31bafbc0e less lines 2024-05-13 23:51:23 -04:00
John-Paul Compagnone
66bae47bb9 seperate these out 2024-05-13 21:26:15 -04:00
John-Paul Compagnone
7a09b66d4e simplify 2024-05-13 21:22:31 -04:00
John-Paul Compagnone
de5932184f use get and cheap way to do full backwards compat 2024-05-13 21:17:48 -04:00
John-Paul Compagnone
2cba62e050 push self into secondary level 2024-05-13 21:03:26 -04:00
John-Paul Compagnone
c7520d98e0 better descriptions 2024-05-13 17:00:02 -04:00
John-Paul Compagnone
92e9f7bc08 fix typo 2024-05-13 16:57:19 -04:00
John-Paul Compagnone
a65c4ba215 feat: add voltage param to minerdata 2024-05-13 15:44:12 -04:00
John-Paul Compagnone
4cd0c3357b remove prints 2024-05-12 16:05:08 -04:00
John-Paul Compagnone
d5cabf8af5 fix: fix send_config for epic, add throttle step and min throttle for Vopt 2024-05-12 14:08:12 -04:00
JP Compagnone
3120de757d Merge branch 'UpstreamData:master' into master 2024-05-12 13:20:50 -04:00
Upstream Data
0b69fe591e version: bump version number. 2024-05-10 13:34:00 -06:00
Upstream Data
032288d062 bug: fix k pro identification on latest versions. 2024-05-10 13:33:42 -06:00
Brett Rowan
5f67b987a0 Merge pull request #139 from UpstreamData/snyk-fix-77dddc60686385a842810025bed37669
[Snyk] Security upgrade jinja2 from 3.1.2 to 3.1.4
2024-05-09 12:26:13 -06:00
snyk-bot
5842b3c4aa fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-JINJA2-6809379
2024-05-08 20:27:28 +00:00
Upstream Data
e2e1d2f2fd version: bump version number. 2024-05-06 14:43:19 -06:00
Upstream Data
dd205c0f06 feature: use semaphore for scanning. 2024-05-06 14:43:03 -06:00
John-Paul Compagnone
3b2b586420 add new critical temp api, add board serial no 2024-04-26 19:27:19 -04:00
12 changed files with 111 additions and 21 deletions

View File

@@ -1,3 +1,3 @@
jinja2<3.1.3
jinja2<3.1.4
mkdocs
mkdocstrings[python]

View File

@@ -156,6 +156,7 @@ class VOptAlgo(MinerConfigValue):
return "VoltageOptimizer"
@dataclass
class ChipTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="chip_tune")
@@ -249,6 +250,8 @@ class MiningModePowerTune(MinerConfigValue):
class MiningModeHashrateTune(MinerConfigValue):
mode: str = field(init=False, default="hashrate_tuning")
hashrate: int = None
throttle_limit: int = None
throttle_step: int = None
algo: TunerAlgo = field(default_factory=TunerAlgo.default)
@classmethod
@@ -256,6 +259,10 @@ class MiningModeHashrateTune(MinerConfigValue):
cls_conf = {}
if dict_conf.get("hashrate"):
cls_conf["hashrate"] = dict_conf["hashrate"]
if dict_conf.get("throttle_limit"):
cls_conf["throttle_limit"] = dict_conf["throttle_limit"]
if dict_conf.get("throttle_step"):
cls_conf["throttle_step"] = dict_conf["throttle_step"]
if dict_conf.get("algo"):
cls_conf["algo"] = TunerAlgo.from_dict(dict_conf["algo"])
@@ -292,7 +299,17 @@ class MiningModeHashrateTune(MinerConfigValue):
return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}}
def as_epic(self) -> dict:
return {"ptune": {"algo": self.algo.as_epic(), "target": self.hashrate}}
mode = {
"ptune": {
"algo": self.algo.as_epic(),
"target": self.hashrate,
}
}
if self.throttle_limit is not None:
mode["ptune"]["min_throttle"] = self.throttle_limit
if self.throttle_step is not None:
mode["ptune"]["throttle_step"] = self.throttle_step
return mode
def as_mara(self) -> dict:
return {
@@ -416,13 +433,19 @@ class MiningModeConfig(MinerConfigOption):
algo_info = web_conf["PerpetualTune"]["Algorithm"]
if algo_info.get("VoltageOptimizer") is not None:
return cls.hashrate_tuning(
hashrate=algo_info["VoltageOptimizer"]["Target"],
algo=TunerAlgo.voltage_optimizer,
hashrate=algo_info["VoltageOptimizer"].get("Target"),
throttle_limit=algo_info["VoltageOptimizer"].get(
"Min Throttle Target"
),
throttle_step=algo_info["VoltageOptimizer"].get(
"Throttle Step"
),
algo=TunerAlgo.voltage_optimizer(),
)
else:
return cls.hashrate_tuning(
hashrate=algo_info["ChipTune"]["Target"],
algo=TunerAlgo.chip_tune,
algo=TunerAlgo.chip_tune(),
)
else:
return cls.normal()

View File

@@ -49,7 +49,9 @@ class TemperatureConfig(MinerConfigValue):
else:
temps_config["fans"]["Auto"]["Target Temperature"] = 60
if self.danger is not None:
temps_config["temps"]["shutdown"] = self.danger
temps_config["temps"]["critical"] = self.danger
if self.hot is not None:
temps_config["temps"]["shutdown"] = self.hot
return temps_config
@classmethod
@@ -74,16 +76,20 @@ class TemperatureConfig(MinerConfigValue):
@classmethod
def from_epic(cls, web_conf: dict) -> "TemperatureConfig":
try:
dangerous_temp = web_conf["Misc"]["Shutdown Temp"]
dangerous_temp = web_conf["Misc"]["Critical Temp"]
except KeyError:
dangerous_temp = None
try:
hot_temp = web_conf["Misc"]["Shutdown Temp"]
except KeyError:
hot_temp = None
# Need to do this in two blocks to avoid KeyError if one is missing
try:
target_temp = web_conf["Fans"]["Fan Mode"]["Auto"]["Target Temperature"]
except KeyError:
target_temp = None
return cls(target=target_temp, danger=dangerous_temp)
return cls(target=target_temp, hot=hot_temp, danger=dangerous_temp)
@classmethod
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":

View File

@@ -50,6 +50,7 @@ class MinerData:
temperature_avg: The average temperature across the boards. Calculated automatically.
env_temp: The environment temps as a float.
wattage: Current power draw of the miner as an int.
voltage: Current output voltage of the PSU as an float.
wattage_limit: Power limit of the miner as an int.
fans: A list of fans on the miner with their speeds.
fan_psu: The speed of the PSU on the fan if the miner collects it.
@@ -84,6 +85,7 @@ class MinerData:
env_temp: float = None
wattage: int = None
wattage_limit: int = field(init=False)
voltage: float = None
_wattage_limit: int = field(repr=False, default=None)
fans: List[Fan] = field(default_factory=list)
fan_psu: int = None

View File

@@ -58,6 +58,10 @@ EPIC_DATA_LOC = DataLocations(
"_get_wattage",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.VOLTAGE): DataFunction(
"_get_voltage",
[WebAPICommand("web_summary", "summary")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[WebAPICommand("web_summary", "summary")],
@@ -119,6 +123,7 @@ class ePIC(BaseMiner):
# Temps
if not conf.get("temps", {}) == {}:
await self.web.set_shutdown_temp(conf["temps"]["shutdown"])
await self.web.set_critical_temp(conf["temps"]["critical"])
# Fans
# set with sub-keys instead of conf["fans"] because sometimes both can be set
if not conf["fans"].get("Manual", {}) == {}:
@@ -129,7 +134,7 @@ class ePIC(BaseMiner):
# Mining Mode -- Need to handle that you may not be able to change while miner is tuning
if conf["ptune"].get("enabled", True):
await self.web.set_ptune_enable(True)
await self.web.set_ptune_algo(**conf["ptune"])
await self.web.set_ptune_algo(conf["ptune"])
## Pools
await self.web.set_pools(conf["pools"])
@@ -216,6 +221,20 @@ class ePIC(BaseMiner):
except KeyError:
pass
async def _get_voltage(self, web_summary: dict = None) -> Optional[float]:
if web_summary is None:
try:
web_summary = await self.web.summary()
except APIError:
pass
if web_summary is not None:
try:
voltage = web_summary["Power Supply Stats"]["Output Voltage"]
return voltage
except KeyError:
pass
async def _get_hashrate(self, web_summary: dict = None) -> Optional[float]:
if web_summary is None:
try:
@@ -306,16 +325,27 @@ class ePIC(BaseMiner):
HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards)
]
if web_summary.get("HBs") is not None:
for hb in web_summary["HBs"]:
num_of_chips = web_capabilities["Performance Estimator"]["Chip Count"]
hashrate = hb["Hashrate"][0]
# Update the Hashboard object
hb_list[hb["Index"]].missing = False
hb_list[hb["Index"]].hashrate = round(hashrate / 1000000, 2)
hb_list[hb["Index"]].chips = num_of_chips
hb_list[hb["Index"]].temp = hb["Temperature"]
return hb_list
if web_summary is not None and web_capabilities is not None:
if web_summary.get("HBs") is not None:
for hb in web_summary["HBs"]:
if web_capabilities.get("Performance Estimator") is not None:
num_of_chips = web_capabilities["Performance Estimator"][
"Chip Count"
]
else:
num_of_chips = self.expected_chips
if web_capabilities.get("Board Serial Numbers") is not None:
if hb["Index"] < len(web_capabilities["Board Serial Numbers"]):
hb_list[hb["Index"]].serial_number = web_capabilities[
"Board Serial Numbers"
][hb["Index"]]
hashrate = hb["Hashrate"][0]
# Update the Hashboard object
hb_list[hb["Index"]].missing = False
hb_list[hb["Index"]].hashrate = round(hashrate / 1000000, 2)
hb_list[hb["Index"]].chips = num_of_chips
hb_list[hb["Index"]].temp = hb["Temperature"]
return hb_list
async def _is_mining(self, web_summary, *args, **kwargs) -> Optional[bool]:
if web_summary is None:

View File

@@ -249,6 +249,14 @@ class MinerProtocol(Protocol):
"""
return await self._get_wattage()
async def get_voltage(self) -> Optional[float]:
"""Get output voltage of the PSU as a float.
Returns:
Output voltage of the PSU as an float.
"""
return await self._get_voltage()
async def get_wattage_limit(self) -> Optional[int]:
"""Get wattage limit from the miner as an int.
@@ -337,6 +345,9 @@ class MinerProtocol(Protocol):
async def _get_wattage(self) -> Optional[int]:
pass
async def _get_voltage(self) -> Optional[float]:
pass
async def _get_wattage_limit(self) -> Optional[int]:
pass

View File

@@ -37,6 +37,7 @@ class DataOptions(Enum):
IS_MINING = "is_mining"
UPTIME = "uptime"
CONFIG = "config"
VOLTAGE = "voltage"
def __str__(self):
return self.value

View File

@@ -364,6 +364,7 @@ MINER_CLASSES = {
"ANTMINER S19J PRO PLUS": BOSMinerS19jProPlus,
"ANTMINER S19J PRO PLUS NOPIC": BOSMinerS19jProPlusNoPIC,
"ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC,
"ANTMINER S19K PRO": BOSMinerS19kProNoPIC,
"ANTMINER S19 XP": BOSMinerS19XP,
"ANTMINER T19": BOSMinerT19,
"ANTMINER S21": BOSMinerS21,

View File

@@ -32,6 +32,10 @@ class MinerNetwork:
def __init__(self, hosts: List[ipaddress.IPv4Address]):
self.hosts = hosts
semaphore_limit = settings.get("network_scan_semaphore", 255)
if semaphore_limit is None:
semaphore_limit = 255
self.semaphore = asyncio.Semaphore(semaphore_limit)
def __len__(self):
return len(self.hosts)
@@ -153,8 +157,16 @@ class MinerNetwork:
except TimeoutError:
yield None
async def ping_and_get_miner(
self, ip: ipaddress.ip_address
) -> Union[None, AnyMiner]:
if settings.get("network_scan_semaphore") is None:
return await self._ping_and_get_miner(ip)
async with self.semaphore:
return await self._ping_and_get_miner(ip)
@staticmethod
async def ping_and_get_miner(ip: ipaddress.ip_address) -> Union[None, AnyMiner]:
async def _ping_and_get_miner(ip: ipaddress.ip_address) -> Union[None, AnyMiner]:
try:
return await ping_and_get_miner(ip)
except ConnectionRefusedError:

View File

@@ -24,6 +24,7 @@ from httpx import AsyncHTTPTransport
_settings = { # defaults
"network_ping_retries": 1,
"network_ping_timeout": 3,
"network_scan_semaphore": None,
"factory_get_retries": 1,
"factory_get_timeout": 3,
"get_data_retries": 1,

View File

@@ -97,6 +97,9 @@ class ePICWebAPI(BaseWebAPI):
async def set_shutdown_temp(self, params: int) -> dict:
return await self.send_command("shutdowntemp", param=params)
async def set_critical_temp(self, params: int) -> dict:
return await self.send_command("criticaltemp", param=params)
async def set_fan(self, params: dict) -> dict:
return await self.send_command("fanspeed", param=params)

View File

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