Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e76d4550b | ||
|
|
2d424025e9 | ||
|
|
bf4903ce4b | ||
|
|
4f7f6bf045 | ||
|
|
824890ec97 | ||
|
|
ce9d7ffb0f | ||
|
|
183b4934c1 | ||
|
|
3d2b260b17 | ||
|
|
f88c1734eb | ||
|
|
b897ca8363 | ||
|
|
dba341fdae | ||
|
|
837794bd57 | ||
|
|
36d16c7235 | ||
|
|
7797023689 |
@@ -62,6 +62,8 @@ def backend_str(backend: MinerTypes) -> str:
|
|||||||
return "Stock Firmware Volcminers"
|
return "Stock Firmware Volcminers"
|
||||||
case MinerTypes.ELPHAPEX:
|
case MinerTypes.ELPHAPEX:
|
||||||
return "Stock Firmware Elphapex Miners"
|
return "Stock Firmware Elphapex Miners"
|
||||||
|
case MinerTypes.MSKMINER:
|
||||||
|
return "MSKMiner Firmware Miners"
|
||||||
raise TypeError("Unknown miner backend, cannot generate docs")
|
raise TypeError("Unknown miner backend, cannot generate docs")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -716,6 +716,19 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 0
|
heading_level: 0
|
||||||
|
|
||||||
|
## S19 No PIC (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.antminer.mskminer.X19.S19.MSKMinerS19NoPIC
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
## S19 (LuxOS)
|
## S19 (LuxOS)
|
||||||
|
|
||||||
- [x] Shutdowns
|
- [x] Shutdowns
|
||||||
|
|||||||
16
docs/miners/avalonminer/A15X.md
Normal file
16
docs/miners/avalonminer/A15X.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# pyasic
|
||||||
|
## A15X Models
|
||||||
|
|
||||||
|
## Avalon 1566 (Stock)
|
||||||
|
|
||||||
|
- [ ] Shutdowns
|
||||||
|
- [ ] Power Modes
|
||||||
|
- [ ] Setpoints
|
||||||
|
- [ ] Presets
|
||||||
|
|
||||||
|
::: pyasic.miners.avalonminer.cgminer.A15X.A1566.CGMinerAvalon1566
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: false
|
||||||
|
heading_level: 0
|
||||||
|
|
||||||
@@ -554,6 +554,12 @@ details {
|
|||||||
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
|
<li><a href="../avalonminer/nano#avalon-nano-3-stock">Avalon Nano 3 (Stock)</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>A15X Series:</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../avalonminer/A15X#avalon-1566-stock">Avalon 1566 (Stock)</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
@@ -769,6 +775,17 @@ details {
|
|||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
|
<summary>MSKMiner Firmware Miners:</summary>
|
||||||
|
<ul>
|
||||||
|
<details>
|
||||||
|
<summary>X19 Series:</summary>
|
||||||
|
<ul>
|
||||||
|
<li><a href="../antminer/X19#s19-no-pic-stock">S19 No PIC (Stock)</a></li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
<summary>LuxOS Firmware Miners:</summary>
|
<summary>LuxOS Firmware Miners:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@@ -56,6 +56,16 @@ class MinerConfig(BaseModel):
|
|||||||
**self.temperature.as_am_modern(),
|
**self.temperature.as_am_modern(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
||||||
|
"""Generates the configuration in the format suitable for modern Elphapex."""
|
||||||
|
return {
|
||||||
|
**self.fan_mode.as_elphapex(),
|
||||||
|
"fc-freq-level": "100",
|
||||||
|
**self.mining_mode.as_elphapex(),
|
||||||
|
**self.pools.as_elphapex(user_suffix=user_suffix),
|
||||||
|
**self.temperature.as_elphapex(),
|
||||||
|
}
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str | None = None) -> dict:
|
def as_wm(self, user_suffix: str | None = None) -> dict:
|
||||||
"""Generates the configuration in the format suitable for Whatsminers."""
|
"""Generates the configuration in the format suitable for Whatsminers."""
|
||||||
return {
|
return {
|
||||||
@@ -199,6 +209,15 @@ class MinerConfig(BaseModel):
|
|||||||
fan_mode=FanModeConfig.from_am_modern(web_conf),
|
fan_mode=FanModeConfig.from_am_modern(web_conf),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict) -> "MinerConfig":
|
||||||
|
"""Constructs a MinerConfig object from web configuration for modern Antminers."""
|
||||||
|
return cls(
|
||||||
|
pools=PoolConfig.from_elphapex(web_conf),
|
||||||
|
mining_mode=MiningModeConfig.from_elphapex(web_conf),
|
||||||
|
fan_mode=FanModeConfig.from_elphapex(web_conf),
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
|
def from_am_old(cls, web_conf: dict) -> "MinerConfig":
|
||||||
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
|
"""Constructs a MinerConfig object from web configuration for old versions of Antminers."""
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ class MinerConfigOption(Enum):
|
|||||||
def as_luxos(self) -> dict:
|
def as_luxos(self) -> dict:
|
||||||
return self.value.as_luxos()
|
return self.value.as_luxos()
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return self.value.as_elphapex()
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
return self.value(*args, **kwargs)
|
return self.value(*args, **kwargs)
|
||||||
|
|
||||||
@@ -131,6 +134,9 @@ class MinerConfigValue(BaseModel):
|
|||||||
def as_luxos(self) -> dict:
|
def as_luxos(self) -> dict:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {}
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
try:
|
try:
|
||||||
return getattr(self, item)
|
return getattr(self, item)
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ class FanModeNormal(MinerConfigValue):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"fc-fan-ctrl": False, "fc-fan-pwn": "100"}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"temp_control": {"mode": "auto"},
|
"temp_control": {"mode": "auto"},
|
||||||
@@ -135,6 +138,9 @@ class FanModeManual(MinerConfigValue):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": str(self.speed)}
|
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)}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"temp_control": {"mode": "manual"},
|
"temp_control": {"mode": "manual"},
|
||||||
@@ -185,6 +191,9 @@ class FanModeImmersion(MinerConfigValue):
|
|||||||
def as_am_modern(self) -> dict:
|
def as_am_modern(self) -> dict:
|
||||||
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
return {"bitmain-fan-ctrl": True, "bitmain-fan-pwm": "0"}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"fc-fan-ctrl": True, "fc-fan-pwm": "0"}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
return {
|
return {
|
||||||
"fan_control": {"min_fans": 0},
|
"fan_control": {"min_fans": 0},
|
||||||
@@ -239,6 +248,20 @@ class FanModeConfig(MinerConfigOption):
|
|||||||
else:
|
else:
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict):
|
||||||
|
if web_conf.get("fc-fan-ctrl") is not None:
|
||||||
|
fan_manual = web_conf["fc-fan-ctrl"]
|
||||||
|
if fan_manual:
|
||||||
|
speed = int(web_conf["fc-fan-pwm"])
|
||||||
|
if speed == 0:
|
||||||
|
return cls.immersion()
|
||||||
|
return cls.manual(speed=speed)
|
||||||
|
else:
|
||||||
|
return cls.normal()
|
||||||
|
else:
|
||||||
|
return cls.default()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict):
|
def from_epic(cls, web_conf: dict):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ class MiningModeNormal(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
def as_wm(self) -> dict:
|
||||||
return {"mode": self.mode}
|
return {"mode": self.mode}
|
||||||
|
|
||||||
@@ -87,6 +90,9 @@ class MiningModeSleep(MinerConfigValue):
|
|||||||
return {"miner-mode": "1"}
|
return {"miner-mode": "1"}
|
||||||
return {"miner-mode": 1}
|
return {"miner-mode": 1}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 1}
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
def as_wm(self) -> dict:
|
||||||
return {"mode": self.mode}
|
return {"mode": self.mode}
|
||||||
|
|
||||||
@@ -119,6 +125,9 @@ class MiningModeLPM(MinerConfigValue):
|
|||||||
return {"miner-mode": "3"}
|
return {"miner-mode": "3"}
|
||||||
return {"miner-mode": 3}
|
return {"miner-mode": 3}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 3}
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
def as_wm(self) -> dict:
|
||||||
return {"mode": self.mode}
|
return {"mode": self.mode}
|
||||||
|
|
||||||
@@ -141,6 +150,9 @@ class MiningModeHPM(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
def as_wm(self) -> dict:
|
||||||
return {"mode": self.mode}
|
return {"mode": self.mode}
|
||||||
|
|
||||||
@@ -174,6 +186,9 @@ class MiningModePowerTune(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
def as_wm(self) -> dict:
|
def as_wm(self) -> dict:
|
||||||
if self.power is not None:
|
if self.power is not None:
|
||||||
return {"mode": self.mode, self.mode: {"wattage": self.power}}
|
return {"mode": self.mode, self.mode: {"wattage": self.power}}
|
||||||
@@ -273,6 +288,9 @@ class MiningModeHashrateTune(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
def as_bosminer(self) -> dict:
|
def as_bosminer(self) -> dict:
|
||||||
conf = {"enabled": True, "mode": "hashrate_target"}
|
conf = {"enabled": True, "mode": "hashrate_target"}
|
||||||
if self.hashrate is not None:
|
if self.hashrate is not None:
|
||||||
@@ -404,6 +422,9 @@ class ManualBoardSettings(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
def as_vnish(self) -> dict:
|
||||||
return {"freq": self.freq}
|
return {"freq": self.freq}
|
||||||
|
|
||||||
@@ -428,6 +449,9 @@ class MiningModeManual(MinerConfigValue):
|
|||||||
return {"miner-mode": "0"}
|
return {"miner-mode": "0"}
|
||||||
return {"miner-mode": 0}
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
|
def as_elphapex(self) -> dict:
|
||||||
|
return {"miner-mode": 0}
|
||||||
|
|
||||||
def as_vnish(self) -> dict:
|
def as_vnish(self) -> dict:
|
||||||
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
|
chains = [b.as_vnish() for b in self.boards.values() if b.freq != 0]
|
||||||
return {
|
return {
|
||||||
@@ -525,6 +549,20 @@ class MiningModeConfig(MinerConfigOption):
|
|||||||
return cls.low()
|
return cls.low()
|
||||||
return cls.default()
|
return cls.default()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict):
|
||||||
|
if web_conf.get("fc-work-mode") is not None:
|
||||||
|
work_mode = web_conf["fc-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
|
@classmethod
|
||||||
def from_epic(cls, web_conf: dict):
|
def from_epic(cls, web_conf: dict):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -24,7 +24,14 @@ class MiningPreset(MinerConfigValue):
|
|||||||
hashrate = None
|
hashrate = None
|
||||||
else:
|
else:
|
||||||
power = hr_power_split[0].replace("watt", "").strip()
|
power = hr_power_split[0].replace("watt", "").strip()
|
||||||
hashrate = hr_power_split[1].replace("TH", "").replace(" LC", "").strip()
|
hashrate = (
|
||||||
|
hr_power_split[1]
|
||||||
|
.replace("TH", "")
|
||||||
|
.replace("GH", "")
|
||||||
|
.replace("MH", "")
|
||||||
|
.replace(" LC", "")
|
||||||
|
.strip()
|
||||||
|
)
|
||||||
tuned = web_preset["status"] == "tuned"
|
tuned = web_preset["status"] == "tuned"
|
||||||
modded_psu = web_preset["modded_psu_required"]
|
modded_psu = web_preset["modded_psu_required"]
|
||||||
return cls(
|
return cls(
|
||||||
|
|||||||
@@ -43,6 +43,13 @@ class Pool(MinerConfigValue):
|
|||||||
"pass": self.password,
|
"pass": self.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> dict:
|
||||||
|
return {
|
||||||
|
"url": self.url,
|
||||||
|
"user": f"{self.user}{user_suffix or ''}",
|
||||||
|
"pass": self.password,
|
||||||
|
}
|
||||||
|
|
||||||
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
def as_wm(self, idx: int = 1, user_suffix: str | None = None) -> dict:
|
||||||
return {
|
return {
|
||||||
f"pool_{idx}": self.url,
|
f"pool_{idx}": self.url,
|
||||||
@@ -146,6 +153,12 @@ class Pool(MinerConfigValue):
|
|||||||
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_pool: dict) -> "Pool":
|
||||||
|
return cls(
|
||||||
|
url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: check if this is accurate, user/username, pass/password
|
# TODO: check if this is accurate, user/username, pass/password
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pool: dict) -> "Pool":
|
def from_goldshell(cls, web_pool: dict) -> "Pool":
|
||||||
@@ -235,6 +248,17 @@ class PoolGroup(MinerConfigValue):
|
|||||||
idx += 1
|
idx += 1
|
||||||
return pools
|
return pools
|
||||||
|
|
||||||
|
def as_elphapex(self, user_suffix: str | None = None) -> list:
|
||||||
|
pools = []
|
||||||
|
idx = 0
|
||||||
|
while idx < 3:
|
||||||
|
if len(self.pools) > idx:
|
||||||
|
pools.append(self.pools[idx].as_elphapex(user_suffix=user_suffix))
|
||||||
|
else:
|
||||||
|
pools.append(Pool(url="", user="", password="").as_elphapex())
|
||||||
|
idx += 1
|
||||||
|
return pools
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str | None = None) -> dict:
|
def as_wm(self, user_suffix: str | None = None) -> dict:
|
||||||
pools = {}
|
pools = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
@@ -351,6 +375,13 @@ class PoolGroup(MinerConfigValue):
|
|||||||
pools.append(Pool.from_am_modern(pool))
|
pools.append(Pool.from_am_modern(pool))
|
||||||
return cls(pools=pools)
|
return cls(pools=pools)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_pool_list: list) -> "PoolGroup":
|
||||||
|
pools = []
|
||||||
|
for pool in web_pool_list:
|
||||||
|
pools.append(Pool.from_elphapex(pool))
|
||||||
|
return cls(pools=pools)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
def from_goldshell(cls, web_pools: list) -> "PoolGroup":
|
||||||
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
|
return cls(pools=[Pool.from_goldshell(p) for p in web_pools])
|
||||||
@@ -436,6 +467,11 @@ class PoolConfig(MinerConfigValue):
|
|||||||
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)}
|
||||||
return {"pools": PoolGroup().as_am_modern()}
|
return {"pools": PoolGroup().as_am_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)}
|
||||||
|
return {"pools": PoolGroup().as_elphapex()}
|
||||||
|
|
||||||
def as_wm(self, user_suffix: str | None = None) -> dict:
|
def as_wm(self, user_suffix: str | None = None) -> dict:
|
||||||
if len(self.groups) > 0:
|
if len(self.groups) > 0:
|
||||||
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
|
return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)}
|
||||||
@@ -537,6 +573,12 @@ class PoolConfig(MinerConfigValue):
|
|||||||
|
|
||||||
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
|
return cls(groups=[PoolGroup.from_am_modern(pool_data)])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_elphapex(cls, web_conf: dict) -> "PoolConfig":
|
||||||
|
pool_data = web_conf["pools"]
|
||||||
|
|
||||||
|
return cls(groups=[PoolGroup.from_elphapex(pool_data)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
|
def from_goldshell(cls, web_pools: list) -> "PoolConfig":
|
||||||
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
|
return cls(groups=[PoolGroup.from_goldshell(web_pools)])
|
||||||
|
|||||||
@@ -448,6 +448,7 @@ class AvalonminerModels(MinerModelType):
|
|||||||
Avalon1166Pro = "Avalon 1166 Pro"
|
Avalon1166Pro = "Avalon 1166 Pro"
|
||||||
Avalon1126Pro = "Avalon 1126 Pro"
|
Avalon1126Pro = "Avalon 1126 Pro"
|
||||||
Avalon1246 = "Avalon 1246"
|
Avalon1246 = "Avalon 1246"
|
||||||
|
Avalon1566 = "Avalon 1566"
|
||||||
AvalonNano3 = "Avalon Nano 3"
|
AvalonNano3 = "Avalon Nano 3"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ from .epic import *
|
|||||||
from .hiveon import *
|
from .hiveon import *
|
||||||
from .luxos import *
|
from .luxos import *
|
||||||
from .marathon import *
|
from .marathon import *
|
||||||
|
from .mskminer import *
|
||||||
from .vnish import *
|
from .vnish import *
|
||||||
|
|||||||
24
pyasic/miners/antminer/mskminer/X19/S19.py
Normal file
24
pyasic/miners/antminer/mskminer/X19/S19.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners.backends.mskminer import MSKMiner
|
||||||
|
from pyasic.miners.device.models import (
|
||||||
|
S19NoPIC,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MSKMinerS19NoPIC(MSKMiner, S19NoPIC):
|
||||||
|
pass
|
||||||
17
pyasic/miners/antminer/mskminer/X19/__init__.py
Normal file
17
pyasic/miners/antminer/mskminer/X19/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .S19 import MSKMinerS19NoPIC
|
||||||
17
pyasic/miners/antminer/mskminer/__init__.py
Normal file
17
pyasic/miners/antminer/mskminer/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .X19 import *
|
||||||
22
pyasic/miners/avalonminer/cgminer/A15X/A1566.py
Normal file
22
pyasic/miners/avalonminer/cgminer/A15X/A1566.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from pyasic.miners.backends import AvalonMiner
|
||||||
|
from pyasic.miners.device.models import Avalon1566
|
||||||
|
|
||||||
|
|
||||||
|
class CGMinerAvalon1566(AvalonMiner, Avalon1566):
|
||||||
|
pass
|
||||||
17
pyasic/miners/avalonminer/cgminer/A15X/__init__.py
Normal file
17
pyasic/miners/avalonminer/cgminer/A15X/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .A1566 import CGMinerAvalon1566
|
||||||
@@ -20,4 +20,5 @@ from .A9X import *
|
|||||||
from .A10X import *
|
from .A10X import *
|
||||||
from .A11X import *
|
from .A11X import *
|
||||||
from .A12X import *
|
from .A12X import *
|
||||||
|
from .A15X import *
|
||||||
from .nano import *
|
from .nano import *
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ AVALON_NANO_DATA_LOC = DataLocations(
|
|||||||
"_get_wattage_limit",
|
"_get_wattage_limit",
|
||||||
[RPCAPICommand("rpc_stats", "stats")],
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
),
|
),
|
||||||
|
str(DataOptions.WATTAGE): DataFunction(
|
||||||
|
"_get_wattage",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
str(DataOptions.FANS): DataFunction(
|
str(DataOptions.FANS): DataFunction(
|
||||||
"_get_fans",
|
"_get_fans",
|
||||||
[RPCAPICommand("rpc_stats", "stats")],
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ from .innosilicon import Innosilicon
|
|||||||
from .luckyminer import LuckyMiner
|
from .luckyminer import LuckyMiner
|
||||||
from .luxminer import LUXMiner
|
from .luxminer import LUXMiner
|
||||||
from .marathon import MaraMiner
|
from .marathon import MaraMiner
|
||||||
|
from .mskminer import MSKMiner
|
||||||
from .unknown import UnknownMiner
|
from .unknown import UnknownMiner
|
||||||
from .vnish import VNish
|
from .vnish import VNish
|
||||||
from .whatsminer import M2X, M3X, M5X, M6X, M7X
|
from .whatsminer import M2X, M3X, M5X, M6X, M7X
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ AVALON_DATA_LOC = DataLocations(
|
|||||||
"_get_wattage_limit",
|
"_get_wattage_limit",
|
||||||
[RPCAPICommand("rpc_stats", "stats")],
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
),
|
),
|
||||||
|
str(DataOptions.WATTAGE): DataFunction(
|
||||||
|
"_get_wattage",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
str(DataOptions.FANS): DataFunction(
|
str(DataOptions.FANS): DataFunction(
|
||||||
"_get_fans",
|
"_get_fans",
|
||||||
[RPCAPICommand("rpc_stats", "stats")],
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
@@ -289,6 +293,21 @@ class AvalonMiner(CGMiner):
|
|||||||
except (IndexError, KeyError, ValueError, TypeError):
|
except (IndexError, KeyError, ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
|
||||||
|
if rpc_stats is None:
|
||||||
|
try:
|
||||||
|
rpc_stats = await self.rpc.stats()
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if rpc_stats is not None:
|
||||||
|
try:
|
||||||
|
unparsed_stats = rpc_stats["STATS"][0]["MM ID0"]
|
||||||
|
parsed_stats = self.parse_stats(unparsed_stats)
|
||||||
|
return int(parsed_stats["WALLPOWER"][0])
|
||||||
|
except (IndexError, KeyError, ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
|
async def _get_fans(self, rpc_stats: dict = None) -> List[Fan]:
|
||||||
if rpc_stats is None:
|
if rpc_stats is None:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -15,9 +15,10 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from pyasic import APIError
|
from pyasic import APIError, MinerConfig
|
||||||
from pyasic.data import Fan, HashBoard, X19Error
|
from pyasic.data import Fan, HashBoard, X19Error
|
||||||
from pyasic.data.error_codes import MinerErrorData
|
from pyasic.data.error_codes import MinerErrorData
|
||||||
|
from pyasic.data.pools import PoolMetrics, PoolUrl
|
||||||
from pyasic.device.algorithm import AlgoHashRate
|
from pyasic.device.algorithm import AlgoHashRate
|
||||||
from pyasic.miners.data import (
|
from pyasic.miners.data import (
|
||||||
DataFunction,
|
DataFunction,
|
||||||
@@ -74,6 +75,10 @@ ELPHAPEX_DATA_LOC = DataLocations(
|
|||||||
"_get_uptime",
|
"_get_uptime",
|
||||||
[WebAPICommand("web_summary", "summary")],
|
[WebAPICommand("web_summary", "summary")],
|
||||||
),
|
),
|
||||||
|
str(DataOptions.POOLS): DataFunction(
|
||||||
|
"_get_pools",
|
||||||
|
[WebAPICommand("web_pools", "pools")],
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,6 +91,16 @@ class ElphapexMiner(StockFirmware):
|
|||||||
|
|
||||||
data_locations = ELPHAPEX_DATA_LOC
|
data_locations = ELPHAPEX_DATA_LOC
|
||||||
|
|
||||||
|
async def get_config(self) -> MinerConfig:
|
||||||
|
data = await self.web.get_miner_conf()
|
||||||
|
if data:
|
||||||
|
self.config = MinerConfig.from_elphapex(data)
|
||||||
|
return self.config
|
||||||
|
|
||||||
|
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||||
|
self.config = config
|
||||||
|
await self.web.set_miner_conf(config.as_elphapex(user_suffix=user_suffix))
|
||||||
|
|
||||||
async def fault_light_on(self) -> bool:
|
async def fault_light_on(self) -> bool:
|
||||||
data = await self.web.blink(blink=True)
|
data = await self.web.blink(blink=True)
|
||||||
if data:
|
if data:
|
||||||
@@ -317,3 +332,43 @@ class ElphapexMiner(StockFirmware):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
return fans
|
return fans
|
||||||
|
|
||||||
|
async def _get_pools(self, web_pools: list = None) -> List[PoolMetrics]:
|
||||||
|
if web_pools is None:
|
||||||
|
try:
|
||||||
|
web_pools = await self.web.pools()
|
||||||
|
except APIError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
active_pool_index = None
|
||||||
|
highest_priority = float("inf")
|
||||||
|
|
||||||
|
for pool_info in web_pools["POOLS"]:
|
||||||
|
if (
|
||||||
|
pool_info.get("status") == "Alive"
|
||||||
|
and pool_info.get("priority", float("inf")) < highest_priority
|
||||||
|
):
|
||||||
|
highest_priority = pool_info["priority"]
|
||||||
|
active_pool_index = pool_info["index"]
|
||||||
|
|
||||||
|
pools_data = []
|
||||||
|
if web_pools is not None:
|
||||||
|
try:
|
||||||
|
for pool_info in web_pools["POOLS"]:
|
||||||
|
url = pool_info.get("url")
|
||||||
|
pool_url = PoolUrl.from_str(url) if url else None
|
||||||
|
pool_data = PoolMetrics(
|
||||||
|
accepted=pool_info.get("accepted"),
|
||||||
|
rejected=pool_info.get("rejected"),
|
||||||
|
get_failures=pool_info.get("stale"),
|
||||||
|
remote_failures=pool_info.get("discarded"),
|
||||||
|
active=pool_info.get("index") == active_pool_index,
|
||||||
|
alive=pool_info.get("status") == "Alive",
|
||||||
|
url=pool_url,
|
||||||
|
user=pool_info.get("user"),
|
||||||
|
index=pool_info.get("index"),
|
||||||
|
)
|
||||||
|
pools_data.append(pool_data)
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
return pools_data
|
||||||
|
|||||||
110
pyasic/miners/backends/mskminer.py
Normal file
110
pyasic/miners/backends/mskminer.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pyasic import APIError
|
||||||
|
from pyasic.device.algorithm import AlgoHashRate
|
||||||
|
from pyasic.miners.backends import BMMiner
|
||||||
|
from pyasic.miners.data import (
|
||||||
|
DataFunction,
|
||||||
|
DataLocations,
|
||||||
|
DataOptions,
|
||||||
|
RPCAPICommand,
|
||||||
|
WebAPICommand,
|
||||||
|
)
|
||||||
|
from pyasic.web.mskminer import MSKMinerWebAPI
|
||||||
|
|
||||||
|
MSKMINER_DATA_LOC = DataLocations(
|
||||||
|
**{
|
||||||
|
str(DataOptions.API_VERSION): DataFunction(
|
||||||
|
"_get_api_ver",
|
||||||
|
[RPCAPICommand("rpc_version", "version")],
|
||||||
|
),
|
||||||
|
str(DataOptions.FW_VERSION): DataFunction(
|
||||||
|
"_get_fw_ver",
|
||||||
|
[RPCAPICommand("rpc_version", "version")],
|
||||||
|
),
|
||||||
|
str(DataOptions.MAC): DataFunction(
|
||||||
|
"_get_mac",
|
||||||
|
[WebAPICommand("web_info_v1", "info_v1")],
|
||||||
|
),
|
||||||
|
str(DataOptions.HASHRATE): DataFunction(
|
||||||
|
"_get_hashrate",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
|
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||||
|
"_get_expected_hashrate",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
|
str(DataOptions.HASHBOARDS): DataFunction(
|
||||||
|
"_get_hashboards",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
|
str(DataOptions.WATTAGE): DataFunction(
|
||||||
|
"_get_wattage",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
|
str(DataOptions.FANS): DataFunction(
|
||||||
|
"_get_fans",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
|
str(DataOptions.UPTIME): DataFunction(
|
||||||
|
"_get_uptime",
|
||||||
|
[RPCAPICommand("rpc_stats", "stats")],
|
||||||
|
),
|
||||||
|
str(DataOptions.POOLS): DataFunction(
|
||||||
|
"_get_pools",
|
||||||
|
[RPCAPICommand("rpc_pools", "pools")],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MSKMiner(BMMiner):
|
||||||
|
"""Handler for MSKMiner"""
|
||||||
|
|
||||||
|
data_locations = MSKMINER_DATA_LOC
|
||||||
|
|
||||||
|
web: MSKMinerWebAPI
|
||||||
|
_web_cls = MSKMinerWebAPI
|
||||||
|
|
||||||
|
async def _get_hashrate(self, rpc_stats: dict = None) -> Optional[AlgoHashRate]:
|
||||||
|
# get hr from API
|
||||||
|
if rpc_stats is None:
|
||||||
|
try:
|
||||||
|
rpc_stats = await self.rpc.stats()
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if rpc_stats is not None:
|
||||||
|
try:
|
||||||
|
return self.algo.hashrate(
|
||||||
|
rate=float(rpc_stats["STATS"][0]["total_rate"]),
|
||||||
|
unit=self.algo.unit.GH,
|
||||||
|
).into(self.algo.unit.default)
|
||||||
|
except (LookupError, ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
|
||||||
|
if rpc_stats is None:
|
||||||
|
try:
|
||||||
|
rpc_stats = await self.rpc.stats()
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if rpc_stats is not None:
|
||||||
|
try:
|
||||||
|
return rpc_stats["STATS"][0]["total_power"]
|
||||||
|
except (LookupError, ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def _get_mac(self, web_info_v1: dict = None) -> Optional[str]:
|
||||||
|
if web_info_v1 is None:
|
||||||
|
try:
|
||||||
|
web_info_v1 = await self.web.info_v1()
|
||||||
|
except APIError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if web_info_v1 is not None:
|
||||||
|
try:
|
||||||
|
return web_info_v1["network_info"]["result"]["macaddr"].upper()
|
||||||
|
except (LookupError, ValueError, TypeError):
|
||||||
|
pass
|
||||||
27
pyasic/miners/device/models/avalonminer/A15X/A1566.py
Normal file
27
pyasic/miners/device/models/avalonminer/A15X/A1566.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from pyasic.device.algorithm import MinerAlgo
|
||||||
|
from pyasic.device.models import MinerModel
|
||||||
|
from pyasic.miners.device.makes import AvalonMinerMake
|
||||||
|
|
||||||
|
|
||||||
|
class Avalon1566(AvalonMinerMake):
|
||||||
|
raw_model = MinerModel.AVALONMINER.Avalon1566
|
||||||
|
|
||||||
|
expected_chips = 160
|
||||||
|
expected_fans = 2
|
||||||
|
expected_hashboards = 3
|
||||||
|
algo = MinerAlgo.SHA256
|
||||||
17
pyasic/miners/device/models/avalonminer/A15X/__init__.py
Normal file
17
pyasic/miners/device/models/avalonminer/A15X/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2022 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from .A1566 import Avalon1566
|
||||||
@@ -20,4 +20,5 @@ from .A9X import *
|
|||||||
from .A10X import *
|
from .A10X import *
|
||||||
from .A11X import *
|
from .A11X import *
|
||||||
from .A12X import *
|
from .A12X import *
|
||||||
|
from .A15X import *
|
||||||
from .nano import *
|
from .nano import *
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class MinerTypes(enum.Enum):
|
|||||||
VOLCMINER = 15
|
VOLCMINER = 15
|
||||||
LUCKYMINER = 16
|
LUCKYMINER = 16
|
||||||
ELPHAPEX = 17
|
ELPHAPEX = 17
|
||||||
|
MSKMINER = 18
|
||||||
|
|
||||||
|
|
||||||
MINER_CLASSES = {
|
MINER_CLASSES = {
|
||||||
@@ -504,6 +505,7 @@ MINER_CLASSES = {
|
|||||||
"AVALONMINER 1166PRO": CGMinerAvalon1166Pro,
|
"AVALONMINER 1166PRO": CGMinerAvalon1166Pro,
|
||||||
"AVALONMINER 1246": CGMinerAvalon1246,
|
"AVALONMINER 1246": CGMinerAvalon1246,
|
||||||
"AVALONMINER NANO3": CGMinerAvalonNano3,
|
"AVALONMINER NANO3": CGMinerAvalonNano3,
|
||||||
|
"AVALONMINER 15-194": CGMinerAvalon1566,
|
||||||
},
|
},
|
||||||
MinerTypes.INNOSILICON: {
|
MinerTypes.INNOSILICON: {
|
||||||
None: type("InnosiliconUnknown", (Innosilicon, InnosiliconMake), {}),
|
None: type("InnosiliconUnknown", (Innosilicon, InnosiliconMake), {}),
|
||||||
@@ -600,6 +602,10 @@ MINER_CLASSES = {
|
|||||||
"ANTMINER S19": HiveonS19,
|
"ANTMINER S19": HiveonS19,
|
||||||
"ANTMINER S19X88": HiveonS19NoPIC,
|
"ANTMINER S19X88": HiveonS19NoPIC,
|
||||||
},
|
},
|
||||||
|
MinerTypes.MSKMINER: {
|
||||||
|
None: MSKMiner,
|
||||||
|
"S19-88": MSKMinerS19NoPIC,
|
||||||
|
},
|
||||||
MinerTypes.LUX_OS: {
|
MinerTypes.LUX_OS: {
|
||||||
None: LUXMiner,
|
None: LUXMiner,
|
||||||
"ANTMINER S9": LUXMinerS9,
|
"ANTMINER S9": LUXMinerS9,
|
||||||
@@ -869,6 +875,8 @@ class MinerFactory:
|
|||||||
return MinerTypes.INNOSILICON
|
return MinerTypes.INNOSILICON
|
||||||
if "Miner UI" in web_text:
|
if "Miner UI" in web_text:
|
||||||
return MinerTypes.AURADINE
|
return MinerTypes.AURADINE
|
||||||
|
if "<title>Antminer</title>" in web_text:
|
||||||
|
return MinerTypes.MSKMINER
|
||||||
|
|
||||||
async def _get_miner_socket(self, ip: str) -> MinerTypes | None:
|
async def _get_miner_socket(self, ip: str) -> MinerTypes | None:
|
||||||
commands = ["version", "devdetails"]
|
commands = ["version", "devdetails"]
|
||||||
@@ -945,6 +953,8 @@ class MinerFactory:
|
|||||||
return MinerTypes.HIVEON
|
return MinerTypes.HIVEON
|
||||||
if "KAONSU" in upper_data:
|
if "KAONSU" in upper_data:
|
||||||
return MinerTypes.MARATHON
|
return MinerTypes.MARATHON
|
||||||
|
if "RWGLR" in upper_data:
|
||||||
|
return MinerTypes.MSKMINER
|
||||||
if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data:
|
if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data:
|
||||||
return MinerTypes.ANTMINER
|
return MinerTypes.ANTMINER
|
||||||
if (
|
if (
|
||||||
@@ -1160,9 +1170,9 @@ class MinerFactory:
|
|||||||
miner_model = sock_json_data["VERSION"][0]["PROD"].upper()
|
miner_model = sock_json_data["VERSION"][0]["PROD"].upper()
|
||||||
if "-" in miner_model:
|
if "-" in miner_model:
|
||||||
miner_model = miner_model.split("-")[0]
|
miner_model = miner_model.split("-")[0]
|
||||||
if miner_model in ["AVALONNANO", "AVALON0O"]:
|
if miner_model in ["AVALONNANO", "AVALON0O", "AVALONMINER 15"]:
|
||||||
nano_subtype = sock_json_data["VERSION"][0]["MODEL"].upper()
|
subtype = sock_json_data["VERSION"][0]["MODEL"].upper()
|
||||||
miner_model = f"AVALONMINER {nano_subtype}"
|
miner_model = f"AVALONMINER {subtype}"
|
||||||
return miner_model
|
return miner_model
|
||||||
except (TypeError, LookupError):
|
except (TypeError, LookupError):
|
||||||
pass
|
pass
|
||||||
@@ -1410,6 +1420,13 @@ class MinerFactory:
|
|||||||
except (TypeError, LookupError):
|
except (TypeError, LookupError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
async def get_miner_model_mskminer(self, ip: str) -> str | None:
|
||||||
|
sock_json_data = await self.send_api_command(ip, "version")
|
||||||
|
try:
|
||||||
|
return sock_json_data["VERSION"][0]["Type"].split(" ")[0]
|
||||||
|
except LookupError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
miner_factory = MinerFactory()
|
miner_factory = MinerFactory()
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ _settings = { # defaults
|
|||||||
"default_hive_web_password": "root",
|
"default_hive_web_password": "root",
|
||||||
"default_iceriver_web_password": "12345678",
|
"default_iceriver_web_password": "12345678",
|
||||||
"default_elphapex_web_password": "root",
|
"default_elphapex_web_password": "root",
|
||||||
|
"default_mskminer_web_password": "root",
|
||||||
"default_antminer_ssh_password": "miner",
|
"default_antminer_ssh_password": "miner",
|
||||||
"default_bosminer_ssh_password": "root",
|
"default_bosminer_ssh_password": "root",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,3 +214,11 @@ class ElphapexWebAPI(BaseWebAPI):
|
|||||||
dict: A dictionary indicating whether the LED is currently blinking.
|
dict: A dictionary indicating whether the LED is currently blinking.
|
||||||
"""
|
"""
|
||||||
return await self.send_command("get_blink_status")
|
return await self.send_command("get_blink_status")
|
||||||
|
|
||||||
|
async def pools(self) -> dict:
|
||||||
|
"""Check the status of the miner's pools.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: A dictionary containing the pool status as information.
|
||||||
|
"""
|
||||||
|
return await self.send_command("pools")
|
||||||
|
|||||||
72
pyasic/web/mskminer.py
Normal file
72
pyasic/web/mskminer.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright 2024 Upstream Data Inc -
|
||||||
|
# -
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||||
|
# you may not use this file except in compliance with the License. -
|
||||||
|
# You may obtain a copy of the License at -
|
||||||
|
# -
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||||
|
# -
|
||||||
|
# Unless required by applicable law or agreed to in writing, software -
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||||
|
# See the License for the specific language governing permissions and -
|
||||||
|
# limitations under the License. -
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import warnings
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from pyasic import settings
|
||||||
|
from pyasic.errors import APIError
|
||||||
|
from pyasic.web.base import BaseWebAPI
|
||||||
|
|
||||||
|
|
||||||
|
class MSKMinerWebAPI(BaseWebAPI):
|
||||||
|
def __init__(self, ip: str) -> None:
|
||||||
|
super().__init__(ip)
|
||||||
|
self.username = "admin"
|
||||||
|
self.pwd = settings.get("default_mskminer_web_password", "root")
|
||||||
|
|
||||||
|
async def multicommand(
|
||||||
|
self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
|
||||||
|
) -> dict:
|
||||||
|
tasks = {c: asyncio.create_task(getattr(self, c)()) for c in commands}
|
||||||
|
await asyncio.gather(*[t for t in tasks.values()])
|
||||||
|
return {t: tasks[t].result() for t in tasks}
|
||||||
|
|
||||||
|
async def send_command(
|
||||||
|
self,
|
||||||
|
command: str | bytes,
|
||||||
|
ignore_errors: bool = False,
|
||||||
|
allow_warning: bool = True,
|
||||||
|
privileged: bool = False,
|
||||||
|
**parameters: Any,
|
||||||
|
) -> dict:
|
||||||
|
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||||
|
try:
|
||||||
|
# auth
|
||||||
|
await client.post(
|
||||||
|
f"http://{self.ip}:{self.port}/admin/login",
|
||||||
|
data={"username": self.username, "password": self.pwd},
|
||||||
|
)
|
||||||
|
except httpx.HTTPError:
|
||||||
|
warnings.warn(f"Could not authenticate with miner web: {self}")
|
||||||
|
try:
|
||||||
|
resp = await client.post(
|
||||||
|
f"http://{self.ip}:{self.port}/api/{command}", params=parameters
|
||||||
|
)
|
||||||
|
if not resp.status_code == 200:
|
||||||
|
if not ignore_errors:
|
||||||
|
raise APIError(f"Command failed: {command}")
|
||||||
|
warnings.warn(f"Command failed: {command}")
|
||||||
|
return resp.json()
|
||||||
|
except httpx.HTTPError:
|
||||||
|
raise APIError(f"Command failed: {command}")
|
||||||
|
|
||||||
|
async def info_v1(self):
|
||||||
|
return await self.send_command("info_v1")
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "pyasic"
|
name = "pyasic"
|
||||||
version = "0.71.5"
|
version = "0.71.10"
|
||||||
|
|
||||||
description = "A simplified and standardized interface for Bitcoin ASICs."
|
description = "A simplified and standardized interface for Bitcoin ASICs."
|
||||||
authors = [{name = "UpstreamData", email = "brett@upstreamdata.ca"}]
|
authors = [{name = "UpstreamData", email = "brett@upstreamdata.ca"}]
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
from tests.config_tests import TestConfig
|
from tests.config_tests import TestConfig
|
||||||
from tests.miners_tests import MinersTest, TestElphapexMiners, TestHammerMiners
|
from tests.miners_tests import *
|
||||||
from tests.network_tests import NetworkTest
|
from tests.network_tests import NetworkTest
|
||||||
from tests.rpc_tests import *
|
from tests.rpc_tests import *
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from .alphapex_tests import *
|
from .avalonminer_tests import *
|
||||||
|
from .elphapex_tests import *
|
||||||
from .hammer_tests import *
|
from .hammer_tests import *
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from .version_24102401_25462b2_9ddf522 import TestAvalonMiners
|
||||||
File diff suppressed because one or more lines are too long
@@ -9,6 +9,25 @@ from pyasic.data import Fan, HashBoard
|
|||||||
from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit
|
from pyasic.device.algorithm.hashrate.unit.scrypt import ScryptUnit
|
||||||
from pyasic.miners.elphapex import ElphapexDG1Plus
|
from pyasic.miners.elphapex import ElphapexDG1Plus
|
||||||
|
|
||||||
|
POOLS = [
|
||||||
|
{
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3333",
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"pwd": "123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3334",
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"pwd": "123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3335",
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"pwd": "123",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
ElphapexDG1Plus: {
|
ElphapexDG1Plus: {
|
||||||
"web_get_system_info": {
|
"web_get_system_info": {
|
||||||
@@ -190,6 +209,81 @@ data = {
|
|||||||
"fc-work-mode": 0,
|
"fc-work-mode": 0,
|
||||||
"fc-freq": "1850",
|
"fc-freq": "1850",
|
||||||
},
|
},
|
||||||
|
"web_pools": {
|
||||||
|
"STATUS": {
|
||||||
|
"STATUS": "S",
|
||||||
|
"when": 5411762,
|
||||||
|
"timestamp": 1738768594,
|
||||||
|
"api_version": "1.0.0",
|
||||||
|
"Msg": "pools",
|
||||||
|
},
|
||||||
|
"Device Total Rejected": 8888,
|
||||||
|
"POOLS": [
|
||||||
|
{
|
||||||
|
"diffs": 0,
|
||||||
|
"diffr": 524288,
|
||||||
|
"index": 0,
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"lsdiff": 524288,
|
||||||
|
"lstime": "00:00:18",
|
||||||
|
"diffa": 524288,
|
||||||
|
"accepted": 798704,
|
||||||
|
"diff1": 0,
|
||||||
|
"stale": 0,
|
||||||
|
"diff": "",
|
||||||
|
"rejected": 3320,
|
||||||
|
"status": "Unreachable",
|
||||||
|
"getworks": 802024,
|
||||||
|
"priority": 0,
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3333",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"diffs": 0,
|
||||||
|
"diffr": 524288,
|
||||||
|
"index": 1,
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"lsdiff": 524288,
|
||||||
|
"lstime": "00:00:00",
|
||||||
|
"diffa": 524288,
|
||||||
|
"accepted": 604803,
|
||||||
|
"diff1": 0,
|
||||||
|
"stale": 0,
|
||||||
|
"diff": "",
|
||||||
|
"rejected": 2492,
|
||||||
|
"status": "Alive",
|
||||||
|
"getworks": 607295,
|
||||||
|
"priority": 1,
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3334",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"diffs": 0,
|
||||||
|
"diffr": 524288,
|
||||||
|
"index": 2,
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"lsdiff": 524288,
|
||||||
|
"lstime": "00:00:05",
|
||||||
|
"diffa": 524288,
|
||||||
|
"accepted": 691522,
|
||||||
|
"diff1": 0,
|
||||||
|
"stale": 0,
|
||||||
|
"diff": "",
|
||||||
|
"rejected": 3076,
|
||||||
|
"status": "Unreachable",
|
||||||
|
"getworks": 694598,
|
||||||
|
"priority": 2,
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3335",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"Device Rejected%": 0.41999999999999998,
|
||||||
|
"Device Total Work": 2103917,
|
||||||
|
"INFO": {
|
||||||
|
"miner_version": "DG1+_SW_V1.0.2",
|
||||||
|
"CompileTime": "",
|
||||||
|
"dev_sn": "28HY245192N000245C23B",
|
||||||
|
"type": "DG1+",
|
||||||
|
"hw_version": "DG1+_HW_V1.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,3 +334,6 @@ class TestElphapexMiners(unittest.IsolatedAsyncioTestCase):
|
|||||||
[Fan(speed=5340), Fan(speed=5400), Fan(speed=5400), Fan(speed=5400)],
|
[Fan(speed=5340), Fan(speed=5400), Fan(speed=5400), Fan(speed=5400)],
|
||||||
)
|
)
|
||||||
self.assertEqual(result.total_chips, result.expected_chips)
|
self.assertEqual(result.total_chips, result.expected_chips)
|
||||||
|
self.assertEqual(
|
||||||
|
set([str(p.url) for p in result.pools]), set(p["url"] for p in POOLS)
|
||||||
|
)
|
||||||
@@ -16,12 +16,12 @@ POOLS = [
|
|||||||
"pwd": "123",
|
"pwd": "123",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "stratum+tcp://stratum.pool.io:3333",
|
"url": "stratum+tcp://stratum.pool.io:3334",
|
||||||
"user": "pool_username.real_worker",
|
"user": "pool_username.real_worker",
|
||||||
"pwd": "123",
|
"pwd": "123",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"url": "stratum+tcp://stratum.pool.io:3333",
|
"url": "stratum+tcp://stratum.pool.io:3335",
|
||||||
"user": "pool_username.real_worker",
|
"user": "pool_username.real_worker",
|
||||||
"pwd": "123",
|
"pwd": "123",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from .version_2_6_0_39 import TestMSKMiners
|
||||||
@@ -0,0 +1,500 @@
|
|||||||
|
"""Tests for MSK miner firmware version 2.6.0.39"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from dataclasses import fields
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from pyasic import APIError, MinerData
|
||||||
|
from pyasic.data import Fan, HashBoard
|
||||||
|
from pyasic.device.algorithm import SHA256Unit
|
||||||
|
from pyasic.miners.antminer import MSKMinerS19NoPIC
|
||||||
|
|
||||||
|
POOLS = [
|
||||||
|
{
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3333",
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"pwd": "123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3334",
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"pwd": "123",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "stratum+tcp://stratum.pool.io:3335",
|
||||||
|
"user": "pool_username.real_worker",
|
||||||
|
"pwd": "123",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
data = {
|
||||||
|
MSKMinerS19NoPIC: {
|
||||||
|
"web_info_v1": {
|
||||||
|
# needs updates with real data
|
||||||
|
"network_info": {
|
||||||
|
"result": {
|
||||||
|
"address": "192.168.1.10",
|
||||||
|
"macaddr": "12:34:56:78:90:12",
|
||||||
|
"netmask": "255.255.255.0",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rpc_version": {
|
||||||
|
"STATUS": [
|
||||||
|
{
|
||||||
|
"STATUS": "S",
|
||||||
|
"When": 1738856891,
|
||||||
|
"Code": 22,
|
||||||
|
"Msg": "BMMiner versions",
|
||||||
|
"Description": "bmminer 1.0.0",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"VERSION": [
|
||||||
|
{
|
||||||
|
"BMMiner": "4.11.1 rwglr",
|
||||||
|
"API": "3.1",
|
||||||
|
"Miner": "0.0.1.3",
|
||||||
|
"CompileTime": "10 Dec 2024 14:34:31 GMT",
|
||||||
|
"Type": "S19-88 v.2.6.0.39 ",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": 1,
|
||||||
|
},
|
||||||
|
"rpc_stats": {
|
||||||
|
"STATUS": [
|
||||||
|
{
|
||||||
|
"STATUS": "S",
|
||||||
|
"When": 1738856891,
|
||||||
|
"Code": 70,
|
||||||
|
"Msg": "BMMiner stats",
|
||||||
|
"Description": "bmminer 1.0.0",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"STATS": [
|
||||||
|
{
|
||||||
|
"BMMiner": "4.11.1 rwglr",
|
||||||
|
"Miner": "0.0.1.3",
|
||||||
|
"CompileTime": "10 Dec 2024 14:34:31 GMT",
|
||||||
|
"Type": "S19-88 v.2.6.0.39 ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"STATS": 0,
|
||||||
|
"ID": "BC50",
|
||||||
|
"Elapsed": 1926,
|
||||||
|
"Calls": 0,
|
||||||
|
"Wait": 0.000000,
|
||||||
|
"Max": 0.000000,
|
||||||
|
"Min": 99999999.000000,
|
||||||
|
"GHS 5s": 99989.59,
|
||||||
|
"GHS av": 99761.40,
|
||||||
|
"miner_count": 3,
|
||||||
|
"frequency": "",
|
||||||
|
"fan_num": 4,
|
||||||
|
"fan1": 5010,
|
||||||
|
"fan2": 5160,
|
||||||
|
"fan3": 5070,
|
||||||
|
"fan4": 5040,
|
||||||
|
"fan5": 0,
|
||||||
|
"fan6": 0,
|
||||||
|
"fan7": 0,
|
||||||
|
"fan8": 0,
|
||||||
|
"temp_num": 3,
|
||||||
|
"temp1": 45,
|
||||||
|
"temp2": 45,
|
||||||
|
"temp3": 47,
|
||||||
|
"temp4": 0,
|
||||||
|
"temp5": 0,
|
||||||
|
"temp6": 0,
|
||||||
|
"temp7": 0,
|
||||||
|
"temp8": 0,
|
||||||
|
"temp9": 0,
|
||||||
|
"temp10": 0,
|
||||||
|
"temp11": 0,
|
||||||
|
"temp12": 0,
|
||||||
|
"temp13": 0,
|
||||||
|
"temp14": 0,
|
||||||
|
"temp15": 0,
|
||||||
|
"temp16": 0,
|
||||||
|
"temp2_1": 59,
|
||||||
|
"temp2_2": 57,
|
||||||
|
"temp2_3": 58,
|
||||||
|
"temp2_4": 0,
|
||||||
|
"temp2_5": 0,
|
||||||
|
"temp2_6": 0,
|
||||||
|
"temp2_7": 0,
|
||||||
|
"temp2_8": 0,
|
||||||
|
"temp2_9": 0,
|
||||||
|
"temp2_10": 0,
|
||||||
|
"temp2_11": 0,
|
||||||
|
"temp2_12": 0,
|
||||||
|
"temp2_13": 0,
|
||||||
|
"temp2_14": 0,
|
||||||
|
"temp2_15": 0,
|
||||||
|
"temp2_16": 0,
|
||||||
|
"temp3_1": 59,
|
||||||
|
"temp3_2": 56,
|
||||||
|
"temp3_3": 57,
|
||||||
|
"temp3_4": 0,
|
||||||
|
"temp3_5": 0,
|
||||||
|
"temp3_6": 0,
|
||||||
|
"temp3_7": 0,
|
||||||
|
"temp3_8": 0,
|
||||||
|
"temp3_9": 0,
|
||||||
|
"temp3_10": 0,
|
||||||
|
"temp3_11": 0,
|
||||||
|
"temp3_12": 0,
|
||||||
|
"temp3_13": 0,
|
||||||
|
"temp3_14": 0,
|
||||||
|
"temp3_15": 0,
|
||||||
|
"temp3_16": 0,
|
||||||
|
"temp_pcb1": "45-42-45-42",
|
||||||
|
"temp_pcb2": "45-42-45-42",
|
||||||
|
"temp_pcb3": "47-43-47-43",
|
||||||
|
"temp_pcb4": "0-0-0-0",
|
||||||
|
"temp_pcb5": "0-0-0-0",
|
||||||
|
"temp_pcb6": "0-0-0-0",
|
||||||
|
"temp_pcb7": "0-0-0-0",
|
||||||
|
"temp_pcb8": "0-0-0-0",
|
||||||
|
"temp_pcb9": "0-0-0-0",
|
||||||
|
"temp_pcb10": "0-0-0-0",
|
||||||
|
"temp_pcb11": "0-0-0-0",
|
||||||
|
"temp_pcb12": "0-0-0-0",
|
||||||
|
"temp_pcb13": "0-0-0-0",
|
||||||
|
"temp_pcb14": "0-0-0-0",
|
||||||
|
"temp_pcb15": "0-0-0-0",
|
||||||
|
"temp_pcb16": "0-0-0-0",
|
||||||
|
"temp_chip1": "59-59-59-59",
|
||||||
|
"temp_chip2": "57-56-57-56",
|
||||||
|
"temp_chip3": "58-57-58-57",
|
||||||
|
"temp_chip4": "0-0-0-0",
|
||||||
|
"temp_chip5": "0-0-0-0",
|
||||||
|
"temp_chip6": "0-0-0-0",
|
||||||
|
"temp_chip7": "0-0-0-0",
|
||||||
|
"temp_chip8": "0-0-0-0",
|
||||||
|
"temp_chip9": "0-0-0-0",
|
||||||
|
"temp_chip10": "0-0-0-0",
|
||||||
|
"temp_chip11": "0-0-0-0",
|
||||||
|
"temp_chip12": "0-0-0-0",
|
||||||
|
"temp_chip13": "0-0-0-0",
|
||||||
|
"temp_chip14": "0-0-0-0",
|
||||||
|
"temp_chip15": "0-0-0-0",
|
||||||
|
"temp_chip16": "0-0-0-0",
|
||||||
|
"total_rateideal": 99674.88,
|
||||||
|
"total_freqavg": 0.00,
|
||||||
|
"total_acn": 264,
|
||||||
|
"total_rate": 99989.59,
|
||||||
|
"chain_rateideal1": 33677.28,
|
||||||
|
"chain_rateideal2": 32788.06,
|
||||||
|
"chain_rateideal3": 33209.54,
|
||||||
|
"chain_rateideal4": 31436.24,
|
||||||
|
"chain_rateideal5": 31436.24,
|
||||||
|
"chain_rateideal6": 31436.24,
|
||||||
|
"chain_rateideal7": 31436.24,
|
||||||
|
"chain_rateideal8": 31436.24,
|
||||||
|
"chain_rateideal9": 31436.24,
|
||||||
|
"chain_rateideal10": 31436.24,
|
||||||
|
"chain_rateideal11": 31436.24,
|
||||||
|
"chain_rateideal12": 31436.24,
|
||||||
|
"chain_rateideal13": 31436.24,
|
||||||
|
"chain_rateideal14": 31436.24,
|
||||||
|
"chain_rateideal15": 31436.24,
|
||||||
|
"chain_rateideal16": 31436.24,
|
||||||
|
"temp_max": 47,
|
||||||
|
"no_matching_work": 0,
|
||||||
|
"chain_acn1": 88,
|
||||||
|
"chain_acn2": 88,
|
||||||
|
"chain_acn3": 88,
|
||||||
|
"chain_acn4": 0,
|
||||||
|
"chain_acn5": 0,
|
||||||
|
"chain_acn6": 0,
|
||||||
|
"chain_acn7": 0,
|
||||||
|
"chain_acn8": 0,
|
||||||
|
"chain_acn9": 0,
|
||||||
|
"chain_acn10": 0,
|
||||||
|
"chain_acn11": 0,
|
||||||
|
"chain_acn12": 0,
|
||||||
|
"chain_acn13": 0,
|
||||||
|
"chain_acn14": 0,
|
||||||
|
"chain_acn15": 0,
|
||||||
|
"chain_acn16": 0,
|
||||||
|
"chain_acs1": " oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo",
|
||||||
|
"chain_acs2": " oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo",
|
||||||
|
"chain_acs3": " oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo oo",
|
||||||
|
"chain_acs4": "",
|
||||||
|
"chain_acs5": "",
|
||||||
|
"chain_acs6": "",
|
||||||
|
"chain_acs7": "",
|
||||||
|
"chain_acs8": "",
|
||||||
|
"chain_acs9": "",
|
||||||
|
"chain_acs10": "",
|
||||||
|
"chain_acs11": "",
|
||||||
|
"chain_acs12": "",
|
||||||
|
"chain_acs13": "",
|
||||||
|
"chain_acs14": "",
|
||||||
|
"chain_acs15": "",
|
||||||
|
"chain_acs16": "",
|
||||||
|
"chain_hw1": 0,
|
||||||
|
"chain_hw2": 0,
|
||||||
|
"chain_hw3": 0,
|
||||||
|
"chain_hw4": 0,
|
||||||
|
"chain_hw5": 0,
|
||||||
|
"chain_hw6": 0,
|
||||||
|
"chain_hw7": 0,
|
||||||
|
"chain_hw8": 0,
|
||||||
|
"chain_hw9": 0,
|
||||||
|
"chain_hw10": 0,
|
||||||
|
"chain_hw11": 0,
|
||||||
|
"chain_hw12": 0,
|
||||||
|
"chain_hw13": 0,
|
||||||
|
"chain_hw14": 0,
|
||||||
|
"chain_hw15": 0,
|
||||||
|
"chain_hw16": 0,
|
||||||
|
"chain_rate1": 34084.86,
|
||||||
|
"chain_rate2": 32303.65,
|
||||||
|
"chain_rate3": 33601.08,
|
||||||
|
"chain_rate4": 0.00,
|
||||||
|
"chain_rate5": 0.00,
|
||||||
|
"chain_rate6": 0.00,
|
||||||
|
"chain_rate7": 0.00,
|
||||||
|
"chain_rate8": 0.00,
|
||||||
|
"chain_rate9": 0.00,
|
||||||
|
"chain_rate10": 0.00,
|
||||||
|
"chain_rate11": 0.00,
|
||||||
|
"chain_rate12": 0.00,
|
||||||
|
"chain_rate13": 0.00,
|
||||||
|
"chain_rate14": 0.00,
|
||||||
|
"chain_rate15": 0.00,
|
||||||
|
"chain_rate16": 0.00,
|
||||||
|
"chain_xtime1": "{}",
|
||||||
|
"chain_xtime2": "{}",
|
||||||
|
"chain_xtime3": "{}",
|
||||||
|
"chain_offside_1": "",
|
||||||
|
"chain_offside_2": "",
|
||||||
|
"chain_offside_3": "",
|
||||||
|
"chain_opencore_0": "1",
|
||||||
|
"chain_opencore_1": "1",
|
||||||
|
"chain_opencore_2": "1",
|
||||||
|
"freq1": 744,
|
||||||
|
"freq2": 724,
|
||||||
|
"freq3": 734,
|
||||||
|
"freq4": 0,
|
||||||
|
"freq5": 0,
|
||||||
|
"freq6": 0,
|
||||||
|
"freq7": 0,
|
||||||
|
"freq8": 0,
|
||||||
|
"freq9": 0,
|
||||||
|
"freq10": 0,
|
||||||
|
"freq11": 0,
|
||||||
|
"freq12": 0,
|
||||||
|
"freq13": 0,
|
||||||
|
"freq14": 0,
|
||||||
|
"freq15": 0,
|
||||||
|
"freq16": 0,
|
||||||
|
"chain_avgrate1": 33585.34,
|
||||||
|
"chain_avgrate2": 32788.97,
|
||||||
|
"chain_avgrate3": 33336.44,
|
||||||
|
"chain_avgrate4": 0.00,
|
||||||
|
"chain_avgrate5": 0.00,
|
||||||
|
"chain_avgrate6": 0.00,
|
||||||
|
"chain_avgrate7": 0.00,
|
||||||
|
"chain_avgrate8": 0.00,
|
||||||
|
"chain_avgrate9": 0.00,
|
||||||
|
"chain_avgrate10": 0.00,
|
||||||
|
"chain_avgrate11": 0.00,
|
||||||
|
"chain_avgrate12": 0.00,
|
||||||
|
"chain_avgrate13": 0.00,
|
||||||
|
"chain_avgrate14": 0.00,
|
||||||
|
"chain_avgrate15": 0.00,
|
||||||
|
"chain_avgrate16": 0.00,
|
||||||
|
"miner_version": "0.0.1.3",
|
||||||
|
"miner_id": "",
|
||||||
|
"chain_power1": 1135,
|
||||||
|
"chain_power2": 1103,
|
||||||
|
"chain_power3": 1118,
|
||||||
|
"total_power": 3358,
|
||||||
|
"chain_voltage1": 15.70,
|
||||||
|
"chain_voltage2": 15.70,
|
||||||
|
"chain_voltage3": 15.70,
|
||||||
|
"chain_voltage4": 15.70,
|
||||||
|
"chain_voltage5": 15.70,
|
||||||
|
"chain_voltage6": 15.70,
|
||||||
|
"chain_voltage7": 15.70,
|
||||||
|
"chain_voltage8": 15.70,
|
||||||
|
"chain_voltage9": 15.70,
|
||||||
|
"chain_voltage10": 15.70,
|
||||||
|
"chain_voltage11": 15.70,
|
||||||
|
"chain_voltage12": 15.70,
|
||||||
|
"chain_voltage13": 15.70,
|
||||||
|
"chain_voltage14": 15.70,
|
||||||
|
"chain_voltage15": 15.70,
|
||||||
|
"chain_voltage16": 15.70,
|
||||||
|
"fan_pwm": 82,
|
||||||
|
"bringup_temp": 16,
|
||||||
|
"has_pic": "1",
|
||||||
|
"tune_running": "0",
|
||||||
|
"psu_status": "PSU OK",
|
||||||
|
"downscale_mode": "0",
|
||||||
|
"has_hotel_fee": "0",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"id": 1,
|
||||||
|
},
|
||||||
|
"rpc_pools": {
|
||||||
|
"STATUS": [
|
||||||
|
{
|
||||||
|
"Code": 7,
|
||||||
|
"Description": "cgminer 1.0.0",
|
||||||
|
"Msg": "3 Pool(s)",
|
||||||
|
"STATUS": "S",
|
||||||
|
"When": 1732121693,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"POOLS": [
|
||||||
|
{
|
||||||
|
"Accepted": 10000,
|
||||||
|
"Best Share": 1000000000.0,
|
||||||
|
"Diff": "100K",
|
||||||
|
"Diff1 Shares": 0,
|
||||||
|
"Difficulty Accepted": 1000000000.0,
|
||||||
|
"Difficulty Rejected": 1000000.0,
|
||||||
|
"Difficulty Stale": 0.0,
|
||||||
|
"Discarded": 100000,
|
||||||
|
"Get Failures": 3,
|
||||||
|
"Getworks": 9000,
|
||||||
|
"Has GBT": False,
|
||||||
|
"Has Stratum": True,
|
||||||
|
"Last Share Difficulty": 100000.0,
|
||||||
|
"Last Share Time": "0:00:02",
|
||||||
|
"Long Poll": "N",
|
||||||
|
"POOL": 0,
|
||||||
|
"Pool Rejected%": 0.0,
|
||||||
|
"Pool Stale%%": 0.0,
|
||||||
|
"Priority": 0,
|
||||||
|
"Proxy": "",
|
||||||
|
"Proxy Type": "",
|
||||||
|
"Quota": 1,
|
||||||
|
"Rejected": 100,
|
||||||
|
"Remote Failures": 0,
|
||||||
|
"Stale": 0,
|
||||||
|
"Status": "Alive",
|
||||||
|
"Stratum Active": True,
|
||||||
|
"Stratum URL": "stratum.pool.io",
|
||||||
|
"URL": "stratum+tcp://stratum.pool.io:3333",
|
||||||
|
"User": "pool_username.real_worker",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Accepted": 10000,
|
||||||
|
"Best Share": 1000000000.0,
|
||||||
|
"Diff": "100K",
|
||||||
|
"Diff1 Shares": 0,
|
||||||
|
"Difficulty Accepted": 1000000000.0,
|
||||||
|
"Difficulty Rejected": 1000000.0,
|
||||||
|
"Difficulty Stale": 0.0,
|
||||||
|
"Discarded": 100000,
|
||||||
|
"Get Failures": 3,
|
||||||
|
"Getworks": 9000,
|
||||||
|
"Has GBT": False,
|
||||||
|
"Has Stratum": True,
|
||||||
|
"Last Share Difficulty": 100000.0,
|
||||||
|
"Last Share Time": "0:00:02",
|
||||||
|
"Long Poll": "N",
|
||||||
|
"POOL": 1,
|
||||||
|
"Pool Rejected%": 0.0,
|
||||||
|
"Pool Stale%%": 0.0,
|
||||||
|
"Priority": 0,
|
||||||
|
"Proxy": "",
|
||||||
|
"Proxy Type": "",
|
||||||
|
"Quota": 1,
|
||||||
|
"Rejected": 100,
|
||||||
|
"Remote Failures": 0,
|
||||||
|
"Stale": 0,
|
||||||
|
"Status": "Alive",
|
||||||
|
"Stratum Active": True,
|
||||||
|
"Stratum URL": "stratum.pool.io",
|
||||||
|
"URL": "stratum+tcp://stratum.pool.io:3333",
|
||||||
|
"User": "pool_username.real_worker",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Accepted": 10000,
|
||||||
|
"Best Share": 1000000000.0,
|
||||||
|
"Diff": "100K",
|
||||||
|
"Diff1 Shares": 0,
|
||||||
|
"Difficulty Accepted": 1000000000.0,
|
||||||
|
"Difficulty Rejected": 1000000.0,
|
||||||
|
"Difficulty Stale": 0.0,
|
||||||
|
"Discarded": 100000,
|
||||||
|
"Get Failures": 3,
|
||||||
|
"Getworks": 9000,
|
||||||
|
"Has GBT": False,
|
||||||
|
"Has Stratum": True,
|
||||||
|
"Last Share Difficulty": 100000.0,
|
||||||
|
"Last Share Time": "0:00:02",
|
||||||
|
"Long Poll": "N",
|
||||||
|
"POOL": 2,
|
||||||
|
"Pool Rejected%": 0.0,
|
||||||
|
"Pool Stale%%": 0.0,
|
||||||
|
"Priority": 0,
|
||||||
|
"Proxy": "",
|
||||||
|
"Proxy Type": "",
|
||||||
|
"Quota": 1,
|
||||||
|
"Rejected": 100,
|
||||||
|
"Remote Failures": 0,
|
||||||
|
"Stale": 0,
|
||||||
|
"Status": "Alive",
|
||||||
|
"Stratum Active": True,
|
||||||
|
"Stratum URL": "stratum.pool.io",
|
||||||
|
"URL": "stratum+tcp://stratum.pool.io:3333",
|
||||||
|
"User": "pool_username.real_worker",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"id": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class TestMSKMiners(unittest.IsolatedAsyncioTestCase):
|
||||||
|
@patch("pyasic.rpc.base.BaseMinerRPCAPI._send_bytes")
|
||||||
|
async def test_all_data_gathering(self, mock_send_bytes):
|
||||||
|
mock_send_bytes.raises = APIError()
|
||||||
|
for m_type in data:
|
||||||
|
gathered_data = {}
|
||||||
|
miner = m_type("127.0.0.1")
|
||||||
|
for data_name in fields(miner.data_locations):
|
||||||
|
if data_name.name == "config":
|
||||||
|
# skip
|
||||||
|
continue
|
||||||
|
data_func = getattr(miner.data_locations, data_name.name)
|
||||||
|
fn_args = data_func.kwargs
|
||||||
|
args_to_send = {k.name: data[m_type][k.name] for k in fn_args}
|
||||||
|
function = getattr(miner, data_func.cmd)
|
||||||
|
gathered_data[data_name.name] = await function(**args_to_send)
|
||||||
|
|
||||||
|
result = MinerData(
|
||||||
|
ip=str(miner.ip),
|
||||||
|
device_info=miner.device_info,
|
||||||
|
expected_chips=(
|
||||||
|
miner.expected_chips * miner.expected_hashboards
|
||||||
|
if miner.expected_chips is not None
|
||||||
|
else 0
|
||||||
|
),
|
||||||
|
expected_hashboards=miner.expected_hashboards,
|
||||||
|
expected_fans=miner.expected_fans,
|
||||||
|
hashboards=[
|
||||||
|
HashBoard(slot=i, expected_chips=miner.expected_chips)
|
||||||
|
for i in range(miner.expected_hashboards)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
for item in gathered_data:
|
||||||
|
if gathered_data[item] is not None:
|
||||||
|
setattr(result, item, gathered_data[item])
|
||||||
|
|
||||||
|
self.assertEqual(result.mac, "12:34:56:78:90:12")
|
||||||
|
self.assertEqual(result.api_ver, "3.1")
|
||||||
|
self.assertEqual(result.fw_ver, "10 Dec 2024 14:34:31 GMT")
|
||||||
|
self.assertEqual(round(result.hashrate.into(SHA256Unit.TH)), 100)
|
||||||
|
self.assertEqual(
|
||||||
|
result.fans,
|
||||||
|
[Fan(speed=5010), Fan(speed=5160), Fan(speed=5070), Fan(speed=5040)],
|
||||||
|
)
|
||||||
|
self.assertEqual(result.total_chips, result.expected_chips)
|
||||||
Reference in New Issue
Block a user