Compare commits

..

13 Commits

Author SHA1 Message Date
Upstream Data
309356243b version: bump version number 2024-11-12 14:28:51 -07:00
Upstream Data
e9b4cc9bd6 feature: add Antminer D9 support 2024-11-12 14:28:31 -07:00
Upstream Data
648c54de93 bug: add iceriver KS5 chip count 2024-11-12 14:25:13 -07:00
Upstream Data
e1ce96ab1b bug: add innosilicon A11 chip count 2024-11-12 14:24:05 -07:00
Brett Rowan
86860a8dc4 version: bump version number 2024-11-08 09:18:34 -07:00
Jacob Roy
5212641f45 add json serialization to the Scheme class (#232) 2024-11-06 15:13:30 -07:00
Upstream Data
52432e6043 version: bump version number 2024-11-06 09:06:11 -07:00
Upstream Data
727e484860 docs: update docs 2024-11-06 09:05:08 -07:00
Upstream Data
6c091756d2 feature: add support for Iceriver KS5 and submodels 2024-11-06 09:04:38 -07:00
Upstream Data
14533ce4fe version: bump version number 2024-11-05 09:46:35 -07:00
Upstream Data
82d1840039 bug: fix inf and nan in factory 2024-11-05 09:46:11 -07:00
Brett Rowan
8e6240cdba feature: LuxOS fixes and updates (#192)
* feature: add luxos tuner support to config.

* feature: add luxos temp control support to config.

* bug: fix failure to identify luxOS miners sometimes.

* feature: add get_config with temperature parsing.

* bug: fix some handling in hashboards.

* feature: add API version and fw version.

* refactor: improve RPC api handling.

* refactor: remove useless code, and tag bug.

* feature: add fault light check support.

* refactor: improve fanset compatibility.

* feature: add fan config parsing.

* feature: add pools parsing from luxos.

---------

Co-authored-by: Upstream Data <brett@upstreamdata.ca>
2024-11-05 09:43:34 -07:00
pre-commit-ci[bot]
5749e173d1 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v5.0.0)
- [github.com/psf/black: 24.3.0 → 24.10.0](https://github.com/psf/black/compare/24.3.0...24.10.0)
2024-11-05 09:38:40 -07:00
24 changed files with 476 additions and 205 deletions

View File

@@ -3,13 +3,13 @@ ci:
- unittest - unittest
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0 rev: v5.0.0
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: check-yaml - id: check-yaml
- id: check-added-large-files - id: check-added-large-files
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 24.3.0 rev: 24.10.0
hooks: hooks:
- id: black - id: black
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort

View File

@@ -43,3 +43,24 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## KS5 (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5L (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5L
handler: python
options:
show_root_heading: false
heading_level: 4
## KS5M (Stock)
::: pyasic.miners.iceriver.iceminer.KSX.KS5.IceRiverKS5M
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -688,6 +688,9 @@ details {
<li><a href="../iceriver/KSX#ks3-stock">KS3 (Stock)</a></li> <li><a href="../iceriver/KSX#ks3-stock">KS3 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3l-stock">KS3L (Stock)</a></li> <li><a href="../iceriver/KSX#ks3l-stock">KS3L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks3m-stock">KS3M (Stock)</a></li> <li><a href="../iceriver/KSX#ks3m-stock">KS3M (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5-stock">KS5 (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5l-stock">KS5L (Stock)</a></li>
<li><a href="../iceriver/KSX#ks5m-stock">KS5M (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>

View File

@@ -148,6 +148,14 @@ class MinerConfig:
**self.pools.as_bitaxe(user_suffix=user_suffix), **self.pools.as_bitaxe(user_suffix=user_suffix),
} }
def as_luxos(self, user_suffix: str = None) -> dict:
return {
**self.fan_mode.as_luxos(),
**self.temperature.as_luxos(),
**self.mining_mode.as_luxos(),
**self.pools.as_luxos(user_suffix=user_suffix),
}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict) -> "MinerConfig": def from_dict(cls, dict_conf: dict) -> "MinerConfig":
"""Constructs a MinerConfig object from a dictionary.""" """Constructs a MinerConfig object from a dictionary."""
@@ -256,3 +264,15 @@ class MinerConfig:
return cls( return cls(
pools=PoolConfig.from_iceriver(web_userpanel), pools=PoolConfig.from_iceriver(web_userpanel),
) )
@classmethod
def from_luxos(
cls, rpc_tempctrl: dict, rpc_fans: dict, rpc_pools: dict, rpc_groups: dict
) -> "MinerConfig":
return cls(
temperature=TemperatureConfig.from_luxos(rpc_tempctrl=rpc_tempctrl),
fan_mode=FanModeConfig.from_luxos(
rpc_tempctrl=rpc_tempctrl, rpc_fans=rpc_fans
),
pools=PoolConfig.from_luxos(rpc_pools=rpc_pools, rpc_groups=rpc_groups),
)

View File

@@ -63,6 +63,9 @@ class MinerConfigOption(Enum):
def as_bitaxe(self) -> dict: def as_bitaxe(self) -> dict:
return self.value.as_bitaxe() return self.value.as_bitaxe()
def as_luxos(self) -> dict:
return self.value.as_luxos()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs) return self.value(*args, **kwargs)
@@ -125,6 +128,9 @@ class MinerConfigValue:
def as_bitaxe(self) -> dict: def as_bitaxe(self) -> dict:
return {} return {}
def as_luxos(self) -> dict:
return {}
def __getitem__(self, item): def __getitem__(self, item):
try: try:
return getattr(self, item) return getattr(self, item)

View File

