diff --git a/pyasic/device/models.py b/pyasic/device/models.py index 24678d72..81a53427 100644 --- a/pyasic/device/models.py +++ b/pyasic/device/models.py @@ -339,6 +339,13 @@ class BitAxeModels(str, Enum): return self.value +class IceRiverModels(str, Enum): + KS2 = "KS2" + + def __str__(self): + return self.value + + class MinerModel: ANTMINER = AntminerModels WHATSMINER = WhatsminerModels @@ -348,3 +355,4 @@ class MinerModel: AURADINE = AuradineModels EPIC = ePICModels BITAXE = BitAxeModels + ICERIVER = IceRiverModels diff --git a/pyasic/miners/backends/__init__.py b/pyasic/miners/backends/__init__.py index 283a598c..a510d001 100644 --- a/pyasic/miners/backends/__init__.py +++ b/pyasic/miners/backends/__init__.py @@ -24,6 +24,7 @@ from .cgminer import CGMiner from .epic import ePIC from .goldshell import GoldshellMiner from .hiveon import Hiveon +from .iceriver import IceRiver from .innosilicon import Innosilicon from .luxminer import LUXMiner from .marathon import MaraMiner diff --git a/pyasic/miners/backends/iceriver.py b/pyasic/miners/backends/iceriver.py new file mode 100644 index 00000000..cff01f90 --- /dev/null +++ b/pyasic/miners/backends/iceriver.py @@ -0,0 +1,14 @@ +from pyasic.miners.data import DataLocations +from pyasic.miners.device.firmware import StockFirmware +from pyasic.web.iceriver import IceRiverWebAPI + +ICERIVER_DATA_LOC = DataLocations() + + +class IceRiver(StockFirmware): + """Handler for IceRiver miners""" + + _web_cls = IceRiverWebAPI + web: IceRiverWebAPI + + data_locations = ICERIVER_DATA_LOC diff --git a/pyasic/miners/device/makes.py b/pyasic/miners/device/makes.py index 6bfbe40b..615b98e3 100644 --- a/pyasic/miners/device/makes.py +++ b/pyasic/miners/device/makes.py @@ -48,3 +48,7 @@ class ePICMake(BaseMiner): class BitAxeMake(BaseMiner): make = MinerMake.BITAXE + + +class IceRiverMake(BaseMiner): + make = MinerMake.BITAXE diff --git a/pyasic/miners/device/models/__init__.py b/pyasic/miners/device/models/__init__.py index a74bf0a9..666a9c8f 100644 --- a/pyasic/miners/device/models/__init__.py +++ b/pyasic/miners/device/models/__init__.py @@ -19,5 +19,6 @@ from .auradine import * from .avalonminer import * from .epic import * from .goldshell import * +from .iceriver import * from .innosilicon import * from .whatsminer import * diff --git a/pyasic/miners/device/models/iceriver/KSX/KS2.py b/pyasic/miners/device/models/iceriver/KSX/KS2.py new file mode 100644 index 00000000..888067e6 --- /dev/null +++ b/pyasic/miners/device/models/iceriver/KSX/KS2.py @@ -0,0 +1,23 @@ +# ------------------------------------------------------------------------------ +# 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 KS2(IceRiverMake): + raw_model = MinerModel.ICERIVER.KS2 + + expected_fans = 4 diff --git a/pyasic/miners/device/models/iceriver/KSX/__init__.py b/pyasic/miners/device/models/iceriver/KSX/__init__.py new file mode 100644 index 00000000..2f2a4251 --- /dev/null +++ b/pyasic/miners/device/models/iceriver/KSX/__init__.py @@ -0,0 +1 @@ +from .KS2 import KS2 diff --git a/pyasic/miners/device/models/iceriver/__init__.py b/pyasic/miners/device/models/iceriver/__init__.py new file mode 100644 index 00000000..54c0f2b8 --- /dev/null +++ b/pyasic/miners/device/models/iceriver/__init__.py @@ -0,0 +1 @@ +from .KSX import * diff --git a/pyasic/miners/factory.py b/pyasic/miners/factory.py index bf5d25b2..2795d2d7 100644 --- a/pyasic/miners/factory.py +++ b/pyasic/miners/factory.py @@ -38,6 +38,7 @@ from pyasic.miners.bitaxe import * from pyasic.miners.blockminer import * from pyasic.miners.device.makes import * from pyasic.miners.goldshell import * +from pyasic.miners.iceriver import * from pyasic.miners.innosilicon import * from pyasic.miners.whatsminer import * @@ -56,6 +57,7 @@ class MinerTypes(enum.Enum): AURADINE = 10 MARATHON = 11 BITAXE = 12 + ICERIVER = 13 MINER_CLASSES = { @@ -451,6 +453,10 @@ MINER_CLASSES = { "BM1366": BitAxeUltra, "BM1397": BitAxeMax, }, + MinerTypes.ICERIVER: { + None: type("IceRiverUnknown", (IceRiver, IceRiverMake), {}), + "KS2": IceRiverKS2, + }, } @@ -623,6 +629,8 @@ class MinerFactory: return MinerTypes.INNOSILICON if "Miner UI" in web_text: return MinerTypes.AURADINE + if "用户界面" in web_text: + return MinerTypes.ICERIVER async def _get_miner_socket(self, ip: str) -> MinerTypes | None: commands = ["version", "devdetails"] diff --git a/pyasic/miners/iceriver/__init__.py b/pyasic/miners/iceriver/__init__.py new file mode 100644 index 00000000..91378053 --- /dev/null +++ b/pyasic/miners/iceriver/__init__.py @@ -0,0 +1 @@ +from .iceminer import * diff --git a/pyasic/miners/iceriver/iceminer/KSX/KS2.py b/pyasic/miners/iceriver/iceminer/KSX/KS2.py new file mode 100644 index 00000000..629f8095 --- /dev/null +++ b/pyasic/miners/iceriver/iceminer/KSX/KS2.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends.iceriver import IceRiver +from pyasic.miners.device.models import KS2 + + +class IceRiverKS2(IceRiver, KS2): + pass diff --git a/pyasic/miners/iceriver/iceminer/KSX/__init__.py b/pyasic/miners/iceriver/iceminer/KSX/__init__.py new file mode 100644 index 00000000..4d6be18f --- /dev/null +++ b/pyasic/miners/iceriver/iceminer/KSX/__init__.py @@ -0,0 +1 @@ +from .KS2 import IceRiverKS2 diff --git a/pyasic/miners/iceriver/iceminer/__init__.py b/pyasic/miners/iceriver/iceminer/__init__.py new file mode 100644 index 00000000..54c0f2b8 --- /dev/null +++ b/pyasic/miners/iceriver/iceminer/__init__.py @@ -0,0 +1 @@ +from .KSX import * diff --git a/pyasic/settings/__init__.py b/pyasic/settings/__init__.py index 8258ae5f..6e394a59 100644 --- a/pyasic/settings/__init__.py +++ b/pyasic/settings/__init__.py @@ -39,6 +39,7 @@ _settings = { # defaults "default_auradine_web_password": "admin", "default_epic_web_password": "letmein", "default_hive_web_password": "admin", + "default_iceriver_web_password": "12345678", "default_antminer_ssh_password": "miner", "default_bosminer_ssh_password": "root", "socket_linger_time": 1000, diff --git a/pyasic/web/__init__.py b/pyasic/web/__init__.py index 5e030e65..f6576a0a 100644 --- a/pyasic/web/__init__.py +++ b/pyasic/web/__init__.py @@ -19,5 +19,6 @@ from .base import BaseWebAPI from .braiins_os import BOSerWebAPI, BOSMinerWebAPI from .epic import ePICWebAPI from .goldshell import GoldshellWebAPI +from .iceriver import IceRiverWebAPI from .innosilicon import InnosiliconWebAPI from .vnish import VNishWebAPI diff --git a/pyasic/web/iceriver.py b/pyasic/web/iceriver.py new file mode 100644 index 00000000..55927642 --- /dev/null +++ b/pyasic/web/iceriver.py @@ -0,0 +1,73 @@ +# ------------------------------------------------------------------------------ +# 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 IceRiverWebAPI(BaseWebAPI): + def __init__(self, ip: str) -> None: + super().__init__(ip) + self.username = "admin" + self.pwd = settings.get("default_iceriver_web_password", "12345678") + + 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}/user/loginpost", + params={"post": "6", "user": self.username, "pwd": 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}/user/{command}", params=parameters + ) + return resp.json() + except httpx.HTTPError: + raise APIError(f"Command failed: {command}") + + async def locate(self, enable: bool): + return await self.send_command( + "userpanel", post="5", locate="1" if enable else "0" + ) + + async def userpanel(self): + return await self.send_command("userpanel", post="4")