Compare commits

..

8 Commits

Author SHA1 Message Date
Upstream Data
5fec3052f6 version: bump version number 2024-12-02 09:22:18 -07:00
Upstream Data
437ee774ab bug: type convert to int for vnish config 2024-12-02 09:22:00 -07:00
Upstream Data
aed9e0e406 version: bump version number 2024-12-02 09:14:05 -07:00
Upstream Data
be96428823 bug: skip vnish boards with 0 freq 2024-12-02 09:13:46 -07:00
Upstream Data
446881b237 version: bump version number 2024-12-02 09:00:15 -07:00
Upstream Data
ceab8e55b5 feature: add send_config support for vnish 2024-12-02 08:59:58 -07:00
Upstream Data
e12f85c94d version: bump version number 2024-11-28 15:09:28 -07:00
Upstream Data
0c85f53177 bug: fix some issues with pool URLs 2024-11-28 15:05:04 -07:00
9 changed files with 98 additions and 13 deletions

View File

@@ -16,7 +16,7 @@
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from pyasic.config.fans import FanMode, FanModeConfig from pyasic.config.fans import FanMode, FanModeConfig, FanModeNormal
from pyasic.config.mining import MiningMode, MiningModeConfig from pyasic.config.mining import MiningMode, MiningModeConfig
from pyasic.config.mining.scaling import ScalingConfig from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig from pyasic.config.pools import PoolConfig
@@ -159,6 +159,19 @@ class MinerConfig(BaseModel):
**self.pools.as_luxos(user_suffix=user_suffix), **self.pools.as_luxos(user_suffix=user_suffix),
} }
def as_vnish(self, user_suffix: str = None) -> dict:
main_cfg = {
"miner": {
**self.fan_mode.as_vnish(),
**self.temperature.as_vnish(),
**self.mining_mode.as_vnish(),
**self.pools.as_vnish(user_suffix=user_suffix),
}
}
if isinstance(self.fan_mode, FanModeNormal):
main_cfg["miner"]["cooling"]["mode"]["param"] = self.temperature.target
return main_cfg
def as_hammer(self, *args, **kwargs) -> dict: def as_hammer(self, *args, **kwargs) -> dict:
return self.as_am_modern(*args, **kwargs) return self.as_am_modern(*args, **kwargs)

View File

@@ -87,6 +87,18 @@ class FanModeNormal(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}} return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
def as_vnish(self) -> dict:
return {
"cooling": {
"fan_min_count": self.minimum_fans,
"fan_min_duty": self.minimum_speed,
"mode": {
"name": "auto",
"param": None, # Target temp, must be set later...
},
}
}
class FanModeManual(MinerConfigValue): class FanModeManual(MinerConfigValue):
mode: str = Field(init=False, default="manual") mode: str = Field(init=False, default="manual")
@@ -150,6 +162,18 @@ class FanModeManual(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}} return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
def as_vnish(self) -> dict:
return {
"cooling": {
"fan_min_count": self.minimum_fans,
"fan_min_duty": self.speed,
"mode": {
"name": "manual",
"param": self.speed, # Speed value
},
}
}
class FanModeImmersion(MinerConfigValue): class FanModeImmersion(MinerConfigValue):
mode: str = Field(init=False, default="immersion") mode: str = Field(init=False, default="immersion")
@@ -175,6 +199,9 @@ class FanModeImmersion(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"fanset": {"speed": 0, "min_fans": 0}} return {"fanset": {"speed": 0, "min_fans": 0}}
def as_vnish(self) -> dict:
return {"cooling": {"mode": {"name": "immers"}}}
class FanModeConfig(MinerConfigOption): class FanModeConfig(MinerConfigOption):
normal = FanModeNormal normal = FanModeNormal

View File