@@ -83,6 +83,9 @@ class FanModeNormal(MinerConfigValue):
def as_bitaxe(self) -> dict: def as_bitaxe(self) -> dict:
return {"autoFanspeed": 1} return {"autoFanspeed": 1}
def as_luxos(self) -> dict:
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
@dataclass @dataclass
class FanModeManual(MinerConfigValue): class FanModeManual(MinerConfigValue):
@@ -144,6 +147,9 @@ class FanModeManual(MinerConfigValue):
def as_bitaxe(self) -> dict: def as_bitaxe(self) -> dict:
return {"autoFanspeed": 0, "fanspeed": self.speed} return {"autoFanspeed": 0, "fanspeed": self.speed}
def as_luxos(self) -> dict:
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
@dataclass @dataclass
class FanModeImmersion(MinerConfigValue): class FanModeImmersion(MinerConfigValue):
@@ -167,6 +173,9 @@ class FanModeImmersion(MinerConfigValue):
def as_mara(self) -> dict: def as_mara(self) -> dict:
return {"general-config": {"environment-profile": "OilImmersionCooling"}} return {"general-config": {"environment-profile": "OilImmersionCooling"}}
def as_luxos(self) -> dict:
return {"fanset": {"speed": 0, "min_fans": 0}}
class FanModeConfig(MinerConfigOption): class FanModeConfig(MinerConfigOption):
normal = FanModeNormal normal = FanModeNormal
@@ -304,3 +313,23 @@ class FanModeConfig(MinerConfigOption):
return cls.normal() return cls.normal()
else: else:
return cls.manual(speed=web_system_info["fanspeed"]) return cls.manual(speed=web_system_info["fanspeed"])
@classmethod
def from_luxos(cls, rpc_fans: dict, rpc_tempctrl: dict):
try:
mode = rpc_tempctrl["TEMPCTRL"][0]["Mode"]
if mode == "Manual":
speed = rpc_fans["FANS"][0]["Speed"]
min_fans = rpc_fans["FANCTRL"][0]["MinFans"]
if min_fans == 0 and speed == 0:
return cls.immersion()
return cls.manual(
speed=speed,
minimum_fans=min_fans,
)
return cls.normal(
minimum_fans=rpc_fans["FANCTRL"][0]["MinFans"],
)
except LookupError:
pass
return cls.default()

View File

@@ -70,6 +70,9 @@ class MiningModeNormal(MinerConfigValue):
} }
} }
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": False}}
@dataclass @dataclass
class MiningModeSleep(MinerConfigValue): class MiningModeSleep(MinerConfigValue):
@@ -240,6 +243,9 @@ class MiningModePowerTune(MinerConfigValue):
} }
} }
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": True}}
@dataclass @dataclass
class MiningModeHashrateTune(MinerConfigValue): class MiningModeHashrateTune(MinerConfigValue):
@@ -333,6 +339,9 @@ class MiningModeHashrateTune(MinerConfigValue):
} }
} }
def as_luxos(self) -> dict:
return {"autotunerset": {"enabled": True}}
@dataclass @dataclass
class ManualBoardSettings(MinerConfigValue): class ManualBoardSettings(MinerConfigValue):

View File

