From 64774d2017cad61dfe798711836caaac867f02e2 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Tue, 23 Jan 2024 14:06:54 -0700 Subject: [PATCH 01/17] feature: add basic auradine miner framework. --- pyasic/miners/backends/auradine.py | 32 ++++ pyasic/miners/miner_factory.py | 21 +++ pyasic/rpc/btminer.py | 2 +- pyasic/rpc/gcminer.py | 182 ++++++++++++++++++++ pyasic/settings/__init__.py | 2 + pyasic/web/antminer.py | 4 +- pyasic/web/auradine.py | 259 +++++++++++++++++++++++++++++ pyasic/web/braiins_os/__init__.py | 2 +- pyasic/web/epic.py | 2 +- pyasic/web/goldshell.py | 4 +- pyasic/web/innosilicon.py | 4 +- pyasic/web/vnish.py | 4 +- 12 files changed, 507 insertions(+), 11 deletions(-) create mode 100644 pyasic/miners/backends/auradine.py create mode 100644 pyasic/rpc/gcminer.py create mode 100644 pyasic/web/auradine.py diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py new file mode 100644 index 00000000..54d8ced9 --- /dev/null +++ b/pyasic/miners/backends/auradine.py @@ -0,0 +1,32 @@ +# ------------------------------------------------------------------------------ +# 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.base import BaseMiner, DataLocations +from pyasic.rpc.gcminer import GCMinerRPCAPI +from pyasic.web.auradine import FluxWebAPI + +AURADINE_DATA_LOC = DataLocations(**{}) + + +class Auradine(BaseMiner): + """Base handler for Auradine miners""" + + _api_cls = GCMinerRPCAPI + api: GCMinerRPCAPI + _web_cls = FluxWebAPI + web: FluxWebAPI + + data_locations = AURADINE_DATA_LOC diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index bddb9bd0..32acdc5f 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -38,6 +38,7 @@ from pyasic.miners.backends import ( VNish, ePIC, ) +from pyasic.miners.backends.auradine import Auradine from pyasic.miners.backends.innosilicon import Innosilicon from pyasic.miners.base import AnyMiner from pyasic.miners.goldshell import * @@ -57,6 +58,7 @@ class MinerTypes(enum.Enum): HIVEON = 7 LUX_OS = 8 EPIC = 9 + AURADINE = 10 MINER_CLASSES = { @@ -392,6 +394,16 @@ MINER_CLASSES = { None: LUXMiner, "ANTMINER S9": LUXMinerS9, }, + MinerTypes.AURADINE: { + None: Auradine, + # "AT1500": None, + # "AT2860": None, + # "AT2880": None, + # "AI2500": None, + # "AI3680": None, + # "AD2500": None, + # "AD3500": None, + }, } @@ -660,6 +672,8 @@ class MinerFactory: return MinerTypes.GOLDSHELL if "AVALON" in upper_data: return MinerTypes.AVALONMINER + if "GCMINER" in upper_data or "FLUXOS" in upper_data: + return MinerTypes.AURADINE async def send_web_command( self, @@ -948,5 +962,12 @@ class MinerFactory: except (TypeError, LookupError): pass + async def get_miner_model_auradine(self, ip: str): + web_json_data = await self.send_web_command(ip, ":8080/ipreport") + try: + return web_json_data["IPReport"][0]["model"] + except (TypeError, LookupError): + pass + miner_factory = MinerFactory() diff --git a/pyasic/rpc/btminer.py b/pyasic/rpc/btminer.py index 89281335..8d8c8270 100644 --- a/pyasic/rpc/btminer.py +++ b/pyasic/rpc/btminer.py @@ -187,7 +187,7 @@ class BTMinerRPCAPI(BaseMinerRPCAPI): def __init__(self, ip: str, port: int = 4028, api_ver: str = "0.0.0"): super().__init__(ip, port, api_ver) - self.pwd = settings.get("default_whatsminer_password", "admin") + self.pwd = settings.get("default_whatsminer_rpc_password", "admin") self.current_token = None async def multicommand(self, *commands: str, allow_warning: bool = True) -> dict: diff --git a/pyasic/rpc/gcminer.py b/pyasic/rpc/gcminer.py new file mode 100644 index 00000000..a4a103a7 --- /dev/null +++ b/pyasic/rpc/gcminer.py @@ -0,0 +1,182 @@ +# ------------------------------------------------------------------------------ +# 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 typing import Literal + +from pyasic.rpc import BaseMinerRPCAPI + + +class GCMinerRPCAPI(BaseMinerRPCAPI): + """An abstraction of the GCMiner API. + + Each method corresponds to an API command in GCMiner. + + No documentation for this API is currently publicly available. + + This class abstracts use of the GCMiner API, as well as the + methods for sending commands to it. The `self.send_command()` + function handles sending a command to the miner asynchronously, and + as such is the base for many of the functions in this class, which + rely on it to send the command for them. + + Parameters: + ip: The IP of the miner to reference the API on. + """ + + async def asc(self, n: int) -> dict: + """Get data for ASC device n. +
+ Expand + + Parameters: + n: The device to get data for. + + Returns: + The data for ASC device n. +
+ """ + return await self.send_command("asc", parameters=n) + + async def asccount(self) -> dict: + """Get data on the number of ASC devices and their info. +
+ Expand + + Returns: + Data on all ASC devices. +
+ """ + return await self.send_command("asccount") + + async def check(self, command: str) -> dict: + """Check if the command `command` exists in LUXMiner. +
+ Expand + + Parameters: + command: The command to check. + + Returns: + ## Information about a command: + * Exists (Y/N) <- the command exists in this version + * Access (Y/N) <- you have access to use the command +
+ """ + return await self.send_command("check", parameters=command) + + async def coin(self) -> dict: + """Get information on the current coin. +
+ Expand + + Returns: + ## Information about the current coin being mined: + * Hash Method <- the hashing algorithm + * Current Block Time <- blocktime as a float, 0 means none + * Current Block Hash <- the hash of the current block, blank means none + * LP <- whether LP is in use on at least 1 pool + * Network Difficulty: the current network difficulty +
+ """ + return await self.send_command("coin") + + async def config(self) -> dict: + """Get some basic configuration info. +
+ Expand + + Returns: + Miner configuration information. +
+ """ + return await self.send_command("config") + + async def devdetails(self) -> dict: + """Get data on all devices with their static details. +
+ Expand + + Returns: + Data on all devices with their static details. +
+ """ + return await self.send_command("devdetails") + + async def devs(self) -> dict: + """Get data on each PGA/ASC with their details. +
+ Expand + + Returns: + Data on each PGA/ASC with their details. +
+ """ + return await self.send_command("devs") + + async def edevs(self) -> dict: + """Alias for devs""" + return await self.send_command("edevs") + + async def pools(self) -> dict: + """Get pool information. + +
+ Expand + + Returns: + Miner pool information. +
+ """ + return await self.send_command("pools") + + async def stats(self) -> dict: + """Get stats of each device/pool with more than 1 getwork. + +
+ Expand + + Returns: + Stats of each device/pool with more than 1 getwork. +
+ """ + return await self.send_command("stats") + + async def estats(self) -> dict: + """Alias for stats""" + return await self.send_command("estats") + + async def summary(self) -> dict: + """Get the status summary of the miner. + +
+ Expand + + Returns: + The status summary of the miner. +
+ """ + return await self.send_command("summary") + + async def version(self) -> dict: + """Get miner version info. + +
+ Expand + + Returns: + Miner version information. +
+ """ + return await self.send_command("version") diff --git a/pyasic/settings/__init__.py b/pyasic/settings/__init__.py index be2cbc2b..5b1a4280 100644 --- a/pyasic/settings/__init__.py +++ b/pyasic/settings/__init__.py @@ -34,6 +34,8 @@ _settings = { # defaults "default_bosminer_web_password": "root", "default_vnish_web_password": "admin", "default_goldshell_web_password": "123456789", + "default_auradine_web_password": "admin", + "default_epic_web_password": "letmein", "default_hive_web_password": "admin", "default_antminer_ssh_password": "miner", "default_bosminer_ssh_password": "root", diff --git a/pyasic/web/antminer.py b/pyasic/web/antminer.py index dc9051bb..944ad55a 100644 --- a/pyasic/web/antminer.py +++ b/pyasic/web/antminer.py @@ -26,7 +26,7 @@ from pyasic.web import BaseWebAPI class AntminerModernWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: super().__init__(ip) - self.pwd = settings.get("default_antminer_password", "root") + self.pwd = settings.get("default_antminer_web_password", "root") async def send_command( self, @@ -142,7 +142,7 @@ class AntminerModernWebAPI(BaseWebAPI): class AntminerOldWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: super().__init__(ip) - self.pwd = settings.get("default_antminer_password", "root") + self.pwd = settings.get("default_antminer_web_password", "root") async def send_command( self, diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py new file mode 100644 index 00000000..31da7883 --- /dev/null +++ b/pyasic/web/auradine.py @@ -0,0 +1,259 @@ +# ------------------------------------------------------------------------------ +# 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. - +# ------------------------------------------------------------------------------ +import asyncio +import json +import warnings +from typing import Any, List, Union + +import httpx + +from pyasic import settings +from pyasic.errors import APIError +from pyasic.web import BaseWebAPI + + +class FluxWebAPI(BaseWebAPI): + def __init__(self, ip: str) -> None: + super().__init__(ip) + self.username = "admin" + self.pwd = settings.get("default_auradine_web_password", "admin") + self.port = 8080 + self.jwt = None + + async def auth(self): + async with httpx.AsyncClient(transport=settings.transport()) as client: + try: + auth = await client.post( + f"http://{self.ip}:{self.port}/token", + data={ + "command": "token", + "username": self.username, + "password": self.pwd, + }, + ) + except httpx.HTTPError: + warnings.warn(f"Could not authenticate web token with miner: {self}") + else: + json_auth = auth.json() + try: + self.jwt = json_auth["Token"][0]["Token"] + except LookupError: + return None + return self.jwt + + async def send_command( + self, + command: Union[str, bytes], + post: bool = False, + ignore_errors: bool = False, + allow_warning: bool = True, + **parameters: Any, + ) -> dict: + if self.jwt is None: + await self.auth() + async with httpx.AsyncClient(transport=settings.transport()) as client: + for i in range(settings.get("get_data_retries", 1)): + try: + if post: + response = await client.post( + f"http://{self.ip}:{self.port}/{command}", + headers={"Token": self.jwt}, + timeout=settings.get("api_function_timeout", 5), + ) + elif parameters: + response = await client.post( + f"http://{self.ip}:{self.port}/{command}", + headers={"Token": self.jwt}, + timeout=settings.get("api_function_timeout", 5), + json={"command": command, **parameters}, + ) + else: + response = await client.get( + f"http://{self.ip}:{self.port}/{command}", + headers={"Token": self.jwt}, + timeout=settings.get("api_function_timeout", 5), + ) + json_data = response.json() + validation = self._validate_command_output(json_data) + if not validation[0]: + if i == settings.get("get_data_retries", 1): + raise APIError(validation[1]) + # refresh the token, retry + await self.auth() + continue + return json_data + except httpx.HTTPError: + pass + except json.JSONDecodeError: + pass + + async def multicommand( + self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True + ) -> dict: + tasks = {} + # send all commands individually + for cmd in commands: + tasks[cmd] = asyncio.create_task( + self.send_command(cmd, allow_warning=allow_warning) + ) + + await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True) + + data = {} + for cmd in tasks: + try: + result = tasks[cmd].result() + if result is None or result == {}: + result = {} + data[cmd] = [result] + except APIError: + pass + + return data + + @staticmethod + def _validate_command_output(data: dict) -> tuple: + # check if the data returned is correct or an error + # if status isn't a key, it is a multicommand + if "STATUS" not in data.keys(): + for key in data.keys(): + # make sure not to try to turn id into a dict + if not key == "id": + # make sure they succeeded + if "STATUS" in data[key][0].keys(): + if data[key][0]["STATUS"][0]["STATUS"] not in ["S", "I"]: + # this is an error + return False, f"{key}: " + data[key][0]["STATUS"][0]["Msg"] + elif "id" not in data.keys(): + if isinstance(data["STATUS"], list): + if data["STATUS"][0].get("STATUS", None) in ["S", "I"]: + return True, None + else: + return False, data["STATUS"][0]["Msg"] + + elif isinstance(data["STATUS"], dict): + # new style X19 command + if data["STATUS"]["STATUS"] not in ["S", "I"]: + return False, data["STATUS"]["Msg"] + return True, None + + if data["STATUS"] not in ["S", "I"]: + return False, data["Msg"] + else: + # make sure the command succeeded + if isinstance(data["STATUS"], str): + if data["STATUS"] in ["RESTART"]: + return True, None + elif isinstance(data["STATUS"], dict): + if data["STATUS"].get("STATUS") in ["S", "I"]: + return True, None + elif data["STATUS"][0]["STATUS"] not in ("S", "I"): + # this is an error + if data["STATUS"][0]["STATUS"] not in ("S", "I"): + return False, data["STATUS"][0]["Msg"] + return True, None + + async def factory_reset(self): + return await self.send_command("factory-reset", post=True) + + async def get_fan(self): + return await self.send_command("fan") + + async def set_fan(self, fan: int, speed_pct: int): + return await self.send_command("fan", index=fan, percentage=speed_pct) + + async def firmware_upgrade(self, url: str = None, version: str = "latest"): + if url is not None: + return await self.send_command("firmware-upgrade", url=url) + return await self.send_command("firmware-upgrade", version=version) + + async def get_frequency(self): + return await self.send_command("frequency") + + async def set_frequency(self, board: int, frequency: float): + return await self.send_command("frequency", board=board, frequency=frequency) + + async def ipreport(self): + return await self.send_command("ipreport") + + async def get_led(self): + return await self.send_command("led") + + async def set_led(self, code: int, led_1: int, led_2: int, msg: str = ""): + return await self.send_command( + "led", code=code, led1=led_1, led2=led_2, msg=msg + ) + + async def get_mode(self): + return await self.send_command("mode") + + async def set_mode(self, **kwargs): + return await self.send_command("mode", **kwargs) + + async def get_network(self): + return await self.send_command("network") + + async def set_network(self, **kwargs): + return await self.send_command("network", **kwargs) + + async def password(self, password: str): + res = await self.send_command( + "password", user=self.username, old=self.pwd, new=password + ) + self.pwd = password + return res + + async def get_psu(self): + return await self.send_command("psu") + + async def set_psu(self, voltage: float): + return await self.send_command("psu", voltage=voltage) + + async def get_register(self): + return await self.send_command("register") + + async def set_register(self, company: str): + return await self.send_command("register", parameter=company) + + async def reboot(self): + return await self.send_command("restart", post=True) + + async def restart_gcminer(self): + return await self.send_command("restart", parameter="gcminer") + + async def restart_api_server(self): + return await self.send_command("restart", parameter="api-server") + + async def temperature(self): + return await self.send_command("temperature") + + async def timedate(self, ntp: str, timezone: str): + return await self.send_command("timedate", ntp=ntp, timezone=timezone) + + async def token(self): + return await self.send_command("token", user=self.username, password=self.pwd) + + async def update_pools(self, pools: List[dict]): + return await self.send_command("updatepools", pools=pools) + + async def voltage(self): + return await self.send_command("voltage") + + async def get_ztp(self): + return await self.send_command("ztp") + + async def set_ztp(self, enable: bool): + return await self.send_command("ztp", parameter="on" if enable else "off") diff --git a/pyasic/web/braiins_os/__init__.py b/pyasic/web/braiins_os/__init__.py index 848e4e62..30737bfa 100644 --- a/pyasic/web/braiins_os/__init__.py +++ b/pyasic/web/braiins_os/__init__.py @@ -109,7 +109,7 @@ class BOSerWebAPI(BOSMinerWebAPI): return await self.gql.send_command(command) elif command_type == "grpc": try: - return await (getattr(self.grpc, command.replace("grpc_", "")))() + return await getattr(self.grpc, command.replace("grpc_", ""))() except AttributeError: raise APIError(f"No gRPC command found for command: {command}") elif command_type == "luci": diff --git a/pyasic/web/epic.py b/pyasic/web/epic.py index 684648b9..8e4811e4 100644 --- a/pyasic/web/epic.py +++ b/pyasic/web/epic.py @@ -27,7 +27,7 @@ class ePICWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: super().__init__(ip) self.username = "root" - self.pwd = settings.get("default_epic_password", "letmein") + self.pwd = settings.get("default_epic_web_password", "letmein") self.token = None self.port = 4028 diff --git a/pyasic/web/goldshell.py b/pyasic/web/goldshell.py index ea4d0ec1..10072bed 100644 --- a/pyasic/web/goldshell.py +++ b/pyasic/web/goldshell.py @@ -27,7 +27,7 @@ class GoldshellWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: super().__init__(ip) self.username = "admin" - self.pwd = settings.get("default_goldshell_password", "123456789") + self.pwd = settings.get("default_goldshell_web_password", "123456789") self.jwt = None async def auth(self): @@ -69,7 +69,7 @@ class GoldshellWebAPI(BaseWebAPI): if parameters.get("pool_pwd"): parameters["pass"] = parameters["pool_pwd"] parameters.pop("pool_pwd") - if not self.jwt: + if self.jwt is None: await self.auth() async with httpx.AsyncClient(transport=settings.transport()) as client: for _ in range(settings.get("get_data_retries", 1)): diff --git a/pyasic/web/innosilicon.py b/pyasic/web/innosilicon.py index 63df22c2..4a842a89 100644 --- a/pyasic/web/innosilicon.py +++ b/pyasic/web/innosilicon.py @@ -28,7 +28,7 @@ class InnosiliconWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: super().__init__(ip) self.username = "admin" - self.pwd = settings.get("default_innosilicon_password", "admin") + self.pwd = settings.get("default_innosilicon_web_password", "admin") self.jwt = None async def auth(self): @@ -52,7 +52,7 @@ class InnosiliconWebAPI(BaseWebAPI): allow_warning: bool = True, **parameters: Union[str, int, bool], ) -> dict: - if not self.jwt: + if self.jwt is None: await self.auth() async with httpx.AsyncClient(transport=settings.transport()) as client: for _ in range(settings.get("get_data_retries", 1)): diff --git a/pyasic/web/vnish.py b/pyasic/web/vnish.py index 91588d8d..cc92bbd4 100644 --- a/pyasic/web/vnish.py +++ b/pyasic/web/vnish.py @@ -27,7 +27,7 @@ class VNishWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: super().__init__(ip) self.username = "admin" - self.pwd = settings.get("default_vnish_password", "admin") + self.pwd = settings.get("default_vnish_web_password", "admin") self.token = None async def auth(self): @@ -56,7 +56,7 @@ class VNishWebAPI(BaseWebAPI): allow_warning: bool = True, **parameters: Union[str, int, bool], ) -> dict: - if not self.token: + if self.token is None: await self.auth() async with httpx.AsyncClient(transport=settings.transport()) as client: for _ in range(settings.get("get_data_retries", 1)): From eed19733455c0fb3d857ca91eb43580f7dfd8caf Mon Sep 17 00:00:00 2001 From: b-rowan Date: Tue, 23 Jan 2024 14:23:57 -0700 Subject: [PATCH 02/17] feature: add auradine models. --- pyasic/miners/auradine/__init__.py | 1 + pyasic/miners/auradine/flux/AD/AT1.py | 6 ++++++ pyasic/miners/auradine/flux/AD/AT2.py | 10 ++++++++++ pyasic/miners/auradine/flux/AD/__init__.py | 2 ++ pyasic/miners/auradine/flux/AI/AI2.py | 6 ++++++ pyasic/miners/auradine/flux/AI/AI3.py | 6 ++++++ pyasic/miners/auradine/flux/AI/__init__.py | 2 ++ pyasic/miners/auradine/flux/AT/AD2.py | 6 ++++++ pyasic/miners/auradine/flux/AT/AD3.py | 6 ++++++ pyasic/miners/auradine/flux/AT/__init__.py | 2 ++ pyasic/miners/auradine/flux/__init__.py | 3 +++ pyasic/miners/backends/__init__.py | 2 ++ pyasic/miners/makes.py | 4 ++++ pyasic/miners/miner_factory.py | 19 ++++++++++--------- pyasic/miners/types/__init__.py | 1 + pyasic/miners/types/auradine/AD/AT1.py | 6 ++++++ pyasic/miners/types/auradine/AD/AT2.py | 11 +++++++++++ pyasic/miners/types/auradine/AD/__init__.py | 2 ++ pyasic/miners/types/auradine/AI/AI2.py | 6 ++++++ pyasic/miners/types/auradine/AI/AI3.py | 6 ++++++ pyasic/miners/types/auradine/AI/__init__.py | 2 ++ pyasic/miners/types/auradine/AT/AD2.py | 6 ++++++ pyasic/miners/types/auradine/AT/AD3.py | 6 ++++++ pyasic/miners/types/auradine/AT/__init__.py | 2 ++ pyasic/miners/types/auradine/__init__.py | 3 +++ 25 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 pyasic/miners/auradine/__init__.py create mode 100644 pyasic/miners/auradine/flux/AD/AT1.py create mode 100644 pyasic/miners/auradine/flux/AD/AT2.py create mode 100644 pyasic/miners/auradine/flux/AD/__init__.py create mode 100644 pyasic/miners/auradine/flux/AI/AI2.py create mode 100644 pyasic/miners/auradine/flux/AI/AI3.py create mode 100644 pyasic/miners/auradine/flux/AI/__init__.py create mode 100644 pyasic/miners/auradine/flux/AT/AD2.py create mode 100644 pyasic/miners/auradine/flux/AT/AD3.py create mode 100644 pyasic/miners/auradine/flux/AT/__init__.py create mode 100644 pyasic/miners/auradine/flux/__init__.py create mode 100644 pyasic/miners/types/auradine/AD/AT1.py create mode 100644 pyasic/miners/types/auradine/AD/AT2.py create mode 100644 pyasic/miners/types/auradine/AD/__init__.py create mode 100644 pyasic/miners/types/auradine/AI/AI2.py create mode 100644 pyasic/miners/types/auradine/AI/AI3.py create mode 100644 pyasic/miners/types/auradine/AI/__init__.py create mode 100644 pyasic/miners/types/auradine/AT/AD2.py create mode 100644 pyasic/miners/types/auradine/AT/AD3.py create mode 100644 pyasic/miners/types/auradine/AT/__init__.py create mode 100644 pyasic/miners/types/auradine/__init__.py diff --git a/pyasic/miners/auradine/__init__.py b/pyasic/miners/auradine/__init__.py new file mode 100644 index 00000000..b6cc0d73 --- /dev/null +++ b/pyasic/miners/auradine/__init__.py @@ -0,0 +1 @@ +from .flux import * diff --git a/pyasic/miners/auradine/flux/AD/AT1.py b/pyasic/miners/auradine/flux/AD/AT1.py new file mode 100644 index 00000000..7ac0b7d6 --- /dev/null +++ b/pyasic/miners/auradine/flux/AD/AT1.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends import Auradine +from pyasic.miners.types import AuradineAT1500 + + +class AuradineFluxAT1500(AuradineAT1500, Auradine): + pass diff --git a/pyasic/miners/auradine/flux/AD/AT2.py b/pyasic/miners/auradine/flux/AD/AT2.py new file mode 100644 index 00000000..208b6e7e --- /dev/null +++ b/pyasic/miners/auradine/flux/AD/AT2.py @@ -0,0 +1,10 @@ +from pyasic.miners.backends import Auradine +from pyasic.miners.types import AuradineAT2860, AuradineAT2880 + + +class AuradineFluxAT2860(AuradineAT2860, Auradine): + pass + + +class AuradineFluxAT2880(AuradineAT2880, Auradine): + pass diff --git a/pyasic/miners/auradine/flux/AD/__init__.py b/pyasic/miners/auradine/flux/AD/__init__.py new file mode 100644 index 00000000..0be7a6fe --- /dev/null +++ b/pyasic/miners/auradine/flux/AD/__init__.py @@ -0,0 +1,2 @@ +from .AT1 import AuradineFluxAT1500 +from .AT2 import AuradineFluxAT2860, AuradineFluxAT2880 diff --git a/pyasic/miners/auradine/flux/AI/AI2.py b/pyasic/miners/auradine/flux/AI/AI2.py new file mode 100644 index 00000000..ebf6506e --- /dev/null +++ b/pyasic/miners/auradine/flux/AI/AI2.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends import Auradine +from pyasic.miners.types import AuradineAI2500 + + +class AuradineFluxAI2500(AuradineAI2500, Auradine): + pass diff --git a/pyasic/miners/auradine/flux/AI/AI3.py b/pyasic/miners/auradine/flux/AI/AI3.py new file mode 100644 index 00000000..d495c711 --- /dev/null +++ b/pyasic/miners/auradine/flux/AI/AI3.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends import Auradine +from pyasic.miners.types import AuradineAI3680 + + +class AuradineFluxAI3680(AuradineAI3680, Auradine): + pass diff --git a/pyasic/miners/auradine/flux/AI/__init__.py b/pyasic/miners/auradine/flux/AI/__init__.py new file mode 100644 index 00000000..ebed7183 --- /dev/null +++ b/pyasic/miners/auradine/flux/AI/__init__.py @@ -0,0 +1,2 @@ +from .AI2 import AuradineFluxAI2500 +from .AI3 import AuradineFluxAI3680 diff --git a/pyasic/miners/auradine/flux/AT/AD2.py b/pyasic/miners/auradine/flux/AT/AD2.py new file mode 100644 index 00000000..051b139c --- /dev/null +++ b/pyasic/miners/auradine/flux/AT/AD2.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends import Auradine +from pyasic.miners.types import AuradineAD2500 + + +class AuradineFluxAD2500(AuradineAD2500, Auradine): + pass diff --git a/pyasic/miners/auradine/flux/AT/AD3.py b/pyasic/miners/auradine/flux/AT/AD3.py new file mode 100644 index 00000000..650ae2aa --- /dev/null +++ b/pyasic/miners/auradine/flux/AT/AD3.py @@ -0,0 +1,6 @@ +from pyasic.miners.backends import Auradine +from pyasic.miners.types import AuradineAD3500 + + +class AuradineFluxAD3500(AuradineAD3500, Auradine): + pass diff --git a/pyasic/miners/auradine/flux/AT/__init__.py b/pyasic/miners/auradine/flux/AT/__init__.py new file mode 100644 index 00000000..3b29e4f2 --- /dev/null +++ b/pyasic/miners/auradine/flux/AT/__init__.py @@ -0,0 +1,2 @@ +from .AD2 import AuradineFluxAD2500 +from .AD3 import AuradineFluxAD3500 diff --git a/pyasic/miners/auradine/flux/__init__.py b/pyasic/miners/auradine/flux/__init__.py new file mode 100644 index 00000000..9a346701 --- /dev/null +++ b/pyasic/miners/auradine/flux/__init__.py @@ -0,0 +1,3 @@ +from .AD import * +from .AI import * +from .AT import * diff --git a/pyasic/miners/backends/__init__.py b/pyasic/miners/backends/__init__.py index 656662db..97121635 100644 --- a/pyasic/miners/backends/__init__.py +++ b/pyasic/miners/backends/__init__.py @@ -14,6 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from .antminer import AntminerModern, AntminerOld +from .auradine import Auradine from .avalonminer import AvalonMiner from .bfgminer import BFGMiner from .bmminer import BMMiner @@ -23,6 +24,7 @@ from .cgminer import CGMiner from .epic import ePIC from .goldshell import GoldshellMiner from .hiveon import Hiveon +from .innosilicon import Innosilicon from .luxminer import LUXMiner from .vnish import VNish from .whatsminer import M2X, M3X, M5X, M6X diff --git a/pyasic/miners/makes.py b/pyasic/miners/makes.py index 1d5c1187..de86dfd2 100644 --- a/pyasic/miners/makes.py +++ b/pyasic/miners/makes.py @@ -35,3 +35,7 @@ class InnosiliconMake(BaseMiner): class GoldshellMake(BaseMiner): make = "Goldshell" + + +class AuradineMake(BaseMiner): + make = "Auradine" diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index 32acdc5f..a4054475 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -26,20 +26,21 @@ import httpx from pyasic import settings from pyasic.logger import logger from pyasic.miners.antminer import * +from pyasic.miners.auradine import * from pyasic.miners.avalonminer import * from pyasic.miners.backends import ( + Auradine, AvalonMiner, BMMiner, BOSMiner, BTMiner, GoldshellMiner, Hiveon, + Innosilicon, LUXMiner, VNish, ePIC, ) -from pyasic.miners.backends.auradine import Auradine -from pyasic.miners.backends.innosilicon import Innosilicon from pyasic.miners.base import AnyMiner from pyasic.miners.goldshell import * from pyasic.miners.innosilicon import * @@ -396,13 +397,13 @@ MINER_CLASSES = { }, MinerTypes.AURADINE: { None: Auradine, - # "AT1500": None, - # "AT2860": None, - # "AT2880": None, - # "AI2500": None, - # "AI3680": None, - # "AD2500": None, - # "AD3500": None, + "AT1500": AuradineFluxAT1500, + "AT2860": AuradineFluxAT2860, + "AT2880": AuradineFluxAT2880, + "AI2500": AuradineFluxAI2500, + "AI3680": AuradineFluxAI3680, + "AD2500": AuradineFluxAD2500, + "AD3500": AuradineFluxAD3500, }, } diff --git a/pyasic/miners/types/__init__.py b/pyasic/miners/types/__init__.py index a35a8fa1..e9f4f41b 100644 --- a/pyasic/miners/types/__init__.py +++ b/pyasic/miners/types/__init__.py @@ -15,6 +15,7 @@ # ------------------------------------------------------------------------------ from .antminer import * +from .auradine import * from .avalonminer import * from .goldshell import * from .innosilicon import * diff --git a/pyasic/miners/types/auradine/AD/AT1.py b/pyasic/miners/types/auradine/AD/AT1.py new file mode 100644 index 00000000..2858a5ad --- /dev/null +++ b/pyasic/miners/types/auradine/AD/AT1.py @@ -0,0 +1,6 @@ +from pyasic.miners.makes import AuradineMake + + +class AuradineAT1500(AuradineMake): + raw_model = "AT1500" + expected_fans = 4 diff --git a/pyasic/miners/types/auradine/AD/AT2.py b/pyasic/miners/types/auradine/AD/AT2.py new file mode 100644 index 00000000..6ce2bc64 --- /dev/null +++ b/pyasic/miners/types/auradine/AD/AT2.py @@ -0,0 +1,11 @@ +from pyasic.miners.makes import AuradineMake + + +class AuradineAT2860(AuradineMake): + raw_model = "AT2860" + expected_fans = 4 + + +class AuradineAT2880(AuradineMake): + raw_model = "AT2880" + expected_fans = 4 diff --git a/pyasic/miners/types/auradine/AD/__init__.py b/pyasic/miners/types/auradine/AD/__init__.py new file mode 100644 index 00000000..070612a0 --- /dev/null +++ b/pyasic/miners/types/auradine/AD/__init__.py @@ -0,0 +1,2 @@ +from .AT1 import AuradineAT1500 +from .AT2 import AuradineAT2860, AuradineAT2880 diff --git a/pyasic/miners/types/auradine/AI/AI2.py b/pyasic/miners/types/auradine/AI/AI2.py new file mode 100644 index 00000000..8cb05218 --- /dev/null +++ b/pyasic/miners/types/auradine/AI/AI2.py @@ -0,0 +1,6 @@ +from pyasic.miners.makes import AuradineMake + + +class AuradineAI2500(AuradineMake): + raw_model = "AI2500" + expected_fans = 0 diff --git a/pyasic/miners/types/auradine/AI/AI3.py b/pyasic/miners/types/auradine/AI/AI3.py new file mode 100644 index 00000000..05a6bf0e --- /dev/null +++ b/pyasic/miners/types/auradine/AI/AI3.py @@ -0,0 +1,6 @@ +from pyasic.miners.makes import AuradineMake + + +class AuradineAI3680(AuradineMake): + raw_model = "AI3680" + expected_fans = 0 diff --git a/pyasic/miners/types/auradine/AI/__init__.py b/pyasic/miners/types/auradine/AI/__init__.py new file mode 100644 index 00000000..b6fa8ad3 --- /dev/null +++ b/pyasic/miners/types/auradine/AI/__init__.py @@ -0,0 +1,2 @@ +from .AI2 import AuradineAI2500 +from .AI3 import AuradineAI3680 diff --git a/pyasic/miners/types/auradine/AT/AD2.py b/pyasic/miners/types/auradine/AT/AD2.py new file mode 100644 index 00000000..a3e704b4 --- /dev/null +++ b/pyasic/miners/types/auradine/AT/AD2.py @@ -0,0 +1,6 @@ +from pyasic.miners.makes import AuradineMake + + +class AuradineAD2500(AuradineMake): + raw_model = "AD2500" + expected_fans = 0 diff --git a/pyasic/miners/types/auradine/AT/AD3.py b/pyasic/miners/types/auradine/AT/AD3.py new file mode 100644 index 00000000..e3b04add --- /dev/null +++ b/pyasic/miners/types/auradine/AT/AD3.py @@ -0,0 +1,6 @@ +from pyasic.miners.makes import AuradineMake + + +class AuradineAD3500(AuradineMake): + raw_model = "AD3500" + expected_fans = 0 diff --git a/pyasic/miners/types/auradine/AT/__init__.py b/pyasic/miners/types/auradine/AT/__init__.py new file mode 100644 index 00000000..a46be8b9 --- /dev/null +++ b/pyasic/miners/types/auradine/AT/__init__.py @@ -0,0 +1,2 @@ +from .AD2 import AuradineAD2500 +from .AD3 import AuradineAD3500 diff --git a/pyasic/miners/types/auradine/__init__.py b/pyasic/miners/types/auradine/__init__.py new file mode 100644 index 00000000..9a346701 --- /dev/null +++ b/pyasic/miners/types/auradine/__init__.py @@ -0,0 +1,3 @@ +from .AD import * +from .AI import * +from .AT import * From b60c7a55d4e8b0ada144d33e961112b29402eac6 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Tue, 23 Jan 2024 15:28:37 -0700 Subject: [PATCH 03/17] feature: add auradine control functions. --- pyasic/config/__init__.py | 17 +++++- pyasic/config/base.py | 6 ++ pyasic/config/fans.py | 16 +++++ pyasic/config/mining.py | 37 ++++++++++++ pyasic/config/pools.py | 39 +++++++++--- pyasic/miners/backends/auradine.py | 97 ++++++++++++++++++++++++++++++ pyasic/web/auradine.py | 5 +- 7 files changed, 206 insertions(+), 11 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 5dfc5c8d..fded4944 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -113,10 +113,17 @@ class MinerConfig: **self.fan_mode.as_epic(), **self.temperature.as_epic(), **self.mining_mode.as_epic(), - **self.pools.as_epic(user_suffix=user_suffix), + **self.pools.as_epic(), **self.power_scaling.as_epic(), } + def as_auradine(self, user_suffix: str = None) -> dict: + return { + **self.fan_mode.as_auradine(), + **self.mining_mode.as_auradine(), + **self.pools.as_auradine(user_suffix=user_suffix), + } + @classmethod def from_dict(cls, dict_conf: dict) -> "MinerConfig": return cls( @@ -189,6 +196,14 @@ class MinerConfig: mining_mode=MiningModeConfig.from_vnish(web_settings), ) + @classmethod + def from_auradine(cls, web_conf: dict) -> "MinerConfig": + return cls( + pools=PoolConfig.from_api(web_conf["pools"][0]), + fan_mode=FanModeConfig.from_auradine(web_conf["fans"][0]), + mining_mode=MiningModeConfig.from_auradine(web_conf["mode"][0]), + ) + def merge(a: dict, b: dict) -> dict: result = deepcopy(a) diff --git a/pyasic/config/base.py b/pyasic/config/base.py index df5946ac..0d416acd 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -53,6 +53,9 @@ class MinerConfigOption(Enum): def as_vnish(self) -> dict: return self.value.as_vnish() + def as_auradine(self) -> dict: + return self.value.as_auradine() + def __call__(self, *args, **kwargs): return self.value(*args, **kwargs) @@ -99,3 +102,6 @@ class MinerConfigValue: def as_vnish(self) -> dict: return {} + + def as_auradine(self) -> dict: + return {} diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index 04d95434..82565596 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -92,6 +92,9 @@ class FanModeManual(MinerConfigValue): "fan_control": {"min_fans": self.minimum_fans, "speed": self.speed}, } + def as_auradine(self) -> dict: + return {"fan": {"percentage": self.speed}} + @dataclass class FanModeImmersion(MinerConfigValue): @@ -107,6 +110,9 @@ class FanModeImmersion(MinerConfigValue): def as_bosminer(self) -> dict: return {"temp_control": {"mode": "disabled"}} + def as_auradine(self) -> dict: + return {"fan": {"percentage": 0}} + class FanModeConfig(MinerConfigOption): normal = FanModeNormal @@ -202,3 +208,13 @@ class FanModeConfig(MinerConfigOption): if "minimumRequiredFans" in keys: conf["minimum_fans"] = int(temperature_conf["minimumRequiredFans"]) return cls.manual(**conf) + + @classmethod + def from_auradine(cls, web_fan: dict): + try: + fan_data = web_fan["Fan"][0] + fan_1_max = fan_data["Max"] + fan_1_target = fan_data["Target"] + return cls.manual(speed=round((fan_1_target / fan_1_max) * 100)) + except LookupError: + return cls.default() diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index b4101a51..4e29c049 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -43,6 +43,9 @@ class MiningModeNormal(MinerConfigValue): def as_wm(self) -> dict: return {"mode": self.mode} + def as_auradine(self) -> dict: + return {"mode": {"mode": self.mode}} + @dataclass class MiningModeSleep(MinerConfigValue): @@ -58,6 +61,9 @@ class MiningModeSleep(MinerConfigValue): def as_wm(self) -> dict: return {"mode": self.mode} + def as_auradine(self) -> dict: + return {"mode": {"sleep": "on"}} + @dataclass class MiningModeLPM(MinerConfigValue): @@ -73,6 +79,9 @@ class MiningModeLPM(MinerConfigValue): def as_wm(self) -> dict: return {"mode": self.mode} + def as_auradine(self) -> dict: + return {"mode": {"mode": "eco"}} + @dataclass class MiningModeHPM(MinerConfigValue): @@ -88,6 +97,9 @@ class MiningModeHPM(MinerConfigValue): def as_wm(self) -> dict: return {"mode": self.mode} + def as_auradine(self) -> dict: + return {"mode": {"mode": "turbo"}} + @dataclass class MiningModePowerTune(MinerConfigValue): @@ -123,6 +135,9 @@ class MiningModePowerTune(MinerConfigValue): ), } + def as_auradine(self) -> dict: + return {"mode": {"mode": "custom", "tune": "power", "power": self.power}} + @dataclass class MiningModeHashrateTune(MinerConfigValue): @@ -152,6 +167,9 @@ class MiningModeHashrateTune(MinerConfigValue): ) } + def as_auradine(self) -> dict: + return {"mode": {"mode": "custom", "tune": "ths", "ths": self.hashrate}} + @dataclass class ManualBoardSettings(MinerConfigValue): @@ -330,3 +348,22 @@ class MiningModeConfig(MinerConfigOption): return cls.hashrate_tuning( int(tuner_conf["hashrateTarget"]["terahashPerSecond"]) ) + + @classmethod + def from_auradine(cls, web_mode: dict): + try: + mode_data = web_mode["Mode"][0] + if mode_data.get("Sleep") == "on": + return cls.sleep() + if mode_data.get("Mode") == "normal": + return cls.normal() + if mode_data.get("Mode") == "eco": + return cls.low() + if mode_data.get("Mode") == "turbo": + return cls.high() + if mode_data.get("Ths") is not None: + return cls.hashrate_tuning(mode_data["Ths"]) + if mode_data.get("Power") is not None: + return cls.power_tuning(mode_data["Power"]) + except LookupError: + return cls.default() diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index e067421b..4d8eb443 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -27,7 +27,7 @@ class Pool(MinerConfigValue): user: str password: str - def as_am_modern(self, user_suffix: str = None): + def as_am_modern(self, user_suffix: str = None) -> dict: if user_suffix is not None: return { "url": self.url, @@ -36,7 +36,7 @@ class Pool(MinerConfigValue): } return {"url": self.url, "user": self.user, "pass": self.password} - def as_wm(self, idx: int = 1, user_suffix: str = None): + def as_wm(self, idx: int = 1, user_suffix: str = None) -> dict: if user_suffix is not None: return { f"pool_{idx}": self.url, @@ -49,7 +49,7 @@ class Pool(MinerConfigValue): f"passwd_{idx}": self.password, } - def as_am_old(self, idx: int = 1, user_suffix: str = None): + def as_am_old(self, idx: int = 1, user_suffix: str = None) -> dict: if user_suffix is not None: return { f"_ant_pool{idx}url": self.url, @@ -62,7 +62,7 @@ class Pool(MinerConfigValue): f"_ant_pool{idx}pw": self.password, } - def as_goldshell(self, user_suffix: str = None): + def as_goldshell(self, user_suffix: str = None) -> dict: if user_suffix is not None: return { "url": self.url, @@ -71,12 +71,12 @@ class Pool(MinerConfigValue): } return {"url": self.url, "user": self.user, "pass": self.password} - def as_avalon(self, user_suffix: str = None): + def as_avalon(self, user_suffix: str = None) -> str: if user_suffix is not None: return ",".join([self.url, f"{self.user}{user_suffix}", self.password]) return ",".join([self.url, self.user, self.password]) - def as_inno(self, idx: int = 1, user_suffix: str = None): + def as_inno(self, idx: int = 1, user_suffix: str = None) -> dict: if user_suffix is not None: return { f"Pool{idx}": self.url, @@ -89,7 +89,7 @@ class Pool(MinerConfigValue): f"Password{idx}": self.password, } - def as_bosminer(self, user_suffix: str = None): + def as_bosminer(self, user_suffix: str = None) -> dict: if user_suffix is not None: return { "url": self.url, @@ -98,6 +98,15 @@ class Pool(MinerConfigValue): } return {"url": self.url, "user": self.user, "password": self.password} + def as_auradine(self, user_suffix: str = None) -> dict: + if user_suffix is not None: + return { + "url": self.url, + "user": f"{self.user}{user_suffix}", + "pass": self.password, + } + return {"url": self.url, "user": self.user, "pass": self.password} + @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "Pool": return cls( @@ -241,6 +250,9 @@ class PoolGroup(MinerConfigValue): return conf return {"name": "Group", "pool": []} + def as_auradine(self, user_suffix: str = None) -> list: + return [p.as_auradine(user_suffix=user_suffix) for p in self.pools] + @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "PoolGroup": cls_conf = {} @@ -296,7 +308,7 @@ class PoolGroup(MinerConfigValue): return cls([Pool.from_vnish(p) for p in web_settings_pools]) @classmethod - def from_boser(cls, grpc_pool_group: dict): + def from_boser(cls, grpc_pool_group: dict) -> "PoolGroup": try: return cls( pools=[Pool.from_boser(p) for p in grpc_pool_group["pools"]], @@ -373,6 +385,15 @@ class PoolConfig(MinerConfigValue): def as_boser(self, user_suffix: str = None) -> dict: return {} + def as_auradine(self, user_suffix: str = None) -> dict: + if len(self.groups) > 0: + return { + "updatepools": { + "pools": self.groups[0].as_auradine(user_suffix=user_suffix) + } + } + return {"updatepools": {"pools": PoolGroup().as_auradine()}} + @classmethod def from_api(cls, api_pools: dict) -> "PoolConfig": try: @@ -417,7 +438,7 @@ class PoolConfig(MinerConfigValue): return cls() @classmethod - def from_boser(cls, grpc_miner_conf: dict): + def from_boser(cls, grpc_miner_conf: dict) -> "PoolConfig": try: return cls( groups=[ diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 54d8ced9..b23efa8d 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -13,7 +13,10 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ +import logging +from enum import Enum +from pyasic import APIError, MinerConfig from pyasic.miners.base import BaseMiner, DataLocations from pyasic.rpc.gcminer import GCMinerRPCAPI from pyasic.web.auradine import FluxWebAPI @@ -21,6 +24,35 @@ from pyasic.web.auradine import FluxWebAPI AURADINE_DATA_LOC = DataLocations(**{}) +class AuradineLEDColors(Enum): + OFF = 0 + GREEN = 1 + RED = 2 + YELLOW = 3 + GREEN_FLASHING = 4 + RED_FLASHING = 5 + YELLOW_FLASHING = 6 + + def __int__(self): + return self.value + + +class AuradineLEDCodes(Enum): + NO_POWER = 1 + NORMAL = 2 + LOCATE_MINER = 3 + TEMPERATURE = 4 + POOL_CONFIG = 5 + NETWORK = 6 + CONTROL_BOARD = 7 + HASH_RATE_LOW = 8 + CUSTOM1 = 101 + CUSTOM2 = 102 + + def __int__(self): + return self.value + + class Auradine(BaseMiner): """Base handler for Auradine miners""" @@ -30,3 +62,68 @@ class Auradine(BaseMiner): web: FluxWebAPI data_locations = AURADINE_DATA_LOC + + supports_shutdown = True + supports_autotuning = True + + async def fault_light_on(self) -> bool: + return await self.web.set_led(code=int(AuradineLEDCodes.LOCATE_MINER)) + + async def fault_light_off(self) -> bool: + return await self.web.set_led(code=int(AuradineLEDCodes.NORMAL)) + + async def reboot(self) -> bool: + try: + await self.web.reboot() + except APIError: + return False + return True + + async def restart_backend(self) -> bool: + try: + await self.web.restart_gcminer() + except APIError: + return False + return True + + async def stop_mining(self) -> bool: + try: + await self.web.set_mode(sleep="on") + except APIError: + return False + return True + + async def resume_mining(self) -> bool: + try: + await self.web.set_mode(sleep="off") + except APIError: + return False + return True + + async def set_power_limit(self, wattage: int) -> bool: + try: + await self.web.set_mode(mode="custom", tune="power", power=wattage) + except APIError: + return False + return True + + async def get_config(self) -> MinerConfig: + try: + web_conf = await self.web.multicommand("pools", "mode", "fan") + return MinerConfig.from_auradine(web_conf=web_conf) + except APIError as e: + logging.warning(e) + except LookupError: + pass + return MinerConfig() + + async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: + self.config = config + + conf = config.as_auradine(user_suffix=user_suffix) + for key in conf.keys(): + await self.web.send_command(command=key, **conf[key]) + + ################################################## + ### DATA GATHERING FUNCTIONS (get_{some_data}) ### + ################################################## diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index 31da7883..979ad805 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -192,7 +192,10 @@ class FluxWebAPI(BaseWebAPI): async def get_led(self): return await self.send_command("led") - async def set_led(self, code: int, led_1: int, led_2: int, msg: str = ""): + async def set_led(self, code: int): + return await self.send_command("led", code=code) + + async def set_led_custom(self, code: int, led_1: int, led_2: int, msg: str): return await self.send_command( "led", code=code, led1=led_1, led2=led_2, msg=msg ) From 41709e4706932d4d9eed3ee15a35651075c39359 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Tue, 23 Jan 2024 16:15:34 -0700 Subject: [PATCH 04/17] feature: add auradine data functions. --- pyasic/miners/backends/auradine.py | 254 ++++++++++++++++++++++++++++- 1 file changed, 252 insertions(+), 2 deletions(-) diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index b23efa8d..67d780ad 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -15,13 +15,72 @@ # ------------------------------------------------------------------------------ import logging from enum import Enum +from typing import List, Optional from pyasic import APIError, MinerConfig -from pyasic.miners.base import BaseMiner, DataLocations +from pyasic.data import Fan, HashBoard +from pyasic.miners.base import ( + BaseMiner, + DataFunction, + DataLocations, + DataOptions, + RPCAPICommand, + WebAPICommand, +) from pyasic.rpc.gcminer import GCMinerRPCAPI from pyasic.web.auradine import FluxWebAPI -AURADINE_DATA_LOC = DataLocations(**{}) +AURADINE_DATA_LOC = DataLocations( + **{ + str(DataOptions.MAC): DataFunction( + "_get_mac", + [WebAPICommand("web_ipreport", "ipreport")], + ), + str(DataOptions.FW_VERSION): DataFunction( + "_get_fw_ver", + [WebAPICommand("web_ipreport", "ipreport")], + ), + str(DataOptions.HOSTNAME): DataFunction( + "_get_hostname", + [WebAPICommand("web_ipreport", "ipreport")], + ), + str(DataOptions.HASHRATE): DataFunction( + "_get_hashrate", + [RPCAPICommand("api_summary", "summary")], + ), + str(DataOptions.HASHBOARDS): DataFunction( + "_get_hashboards", + [ + RPCAPICommand("api_devs", "devs"), + WebAPICommand("web_ipreport", "ipreport"), + ], + ), + str(DataOptions.WATTAGE): DataFunction( + "_get_wattage", + [WebAPICommand("web_psu", "psu")], + ), + str(DataOptions.WATTAGE_LIMIT): DataFunction( + "_get_wattage_limit", + [WebAPICommand("web_mode", "mode"), WebAPICommand("web_psu", "psu")], + ), + str(DataOptions.FANS): DataFunction( + "_get_fans", + [WebAPICommand("web_fan", "fan")], + ), + str(DataOptions.FAULT_LIGHT): DataFunction( + "_get_fault_light", + [WebAPICommand("web_led", "led")], + ), + str(DataOptions.IS_MINING): DataFunction( + "_is_mining", + [WebAPICommand("web_mode", "mode")], + ), + str(DataOptions.UPTIME): DataFunction( + "_get_uptime", + [RPCAPICommand("api_summary", "summary")], + ), + } +) class AuradineLEDColors(Enum): @@ -127,3 +186,194 @@ class Auradine(BaseMiner): ################################################## ### DATA GATHERING FUNCTIONS (get_{some_data}) ### ################################################## + + async def _get_mac(self, web_ipreport: dict = None) -> Optional[str]: + if web_ipreport is None: + try: + web_ipreport = await self.web.ipreport() + except APIError: + pass + + if web_ipreport is not None: + try: + return web_ipreport["IPReport"][0]["mac"] + except LookupError: + pass + + async def _get_fw_ver(self, web_ipreport: dict = None) -> Optional[str]: + if web_ipreport is None: + try: + web_ipreport = await self.web.ipreport() + except APIError: + pass + + if web_ipreport is not None: + try: + return web_ipreport["IPReport"][0]["version"] + except LookupError: + pass + + async def _get_hostname(self, web_ipreport: dict = None) -> Optional[str]: + if web_ipreport is None: + try: + web_ipreport = await self.web.ipreport() + except APIError: + pass + + if web_ipreport is not None: + try: + return web_ipreport["IPReport"][0]["hostname"] + except LookupError: + pass + + async def _get_hashrate(self, api_summary: dict = None) -> Optional[float]: + if api_summary is None: + try: + api_summary = await self.api.summary() + except APIError: + pass + + if api_summary is not None: + try: + return round( + float(float(api_summary["SUMMARY"][0]["MHS 5s"]) / 1000000), 2 + ) + except (LookupError, ValueError, TypeError): + pass + + async def _get_hashboards( + self, api_devs: dict = None, web_ipreport: dict = None + ) -> List[HashBoard]: + hashboards = [ + HashBoard(slot=i, expected_chips=self.expected_chips) + for i in range(self.expected_hashboards) + ] + + if api_devs is None: + try: + api_devs = await self.api.devs() + except APIError: + pass + if web_ipreport is None: + try: + web_ipreport = await self.web.ipreport() + except APIError: + pass + + if api_devs is not None: + try: + for board in api_devs["DEVS"][0]: + b_id = board["ID"] + hashboards[b_id].hashrate = round( + float(float(board["MHS 5s"]) / 1000000), 2 + ) + hashboards[b_id].temp = round(float(float(board["Temperature"])), 2) + except LookupError: + pass + + if web_ipreport is not None: + try: + for board, sn in enumerate(web_ipreport["IPReport"][0]["HBSerialNo"]): + hashboards[board].serial_number = sn + + except LookupError: + pass + + return hashboards + + async def _get_wattage(self, web_psu: dict = None) -> Optional[int]: + if web_psu is None: + try: + web_psu = await self.web.get_psu() + except APIError: + pass + + if web_psu is not None: + try: + return int(web_psu["PSU"][0]["PowerIn"].replace("W", "")) + except (LookupError, TypeError, ValueError): + pass + + async def _get_wattage_limit( + self, web_mode: dict = None, web_psu: dict = None + ) -> Optional[int]: + if web_mode is None: + try: + web_mode = await self.web.get_mode() + except APIError: + pass + + if web_mode is not None: + try: + return web_mode["Mode"][0]["Power"] + except (LookupError, TypeError, ValueError): + pass + + if web_psu is None: + try: + web_psu = await self.web.get_psu() + except APIError: + pass + + if web_psu is not None: + try: + return int(web_psu["PSU"][0]["PoutMax"].replace("W", "")) + except (LookupError, TypeError, ValueError): + pass + + async def _get_fans(self, web_fan: dict = None) -> List[Fan]: + if web_fan is None: + try: + web_fan = await self.web.get_fan() + except APIError: + pass + + fans = [] + if web_fan is not None: + try: + for fan in web_fan["Fan"]: + f_max = fan["Max"] + f_speed = fan["Speed"] + fans.append(Fan(round((f_speed / f_max) * 100))) + except LookupError: + pass + return fans + + async def _get_fault_light(self, web_led: dict = None) -> Optional[bool]: + if web_led is None: + try: + web_led = await self.web.get_led() + except APIError: + pass + + if web_led is not None: + try: + return web_led["LED"][0]["Code"] == int(AuradineLEDCodes.LOCATE_MINER) + except LookupError: + pass + + async def _is_mining(self, web_mode: dict = None) -> Optional[bool]: + if web_mode is None: + try: + web_mode = await self.web.get_mode() + except APIError: + pass + + if web_mode is not None: + try: + return web_mode["Mode"][0]["Sleep"] == "off" + except (LookupError, TypeError, ValueError): + pass + + async def _get_uptime(self, api_summary: dict = None) -> Optional[int]: + if api_summary is None: + try: + api_summary = await self.api.summary() + except APIError: + pass + + if api_summary is not None: + try: + return api_summary["SUMMARY"][0]["Elapsed"] + except LookupError: + pass From 6a2a3e836dc815e59dc8101f840ed3b5ff59c7bb Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Wed, 24 Jan 2024 16:23:25 -0700 Subject: [PATCH 05/17] bug: fix auradine selection. --- pyasic/miners/miner_factory.py | 24 +++++++++++++++++++++--- pyproject.toml | 1 - 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index a4054475..85679b30 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -485,6 +485,7 @@ class MinerFactory: MinerTypes.EPIC: self.get_miner_model_epic, MinerTypes.HIVEON: self.get_miner_model_hiveon, MinerTypes.LUX_OS: self.get_miner_model_luxos, + MinerTypes.AURADINE: self.get_miner_model_auradine, } fn = miner_model_fns.get(miner_type) @@ -964,10 +965,27 @@ class MinerFactory: pass async def get_miner_model_auradine(self, ip: str): - web_json_data = await self.send_web_command(ip, ":8080/ipreport") try: - return web_json_data["IPReport"][0]["model"] - except (TypeError, LookupError): + async with httpx.AsyncClient(transport=settings.transport()) as client: + auth = await client.post( + f"http://{ip}:8080/token", + data={ + "command": "token", + "username": "admin", + "password": settings.get( + "default_auradine_web_password", "admin" + ), + }, + timeout=settings.get("factory_get_timeout", 3), + ) + token = auth.json()["Token"][0]["Token"] + web_json_data = await client.get( + f"http://{ip}:8080/ipreport", + headers={"Token": token}, + timeout=settings.get("factory_get_timeout", 3), + ) + return web_json_data.json()["IPReport"][0]["model"] + except (TypeError, LookupError, httpx.HTTPError): pass diff --git a/pyproject.toml b/pyproject.toml index b8a9f255..13dfd6c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,6 @@ readme = "README.md" python = "^3.8" httpx = "^0.26.0" asyncssh = "^2.14.2" -grpc-requests = "^0.1.13" passlib = "^1.7.4" pyaml = "^23.12.0" toml = "^0.10.2" From af354fd8e2506c59f00e8a0ab624a91484b092f7 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 17:17:03 -0700 Subject: [PATCH 06/17] feature: add auradine to web selection options. --- pyasic/miners/miner_factory.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index 85679b30..3e85181f 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -585,6 +585,8 @@ class MinerFactory: return MinerTypes.AVALONMINER if "DragonMint" in web_text: return MinerTypes.INNOSILICON + if "Miner UI" in web_text: + return MinerTypes.AURADINE async def _get_miner_socket(self, ip: str): tasks = [] From edf31ae7df4377457cc2fe3361782a9cce3b06c7 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 17:33:41 -0700 Subject: [PATCH 07/17] bug: fix auradine identification. --- pyasic/miners/miner_factory.py | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index 3e85181f..123943d6 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -967,27 +967,10 @@ class MinerFactory: pass async def get_miner_model_auradine(self, ip: str): + sock_json_data = await self.send_api_command(ip, "devdetails") try: - async with httpx.AsyncClient(transport=settings.transport()) as client: - auth = await client.post( - f"http://{ip}:8080/token", - data={ - "command": "token", - "username": "admin", - "password": settings.get( - "default_auradine_web_password", "admin" - ), - }, - timeout=settings.get("factory_get_timeout", 3), - ) - token = auth.json()["Token"][0]["Token"] - web_json_data = await client.get( - f"http://{ip}:8080/ipreport", - headers={"Token": token}, - timeout=settings.get("factory_get_timeout", 3), - ) - return web_json_data.json()["IPReport"][0]["model"] - except (TypeError, LookupError, httpx.HTTPError): + return sock_json_data["DEVDETAILS"][0]["Model"] + except LookupError: pass From 90fb67f5866e0fa088e3eb109d4b7521212d41c5 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 17:38:49 -0700 Subject: [PATCH 08/17] bug: fix auradine web api token. --- pyasic/web/auradine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index 979ad805..1f5ad828 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -40,7 +40,7 @@ class FluxWebAPI(BaseWebAPI): f"http://{self.ip}:{self.port}/token", data={ "command": "token", - "username": self.username, + "user": self.username, "password": self.pwd, }, ) From 4b36044e566cd3a399a5f8901d97eeade38f6f66 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 17:45:42 -0700 Subject: [PATCH 09/17] bug: fix auradine web api token format. --- pyasic/web/auradine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index 1f5ad828..1986525f 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -38,7 +38,7 @@ class FluxWebAPI(BaseWebAPI): try: auth = await client.post( f"http://{self.ip}:{self.port}/token", - data={ + json={ "command": "token", "user": self.username, "password": self.pwd, From 49fc0f3c54ec59ac537c459ab0f2279e85c64196 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 17:55:47 -0700 Subject: [PATCH 10/17] bug: fix auradine hashboards. --- pyasic/miners/backends/auradine.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 67d780ad..4a73c798 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -262,12 +262,13 @@ class Auradine(BaseMiner): if api_devs is not None: try: - for board in api_devs["DEVS"][0]: - b_id = board["ID"] + for board in api_devs["DEVS"]: + b_id = board["ID"] - 1 hashboards[b_id].hashrate = round( float(float(board["MHS 5s"]) / 1000000), 2 ) hashboards[b_id].temp = round(float(float(board["Temperature"])), 2) + hashboards[b_id].missing = False except LookupError: pass @@ -275,7 +276,7 @@ class Auradine(BaseMiner): try: for board, sn in enumerate(web_ipreport["IPReport"][0]["HBSerialNo"]): hashboards[board].serial_number = sn - + hashboards[board].missing = False except LookupError: pass From d81e3e9f88572637459fa8af7c53db7e83639dbd Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:05:32 -0700 Subject: [PATCH 11/17] bug: fix auradine multicommand format for get_data. --- pyasic/web/auradine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index 1986525f..fb7c0f2b 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -118,7 +118,7 @@ class FluxWebAPI(BaseWebAPI): result = tasks[cmd].result() if result is None or result == {}: result = {} - data[cmd] = [result] + data[cmd] = result except APIError: pass From 91de12467b1a5d97bce310da6b75f3055ccc61ea Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:08:22 -0700 Subject: [PATCH 12/17] bug: add multicommand flag to auradine multicommand output. --- pyasic/web/auradine.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/web/auradine.py b/pyasic/web/auradine.py index fb7c0f2b..621ae6d7 100644 --- a/pyasic/web/auradine.py +++ b/pyasic/web/auradine.py @@ -112,7 +112,7 @@ class FluxWebAPI(BaseWebAPI): await asyncio.gather(*[tasks[cmd] for cmd in tasks], return_exceptions=True) - data = {} + data = {"multicommand": True} for cmd in tasks: try: result = tasks[cmd].result() From e6523fc7d5adda4213fab6f703387a0863144029 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:18:11 -0700 Subject: [PATCH 13/17] bug: fix auradine wattage data. --- pyasic/miners/backends/auradine.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 4a73c798..76305d10 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -196,8 +196,8 @@ class Auradine(BaseMiner): if web_ipreport is not None: try: - return web_ipreport["IPReport"][0]["mac"] - except LookupError: + return web_ipreport["IPReport"][0]["mac"].upper() + except (LookupError, AttributeError): pass async def _get_fw_ver(self, web_ipreport: dict = None) -> Optional[str]: @@ -291,7 +291,7 @@ class Auradine(BaseMiner): if web_psu is not None: try: - return int(web_psu["PSU"][0]["PowerIn"].replace("W", "")) + return int(float(web_psu["PSU"][0]["PowerIn"].replace("W", ""))) except (LookupError, TypeError, ValueError): pass @@ -318,7 +318,7 @@ class Auradine(BaseMiner): if web_psu is not None: try: - return int(web_psu["PSU"][0]["PoutMax"].replace("W", "")) + return int(float(web_psu["PSU"][0]["PoutMax"].replace("W", ""))) except (LookupError, TypeError, ValueError): pass From 31682b7fae55399439d1eaeed7a36dcae5c8b130 Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:22:28 -0700 Subject: [PATCH 14/17] bug: fix auradine fan data and config parsing. --- pyasic/config/__init__.py | 6 +++--- pyasic/miners/backends/auradine.py | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index fded4944..fb7693da 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -199,9 +199,9 @@ class MinerConfig: @classmethod def from_auradine(cls, web_conf: dict) -> "MinerConfig": return cls( - pools=PoolConfig.from_api(web_conf["pools"][0]), - fan_mode=FanModeConfig.from_auradine(web_conf["fans"][0]), - mining_mode=MiningModeConfig.from_auradine(web_conf["mode"][0]), + pools=PoolConfig.from_api(web_conf["pools"]), + fan_mode=FanModeConfig.from_auradine(web_conf["fans"]), + mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]), ) diff --git a/pyasic/miners/backends/auradine.py b/pyasic/miners/backends/auradine.py index 76305d10..d84d5763 100644 --- a/pyasic/miners/backends/auradine.py +++ b/pyasic/miners/backends/auradine.py @@ -333,9 +333,7 @@ class Auradine(BaseMiner): if web_fan is not None: try: for fan in web_fan["Fan"]: - f_max = fan["Max"] - f_speed = fan["Speed"] - fans.append(Fan(round((f_speed / f_max) * 100))) + fans.append(Fan(round(fan["Speed"]))) except LookupError: pass return fans From 95d1e40b4fbe2e2434615f4a9222ce8248d2ba2a Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:28:10 -0700 Subject: [PATCH 15/17] bug: fix auradine fan config parsing. --- pyasic/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index fb7693da..43f13d17 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -200,7 +200,7 @@ class MinerConfig: def from_auradine(cls, web_conf: dict) -> "MinerConfig": return cls( pools=PoolConfig.from_api(web_conf["pools"]), - fan_mode=FanModeConfig.from_auradine(web_conf["fans"]), + fan_mode=FanModeConfig.from_auradine(web_conf["fan"]), mining_mode=MiningModeConfig.from_auradine(web_conf["mode"]), ) From 1dd87ac1022459c9c77ce344e630a39d7c30aa4e Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:32:50 -0700 Subject: [PATCH 16/17] feature: add expected chips for M50S++VK10 --- pyasic/miners/types/whatsminer/M5X/M50S_Plus_Plus.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyasic/miners/types/whatsminer/M5X/M50S_Plus_Plus.py b/pyasic/miners/types/whatsminer/M5X/M50S_Plus_Plus.py index 1f1e3424..e0abaca1 100644 --- a/pyasic/miners/types/whatsminer/M5X/M50S_Plus_Plus.py +++ b/pyasic/miners/types/whatsminer/M5X/M50S_Plus_Plus.py @@ -19,6 +19,7 @@ from pyasic.miners.makes import WhatsMinerMake class M50SPlusPlusVK10(WhatsMinerMake): raw_model = "M50S++ VK10" + expected_chips = 117 expected_fans = 2 From 6c1931fe7e4d4c126ece380325fb327c0efbbcbb Mon Sep 17 00:00:00 2001 From: b-rowan Date: Wed, 24 Jan 2024 18:37:29 -0700 Subject: [PATCH 17/17] bug: fix some naming issues with auradine, and add chip count for AT1500. --- pyasic/miners/types/auradine/{AT => AD}/AD2.py | 0 pyasic/miners/types/auradine/{AT => AD}/AD3.py | 0 pyasic/miners/types/auradine/AD/__init__.py | 4 ++-- pyasic/miners/types/auradine/{AD => AT}/AT1.py | 1 + pyasic/miners/types/auradine/{AD => AT}/AT2.py | 0 pyasic/miners/types/auradine/AT/__init__.py | 4 ++-- 6 files changed, 5 insertions(+), 4 deletions(-) rename pyasic/miners/types/auradine/{AT => AD}/AD2.py (100%) rename pyasic/miners/types/auradine/{AT => AD}/AD3.py (100%) rename pyasic/miners/types/auradine/{AD => AT}/AT1.py (83%) rename pyasic/miners/types/auradine/{AD => AT}/AT2.py (100%) diff --git a/pyasic/miners/types/auradine/AT/AD2.py b/pyasic/miners/types/auradine/AD/AD2.py similarity index 100% rename from pyasic/miners/types/auradine/AT/AD2.py rename to pyasic/miners/types/auradine/AD/AD2.py diff --git a/pyasic/miners/types/auradine/AT/AD3.py b/pyasic/miners/types/auradine/AD/AD3.py similarity index 100% rename from pyasic/miners/types/auradine/AT/AD3.py rename to pyasic/miners/types/auradine/AD/AD3.py diff --git a/pyasic/miners/types/auradine/AD/__init__.py b/pyasic/miners/types/auradine/AD/__init__.py index 070612a0..a46be8b9 100644 --- a/pyasic/miners/types/auradine/AD/__init__.py +++ b/pyasic/miners/types/auradine/AD/__init__.py @@ -1,2 +1,2 @@ -from .AT1 import AuradineAT1500 -from .AT2 import AuradineAT2860, AuradineAT2880 +from .AD2 import AuradineAD2500 +from .AD3 import AuradineAD3500 diff --git a/pyasic/miners/types/auradine/AD/AT1.py b/pyasic/miners/types/auradine/AT/AT1.py similarity index 83% rename from pyasic/miners/types/auradine/AD/AT1.py rename to pyasic/miners/types/auradine/AT/AT1.py index 2858a5ad..1e8c84ad 100644 --- a/pyasic/miners/types/auradine/AD/AT1.py +++ b/pyasic/miners/types/auradine/AT/AT1.py @@ -3,4 +3,5 @@ from pyasic.miners.makes import AuradineMake class AuradineAT1500(AuradineMake): raw_model = "AT1500" + expected_chips = 132 expected_fans = 4 diff --git a/pyasic/miners/types/auradine/AD/AT2.py b/pyasic/miners/types/auradine/AT/AT2.py similarity index 100% rename from pyasic/miners/types/auradine/AD/AT2.py rename to pyasic/miners/types/auradine/AT/AT2.py diff --git a/pyasic/miners/types/auradine/AT/__init__.py b/pyasic/miners/types/auradine/AT/__init__.py index a46be8b9..070612a0 100644 --- a/pyasic/miners/types/auradine/AT/__init__.py +++ b/pyasic/miners/types/auradine/AT/__init__.py @@ -1,2 +1,2 @@ -from .AD2 import AuradineAD2500 -from .AD3 import AuradineAD3500 +from .AT1 import AuradineAT1500 +from .AT2 import AuradineAT2860, AuradineAT2880