From de641720734e687ef86701c83675af0e1f511838 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Thu, 13 Feb 2025 15:38:28 -0700 Subject: [PATCH] feature: add support for Hiveon S19kPro --- docs/miners/antminer/X19.md | 13 +++++ docs/miners/supported_types.md | 1 + pyasic/config/__init__.py | 23 +++++++-- pyasic/config/base.py | 6 +++ pyasic/config/fans.py | 23 +++++++++ pyasic/config/mining/__init__.py | 49 ++++++++++++++++++ pyasic/config/pools.py | 50 ++++++++++++++++++- pyasic/miners/antminer/hiveon/X19/S19.py | 2 +- pyasic/miners/antminer/hiveon/X19/__init__.py | 2 +- pyasic/miners/factory.py | 1 + 10 files changed, 163 insertions(+), 7 deletions(-) diff --git a/docs/miners/antminer/X19.md b/docs/miners/antminer/X19.md index d7421172..e3b0b657 100644 --- a/docs/miners/antminer/X19.md +++ b/docs/miners/antminer/X19.md @@ -703,6 +703,19 @@ show_root_heading: false heading_level: 0 +## S19K Pro (Hive) + +- [ ] Shutdowns +- [ ] Power Modes +- [ ] Setpoints +- [ ] Presets + +::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19kPro + handler: python + options: + show_root_heading: false + heading_level: 0 + ## S19 No PIC (Hive) - [ ] Shutdowns diff --git a/docs/miners/supported_types.md b/docs/miners/supported_types.md index 9aa80702..75f91f82 100644 --- a/docs/miners/supported_types.md +++ b/docs/miners/supported_types.md @@ -769,6 +769,7 @@ details { diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 61af390a..7556b0e2 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -56,6 +56,16 @@ class MinerConfig(BaseModel): **self.temperature.as_am_modern(), } + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: + """Generates the configuration in the format suitable for modern Hiveon.""" + return { + **self.fan_mode.as_hiveon_modern(), + "freq-level": "100", + **self.mining_mode.as_hiveon_modern(), + **self.pools.as_hiveon_modern(user_suffix=user_suffix), + **self.temperature.as_hiveon_modern(), + } + def as_elphapex(self, user_suffix: str | None = None) -> dict: """Generates the configuration in the format suitable for modern Elphapex.""" return { @@ -209,6 +219,15 @@ class MinerConfig(BaseModel): fan_mode=FanModeConfig.from_am_modern(web_conf), ) + @classmethod + def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig": + """Constructs a MinerConfig object from web configuration for Hiveon.""" + return cls( + pools=PoolConfig.from_hiveon_modern(web_conf), + mining_mode=MiningModeConfig.from_hiveon_modern(web_conf), + fan_mode=FanModeConfig.from_hiveon_modern(web_conf), + ) + @classmethod def from_elphapex(cls, web_conf: dict) -> "MinerConfig": """Constructs a MinerConfig object from web configuration for modern Antminers.""" @@ -327,7 +346,3 @@ class MinerConfig(BaseModel): @classmethod def from_hammer(cls, *args, **kwargs) -> "MinerConfig": return cls.from_am_modern(*args, **kwargs) - - @classmethod - def from_hiveon_modern(cls, web_conf: dict) -> "MinerConfig": - return cls.from_am_modern(web_conf) diff --git a/pyasic/config/base.py b/pyasic/config/base.py index c3b7370d..f9ea947a 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -28,6 +28,9 @@ class MinerConfigOption(Enum): def as_am_modern(self) -> dict: return self.value.as_am_modern() + def as_hiveon_modern(self) -> dict: + return self.value.as_hiveon_modern() + def as_am_old(self) -> dict: return self.value.as_am_old() @@ -95,6 +98,9 @@ class MinerConfigValue(BaseModel): def as_am_modern(self) -> dict: return {} + def as_hiveon_modern(self) -> dict: + return {} + def as_am_old(self) -> dict: return {} diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index 77f7744f..aaa4af21 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -55,6 +55,9 @@ class FanModeNormal(MinerConfigValue): def as_am_modern(self) -> dict: return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} + def as_hiveon_modern(self) -> dict: + return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} + def as_elphapex(self) -> dict: return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"} @@ -138,6 +141,9 @@ class FanModeManual(MinerConfigValue): def as_am_modern(self) -> dict: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)} + def as_hiveon_modern(self) -> dict: + return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)} + def as_elphapex(self) -> dict: return {"fc-fan-ctrl": True, "fc-fan-pwm": str(self.speed)} @@ -191,6 +197,9 @@ class FanModeImmersion(MinerConfigValue): def as_am_modern(self) -> dict: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"} + def as_hiveon_modern(self) -> dict: + return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"} + def as_elphapex(self) -> dict: return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"} @@ -248,6 +257,20 @@ class FanModeConfig(MinerConfigOption): else: return cls.default() + @classmethod + def from_hiveon_modern(cls, web_conf: dict): + if web_conf.get("bitmain-fan-ctrl") is not None: + fan_manual = web_conf["bitmain-fan-ctrl"] + if fan_manual: + speed = int(web_conf["bitmain-fan-pwm"]) + if speed == 0: + return cls.immersion() + return cls.manual(speed=speed) + else: + return cls.normal() + else: + return cls.default() + @classmethod def from_elphapex(cls, web_conf: dict): if web_conf.get("fc-fan-ctrl") is not None: diff --git a/pyasic/config/mining/__init__.py b/pyasic/config/mining/__init__.py index 2e151385..d5359b04 100644 --- a/pyasic/config/mining/__init__.py +++ b/pyasic/config/mining/__init__.py @@ -52,6 +52,11 @@ class MiningModeNormal(MinerConfigValue): return {"miner-mode": "0"} return {"miner-mode": 0} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "0"} + return {"miner-mode": 0} + def as_elphapex(self) -> dict: return {"miner-mode": 0} @@ -90,6 +95,11 @@ class MiningModeSleep(MinerConfigValue): return {"miner-mode": "1"} return {"miner-mode": 1} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "1"} + return {"miner-mode": 1} + def as_elphapex(self) -> dict: return {"miner-mode": 1} @@ -125,6 +135,11 @@ class MiningModeLPM(MinerConfigValue): return {"miner-mode": "3"} return {"miner-mode": 3} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "3"} + return {"miner-mode": 3} + def as_elphapex(self) -> dict: return {"miner-mode": 3} @@ -150,6 +165,11 @@ class MiningModeHPM(MinerConfigValue): return {"miner-mode": "0"} return {"miner-mode": 0} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "0"} + return {"miner-mode": 0} + def as_elphapex(self) -> dict: return {"miner-mode": 0} @@ -186,6 +206,11 @@ class MiningModePowerTune(MinerConfigValue): return {"miner-mode": "0"} return {"miner-mode": 0} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "0"} + return {"miner-mode": 0} + def as_elphapex(self) -> dict: return {"miner-mode": 0} @@ -288,6 +313,11 @@ class MiningModeHashrateTune(MinerConfigValue): return {"miner-mode": "0"} return {"miner-mode": 0} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "0"} + return {"miner-mode": 0} + def as_elphapex(self) -> dict: return {"miner-mode": 0} @@ -422,6 +452,11 @@ class ManualBoardSettings(MinerConfigValue): return {"miner-mode": "0"} return {"miner-mode": 0} + def as_hiveon_modern(self) -> dict: + if settings.get("antminer_mining_mode_as_str", False): + return {"miner-mode": "0"} + return {"miner-mode": 0} + def as_elphapex(self) -> dict: return {"miner-mode": 0} @@ -549,6 +584,20 @@ class MiningModeConfig(MinerConfigOption): return cls.low() return cls.default() + @classmethod + def from_hiveon_modern(cls, web_conf: dict): + if web_conf.get("bitmain-work-mode") is not None: + work_mode = web_conf["bitmain-work-mode"] + if work_mode == "": + return cls.default() + if int(work_mode) == 0: + return cls.normal() + elif int(work_mode) == 1: + return cls.sleep() + elif int(work_mode) == 3: + return cls.low() + return cls.default() + @classmethod def from_elphapex(cls, web_conf: dict): if web_conf.get("fc-work-mode") is not None: diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index ff0ae166..42b6cb3b 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -43,6 +43,13 @@ class Pool(MinerConfigValue): "pass": self.password, } + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: + return { + "url": self.url, + "user": f"{self.user}{user_suffix or ''}", + "pass": self.password, + } + def as_elphapex(self, user_suffix: str | None = None) -> dict: return { "url": self.url, @@ -153,6 +160,12 @@ class Pool(MinerConfigValue): url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) + @classmethod + def from_hiveon_modern(cls, web_pool: dict) -> "Pool": + return cls( + url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] + ) + @classmethod def from_elphapex(cls, web_pool: dict) -> "Pool": return cls( @@ -248,6 +261,17 @@ class PoolGroup(MinerConfigValue): idx += 1 return pools + def as_hiveon_modern(self, user_suffix: str | None = None) -> list: + pools = [] + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.append(self.pools[idx].as_hiveon_modern(user_suffix=user_suffix)) + else: + pools.append(Pool(url="", user="", password="").as_hiveon_modern()) + idx += 1 + return pools + def as_elphapex(self, user_suffix: str | None = None) -> list: pools = [] idx = 0 @@ -375,6 +399,13 @@ class PoolGroup(MinerConfigValue): pools.append(Pool.from_am_modern(pool)) return cls(pools=pools) + @classmethod + def from_hiveon_modern(cls, web_pool_list: list) -> "PoolGroup": + pools = [] + for pool in web_pool_list: + pools.append(Pool.from_hiveon_modern(pool)) + return cls(pools=pools) + @classmethod def from_elphapex(cls, web_pool_list: list) -> "PoolGroup": pools = [] @@ -467,6 +498,11 @@ class PoolConfig(MinerConfigValue): return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)} return {"pools": PoolGroup().as_am_modern()} + def as_hiveon_modern(self, user_suffix: str | None = None) -> dict: + if len(self.groups) > 0: + return {"pools": self.groups[0].as_hiveon_modern(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_hiveon_modern()} + def as_elphapex(self, user_suffix: str | None = None) -> dict: if len(self.groups) > 0: return {"pools": self.groups[0].as_elphapex(user_suffix=user_suffix)} @@ -569,10 +605,22 @@ class PoolConfig(MinerConfigValue): @classmethod def from_am_modern(cls, web_conf: dict) -> "PoolConfig": - pool_data = web_conf["pools"] + try: + pool_data = web_conf["pools"] + except KeyError: + return cls(groups=[]) return cls(groups=[PoolGroup.from_am_modern(pool_data)]) + @classmethod + def from_hiveon_modern(cls, web_conf: dict) -> "PoolConfig": + try: + pool_data = web_conf["pools"] + except KeyError: + return cls(groups=[]) + + return cls(groups=[PoolGroup.from_hiveon_modern(pool_data)]) + @classmethod def from_elphapex(cls, web_conf: dict) -> "PoolConfig": pool_data = web_conf["pools"] diff --git a/pyasic/miners/antminer/hiveon/X19/S19.py b/pyasic/miners/antminer/hiveon/X19/S19.py index d0420637..6d2f4d54 100644 --- a/pyasic/miners/antminer/hiveon/X19/S19.py +++ b/pyasic/miners/antminer/hiveon/X19/S19.py @@ -100,5 +100,5 @@ class HiveonS19ProPlusHydro(HiveonModern, S19ProPlusHydro): pass -class HiveonS19KPro(HiveonModern, S19KPro): +class HiveonS19kPro(HiveonModern, S19KPro): pass diff --git a/pyasic/miners/antminer/hiveon/X19/__init__.py b/pyasic/miners/antminer/hiveon/X19/__init__.py index ffb8821a..7cbf4595 100644 --- a/pyasic/miners/antminer/hiveon/X19/__init__.py +++ b/pyasic/miners/antminer/hiveon/X19/__init__.py @@ -23,7 +23,7 @@ from .S19 import ( HiveonS19j, HiveonS19jNoPIC, HiveonS19jPro, - HiveonS19KPro, + HiveonS19kPro, HiveonS19L, HiveonS19NoPIC, HiveonS19Plus, diff --git a/pyasic/miners/factory.py b/pyasic/miners/factory.py index d870ba61..1193bd71 100644 --- a/pyasic/miners/factory.py +++ b/pyasic/miners/factory.py @@ -600,6 +600,7 @@ MINER_CLASSES = { "ANTMINER T9": HiveonT9, "ANTMINER S19JPRO": HiveonS19jPro, "ANTMINER S19": HiveonS19, + "ANTMINER S19K PRO": HiveonS19kPro, "ANTMINER S19X88": HiveonS19NoPIC, }, MinerTypes.MSKMINER: {