Add ePIC send_config and config.as_epic (#101)

* feature: Add epic send_config.

* feature: remove UID from epic config.

* feature: add default for temp configs in epic.
This commit is contained in:
UpstreamData
2024-01-26 12:47:19 -07:00
committed by GitHub
parent 96aa346f00
commit 6c14902484
9 changed files with 163 additions and 18 deletions

View File

@@ -110,10 +110,9 @@ class MinerConfig:
def as_epic(self, user_suffix: str = None) -> dict: def as_epic(self, user_suffix: str = None) -> dict:
return { return {
**self.fan_mode.as_epic(), **merge_dicts(self.fan_mode.as_epic(), self.temperature.as_epic()),
**self.temperature.as_epic(),
**self.mining_mode.as_epic(), **self.mining_mode.as_epic(),
**self.pools.as_epic(), **self.pools.as_epic(user_suffix=user_suffix),
**self.power_scaling.as_epic(), **self.power_scaling.as_epic(),
} }

View File

@@ -50,6 +50,17 @@ class FanModeNormal(MinerConfigValue):
def as_bosminer(self) -> dict: def as_bosminer(self) -> dict:
return {"temp_control": {"mode": "auto"}} return {"temp_control": {"mode": "auto"}}
def as_epic(self) -> dict:
return {
"fans": {
"Auto": {
"Idle Speed": self.minimum_speed
if not self.minimum_speed == 0
else 100
}
}
}
@dataclass @dataclass
class FanModeManual(MinerConfigValue): class FanModeManual(MinerConfigValue):
@@ -96,6 +107,9 @@ class FanModeManual(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"fan": {"percentage": self.speed}} return {"fan": {"percentage": self.speed}}
def as_epic(self) -> dict:
return {"fans": {"Manual": {"speed": self.speed}}}
@dataclass @dataclass
class FanModeImmersion(MinerConfigValue): class FanModeImmersion(MinerConfigValue):

View File

@@ -47,6 +47,9 @@ class MiningModeNormal(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": self.mode}} return {"mode": {"mode": self.mode}}
def as_epic(self) -> dict:
return {"ptune": {"enabled": False}}
@dataclass @dataclass
class MiningModeSleep(MinerConfigValue): class MiningModeSleep(MinerConfigValue):
@@ -65,6 +68,9 @@ class MiningModeSleep(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"sleep": "on"}} return {"mode": {"sleep": "on"}}
def as_epic(self) -> dict:
return {"ptune": {"algo": "Sleep", "target": 0}}
@dataclass @dataclass
class MiningModeLPM(MinerConfigValue): class MiningModeLPM(MinerConfigValue):
@@ -102,14 +108,52 @@ class MiningModeHPM(MinerConfigValue):
return {"mode": {"mode": "turbo"}} return {"mode": {"mode": "turbo"}}
class StandardPowerTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
def as_epic(self):
return VOptPowerTuneAlgo().as_epic()
class VOptPowerTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
def as_epic(self):
return "VoltageOptimizer"
class ChipTunePowerTuneAlgo(MinerConfigValue):
mode: str = field(init=False, default="standard")
def as_epic(self):
return "ChipTune"
class PowerTunerAlgo(MinerConfigOption):
standard = StandardPowerTuneAlgo
voltage_optimizer = VOptPowerTuneAlgo
chip_tune = ChipTunePowerTuneAlgo
@classmethod
def default(cls):
return cls.standard()
@dataclass @dataclass
class MiningModePowerTune(MinerConfigValue): class MiningModePowerTune(MinerConfigValue):
mode: str = field(init=False, default="power_tuning") mode: str = field(init=False, default="power_tuning")
power: int = None power: int = None
algo: PowerTunerAlgo = PowerTunerAlgo.default()
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune": def from_dict(cls, dict_conf: dict | None) -> "MiningModePowerTune":
return cls(dict_conf.get("power")) cls_conf = {}
if dict_conf.get("power"):
cls_conf["power"] = dict_conf["power"]
if dict_conf.get("algo"):
cls_conf["algo"] = dict_conf["algo"]
return cls(**cls_conf)
def as_am_modern(self) -> dict: def as_am_modern(self) -> dict:
return {"miner-mode": "0"} return {"miner-mode": "0"}
@@ -139,6 +183,9 @@ class MiningModePowerTune(MinerConfigValue):
def as_auradine(self) -> dict: def as_auradine(self) -> dict:
return {"mode": {"mode": "custom", "tune": "power", "power": self.power}} return {"mode": {"mode": "custom", "tune": "power", "power": self.power}}
def as_epic(self) -> dict:
return {"ptune": {**self.algo.as_epic(), "target": self.power}}
@dataclass @dataclass
class MiningModeHashrateTune(MinerConfigValue): class MiningModeHashrateTune(MinerConfigValue):
@@ -262,20 +309,18 @@ class MiningModeConfig(MinerConfigOption):
@classmethod @classmethod
def from_epic(cls, web_conf: dict): def from_epic(cls, web_conf: dict):
try: try:
work_mode = web_conf["PerpetualTune"]["Running"] tuner_running = web_conf["PerpetualTune"]["Running"]
if work_mode: if tuner_running:
if ( algo_info = web_conf["PerpetualTune"]["Algorithm"]
web_conf["PerpetualTune"]["Algorithm"].get("VoltageOptimizer") if algo_info.get("VoltageOptimizer") is not None:
is not None return cls.power_tuning(
): power=algo_info["VoltageOptimizer"]["Target"],
return cls.hashrate_tuning( algo=PowerTunerAlgo.voltage_optimizer,
web_conf["PerpetualTune"]["Algorithm"]["VoltageOptimizer"][
"Target"
]
) )
else: else:
return cls.hashrate_tuning( return cls.power_tuning(
web_conf["PerpetualTune"]["Algorithm"]["ChipTune"]["Target"] power=algo_info["ChipTune"]["Target"],
algo=PowerTunerAlgo.chip_tune,
) )
else: else:
return cls.normal() return cls.normal()

View File

@@ -109,6 +109,15 @@ class Pool(MinerConfigValue):
} }
return {"url": self.url, "user": self.user, "pass": self.password} return {"url": self.url, "user": self.user, "pass": self.password}
def as_epic(self, user_suffix: str = None):
if user_suffix is not None:
return {
"pool": self.url,
"login": f"{self.user}{user_suffix}",
"password": self.password,
}
return {"pool": self.url, "login": self.user, "password": 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(
@@ -255,6 +264,17 @@ class PoolGroup(MinerConfigValue):
def as_auradine(self, user_suffix: str = None) -> list: def as_auradine(self, user_suffix: str = None) -> list:
return [p.as_auradine(user_suffix=user_suffix) for p in self.pools] return [p.as_auradine(user_suffix=user_suffix) for p in self.pools]
def as_epic(self, user_suffix: str = None) -> dict:
if len(self.pools) > 0:
conf = {
"name": self.name,
"pool": [pool.as_epic(user_suffix=user_suffix) for pool in self.pools],
}
if self.quota is not None:
conf["quota"] = self.quota
return conf
return {"name": self.name, "pool": []}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup": def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
cls_conf = {} cls_conf = {}
@@ -396,6 +416,25 @@ class PoolConfig(MinerConfigValue):
} }
return {"updatepools": {"pools": PoolGroup().as_auradine()}} return {"updatepools": {"pools": PoolGroup().as_auradine()}}
def as_epic(self, user_suffix: str = None) -> dict:
if len(self.groups) > 0:
return {
"pools": {
"coin": "Btc",
"stratum_configs": [
g.as_epic(user_suffix=user_suffix) for g in self.groups
],
"unique_id": False,
}
}
return {
"pools": {
"coin": "Btc",
"stratum_configs": [PoolGroup().as_epic()],
"unique_id": False,
}
}
@classmethod @classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig": def from_api(cls, api_pools: dict) -> "PoolConfig":
try: try:

