Merge pull request #143 from jpcomps/master

feat: add additional ePIC UMC features, add Output Voltage to MinerData
This commit is contained in:
Brett Rowan
2024-05-14 08:47:00 -06:00
committed by GitHub
7 changed files with 94 additions and 18 deletions

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

@@ -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)