@@ -357,6 +357,9 @@ class ManualBoardSettings(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {"freq": self.freq}
class MiningModeManual(MinerConfigValue): class MiningModeManual(MinerConfigValue):
mode: str = field(init=False, default="manual") mode: str = field(init=False, default="manual")
@@ -378,6 +381,12 @@ class MiningModeManual(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {
"chains": [b.as_vnish() for b in self.boards.values() if b.freq != 0],
"globals": {"freq": int(self.global_freq), "volt": int(self.global_volt)},
}
@classmethod @classmethod
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual": def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
# will raise KeyError if it cant find the settings, values cannot be empty # will raise KeyError if it cant find the settings, values cannot be empty

View File

@@ -146,6 +146,15 @@ class Pool(MinerConfigValue):
url=self.url, user=self.user, password=self.password, enabled=True url=self.url, user=self.user, password=self.password, enabled=True
) )
def as_vnish(self, user_suffix: str = None) -> dict:
if user_suffix is not None:
return {
"url": self.url,
"user": f"{self.user}{user_suffix}",
"pass": self.password,
}
return {"url": self.url, "user": self.user, "pass": self.password}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "Pool": def from_dict(cls, dict_conf: dict | None) -> "Pool":
return cls( return cls(
@@ -338,6 +347,9 @@ class PoolGroup(MinerConfigValue):
pools=[p.as_boser() for p in self.pools], pools=[p.as_boser() for p in self.pools],
) )
def as_vnish(self, user_suffix: str = None) -> dict:
return {"pools": [p.as_vnish(user_suffix=user_suffix) for p in self.pools]}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup": def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
cls_conf = {} cls_conf = {}
@@ -530,6 +542,9 @@ class PoolConfig(MinerConfigValue):
def as_luxos(self, user_suffix: str = None) -> dict: def as_luxos(self, user_suffix: str = None) -> dict:
return {} return {}
def as_vnish(self, user_suffix: str = None) -> dict:
return self.groups[0].as_vnish(user_suffix=user_suffix)
@classmethod @classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig": def from_api(cls, api_pools: dict) -> "PoolConfig":
try: try:

View File

@@ -56,6 +56,9 @@ class TemperatureConfig(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]} return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
def as_vnish(self) -> dict:
return {"misc": {"restart_temp": self.danger}}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig": def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
return cls( return cls(
@@ -95,9 +98,16 @@ class TemperatureConfig(MinerConfigValue):
@classmethod @classmethod
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig": def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":
try:
dangerous_temp = web_settings["misc"]["restart_temp"]
except KeyError:
dangerous_temp = None
try: try:
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto": if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
return cls(target=web_settings["miner"]["cooling"]["mode"]["param"]) return cls(
target=web_settings["miner"]["cooling"]["mode"]["param"],
danger=dangerous_temp,
)
except KeyError: except KeyError:
pass pass
return cls() return cls()

View File

@@ -3,6 +3,7 @@ from typing import Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from pydantic import BaseModel, computed_field, model_serializer from pydantic import BaseModel, computed_field, model_serializer
from typing_extensions import Self
class Scheme(Enum): class Scheme(Enum):
@@ -28,8 +29,10 @@ class PoolUrl(BaseModel):
return f"{self.scheme.value}://{self.host}:{self.port}" return f"{self.scheme.value}://{self.host}:{self.port}"
@classmethod @classmethod
def from_str(cls, url: str) -> "PoolUrl": def from_str(cls, url: str) -> Self | None:
parsed_url = urlparse(url) parsed_url = urlparse(url)
if not parsed_url.hostname:
return None
if not parsed_url.scheme.strip() == "": if not parsed_url.scheme.strip() == "":
scheme = Scheme(parsed_url.scheme) scheme = Scheme(parsed_url.scheme)
else: else:
@@ -57,15 +60,15 @@ class PoolMetrics(BaseModel):
pool_stale_percent: Percentage of stale shares by the pool. pool_stale_percent: Percentage of stale shares by the pool.
""" """
url: PoolUrl url: PoolUrl | None
accepted: int = None accepted: int | None = None
rejected: int = None rejected: int | None = None
get_failures: int = None get_failures: int | None = None
remote_failures: int = None remote_failures: int | None = None
active: bool = None active: bool | None = None
alive: bool = None alive: bool | None = None
index: int = None index: int | None = None
user: str = None user: str | None = None
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property

View File

@@ -98,6 +98,11 @@ class VNish(VNishFirmware, BMMiner):
data_locations = VNISH_DATA_LOC data_locations = VNISH_DATA_LOC
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
await self.web.post_settings(
miner_settings=config.as_vnish(user_suffix=user_suffix)
)
async def restart_backend(self) -> bool: async def restart_backend(self) -> bool:
data = await self.web.restart_vnish() data = await self.web.restart_vnish()
if data: if data:

View File

@@ -143,3 +143,6 @@ class VNishWebAPI(BaseWebAPI):
async def find_miner(self) -> dict: async def find_miner(self) -> dict:
return await self.send_command("find-miner", privileged=True) return await self.send_command("find-miner", privileged=True)
async def post_settings(self, miner_settings: dict):
return await self.send_command("settings", post=True, **miner_settings)

View File

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