View File

@@ -40,6 +40,16 @@ class TemperatureConfig(MinerConfigValue):
temp_cfg["dangerous_temp"] = self.danger temp_cfg["dangerous_temp"] = self.danger
return {"temp_control": temp_cfg} return {"temp_control": temp_cfg}
def as_epic(self) -> dict:
temps_config = {"temps": {}, "fans": {"Auto": {}}}
if self.target is not None:
temps_config["fans"]["Target Temperature"] = self.target
else:
temps_config["fans"]["Target Temperature"] = 60
if self.danger is not None:
temps_config["temps"]["shutdown"] = self.danger
return temps_config
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig": def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
return cls( return cls(

View File

@@ -107,6 +107,30 @@ class ePIC(BaseMiner):
self.config = cfg self.config = cfg
return self.config return self.config
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
self.config = config
conf = self.config.as_epic(user_suffix=user_suffix)
try:
# Temps
if not conf.get("temps", {}) == {}:
await self.web.set_shutdown_temp(conf["temps"]["shutdown"])
# Fans
if not conf["fans"].get("Manual", {}) == {}:
await self.web.set_fan({"Manual": conf["fans"]["Manual"]})
elif not conf["fans"].get("Auto", {}) == {}:
await self.web.set_fan({"Auto": conf["fans"]["Auto"]})
# 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"])
## Pools
await self.web.set_pools(conf["pools"])
except APIError:
pass
async def restart_backend(self) -> bool: async def restart_backend(self) -> bool:
data = await self.web.restart_epic() data = await self.web.restart_epic()
if data: if data:

View File

@@ -24,7 +24,6 @@ from pyasic.data.error_codes import MinerErrorData
from pyasic.errors import APIError from pyasic.errors import APIError
from pyasic.logger import logger from pyasic.logger import logger
from pyasic.miners.data import DataLocations, DataOptions, RPCAPICommand, WebAPICommand from pyasic.miners.data import DataLocations, DataOptions, RPCAPICommand, WebAPICommand
from pyasic.rpc.base import BaseMinerRPCAPI
class MinerProtocol(Protocol): class MinerProtocol(Protocol):

View File

@@ -94,6 +94,21 @@ class ePICWebAPI(BaseWebAPI):
async def reboot(self) -> dict: async def reboot(self) -> dict:
return await self.send_command("reboot", privileged=True) return await self.send_command("reboot", privileged=True)
async def set_shutdown_temp(self, params: int) -> dict:
return await self.send_command("shutdowntemp", parameters=params)
async def set_fan(self, params: dict) -> dict:
return await self.send_command("fanspeed", parameters=params)
async def set_ptune_enable(self, params: bool) -> dict:
return await self.send_command("perpetualtune", parameters=params)
async def set_ptune_algo(self, params: dict) -> dict:
return await self.send_command("perpetualtune/algo", parameters=params)
async def set_pools(self, params: dict) -> dict:
return await self.send_command("coin", parameters=params)
async def pause_mining(self) -> dict: async def pause_mining(self) -> dict:
return await self.send_command("miner", param="Stop") return await self.send_command("miner", param="Stop")

View File

@@ -72,7 +72,7 @@ class TestConfig(unittest.TestCase):
}, },
"fan_mode": {"mode": "manual", "speed": 90, "minimum_fans": 2}, "fan_mode": {"mode": "manual", "speed": 90, "minimum_fans": 2},
"temperature": {"target": 70, "hot": None, "danger": 120}, "temperature": {"target": 70, "hot": None, "danger": 120},
"mining_mode": {"mode": "power_tuning", "power": 3000}, "mining_mode": {"mode": "power_tuning", "power": 3000, "algo": {}},
"power_scaling": { "power_scaling": {
"mode": "enabled", "mode": "enabled",
"power_step": 100, "power_step": 100,