@@ -222,6 +222,10 @@ class Pool(MinerConfigValue):
password=web_system_info.get("stratumPassword", ""), password=web_system_info.get("stratumPassword", ""),
) )
@classmethod
def from_luxos(cls, rpc_pools: dict) -> "Pool":
return cls.from_api(rpc_pools)
@classmethod @classmethod
def from_iceriver(cls, web_pool: dict) -> "Pool": def from_iceriver(cls, web_pool: dict) -> "Pool":
return cls( return cls(
@@ -523,6 +527,9 @@ class PoolConfig(MinerConfigValue):
def as_bitaxe(self, user_suffix: str = None) -> dict: def as_bitaxe(self, user_suffix: str = None) -> dict:
return self.groups[0].as_bitaxe(user_suffix=user_suffix) return self.groups[0].as_bitaxe(user_suffix=user_suffix)
def as_luxos(self, user_suffix: str = None) -> dict:
return {}
@classmethod @classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig": def from_api(cls, api_pools: dict) -> "PoolConfig":
try: try:
@@ -589,3 +596,20 @@ class PoolConfig(MinerConfigValue):
@classmethod @classmethod
def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig": def from_iceriver(cls, web_userpanel: dict) -> "PoolConfig":
return cls(groups=[PoolGroup.from_iceriver(web_userpanel)]) return cls(groups=[PoolGroup.from_iceriver(web_userpanel)])
@classmethod
def from_luxos(cls, rpc_groups: dict, rpc_pools: dict) -> "PoolConfig":
return cls(
groups=[
PoolGroup(
pools=[
Pool.from_luxos(pool)
for pool in rpc_pools["POOLS"]
if pool["GROUP"] == group["GROUP"]
],
name=group["Name"],
quota=group["Quota"],
)
for group in rpc_groups["GROUPS"]
]
)

View File

@@ -54,6 +54,9 @@ class TemperatureConfig(MinerConfigValue):
temps_config["temps"]["shutdown"] = self.hot temps_config["temps"]["shutdown"] = self.hot
return temps_config return temps_config
def as_luxos(self) -> dict:
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig": def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
return cls( return cls(
@@ -130,3 +133,16 @@ class TemperatureConfig(MinerConfigValue):
return cls(**conf) return cls(**conf)
return cls.default() return cls.default()
@classmethod
def from_luxos(cls, rpc_tempctrl: dict) -> "TemperatureConfig":
try:
tempctrl_config = rpc_tempctrl["TEMPCTRL"][0]
return cls(
target=tempctrl_config.get("Target"),
hot=tempctrl_config.get("Hot"),
danger=tempctrl_config.get("Dangerous"),
)
except LookupError:
pass
return cls.default()

View File

@@ -23,7 +23,7 @@ from typing import Any, List, Union
from pyasic.config import MinerConfig from pyasic.config import MinerConfig
from pyasic.config.mining import MiningModePowerTune from pyasic.config.mining import MiningModePowerTune
from pyasic.data.pools import PoolMetrics from pyasic.data.pools import PoolMetrics, Scheme
from .boards import HashBoard from .boards import HashBoard
from .device import DeviceInfo from .device import DeviceInfo
@@ -154,7 +154,11 @@ class MinerData:
@staticmethod @staticmethod
def dict_factory(x): def dict_factory(x):
return {k: v for (k, v) in x if not k.startswith("_")} return {
k: v.value if isinstance(v, Scheme) else v
for (k, v) in x
if not k.startswith("_")
}
def __post_init__(self): def __post_init__(self):
self._datetime = datetime.now(timezone.utc).astimezone() self._datetime = datetime.now(timezone.utc).astimezone()

View File

@@ -16,6 +16,7 @@ class AntminerModels(str, Enum):
S9i = "S9i" S9i = "S9i"
S9j = "S9j" S9j = "S9j"
T9 = "T9" T9 = "T9"
D9 = "D9"
Z15 = "Z15" Z15 = "Z15"
Z15Pro = "Z15 Pro" Z15Pro = "Z15 Pro"
S17 = "S17" S17 = "S17"
@@ -355,6 +356,9 @@ class IceRiverModels(str, Enum):
KS3 = "KS3" KS3 = "KS3"
KS3L = "KS3L" KS3L = "KS3L"
KS3M = "KS3M" KS3M = "KS3M"
KS5 = "KS5"
KS5L = "KS5L"
KS5M = "KS5M"
def __str__(self): def __str__(self):
return self.value return self.value

View 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 AntminerModern
from pyasic.miners.device.models import D9
class BMMinerD9(AntminerModern, D9):
pass

View File

@@ -14,6 +14,7 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .D9 import BMMinerD9
from .E9 import BMMinerE9Pro from .E9 import BMMinerE9Pro
from .S9 import BMMinerS9, BMMinerS9i, BMMinerS9j from .S9 import BMMinerS9, BMMinerS9i, BMMinerS9j
from .T9 import BMMinerT9 from .T9 import BMMinerT9

View File

@@ -56,6 +56,15 @@ LUXMINER_DATA_LOC = DataLocations(
str(DataOptions.POOLS): DataFunction( str(DataOptions.POOLS): DataFunction(
"_get_pools", [RPCAPICommand("rpc_pools", "pools")] "_get_pools", [RPCAPICommand("rpc_pools", "pools")]
), ),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver", [RPCAPICommand("rpc_version", "version")]
),
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver", [RPCAPICommand("rpc_version", "version")]
),
str(DataOptions.FAULT_LIGHT): DataFunction(
"_get_fault_light", [RPCAPICommand("rpc_config", "config")]
),
} }
) )
@@ -68,25 +77,9 @@ class LUXMiner(LuxOSFirmware):
data_locations = LUXMINER_DATA_LOC data_locations = LUXMINER_DATA_LOC
async def _get_session(self) -> Optional[str]:
try:
data = await self.rpc.session()
if not data["SESSION"][0]["SessionID"] == "":
return data["SESSION"][0]["SessionID"]
except APIError:
pass
try:
data = await self.rpc.logon()
return data["SESSION"][0]["SessionID"]
except (LookupError, APIError):
return
async def fault_light_on(self) -> bool: async def fault_light_on(self) -> bool:
try: try:
session_id = await self._get_session() await self.rpc.ledset("red", "blink")
if session_id:
await self.rpc.ledset(session_id, "red", "blink")
return True return True
except (APIError, LookupError): except (APIError, LookupError):
pass pass
@@ -94,9 +87,7 @@ class LUXMiner(LuxOSFirmware):
async def fault_light_off(self) -> bool: async def fault_light_off(self) -> bool:
try: try:
session_id = await self._get_session() await self.rpc.ledset("red", "off")
if session_id:
await self.rpc.ledset(session_id, "red", "off")
return True return True
except (APIError, LookupError): except (APIError, LookupError):
pass pass
@@ -107,9 +98,7 @@ class LUXMiner(LuxOSFirmware):
async def restart_luxminer(self) -> bool: async def restart_luxminer(self) -> bool:
try: try:
session_id = await self._get_session() await self.rpc.resetminer()
if session_id:
await self.rpc.resetminer(session_id)
return True return True
except (APIError, LookupError): except (APIError, LookupError):
pass pass
@@ -117,9 +106,7 @@ class LUXMiner(LuxOSFirmware):
async def stop_mining(self) -> bool: async def stop_mining(self) -> bool:
try: try:
session_id = await self._get_session() await self.rpc.sleep()
if session_id:
await self.rpc.curtail(session_id)
return True return True
except (APIError, LookupError): except (APIError, LookupError):
pass pass
@@ -127,25 +114,27 @@ class LUXMiner(LuxOSFirmware):
async def resume_mining(self) -> bool: async def resume_mining(self) -> bool:
try: try:
session_id = await self._get_session() await self.rpc.wakeup()
if session_id:
await self.rpc.wakeup(session_id)
return True return True
except (APIError, LookupError): except (APIError, LookupError):
pass pass
async def reboot(self) -> bool: async def reboot(self) -> bool:
try: try:
session_id = await self._get_session() await self.rpc.rebootdevice()
if session_id:
await self.rpc.rebootdevice(session_id)
return True return True
except (APIError, LookupError): except (APIError, LookupError):
pass pass
return False return False
async def get_config(self) -> MinerConfig: async def get_config(self) -> MinerConfig:
return self.config data = await self.rpc.multicommand("tempctrl", "fans", "pools", "groups")
return MinerConfig.from_luxos(
rpc_tempctrl=data.get("tempctrl", [{}])[0],
rpc_fans=data.get("fans", [{}])[0],
rpc_pools=data.get("pools", [{}])[0],
rpc_groups=data.get("groups", [{}])[0],
)
async def upgrade_firmware(self) -> bool: async def upgrade_firmware(self) -> bool:
""" """
@@ -168,20 +157,17 @@ class LUXMiner(LuxOSFirmware):
################################################## ##################################################
async def _get_mac(self, rpc_config: dict = None) -> Optional[str]: async def _get_mac(self, rpc_config: dict = None) -> Optional[str]:
mac = None
if rpc_config is None: if rpc_config is None:
try: try:
rpc_config = await self.rpc.config() rpc_config = await self.rpc.config()
except APIError: except APIError:
return None pass
if rpc_config is not None: if rpc_config is not None:
try: try:
mac = rpc_config["CONFIG"][0]["MACAddr"] return rpc_config["CONFIG"][0]["MACAddr"].upper()
except KeyError: except KeyError:
return None pass
return mac
async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]: async def _get_hashrate(self, rpc_summary: dict = None) -> Optional[AlgoHashRate]:
if rpc_summary is None: if rpc_summary is None:
@@ -199,59 +185,47 @@ class LUXMiner(LuxOSFirmware):
pass pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = [] hashboards = [
HashBoard(idx, expected_chips=self.expected_chips)
for idx in range(self.expected_hashboards)
]
if rpc_stats is None: if rpc_stats is None:
try: try:
rpc_stats = await self.rpc.stats() rpc_stats = await self.rpc.stats()
except APIError: except APIError:
pass pass
if rpc_stats is not None: if rpc_stats is not None:
try: try:
board_offset = -1 # TODO: bugged on S9 because of index issues, fix later.
boards = rpc_stats["STATS"] board_stats = rpc_stats["STATS"][1]
if len(boards) > 1: for idx in range(3):
for board_num in range(1, 16, 5): board_n = idx + 1
for _b_num in range(5): hashboards[idx].hashrate = AlgoHashRate.SHA256(
b = boards[1].get(f"chain_acn{board_num + _b_num}") float(board_stats[f"chain_rate{board_n}"]), HashUnit.SHA256.GH
).into(self.algo.unit.default)
if b and not b == 0 and board_offset == -1: hashboards[idx].chips = int(board_stats[f"chain_acn{board_n}"])
board_offset = board_num chip_temp_data = list(
if board_offset == -1: filter(
board_offset = 1 lambda x: not x == 0,
map(int, board_stats[f"temp_chip{board_n}"].split("-")),
for i in range(
board_offset, board_offset + self.expected_hashboards
):
hashboard = HashBoard(
slot=i - board_offset, expected_chips=self.expected_chips
) )
)
chip_temp = boards[1].get(f"temp{i}") hashboards[idx].chip_temp = (
if chip_temp: sum([chip_temp_data[0], chip_temp_data[3]]) / 2
hashboard.chip_temp = round(chip_temp) )
board_temp_data = list(
temp = boards[1].get(f"temp2_{i}") filter(
if temp: lambda x: not x == 0,
hashboard.temp = round(temp) map(int, board_stats[f"temp_pcb{board_n}"].split("-")),
)
hashrate = boards[1].get(f"chain_rate{i}") )
if hashrate: hashboards[idx].temp = (
hashboard.hashrate = AlgoHashRate.SHA256( sum([board_temp_data[1], board_temp_data[2]]) / 2
hashrate, HashUnit.SHA256.GH )
).into(self.algo.unit.default) hashboards[idx].missing = False
except LookupError:
chips = boards[1].get(f"chain_acn{i}")
if chips:
hashboard.chips = chips
hashboard.missing = False
if (not chips) or (not chips > 0):
hashboard.missing = True
hashboards.append(hashboard)
except (LookupError, ValueError, TypeError):
pass pass
return hashboards return hashboards
async def _get_wattage(self, rpc_power: dict = None) -> Optional[int]: async def _get_wattage(self, rpc_power: dict = None) -> Optional[int]:
@@ -319,6 +293,45 @@ class LUXMiner(LuxOSFirmware):
except LookupError: except LookupError:
pass pass
async def _get_fw_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None:
try:
rpc_version = await self.rpc.version()
except APIError:
pass
if rpc_version is not None:
try:
return rpc_version["VERSION"][0]["Miner"]
except LookupError:
pass
async def _get_api_ver(self, rpc_version: dict = None) -> Optional[str]:
if rpc_version is None:
try:
rpc_version = await self.rpc.version()
except APIError:
pass
if rpc_version is not None:
try:
return rpc_version["VERSION"][0]["API"]
except LookupError:
pass
async def _get_fault_light(self, rpc_config: dict = None) -> Optional[bool]:
if rpc_config is None:
try:
rpc_config = await self.rpc.config()
except APIError:
pass
if rpc_config is not None:
try:
return not rpc_config["CONFIG"][0]["RedLed"] == "off"
except LookupError:
pass
async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]: async def _get_pools(self, rpc_pools: dict = None) -> List[PoolMetrics]:
if rpc_pools is None: if rpc_pools is None:
try: try:

