Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39f9d087db | ||
|
|
067a376f94 |
@@ -51,6 +51,8 @@ def backend_str(backend: MinerTypes) -> str:
|
||||
return "Mara Firmware Miners"
|
||||
case MinerTypes.BITAXE:
|
||||
return "Stock Firmware BitAxe Miners"
|
||||
case MinerTypes.LUCKYMINER:
|
||||
return "Stock Firmware Lucky Miners"
|
||||
case MinerTypes.ICERIVER:
|
||||
return "Stock Firmware IceRiver Miners"
|
||||
case MinerTypes.HAMMER:
|
||||
|
||||
@@ -495,6 +495,19 @@
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S19i (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [x] Setpoints
|
||||
- [x] Presets
|
||||
|
||||
::: pyasic.miners.antminer.vnish.X19.S19.VNishS19i
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
## S19j (VNish)
|
||||
|
||||
- [x] Shutdowns
|
||||
|
||||
16
docs/miners/luckyminer/LV.md
Normal file
16
docs/miners/luckyminer/LV.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# pyasic
|
||||
## LV Models
|
||||
|
||||
## LV08 (Stock)
|
||||
|
||||
- [ ] Shutdowns
|
||||
- [ ] Power Modes
|
||||
- [ ] Setpoints
|
||||
- [ ] Presets
|
||||
|
||||
::: pyasic.miners.luckyminer.espminer.LV.LV08.LuckyMinerLV08
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 0
|
||||
|
||||
@@ -694,6 +694,7 @@ details {
|
||||
<li><a href="../antminer/X19#s19-no-pic-vnish">S19 No PIC (VNish)</a></li>
|
||||
<li><a href="../antminer/X19#s19-pro-vnish">S19 Pro (VNish)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-vnish">S19j (VNish)</a></li>
|
||||
<li><a href="../antminer/X19#s19i-vnish">S19i (VNish)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro-vnish">S19j Pro (VNish)</a></li>
|
||||
@@ -859,6 +860,17 @@ details {
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Stock Firmware Lucky Miners:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary>LV Series:</summary>
|
||||
<ul>
|
||||
<li><a href="../luckyminer/LV#lv08-stock">LV08 (Stock)</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Stock Firmware IceRiver Miners:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
|
||||
@@ -143,12 +143,12 @@ class MinerConfig(BaseModel):
|
||||
**self.pools.as_mara(user_suffix=user_suffix),
|
||||
}
|
||||
|
||||
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||
return {
|
||||
**self.fan_mode.as_bitaxe(),
|
||||
**self.temperature.as_bitaxe(),
|
||||
**self.mining_mode.as_bitaxe(),
|
||||
**self.pools.as_bitaxe(user_suffix=user_suffix),
|
||||
**self.fan_mode.as_espminer(),
|
||||
**self.temperature.as_espminer(),
|
||||
**self.mining_mode.as_espminer(),
|
||||
**self.pools.as_espminer(user_suffix=user_suffix),
|
||||
}
|
||||
|
||||
def as_luxos(self, user_suffix: str | None = None) -> dict:
|
||||
@@ -272,10 +272,10 @@ class MinerConfig(BaseModel):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bitaxe(cls, web_system_info: dict) -> "MinerConfig":
|
||||
def from_espminer(cls, web_system_info: dict) -> "MinerConfig":
|
||||
return cls(
|
||||
pools=PoolConfig.from_bitaxe(web_system_info),
|
||||
fan_mode=FanModeConfig.from_bitaxe(web_system_info),
|
||||
pools=PoolConfig.from_espminer(web_system_info),
|
||||
fan_mode=FanModeConfig.from_espminer(web_system_info),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -61,8 +61,8 @@ class MinerConfigOption(Enum):
|
||||
def as_mara(self) -> dict:
|
||||
return self.value.as_mara()
|
||||
|
||||
def as_bitaxe(self) -> dict:
|
||||
return self.value.as_bitaxe()
|
||||
def as_espminer(self) -> dict:
|
||||
return self.value.as_espminer()
|
||||
|
||||
def as_luxos(self) -> dict:
|
||||
return self.value.as_luxos()
|
||||
@@ -125,7 +125,7 @@ class MinerConfigValue(BaseModel):
|
||||
def as_mara(self) -> dict:
|
||||
return {}
|
||||
|
||||
def as_bitaxe(self) -> dict:
|
||||
def as_espminer(self) -> dict:
|
||||
return {}
|
||||
|
||||
def as_luxos(self) -> dict:
|
||||
|
||||
@@ -81,7 +81,7 @@ class FanModeNormal(MinerConfigValue):
|
||||
},
|
||||
}
|
||||
|
||||
def as_bitaxe(self) -> dict:
|
||||
def as_espminer(self) -> dict:
|
||||
return {"autoFanspeed": 1}
|
||||
|
||||
def as_luxos(self) -> dict:
|
||||
@@ -156,7 +156,7 @@ class FanModeManual(MinerConfigValue):
|
||||
},
|
||||
}
|
||||
|
||||
def as_bitaxe(self) -> dict:
|
||||
def as_espminer(self) -> dict:
|
||||
return {"autoFanspeed": 0, "fanspeed": self.speed}
|
||||
|
||||
def as_luxos(self) -> dict:
|
||||
@@ -342,7 +342,7 @@ class FanModeConfig(MinerConfigOption):
|
||||
return cls.default()
|
||||
|
||||
@classmethod
|
||||
def from_bitaxe(cls, web_system_info: dict):
|
||||
def from_espminer(cls, web_system_info: dict):
|
||||
if web_system_info["autofanspeed"] == 1:
|
||||
return cls.normal()
|
||||
else:
|
||||
|
||||
@@ -102,7 +102,7 @@ class Pool(MinerConfigValue):
|
||||
"pass": self.password,
|
||||
}
|
||||
|
||||
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||
return {
|
||||
"stratumURL": self.url,
|
||||
"stratumUser": f"{self.user}{user_suffix or ''}",
|
||||
@@ -192,7 +192,7 @@ class Pool(MinerConfigValue):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bitaxe(cls, web_system_info: dict) -> "Pool":
|
||||
def from_espminer(cls, web_system_info: dict) -> "Pool":
|
||||
url = f"stratum+tcp://{web_system_info['stratumURL']}:{web_system_info['stratumPort']}"
|
||||
return cls(
|
||||
url=url,
|
||||
@@ -306,8 +306,8 @@ class PoolGroup(MinerConfigValue):
|
||||
def as_mara(self, user_suffix: str | None = None) -> list:
|
||||
return [p.as_mara(user_suffix=user_suffix) for p in self.pools]
|
||||
|
||||
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||
return self.pools[0].as_bitaxe(user_suffix=user_suffix)
|
||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||
return self.pools[0].as_espminer(user_suffix=user_suffix)
|
||||
|
||||
def as_boser(self, user_suffix: str | None = None) -> PoolGroupConfiguration:
|
||||
return PoolGroupConfiguration(
|
||||
@@ -395,8 +395,8 @@ class PoolGroup(MinerConfigValue):
|
||||
return cls(pools=[Pool.from_mara(pool_conf) for pool_conf in web_config_pools])
|
||||
|
||||
@classmethod
|
||||
def from_bitaxe(cls, web_system_info: dict) -> "PoolGroup":
|
||||
return cls(pools=[Pool.from_bitaxe(web_system_info)])
|
||||
def from_espminer(cls, web_system_info: dict) -> "PoolGroup":
|
||||
return cls(pools=[Pool.from_espminer(web_system_info)])
|
||||
|
||||
@classmethod
|
||||
def from_iceriver(cls, web_userpanel: dict) -> "PoolGroup":
|
||||
@@ -507,8 +507,8 @@ class PoolConfig(MinerConfigValue):
|
||||
return {"pools": self.groups[0].as_mara(user_suffix=user_suffix)}
|
||||
return {"pools": []}
|
||||
|
||||
def as_bitaxe(self, user_suffix: str | None = None) -> dict:
|
||||
return self.groups[0].as_bitaxe(user_suffix=user_suffix)
|
||||
def as_espminer(self, user_suffix: str | None = None) -> dict:
|
||||
return self.groups[0].as_espminer(user_suffix=user_suffix)
|
||||
|
||||
def as_luxos(self, user_suffix: str | None = None) -> dict:
|
||||
return {}
|
||||
@@ -576,8 +576,8 @@ class PoolConfig(MinerConfigValue):
|
||||
return cls(groups=[PoolGroup.from_mara(web_config["pools"])])
|
||||
|
||||
@classmethod
|
||||
def from_bitaxe(cls, web_system_info: dict) -> "PoolConfig":
|
||||
return cls(groups=[PoolGroup.from_bitaxe(web_system_info)])
|
||||
def from_espminer(cls, web_system_info: dict) -> "PoolConfig":
|
||||
return cls(groups=[PoolGroup.from_espminer(web_system_info)])
|
||||
|
||||
@classmethod
|
||||
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":
|
||||
|
||||
@@ -26,6 +26,7 @@ class MinerMake(str, Enum):
|
||||
AURADINE = "Auradine"
|
||||
EPIC = "ePIC"
|
||||
BITAXE = "BitAxe"
|
||||
LUCKYMINER = "LuckyMiner"
|
||||
ICERIVER = "IceRiver"
|
||||
HAMMER = "Hammer"
|
||||
VOLCMINER = "VolcMiner"
|
||||
|
||||
@@ -507,6 +507,13 @@ class BitAxeModels(MinerModelType):
|
||||
return self.value
|
||||
|
||||
|
||||
class LuckyMinerModels(MinerModelType):
|
||||
BM1366 = "LV08"
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class IceRiverModels(MinerModelType):
|
||||
KS0 = "KS0"
|
||||
KS1 = "KS1"
|
||||
@@ -550,6 +557,7 @@ class MinerModel:
|
||||
AURADINE = AuradineModels
|
||||
EPIC = ePICModels
|
||||
BITAXE = BitAxeModels
|
||||
LUCKYMINER = LuckyMinerModels
|
||||
ICERIVER = IceRiverModels
|
||||
HAMMER = HammerModels
|
||||
VOLCMINER = VolcMinerModels
|
||||
|
||||
@@ -28,6 +28,7 @@ from .hammer import BlackMiner
|
||||
from .hiveon import HiveonModern, HiveonOld
|
||||
from .iceriver import IceRiver
|
||||
from .innosilicon import Innosilicon
|
||||
from .luckyminer import LuckyMiner
|
||||
from .luxminer import LUXMiner
|
||||
from .marathon import MaraMiner
|
||||
from .unknown import UnknownMiner
|
||||
|
||||
@@ -1,235 +1,7 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from pyasic import APIError, MinerConfig
|
||||
from pyasic.data import Fan, HashBoard
|
||||
from pyasic.device.algorithm import AlgoHashRate
|
||||
from pyasic.device.firmware import MinerFirmware
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
|
||||
from pyasic.web.bitaxe import BitAxeWebAPI
|
||||
|
||||
BITAXE_DATA_LOC = DataLocations(
|
||||
**{
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"_get_hashrate",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"_get_expected_hashrate",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.WATTAGE): DataFunction(
|
||||
"_get_wattage",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.UPTIME): DataFunction(
|
||||
"_get_uptime",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"_get_hashboards",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction(
|
||||
"_get_hostname",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"_get_fans",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"_get_fw_ver",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
"_get_api_ver",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.MAC): DataFunction(
|
||||
"_get_mac",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
}
|
||||
)
|
||||
from pyasic.miners.backends.espminer import ESPMiner
|
||||
|
||||
|
||||
class BitAxe(BaseMiner):
|
||||
class BitAxe(ESPMiner):
|
||||
"""Handler for BitAxe"""
|
||||
|
||||
web: BitAxeWebAPI
|
||||
_web_cls = BitAxeWebAPI
|
||||
|
||||
firmware = MinerFirmware.STOCK
|
||||
|
||||
data_locations = BITAXE_DATA_LOC
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
await self.web.restart()
|
||||
return True
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
web_system_info = await self.web.system_info()
|
||||
return MinerConfig.from_bitaxe(web_system_info)
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
await self.web.update_settings(**config.as_bitaxe())
|
||||
|
||||
async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return round(web_system_info["power"])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_hashrate(
|
||||
self, web_system_info: dict = None
|
||||
) -> Optional[AlgoHashRate]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return self.algo.hashrate(
|
||||
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.GH
|
||||
).into(self.algo.unit.default)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_expected_hashrate(
|
||||
self, web_system_info: dict = None
|
||||
) -> Optional[AlgoHashRate]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
expected_hashrate = (
|
||||
web_system_info.get("smallCoreCount")
|
||||
* web_system_info.get("asicCount")
|
||||
* web_system_info.get("frequency")
|
||||
)
|
||||
|
||||
return self.algo.hashrate(
|
||||
rate=float(expected_hashrate), unit=self.algo.unit.MH
|
||||
).into(self.algo.unit.default)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_uptime(self, web_system_info: dict = None) -> Optional[int]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["uptimeSeconds"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_hashboards(self, web_system_info: dict = None) -> List[HashBoard]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return [
|
||||
HashBoard(
|
||||
hashrate=self.algo.hashrate(
|
||||
rate=float(web_system_info["hashRate"]),
|
||||
unit=self.algo.unit.GH,
|
||||
).into(self.algo.unit.default),
|
||||
chip_temp=web_system_info.get("temp"),
|
||||
temp=web_system_info.get("vrTemp"),
|
||||
chips=web_system_info.get("asicCount", 1),
|
||||
expected_chips=self.expected_chips,
|
||||
missing=False,
|
||||
active=True,
|
||||
voltage=web_system_info.get("voltage"),
|
||||
)
|
||||
]
|
||||
except KeyError:
|
||||
pass
|
||||
return []
|
||||
|
||||
async def _get_fans(self, web_system_info: dict = None) -> List[Fan]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return [Fan(speed=web_system_info["fanrpm"])]
|
||||
except KeyError:
|
||||
pass
|
||||
return []
|
||||
|
||||
async def _get_hostname(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["hostname"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_api_ver(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["version"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_fw_ver(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["version"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_mac(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["macAddr"].upper()
|
||||
except KeyError:
|
||||
pass
|
||||
pass
|
||||
|
||||
235
pyasic/miners/backends/espminer.py
Normal file
235
pyasic/miners/backends/espminer.py
Normal file
@@ -0,0 +1,235 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from pyasic import APIError, MinerConfig
|
||||
from pyasic.data import Fan, HashBoard
|
||||
from pyasic.device.algorithm import AlgoHashRate
|
||||
from pyasic.device.firmware import MinerFirmware
|
||||
from pyasic.miners.base import BaseMiner
|
||||
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, WebAPICommand
|
||||
from pyasic.web.espminer import ESPMinerWebAPI
|
||||
|
||||
ESPMINER_DATA_LOC = DataLocations(
|
||||
**{
|
||||
str(DataOptions.HASHRATE): DataFunction(
|
||||
"_get_hashrate",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
|
||||
"_get_expected_hashrate",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.WATTAGE): DataFunction(
|
||||
"_get_wattage",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.UPTIME): DataFunction(
|
||||
"_get_uptime",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.HASHBOARDS): DataFunction(
|
||||
"_get_hashboards",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.HOSTNAME): DataFunction(
|
||||
"_get_hostname",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.FANS): DataFunction(
|
||||
"_get_fans",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.FW_VERSION): DataFunction(
|
||||
"_get_fw_ver",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.API_VERSION): DataFunction(
|
||||
"_get_api_ver",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
str(DataOptions.MAC): DataFunction(
|
||||
"_get_mac",
|
||||
[WebAPICommand("web_system_info", "system/info")],
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ESPMiner(BaseMiner):
|
||||
"""Handler for ESPMiner"""
|
||||
|
||||
web: ESPMinerWebAPI
|
||||
_web_cls = ESPMinerWebAPI
|
||||
|
||||
firmware = MinerFirmware.STOCK
|
||||
|
||||
data_locations = ESPMINER_DATA_LOC
|
||||
|
||||
async def reboot(self) -> bool:
|
||||
await self.web.restart()
|
||||
return True
|
||||
|
||||
async def get_config(self) -> MinerConfig:
|
||||
web_system_info = await self.web.system_info()
|
||||
return MinerConfig.from_espminer(web_system_info)
|
||||
|
||||
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
|
||||
await self.web.update_settings(**config.as_espminer())
|
||||
|
||||
async def _get_wattage(self, web_system_info: dict = None) -> Optional[int]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return round(web_system_info["power"])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_hashrate(
|
||||
self, web_system_info: dict = None
|
||||
) -> Optional[AlgoHashRate]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return self.algo.hashrate(
|
||||
rate=float(web_system_info["hashRate"]), unit=self.algo.unit.GH
|
||||
).into(self.algo.unit.default)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_expected_hashrate(
|
||||
self, web_system_info: dict = None
|
||||
) -> Optional[AlgoHashRate]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
expected_hashrate = (
|
||||
web_system_info.get("smallCoreCount")
|
||||
* web_system_info.get("asicCount")
|
||||
* web_system_info.get("frequency")
|
||||
)
|
||||
|
||||
return self.algo.hashrate(
|
||||
rate=float(expected_hashrate), unit=self.algo.unit.MH
|
||||
).into(self.algo.unit.default)
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_uptime(self, web_system_info: dict = None) -> Optional[int]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["uptimeSeconds"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_hashboards(self, web_system_info: dict = None) -> List[HashBoard]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return [
|
||||
HashBoard(
|
||||
hashrate=self.algo.hashrate(
|
||||
rate=float(web_system_info["hashRate"]),
|
||||
unit=self.algo.unit.GH,
|
||||
).into(self.algo.unit.default),
|
||||
chip_temp=web_system_info.get("temp"),
|
||||
temp=web_system_info.get("vrTemp"),
|
||||
chips=web_system_info.get("asicCount", 1),
|
||||
expected_chips=self.expected_chips,
|
||||
missing=False,
|
||||
active=True,
|
||||
voltage=web_system_info.get("voltage"),
|
||||
)
|
||||
]
|
||||
except KeyError:
|
||||
pass
|
||||
return []
|
||||
|
||||
async def _get_fans(self, web_system_info: dict = None) -> List[Fan]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return [Fan(speed=web_system_info["fanrpm"])]
|
||||
except KeyError:
|
||||
pass
|
||||
return []
|
||||
|
||||
async def _get_hostname(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["hostname"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_api_ver(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["version"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_fw_ver(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["version"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
async def _get_mac(self, web_system_info: dict = None) -> Optional[str]:
|
||||
if web_system_info is None:
|
||||
try:
|
||||
web_system_info = await self.web.system_info()
|
||||
except APIError:
|
||||
pass
|
||||
|
||||
if web_system_info is not None:
|
||||
try:
|
||||
return web_system_info["macAddr"].upper()
|
||||
except KeyError:
|
||||
pass
|
||||
7
pyasic/miners/backends/luckyminer.py
Normal file
7
pyasic/miners/backends/luckyminer.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from pyasic.miners.backends.espminer import ESPMiner
|
||||
|
||||
|
||||
class LuckyMiner(ESPMiner):
|
||||
"""Handler for LuckyMiner"""
|
||||
|
||||
pass
|
||||
@@ -50,6 +50,10 @@ class BitAxeMake(BaseMiner):
|
||||
make = MinerMake.BITAXE
|
||||
|
||||
|
||||
class LuckyMinerMake(BaseMiner):
|
||||
make = MinerMake.LUCKYMINER
|
||||
|
||||
|
||||
class IceRiverMake(BaseMiner):
|
||||
make = MinerMake.ICERIVER
|
||||
|
||||
|
||||
12
pyasic/miners/device/models/luckyminer/LV/LV08.py
Normal file
12
pyasic/miners/device/models/luckyminer/LV/LV08.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from pyasic.device.algorithm import MinerAlgo
|
||||
from pyasic.device.models import MinerModel
|
||||
from pyasic.miners.device.makes import LuckyMinerMake
|
||||
|
||||
|
||||
class LV08(LuckyMinerMake):
|
||||
raw_model = MinerModel.LUCKYMINER.BM1366
|
||||
|
||||
expected_hashboards = 1
|
||||
expected_chips = 1
|
||||
expected_fans = 1
|
||||
algo = MinerAlgo.SHA256
|
||||
1
pyasic/miners/device/models/luckyminer/LV/__init__.py
Normal file
1
pyasic/miners/device/models/luckyminer/LV/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .LV08 import LV08
|
||||
1
pyasic/miners/device/models/luckyminer/__init__.py
Normal file
1
pyasic/miners/device/models/luckyminer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .LV import *
|
||||
@@ -41,6 +41,7 @@ from pyasic.miners.goldshell import *
|
||||
from pyasic.miners.hammer import *
|
||||
from pyasic.miners.iceriver import *
|
||||
from pyasic.miners.innosilicon import *
|
||||
from pyasic.miners.luckyminer import *
|
||||
from pyasic.miners.volcminer import *
|
||||
from pyasic.miners.whatsminer import *
|
||||
|
||||
@@ -62,6 +63,7 @@ class MinerTypes(enum.Enum):
|
||||
ICERIVER = 13
|
||||
HAMMER = 14
|
||||
VOLCMINER = 15
|
||||
LUCKYMINER = 16
|
||||
|
||||
|
||||
MINER_CLASSES = {
|
||||
@@ -559,7 +561,7 @@ MINER_CLASSES = {
|
||||
"ANTMINER S19NOPIC": VNishS19NoPIC,
|
||||
"ANTMINER S19 PRO": VNishS19Pro,
|
||||
"ANTMINER S19J": VNishS19j,
|
||||
"ANTMINER S19I": VNishS19j,
|
||||
"ANTMINER S19I": VNishS19i,
|
||||
"ANTMINER S19J PRO": VNishS19jPro,
|
||||
"ANTMINER S19J PRO A": VNishS19jPro,
|
||||
"ANTMINER S19J PRO BB": VNishS19jPro,
|
||||
@@ -634,6 +636,10 @@ MINER_CLASSES = {
|
||||
"BM1397": BitAxeMax,
|
||||
"BM1370": BitAxeGamma,
|
||||
},
|
||||
MinerTypes.LUCKYMINER: {
|
||||
None: LuckyMiner,
|
||||
"BM1366": LuckyMinerLV08,
|
||||
},
|
||||
MinerTypes.ICERIVER: {
|
||||
None: type("IceRiverUnknown", (IceRiver, IceRiverMake), {}),
|
||||
"KS0": IceRiverKS0,
|
||||
@@ -731,6 +737,7 @@ class MinerFactory:
|
||||
MinerTypes.AURADINE: self.get_miner_model_auradine,
|
||||
MinerTypes.MARATHON: self.get_miner_model_marathon,
|
||||
MinerTypes.BITAXE: self.get_miner_model_bitaxe,
|
||||
MinerTypes.LUCKYMINER: self.get_miner_model_luckyminer,
|
||||
MinerTypes.ICERIVER: self.get_miner_model_iceriver,
|
||||
MinerTypes.HAMMER: self.get_miner_model_hammer,
|
||||
MinerTypes.VOLCMINER: self.get_miner_model_volcminer,
|
||||
@@ -833,6 +840,8 @@ class MinerFactory:
|
||||
return MinerTypes.ICERIVER
|
||||
if "AxeOS" in web_text:
|
||||
return MinerTypes.BITAXE
|
||||
if "Lucky miner" in web_text:
|
||||
return MinerTypes.LUCKYMINER
|
||||
if "cloud-box" in web_text:
|
||||
return MinerTypes.GOLDSHELL
|
||||
if "AnthillOS" in web_text:
|
||||
@@ -1292,6 +1301,18 @@ class MinerFactory:
|
||||
except (TypeError, LookupError):
|
||||
pass
|
||||
|
||||
async def get_miner_model_luckyminer(self, ip: str) -> str | None:
|
||||
web_json_data = await self.send_web_command(ip, "/api/system/info")
|
||||
|
||||
try:
|
||||
miner_model = web_json_data["ASICModel"]
|
||||
if miner_model == "":
|
||||
return None
|
||||
|
||||
return miner_model
|
||||
except (TypeError, LookupError):
|
||||
pass
|
||||
|
||||
async def get_miner_model_iceriver(self, ip: str) -> str | None:
|
||||
async with httpx.AsyncClient(transport=settings.transport()) as client:
|
||||
try:
|
||||
|
||||
1
pyasic/miners/luckyminer/__init__.py
Normal file
1
pyasic/miners/luckyminer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .espminer import *
|
||||
6
pyasic/miners/luckyminer/espminer/LV/LV08.py
Normal file
6
pyasic/miners/luckyminer/espminer/LV/LV08.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from pyasic.miners.backends.luckyminer import LuckyMiner
|
||||
from pyasic.miners.device.models.luckyminer import LV08
|
||||
|
||||
|
||||
class LuckyMinerLV08(LuckyMiner, LV08):
|
||||
pass
|
||||
1
pyasic/miners/luckyminer/espminer/LV/__init__.py
Normal file
1
pyasic/miners/luckyminer/espminer/LV/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .LV08 import LuckyMinerLV08
|
||||
1
pyasic/miners/luckyminer/espminer/__init__.py
Normal file
1
pyasic/miners/luckyminer/espminer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .LV import *
|
||||
@@ -10,7 +10,7 @@ from pyasic import APIError, settings
|
||||
from pyasic.web.base import BaseWebAPI
|
||||
|
||||
|
||||
class BitAxeWebAPI(BaseWebAPI):
|
||||
class ESPMinerWebAPI(BaseWebAPI):
|
||||
async def send_command(
|
||||
self,
|
||||
command: str | bytes,
|
||||
7
pyasic/web/luckyminer.py
Normal file
7
pyasic/web/luckyminer.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .bitaxe import ESPMinerWebAPI
|
||||
|
||||
|
||||
class LuckyMinerWebAPI(ESPMinerWebAPI):
|
||||
pass
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "pyasic"
|
||||
version = "0.70.1"
|
||||
version = "0.70.2"
|
||||
|
||||
description = "A simplified and standardized interface for Bitcoin ASICs."
|
||||
authors = [{name = "UpstreamData", email = "brett@upstreamdata.ca"}]
|
||||
|
||||
Reference in New Issue
Block a user