View File

@@ -0,0 +1,23 @@
# ------------------------------------------------------------------------------
# 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.models import MinerModel
from pyasic.miners.device.makes import AntMinerMake
class D9(AntMinerMake):
raw_model = MinerModel.ANTMINER.D9
expected_chips = 126

View File

@@ -14,6 +14,7 @@
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .D9 import D9
from .E9 import E9Pro from .E9 import E9Pro
from .S9 import S9, S9i, S9j from .S9 import S9, S9i, S9j
from .T9 import T9 from .T9 import T9

View File

@@ -0,0 +1,37 @@
# ------------------------------------------------------------------------------
# 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 pyasic.device.models import MinerModel
from pyasic.miners.device.makes import IceRiverMake
class KS5(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS5
expected_fans = 4
expected_chips = 92
class KS5L(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS5L
expected_fans = 4
expected_chips = 18
class KS5M(IceRiverMake):
raw_model = MinerModel.ICERIVER.KS5M
expected_fans = 4

View File

@@ -2,3 +2,4 @@ from .KS0 import KS0
from .KS1 import KS1 from .KS1 import KS1
from .KS2 import KS2 from .KS2 import KS2
from .KS3 import KS3, KS3L, KS3M from .KS3 import KS3, KS3L, KS3M
from .KS5 import KS5, KS5L, KS5M

View File

@@ -21,3 +21,4 @@ class A11(InnosiliconMake):
raw_model = MinerModel.INNOSILICON.A11 raw_model = MinerModel.INNOSILICON.A11
expected_hashboards = 4 expected_hashboards = 4
expected_chips = 8

View File

@@ -74,6 +74,7 @@ MINER_CLASSES = {
"ANTMINER L7": BMMinerL7, "ANTMINER L7": BMMinerL7,
"ANTMINER K7": BMMinerK7, "ANTMINER K7": BMMinerK7,
"ANTMINER E9 PRO": BMMinerE9Pro, "ANTMINER E9 PRO": BMMinerE9Pro,
"ANTMINER D9": BMMinerD9,
"ANTMINER S9": BMMinerS9, "ANTMINER S9": BMMinerS9,
"ANTMINER S9I": BMMinerS9i, "ANTMINER S9I": BMMinerS9i,
"ANTMINER S9J": BMMinerS9j, "ANTMINER S9J": BMMinerS9j,
@@ -473,6 +474,9 @@ MINER_CLASSES = {
"KS3": IceRiverKS3, "KS3": IceRiverKS3,
"KS3L": IceRiverKS3L, "KS3L": IceRiverKS3L,
"KS3M": IceRiverKS3M, "KS3M": IceRiverKS3M,
"KS5": IceRiverKS5,
"KS5L": IceRiverKS5L,
"KS5M": IceRiverKS5M,
}, },
} }
@@ -717,10 +721,10 @@ class MinerFactory:
return MinerTypes.BRAIINS_OS return MinerTypes.BRAIINS_OS
if "BTMINER" in upper_data or "BITMICRO" in upper_data: if "BTMINER" in upper_data or "BITMICRO" in upper_data:
return MinerTypes.WHATSMINER return MinerTypes.WHATSMINER
if "HIVEON" in upper_data:
return MinerTypes.HIVEON
if "LUXMINER" in upper_data: if "LUXMINER" in upper_data:
return MinerTypes.LUX_OS return MinerTypes.LUX_OS
if "HIVEON" in upper_data:
return MinerTypes.HIVEON
if "KAONSU" in upper_data: if "KAONSU" in upper_data:
return MinerTypes.MARATHON return MinerTypes.MARATHON
if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data: if "ANTMINER" in upper_data and "DEVDETAILS" not in upper_data:
@@ -825,12 +829,8 @@ class MinerFactory:
# fix an error with a btminer return having a missing comma. (2023-01-06 version) # fix an error with a btminer return having a missing comma. (2023-01-06 version)
str_data = str_data.replace('""temp0', '","temp0') str_data = str_data.replace('""temp0', '","temp0')
# fix an error with Avalonminers returning inf and nan # fix an error with Avalonminers returning inf and nan
str_data = str_data.replace("info", "1nfo") str_data = str_data.replace('"inf"', "0")
str_data = str_data.replace("inf", "0") str_data = str_data.replace('"nan"', "0")
str_data = str_data.replace("1nfo", "info")
str_data = str_data.replace("nano", "n4no")
str_data = str_data.replace("nan", "0")
str_data = str_data.replace("n4no", "nano")
# fix whatever this garbage from avalonminers is `,"id":1}` # fix whatever this garbage from avalonminers is `,"id":1}`
if str_data.startswith(","): if str_data.startswith(","):
str_data = f"{{{str_data[1:]}" str_data = f"{{{str_data[1:]}"

View File

@@ -0,0 +1,14 @@
from pyasic.miners.backends.iceriver import IceRiver
from pyasic.miners.device.models.iceriver import KS5, KS5L, KS5M
class IceRiverKS5(IceRiver, KS5):
pass
class IceRiverKS5L(IceRiver, KS5L):
pass
class IceRiverKS5M(IceRiver, KS5M):
pass

View File

@@ -2,3 +2,4 @@ from .KS0 import IceRiverKS0
from .KS1 import IceRiverKS1 from .KS1 import IceRiverKS1
from .KS2 import IceRiverKS2 from .KS2 import IceRiverKS2
from .KS3 import IceRiverKS3, IceRiverKS3L, IceRiverKS3M from .KS3 import IceRiverKS3, IceRiverKS3L, IceRiverKS3M
from .KS5 import IceRiverKS5, IceRiverKS5L, IceRiverKS5M

View File

@@ -13,8 +13,9 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from typing import Literal from typing import Literal, Optional, Union
from pyasic import APIError
from pyasic.rpc.base import BaseMinerRPCAPI from pyasic.rpc.base import BaseMinerRPCAPI
@@ -32,6 +33,48 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
rely on it to send the command for them. rely on it to send the command for them.
""" """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.session_token = None
async def send_privileged_command(
self, command: Union[str, bytes], *args, **kwargs
) -> dict:
if self.session_token is None:
await self.auth()
return await self.send_command(
command,
self.session_token,
*args,
**kwargs,
)
async def send_command(
self,
command: Union[str, bytes],
*args,
**kwargs,
) -> dict:
if kwargs.get("parameters") is not None and len(args) == 0:
return await super().send_command(command, **kwargs)
return await super().send_command(command, parameters=",".join(args), **kwargs)
async def auth(self) -> Optional[str]:
try:
data = await self.session()
if not data["SESSION"][0]["SessionID"] == "":
self.session_token = data["SESSION"][0]["SessionID"]
return self.session_token
except APIError:
pass
try:
data = await self.logon()
self.session_token = data["SESSION"][0]["SessionID"]
return self.session_token
except (LookupError, APIError):
pass
async def addgroup(self, name: str, quota: int) -> dict: async def addgroup(self, name: str, quota: int) -> dict:
"""Add a pool group. """Add a pool group.
<details> <details>
@@ -45,7 +88,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Confirmation of adding a pool group. Confirmation of adding a pool group.
</details> </details>
""" """
return await self.send_command("addgroup", parameters=f"{name},{quota}") return await self.send_command("addgroup", name, quota)
async def addpool( async def addpool(
self, url: str, user: str, pwd: str = "", group_id: str = None self, url: str, user: str, pwd: str = "", group_id: str = None
@@ -67,7 +110,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
pool_data = [url, user, pwd] pool_data = [url, user, pwd]
if group_id is not None: if group_id is not None:
pool_data.append(group_id) pool_data.append(group_id)
return await self.send_command("addpool", parameters=",".join(pool_data)) return await self.send_command("addpool", *pool_data)
async def asc(self, n: int) -> dict: async def asc(self, n: int) -> dict:
"""Get data for ASC device n. """Get data for ASC device n.
@@ -81,7 +124,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
The data for ASC device n. The data for ASC device n.
</details> </details>
""" """
return await self.send_command("asc", parameters=n) return await self.send_command("asc", n)
async def asccount(self) -> dict: async def asccount(self) -> dict:
"""Get data on the number of ASC devices and their info. """Get data on the number of ASC devices and their info.
@@ -108,7 +151,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
* Access (Y/N) <- you have access to use the command * Access (Y/N) <- you have access to use the command
</details> </details>
""" """
return await self.send_command("check", parameters=command) return await self.send_command("check", command)
async def coin(self) -> dict: async def coin(self) -> dict:
"""Get information on the current coin. """Get information on the current coin.
@@ -137,19 +180,38 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("config") return await self.send_command("config")
async def curtail(self, session_id: str) -> dict: async def curtail(self) -> dict:
"""Put the miner into sleep mode. Requires a session_id from logon. """Put the miner into sleep mode.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns: Returns:
A confirmation of putting the miner to sleep. A confirmation of putting the miner to sleep.
</details> </details>
""" """
return await self.send_command("curtail", parameters=session_id) return await self.send_privileged_command("curtail", "sleep")
async def sleep(self) -> dict:
"""Put the miner into sleep mode.
<details>
<summary>Expand</summary>
Returns:
A confirmation of putting the miner to sleep.
</details>
"""
return await self.send_privileged_command("curtail", "sleep")
async def wakeup(self) -> dict:
"""Wake the miner up from sleep mode.
<details>
<summary>Expand</summary>
Returns:
A confirmation of waking the miner up.
</details>
"""
return await self.send_privileged_command("curtail", "wakeup")
async def devdetails(self) -> dict: async def devdetails(self) -> dict:
"""Get data on all devices with their static details. """Get data on all devices with their static details.
@@ -185,7 +247,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of diabling the pool. A confirmation of diabling the pool.
</details> </details>
""" """
return await self.send_command("disablepool", parameters=n) return await self.send_command("disablepool", n)
async def edevs(self) -> dict: async def edevs(self) -> dict:
"""Alias for devs""" """Alias for devs"""
@@ -203,7 +265,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of enabling pool n. A confirmation of enabling pool n.
</details> </details>
""" """
return await self.send_command("enablepool", parameters=n) return await self.send_command("enablepool", n)
async def estats(self) -> dict: async def estats(self) -> dict:
"""Alias for stats""" """Alias for stats"""
@@ -220,13 +282,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("fans") return await self.send_command("fans")
async def fanset(self, session_id: str, speed: int, min_fans: int = None) -> dict: async def fanset(
"""Set fan control. Requires a session_id from logon. self, speed: int = None, min_fans: int = None, power_off_speed: int = None
) -> dict:
"""Set fan control.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
speed: The fan speed to set. Use -1 to set automatically. speed: The fan speed to set. Use -1 to set automatically.
min_fans: The minimum number of fans to use. Optional. min_fans: The minimum number of fans to use. Optional.
@@ -234,10 +297,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting fan control values. A confirmation of setting fan control values.
</details> </details>
""" """
fanset_data = [str(session_id), str(speed)] fanset_data = []
if speed is not None:
fanset_data.append(f"speed={speed}")
if min_fans is not None: if min_fans is not None:
fanset_data.append(str(min_fans)) fanset_data.append(f"min_fans={min_fans}")
return await self.send_command("fanset", parameters=",".join(fanset_data)) if power_off_speed is not None:
fanset_data.append(f"power_off_speed={power_off_speed}")
return await self.send_privileged_command("fanset", *fanset_data)
async def frequencyget(self, board_n: int, chip_n: int = None) -> dict: async def frequencyget(self, board_n: int, chip_n: int = None) -> dict:
"""Get frequency data for a board and chips. """Get frequency data for a board and chips.
@@ -255,17 +322,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
frequencyget_data = [str(board_n)] frequencyget_data = [str(board_n)]
if chip_n is not None: if chip_n is not None:
frequencyget_data.append(str(chip_n)) frequencyget_data.append(str(chip_n))
return await self.send_command( return await self.send_command("frequencyget", *frequencyget_data)
"frequencyget", parameters=",".join(frequencyget_data)
)
async def frequencyset(self, session_id: str, board_n: int, freq: int) -> dict: async def frequencyset(self, board_n: int, freq: int) -> dict:
"""Set frequency. Requires a session_id from logon. """Set frequency.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
board_n: The board number to set frequency on. board_n: The board number to set frequency on.
freq: The frequency to set. freq: The frequency to set.
@@ -273,26 +337,21 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting frequency values. A confirmation of setting frequency values.
</details> </details>
""" """
return await self.send_command( return await self.send_privileged_command("frequencyset", board_n, freq)
"frequencyset", parameters=f"{session_id},{board_n},{freq}"
)
async def frequencystop(self, session_id: str, board_n: int) -> dict: async def frequencystop(self, board_n: int) -> dict:
"""Stop set frequency. Requires a session_id from logon. """Stop set frequency.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
board_n: The board number to set frequency on. board_n: The board number to set frequency on.
Returns: Returns:
A confirmation of stopping frequencyset value. A confirmation of stopping frequencyset value.
</details> </details>
""" """
return await self.send_command( return await self.send_privileged_command("frequencystop", board_n)
"frequencystop", parameters=f"{session_id},{board_n}"
)
async def groupquota(self, group_n: int, quota: int) -> dict: async def groupquota(self, group_n: int, quota: int) -> dict:
"""Set a group's quota. """Set a group's quota.
@@ -307,7 +366,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting quota value. A confirmation of setting quota value.
</details> </details>
""" """
return await self.send_command("groupquota", parameters=f"{group_n},{quota}") return await self.send_command("groupquota", group_n, quota)
async def groups(self) -> dict: async def groups(self) -> dict:
"""Get pool group data. """Get pool group data.
@@ -336,19 +395,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
healthchipget_data = [str(board_n)] healthchipget_data = [str(board_n)]
if chip_n is not None: if chip_n is not None:
healthchipget_data.append(str(chip_n)) healthchipget_data.append(str(chip_n))
return await self.send_command( return await self.send_command("healthchipget", *healthchipget_data)
"healthchipget", parameters=",".join(healthchipget_data)
)
async def healthchipset( async def healthchipset(self, board_n: int, chip_n: int = None) -> dict:
self, session_id: str, board_n: int, chip_n: int = None """Select the next chip to have its health checked.
) -> dict:
"""Select the next chip to have its health checked. Requires a session_id from logon.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
board_n: The board number to next get chip health of. board_n: The board number to next get chip health of.
chip_n: The chip number to next get chip health of. Optional. chip_n: The chip number to next get chip health of. Optional.
@@ -356,12 +410,10 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Confirmation of selecting the next health check chip. Confirmation of selecting the next health check chip.
</details> </details>
""" """
healthchipset_data = [session_id, str(board_n)] healthchipset_data = [str(board_n)]
if chip_n is not None: if chip_n is not None:
healthchipset_data.append(str(chip_n)) healthchipset_data.append(str(chip_n))
return await self.send_command( return await self.send_privileged_command("healthchipset", *healthchipset_data)
"healthchipset", parameters=",".join(healthchipset_data)
)
async def healthctrl(self) -> dict: async def healthctrl(self) -> dict:
"""Get health check config. """Get health check config.
@@ -374,15 +426,12 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("healthctrl") return await self.send_command("healthctrl")
async def healthctrlset( async def healthctrlset(self, num_readings: int, amplified_factor: float) -> dict:
self, session_id: str, num_readings: int, amplified_factor: float """Set health control config.
) -> dict:
"""Set health control config. Requires a session_id from logon.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
num_readings: The minimum number of readings for evaluation. num_readings: The minimum number of readings for evaluation.
amplified_factor: Performance factor of the evaluation. amplified_factor: Performance factor of the evaluation.
@@ -390,9 +439,8 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting health control config. A confirmation of setting health control config.
</details> </details>
""" """
return await self.send_command( return await self.send_privileged_command(
"healthctrlset", "healthctrlset", num_readings, amplified_factor
parameters=f"{session_id},{num_readings},{amplified_factor}",
) )
async def kill(self) -> dict: async def kill(self) -> dict:
@@ -419,16 +467,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
async def ledset( async def ledset(
self, self,
session_id: str,
color: Literal["red"], color: Literal["red"],
state: Literal["on", "off", "blink"], state: Literal["on", "off", "blink"],
) -> dict: ) -> dict:
"""Set led. Requires a session_id from logon. """Set led.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
color: The color LED to set. Can be "red". color: The color LED to set. Can be "red".
state: The state to set the LED to. Can be "on", "off", or "blink". state: The state to set the LED to. Can be "on", "off", or "blink".
@@ -436,9 +482,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting LED. A confirmation of setting LED.
</details> </details>
""" """
return await self.send_command( return await self.send_privileged_command("ledset", color, state)
"ledset", parameters=f"{session_id},{color},{state}"
)
async def limits(self) -> dict: async def limits(self) -> dict:
"""Get max and min values of config parameters. """Get max and min values of config parameters.
@@ -451,8 +495,8 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("limits") return await self.send_command("limits")
async def logoff(self, session_id: str) -> dict: async def logoff(self) -> dict:
"""Log off of a session. Requires a session id from an active session. """Log off of a session.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
@@ -463,7 +507,9 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Confirmation of logging off a session. Confirmation of logging off a session.
</details> </details>
""" """
return await self.send_command("logoff", parameters=session_id) res = await self.send_privileged_command("logoff")
self.session_token = None
return res
async def logon(self) -> dict: async def logon(self) -> dict:
"""Get or create a session. """Get or create a session.
@@ -510,13 +556,12 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("profiles") return await self.send_command("profiles")
async def profileset(self, session_id: str, board_n: int, profile: str) -> dict: async def profileset(self, board_n: int, profile: str) -> dict:
"""Set active profile for a board. Requires a session_id from logon. """Set active profile for a board.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
board_n: The board to set the profile on. board_n: The board to set the profile on.
profile: The profile name to use. profile: The profile name to use.
@@ -524,17 +569,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting the profile on board_n. A confirmation of setting the profile on board_n.
</details> </details>
""" """
return await self.send_command( return await self.send_privileged_command("profileset", board_n, profile)
"profileset", parameters=f"{session_id},{board_n},{profile}"
)
async def reboot(self, session_id: str, board_n: int, delay_s: int = None) -> dict: async def reboot(self, board_n: int, delay_s: int = None) -> dict:
"""Reboot a board. Requires a session_id from logon. """Reboot a board.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
board_n: The board to reboot. board_n: The board to reboot.
delay_s: The number of seconds to delay until startup. If it is 0, the board will just stop. Optional. delay_s: The number of seconds to delay until startup. If it is 0, the board will just stop. Optional.
@@ -542,24 +584,21 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of rebooting board_n. A confirmation of rebooting board_n.
</details> </details>
""" """
reboot_data = [session_id, str(board_n)] reboot_data = [str(board_n)]
if delay_s is not None: if delay_s is not None:
reboot_data.append(str(delay_s)) reboot_data.append(str(delay_s))
return await self.send_command("reboot", parameters=",".join(reboot_data)) return await self.send_privileged_command("reboot", *reboot_data)
async def rebootdevice(self, session_id: str) -> dict: async def rebootdevice(self) -> dict:
"""Reboot the miner. Requires a session_id from logon. """Reboot the miner.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns: Returns:
A confirmation of rebooting the miner. A confirmation of rebooting the miner.
</details> </details>
""" """
return await self.send_command("rebootdevice", parameters=session_id) return await self.send_privileged_command("rebootdevice")
async def removegroup(self, group_id: str) -> dict: async def removegroup(self, group_id: str) -> dict:
"""Remove a pool group. """Remove a pool group.
@@ -575,19 +614,16 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("removegroup", parameters=group_id) return await self.send_command("removegroup", parameters=group_id)
async def resetminer(self, session_id: str) -> dict: async def resetminer(self) -> dict:
"""Restart the mining process. Requires a session_id from logon. """Restart the mining process.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns: Returns:
A confirmation of restarting the mining process. A confirmation of restarting the mining process.
</details> </details>
""" """
return await self.send_command("resetminer", parameters=session_id) return await self.send_privileged_command("resetminer")
async def removepool(self, pool_id: int) -> dict: async def removepool(self, pool_id: int) -> dict:
"""Remove a pool. """Remove a pool.
@@ -614,7 +650,9 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
""" """
return await self.send_command("session") return await self.send_command("session")
async def tempctrlset(self, target: int, hot: int, dangerous: int) -> dict: async def tempctrlset(
self, target: int = None, hot: int = None, dangerous: int = None
) -> dict:
"""Set temp control values. """Set temp control values.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
@@ -629,7 +667,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
</details> </details>
""" """
return await self.send_command( return await self.send_command(
"tempctrlset", parameters=f"{target},{hot},{dangerous}" "tempctrlset", target or "", hot or "", dangerous or ""
) )
async def stats(self) -> dict: async def stats(self) -> dict:
@@ -668,7 +706,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of switching to the pool. A confirmation of switching to the pool.
</details> </details>
""" """
return await self.send_command("switchpool", parameters=str(pool_id)) return await self.send_command("switchpool", pool_id)
async def tempctrl(self) -> dict: async def tempctrl(self) -> dict:
"""Get temperature control data. """Get temperature control data.
@@ -716,15 +754,14 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
Board voltage values. Board voltage values.
</details> </details>
""" """
return await self.send_command("frequencyget", parameters=str(board_n)) return await self.send_command("frequencyget", board_n)
async def voltageset(self, session_id: str, board_n: int, voltage: float) -> dict: async def voltageset(self, board_n: int, voltage: float) -> dict:
"""Set voltage values. """Set voltage values.
<details> <details>
<summary>Expand</summary> <summary>Expand</summary>
Parameters: Parameters:
session_id: Session id from the logon command.
board_n: The board to set the voltage on. board_n: The board to set the voltage on.
voltage: The voltage to use. voltage: The voltage to use.
@@ -732,23 +769,7 @@ class LUXMinerRPCAPI(BaseMinerRPCAPI):
A confirmation of setting the voltage. A confirmation of setting the voltage.
</details> </details>
""" """
return await self.send_command( return await self.send_privileged_command("voltageset", board_n, voltage)
"voltageset", parameters=f"{session_id},{board_n},{voltage}"
)
async def wakeup(self, session_id: str) -> dict:
"""Take the miner out of sleep mode. Requires a session_id from logon.
<details>
<summary>Expand</summary>
Parameters:
session_id: Session id from the logon command.
Returns:
A confirmation of resuming mining.
</details>
"""
return await self.send_command("wakeup", parameters=session_id)
async def upgraderun(self): async def upgraderun(self):
""" """

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.62.0" version = "0.62.4"
description = "A simplified and standardized interface for Bitcoin ASICs." description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"] authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic" repository = "https://github.com/UpstreamData/pyasic"