From 6d75565bafead662ce7025091ae730da77dd60aa Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Fri, 8 Dec 2023 09:16:04 -0700 Subject: [PATCH 01/37] feature: start adding new config implementation. --- pyasic/config/__init__.py | 667 +---------------------------- pyasic/config/fans.py | 72 ++++ pyasic/config/miners/__init__.py | 15 + pyasic/config/miners/bosminer.py | 15 + pyasic/config/mining.py | 93 ++++ pyasic/config/pools.py | 56 +++ pyasic/config/power_scaling.py | 58 +++ pyasic/config/temperature.py | 27 ++ pyasic/miners/backends/antminer.py | 2 +- 9 files changed, 350 insertions(+), 655 deletions(-) create mode 100644 pyasic/config/fans.py create mode 100644 pyasic/config/miners/__init__.py create mode 100644 pyasic/config/miners/bosminer.py create mode 100644 pyasic/config/mining.py create mode 100644 pyasic/config/pools.py create mode 100644 pyasic/config/power_scaling.py create mode 100644 pyasic/config/temperature.py diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 603ee052..944bfbe3 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -13,664 +13,23 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ +from dataclasses import asdict, dataclass -import logging -import random -import string -import time -from dataclasses import asdict, dataclass, fields -from enum import IntEnum -from typing import List, Literal - -import toml -import yaml - - -class X19PowerMode(IntEnum): - Normal = 0 - Sleep = 1 - LPM = 3 - - -@dataclass -class _Pool: - """A dataclass for pool information. - - Attributes: - url: URL of the pool. - username: Username on the pool. - password: Worker password on the pool. - """ - - url: str = "" - username: str = "" - password: str = "" - - @classmethod - def fields(cls): - return fields(cls) - - def from_dict(self, data: dict): - """Convert raw pool data as a dict to usable data and save it to this class. - - Parameters: - data: The raw config data to convert. - """ - for key in data.keys(): - if key == "url": - self.url = data[key] - if key in ["user", "username"]: - self.username = data[key] - if key in ["pass", "password"]: - self.password = data[key] - return self - - def as_wm(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by an Whatsminer device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = {"url": self.url, "user": username, "pass": self.password} - return pool - - def as_x19(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by an X19 device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = {"url": self.url, "user": username, "pass": self.password} - return pool - - def as_x17(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by an X5 device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = {"url": self.url, "user": username, "pass": self.password} - return pool - - def as_goldshell(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by a goldshell device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = {"url": self.url, "user": username, "pass": self.password} - return pool - - def as_inno(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by an Innosilicon device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = { - f"Pool": self.url, - f"UserName": username, - f"Password": self.password, - } - return pool - - def as_avalon(self, user_suffix: str = None) -> str: - """Convert the data in this class to a string usable by an Avalonminer device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = ",".join([self.url, username, self.password]) - return pool - - def as_bos(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by an BOSMiner device. - - Parameters: - user_suffix: The suffix to append to username. - """ - username = self.username - if user_suffix: - username = f"{username}{user_suffix}" - - pool = {"url": self.url, "user": username, "password": self.password} - return pool - - -@dataclass -class _PoolGroup: - """A dataclass for pool group information. - - Attributes: - quota: The group quota. - group_name: The name of the pool group. - pools: A list of pools in this group. - """ - - quota: int = 1 - group_name: str = None - pools: List[_Pool] = None - - @classmethod - def fields(cls): - return fields(cls) - - def __post_init__(self): - if not self.group_name: - self.group_name = "".join( - random.choice(string.ascii_uppercase + string.digits) for _ in range(6) - ) # generate random pool group name in case it isn't set - - def from_dict(self, data: dict): - """Convert raw pool group data as a dict to usable data and save it to this class. - - Parameters: - data: The raw config data to convert. - """ - pools = [] - for key in data.keys(): - if key in ["name", "group_name"]: - self.group_name = data[key] - if key == "quota": - self.quota = data[key] - if key in ["pools", "pool"]: - for pool in data[key]: - pools.append(_Pool().from_dict(pool)) - self.pools = pools - return self - - def as_x19(self, user_suffix: str = None) -> List[dict]: - """Convert the data in this class to a list usable by an X19 device. - - Parameters: - user_suffix: The suffix to append to username. - """ - pools = [] - for pool in self.pools[:3]: - pools.append(pool.as_x19(user_suffix=user_suffix)) - return pools - - def as_x17(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a list usable by an X17 device. - - Parameters: - user_suffix: The suffix to append to username. - """ - pools = { - "_ant_pool1url": "", - "_ant_pool1user": "", - "_ant_pool1pw": "", - "_ant_pool2url": "", - "_ant_pool2user": "", - "_ant_pool2pw": "", - "_ant_pool3url": "", - "_ant_pool3user": "", - "_ant_pool3pw": "", - } - for idx, pool in enumerate(self.pools[:3]): - pools[f"_ant_pool{idx+1}url"] = pool.as_x17(user_suffix=user_suffix)["url"] - pools[f"_ant_pool{idx+1}user"] = pool.as_x17(user_suffix=user_suffix)[ - "user" - ] - pools[f"_ant_pool{idx+1}pw"] = pool.as_x17(user_suffix=user_suffix)["pass"] - - return pools - - def as_goldshell(self, user_suffix: str = None) -> list: - """Convert the data in this class to a list usable by a goldshell device. - - Parameters: - user_suffix: The suffix to append to username. - """ - return [pool.as_goldshell(user_suffix=user_suffix) for pool in self.pools[:3]] - - def as_inno(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a list usable by an Innosilicon device. - - Parameters: - user_suffix: The suffix to append to username. - """ - pools = { - "Pool1": None, - "UserName1": None, - "Password1": None, - "Pool2": None, - "UserName2": None, - "Password2": None, - "Pool3": None, - "UserName3": None, - "Password3": None, - } - for idx, pool in enumerate(self.pools[:3]): - pool_data = pool.as_inno(user_suffix=user_suffix) - for key in pool_data: - pools[f"{key}{idx+1}"] = pool_data[key] - return pools - - def as_wm(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a list usable by a Whatsminer device. - - Parameters: - user_suffix: The suffix to append to username. - """ - pools = {} - for i in range(1, 4): - if i <= len(self.pools): - pool_wm = self.pools[i - 1].as_wm(user_suffix) - pools[f"pool_{i}"] = pool_wm["url"] - pools[f"worker_{i}"] = pool_wm["user"] - pools[f"passwd_{i}"] = pool_wm["pass"] - else: - pools[f"pool_{i}"] = "" - pools[f"worker_{i}"] = "" - pools[f"passwd_{i}"] = "" - return pools - - def as_avalon(self, user_suffix: str = None) -> str: - """Convert the data in this class to a dict usable by an Avalonminer device. - - Parameters: - user_suffix: The suffix to append to username. - """ - pool = self.pools[0].as_avalon(user_suffix=user_suffix) - return pool - - def as_bos(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a dict usable by an BOSMiner device. - - Parameters: - user_suffix: The suffix to append to username. - """ - group = { - "name": self.group_name, - "quota": self.quota, - "pool": [pool.as_bos(user_suffix=user_suffix) for pool in self.pools], - } - return group +from pyasic.config.fans import FanModeConfig +from pyasic.config.mining import MiningModeConfig +from pyasic.config.pools import PoolConfig +from pyasic.config.power_scaling import PowerScalingConfig +from pyasic.config.temperature import TemperatureConfig @dataclass class MinerConfig: - """A dataclass for miner configuration information. + pools: PoolConfig = PoolConfig.default() + mining_mode: MiningModeConfig = MiningModeConfig.default() + fan_mode: FanModeConfig = FanModeConfig.default() + temperature: TemperatureConfig = TemperatureConfig.default() + power_scaling: PowerScalingConfig = PowerScalingConfig.default() - Attributes: - pool_groups: A list of pool groups in this config. - temp_mode: The temperature control mode. - temp_target: The target temp. - temp_hot: The hot temp (100% fans). - temp_dangerous: The dangerous temp (shutdown). - minimum_fans: The minimum numbers of fans to run the miner. - fan_speed: Manual fan speed to run the fan at (only if temp_mode == "manual"). - asicboost: Whether or not to enable asicboost. - autotuning_enabled: Whether or not to enable autotuning. - autotuning_mode: Autotuning mode, either "wattage" or "hashrate". - autotuning_wattage: The wattage to use when autotuning. - autotuning_hashrate: The hashrate to use when autotuning. - dps_enabled: Whether or not to enable dynamic power scaling. - dps_power_step: The amount of power to reduce autotuning by when the miner reaches dangerous temp. - dps_min_power: The minimum power to reduce autotuning to. - dps_shutdown_enabled: Whether or not to shutdown the miner when `dps_min_power` is reached. - dps_shutdown_duration: The amount of time to shutdown for (in hours). - """ - pool_groups: List[_PoolGroup] = None - - temp_mode: Literal["auto", "manual", "disabled"] = "auto" - temp_target: float = 70.0 - temp_hot: float = 80.0 - temp_dangerous: float = 100.0 - - minimum_fans: int = None - fan_speed: Literal[tuple(range(101))] = None # noqa - Ignore weird Literal usage - - asicboost: bool = None - - miner_mode: IntEnum = X19PowerMode.Normal - autotuning_enabled: bool = True - autotuning_mode: Literal["power", "hashrate"] = None - autotuning_wattage: int = None - autotuning_hashrate: int = None - - dps_enabled: bool = None - dps_power_step: int = None - dps_min_power: int = None - dps_shutdown_enabled: bool = None - dps_shutdown_duration: float = None - - @classmethod - def fields(cls): - return fields(cls) - - def as_dict(self) -> dict: - """Convert the data in this class to a dict.""" - logging.debug(f"MinerConfig - (To Dict) - Dumping Dict config") - data_dict = asdict(self) - for key in asdict(self).keys(): - if isinstance(data_dict[key], IntEnum): - data_dict[key] = data_dict[key].value - if data_dict[key] is None: - del data_dict[key] - return data_dict - - def as_toml(self) -> str: - """Convert the data in this class to toml.""" - logging.debug(f"MinerConfig - (To TOML) - Dumping TOML config") - return toml.dumps(self.as_dict()) - - def as_yaml(self) -> str: - """Convert the data in this class to yaml.""" - logging.debug(f"MinerConfig - (To YAML) - Dumping YAML config") - return yaml.dump(self.as_dict(), sort_keys=False) - - def from_raw(self, data: dict): - """Convert raw config data as a dict to usable data and save it to this class. - This should be able to handle any raw config file from any miner supported by pyasic. - - Parameters: - data: The raw config data to convert. - """ - logging.debug(f"MinerConfig - (From Raw) - Loading raw config") - pool_groups = [] - if isinstance(data, list): - # goldshell config list - data = {"pools": data} - for key in data.keys(): - if key == "pools": - pool_groups.append(_PoolGroup().from_dict({"pools": data[key]})) - elif key == "group": - for group in data[key]: - pool_groups.append(_PoolGroup().from_dict(group)) - - if key == "bitmain-fan-ctrl": - if data[key]: - self.temp_mode = "manual" - if data.get("bitmain-fan-pwm"): - self.fan_speed = int(data["bitmain-fan-pwm"]) - elif key == "bitmain-work-mode": - if data[key]: - self.miner_mode = X19PowerMode(int(data[key])) - elif key == "fan_control": - for _key in data[key]: - if _key == "min_fans": - self.minimum_fans = data[key][_key] - elif _key == "speed": - self.fan_speed = data[key][_key] - elif key == "temp_control": - for _key in data[key]: - if _key == "mode": - self.temp_mode = data[key][_key] - elif _key == "target_temp": - self.temp_target = data[key][_key] - elif _key == "hot_temp": - self.temp_hot = data[key][_key] - elif _key == "dangerous_temp": - self.temp_dangerous = data[key][_key] - - if key == "hash_chain_global": - if data[key].get("asic_boost"): - self.asicboost = data[key]["asic_boost"] - - if key == "autotuning": - for _key in data[key]: - if _key == "enabled": - self.autotuning_enabled = data[key][_key] - elif _key == "psu_power_limit": - self.autotuning_wattage = data[key][_key] - elif _key == "power_target": - self.autotuning_wattage = data[key][_key] - elif _key == "hashrate_target": - self.autotuning_hashrate = data[key][_key] - elif _key == "mode": - self.autotuning_mode = data[key][_key].replace("_target", "") - - if key in ["power_scaling", "performance_scaling"]: - for _key in data[key]: - if _key == "enabled": - self.dps_enabled = data[key][_key] - elif _key == "power_step": - self.dps_power_step = data[key][_key] - elif _key in ["min_psu_power_limit", "min_power_target"]: - self.dps_min_power = data[key][_key] - elif _key == "shutdown_enabled": - self.dps_shutdown_enabled = data[key][_key] - elif _key == "shutdown_duration": - self.dps_shutdown_duration = data[key][_key] - - self.pool_groups = pool_groups - return self - - def from_api(self, pools: list): - """Convert list output from the `AnyMiner.api.pools()` command into a usable data and save it to this class. - - Parameters: - pools: The list of pool data to convert. - """ - logging.debug(f"MinerConfig - (From API) - Loading API config") - _pools = [] - for pool in pools: - url = pool.get("URL") - user = pool.get("User") - _pools.append({"url": url, "user": user, "pass": "123"}) - self.pool_groups = [_PoolGroup().from_dict({"pools": _pools})] - return self - - def from_dict(self, data: dict): - """Convert an output dict of this class back into usable data and save it to this class. - - Parameters: - data: The dict config data to convert. - """ - logging.debug(f"MinerConfig - (From Dict) - Loading Dict config") - pool_groups = [] - for group in data["pool_groups"]: - pool_groups.append(_PoolGroup().from_dict(group)) - for key in data: - if ( - hasattr(self, key) - and not key == "pool_groups" - and not key == "miner_mode" - ): - setattr(self, key, data[key]) - if key == "miner_mode": - self.miner_mode = X19PowerMode(data[key]) - self.pool_groups = pool_groups - return self - - def from_toml(self, data: str): - """Convert output toml of this class back into usable data and save it to this class. - - Parameters: - data: The toml config data to convert. - """ - logging.debug(f"MinerConfig - (From TOML) - Loading TOML config") - return self.from_dict(toml.loads(data)) - - def from_yaml(self, data: str): - """Convert output yaml of this class back into usable data and save it to this class. - - Parameters: - data: The yaml config data to convert. - """ - logging.debug(f"MinerConfig - (From YAML) - Loading YAML config") - return self.from_dict(yaml.load(data, Loader=yaml.SafeLoader)) - - def as_wm(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a config usable by a Whatsminer device. - - Parameters: - user_suffix: The suffix to append to username. - """ - logging.debug(f"MinerConfig - (As Whatsminer) - Generating Whatsminer config") - return { - "pools": self.pool_groups[0].as_wm(user_suffix=user_suffix), - "wattage": self.autotuning_wattage, - } - - def as_inno(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a config usable by an Innosilicon device. - - Parameters: - user_suffix: The suffix to append to username. - """ - logging.debug(f"MinerConfig - (As Inno) - Generating Innosilicon config") - return self.pool_groups[0].as_inno(user_suffix=user_suffix) - - def as_x19(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a config usable by an X19 device. - - Parameters: - user_suffix: The suffix to append to username. - """ - logging.debug(f"MinerConfig - (As X19) - Generating X19 config") - cfg = { - "bitmain-fan-ctrl": False, - "bitmain-fan-pwn": "100", - "freq-level": "100", - "miner-mode": str(self.miner_mode.value), - "pools": self.pool_groups[0].as_x19(user_suffix=user_suffix), - } - - if not self.temp_mode == "auto": - cfg["bitmain-fan-ctrl"] = True - - if self.fan_speed: - cfg["bitmain-fan-pwn"] = str(self.fan_speed) - - return cfg - - def as_x17(self, user_suffix: str = None) -> dict: - """Convert the data in this class to a config usable by an X5 device. - - Parameters: - user_suffix: The suffix to append to username. - """ - cfg = self.pool_groups[0].as_x17(user_suffix=user_suffix) - - return cfg - - def as_goldshell(self, user_suffix: str = None) -> list: - """Convert the data in this class to a config usable by a goldshell device. - - Parameters: - user_suffix: The suffix to append to username. - """ - cfg = self.pool_groups[0].as_goldshell(user_suffix=user_suffix) - - return cfg - - def as_avalon(self, user_suffix: str = None) -> str: - """Convert the data in this class to a config usable by an Avalonminer device. - - Parameters: - user_suffix: The suffix to append to username. - """ - logging.debug(f"MinerConfig - (As Avalon) - Generating AvalonMiner config") - cfg = self.pool_groups[0].as_avalon(user_suffix=user_suffix) - return cfg - - def as_bos(self, model: str = "S9", user_suffix: str = None) -> str: - """Convert the data in this class to a config usable by an BOSMiner device. - - Parameters: - model: The model of the miner to be used in the format portion of the config. - user_suffix: The suffix to append to username. - """ - logging.debug(f"MinerConfig - (As BOS) - Generating BOSMiner config") - cfg = { - "format": { - "version": "1.2+", - "model": f"Antminer {model.replace('j', 'J')}", - "generator": "pyasic", - "timestamp": int(time.time()), - }, - "group": [ - group.as_bos(user_suffix=user_suffix) for group in self.pool_groups - ], - "temp_control": { - "mode": self.temp_mode, - "target_temp": self.temp_target, - "hot_temp": self.temp_hot, - "dangerous_temp": self.temp_dangerous, - }, - } - - if self.autotuning_enabled or self.autotuning_wattage: - cfg["autotuning"] = {} - if self.autotuning_enabled: - cfg["autotuning"]["enabled"] = True - else: - cfg["autotuning"]["enabled"] = False - if self.autotuning_mode: - cfg["format"]["version"] = "2.0" - cfg["autotuning"]["mode"] = self.autotuning_mode + "_target" - if self.autotuning_wattage: - cfg["autotuning"]["power_target"] = self.autotuning_wattage - elif self.autotuning_hashrate: - cfg["autotuning"]["hashrate_target"] = self.autotuning_hashrate - else: - if self.autotuning_wattage: - cfg["autotuning"]["psu_power_limit"] = self.autotuning_wattage - - if self.asicboost: - cfg["hash_chain_global"] = {} - cfg["hash_chain_global"]["asic_boost"] = self.asicboost - - if self.minimum_fans is not None or self.fan_speed is not None: - cfg["fan_control"] = {} - if self.minimum_fans is not None: - cfg["fan_control"]["min_fans"] = self.minimum_fans - if self.fan_speed is not None: - cfg["fan_control"]["speed"] = self.fan_speed - - if any( - [ - getattr(self, item) - for item in [ - "dps_enabled", - "dps_power_step", - "dps_min_power", - "dps_shutdown_enabled", - "dps_shutdown_duration", - ] - ] - ): - cfg["power_scaling"] = {} - if self.dps_enabled: - cfg["power_scaling"]["enabled"] = self.dps_enabled - if self.dps_power_step: - cfg["power_scaling"]["power_step"] = self.dps_power_step - if self.dps_min_power: - if cfg["format"]["version"] == "2.0": - cfg["power_scaling"]["min_power_target"] = self.dps_min_power - else: - cfg["power_scaling"]["min_psu_power_limit"] = self.dps_min_power - if self.dps_shutdown_enabled: - cfg["power_scaling"]["shutdown_enabled"] = self.dps_shutdown_enabled - if self.dps_shutdown_duration: - cfg["power_scaling"]["shutdown_duration"] = self.dps_shutdown_duration - - return toml.dumps(cfg) +if __name__ == "__main__": + print(asdict(MinerConfig())) diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py new file mode 100644 index 00000000..ac74b0a4 --- /dev/null +++ b/pyasic/config/fans.py @@ -0,0 +1,72 @@ +# ------------------------------------------------------------------------------ +# 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 dataclasses import dataclass, field +from enum import Enum + + +@dataclass +class FanModeNormal: + mode: str = field(init=False, default="auto") + + @staticmethod + def as_am_modern(): + return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} + + @staticmethod + def as_bos(): + return {"temp_control": {"mode": "auto"}} + + +@dataclass +class FanModeManual: + mode: str = field(init=False, default="manual") + minimum_fans: int = 1 + speed: int = 100 + + def as_am_modern(self): + return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)} + + def as_bos(self): + return { + "temp_control": {"mode": "manual"}, + "fan_control": {"min_fans": self.minimum_fans, "speed": self.speed}, + } + + +@dataclass +class FanModeImmersion: + mode: str = field(init=False, default="immersion") + + @staticmethod + def as_am_modern(): + return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"} + + @staticmethod + def as_bos(): + return {"temp_control": {"mode": "manual"}, "fan_control": {"min_fans": 0}} + + +class FanModeConfig(Enum): + normal = FanModeNormal + manual = FanModeManual + immersion = FanModeImmersion + + @classmethod + def default(cls): + return cls.normal() + + def __call__(self, *args, **kwargs): + return self.value(*args, **kwargs) diff --git a/pyasic/config/miners/__init__.py b/pyasic/config/miners/__init__.py new file mode 100644 index 00000000..d3221b3d --- /dev/null +++ b/pyasic/config/miners/__init__.py @@ -0,0 +1,15 @@ +# ------------------------------------------------------------------------------ +# 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. - +# ------------------------------------------------------------------------------ diff --git a/pyasic/config/miners/bosminer.py b/pyasic/config/miners/bosminer.py new file mode 100644 index 00000000..d3221b3d --- /dev/null +++ b/pyasic/config/miners/bosminer.py @@ -0,0 +1,15 @@ +# ------------------------------------------------------------------------------ +# 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. - +# ------------------------------------------------------------------------------ diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py new file mode 100644 index 00000000..6d779a2e --- /dev/null +++ b/pyasic/config/mining.py @@ -0,0 +1,93 @@ +# ------------------------------------------------------------------------------ +# 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 dataclasses import dataclass, field +from enum import Enum + + +@dataclass +class MiningModeNormal: + mode: str = field(init=False, default="normal") + + @staticmethod + def as_am_modern(): + return {"miner-mode": "0"} + + +@dataclass +class MiningModeSleep: + mode: str = field(init=False, default="sleep") + + @staticmethod + def as_am_modern(): + return {"miner-mode": "1"} + + +@dataclass +class MiningModeLPM: + mode: str = field(init=False, default="low") + + @staticmethod + def as_am_modern(): + return {"miner-mode": "3"} + + +@dataclass +class MiningModeHPM(MiningModeNormal): + mode: str = field(init=False, default="high") + + +@dataclass +class MiningModePowerTune(MiningModeNormal): + mode: str = field(init=False, default="power_tuning") + power: int + + +@dataclass +class MiningModeHashrateTune(MiningModeNormal): + mode: str = field(init=False, default="hashrate_tuning") + hashrate: int + + +@dataclass +class ManualBoardSettings: + freq: float + volt: float + + +@dataclass +class MiningModeManual(MiningModeNormal): + mode: str = field(init=False, default="manual") + + global_freq: float + global_volt: float + boards: dict[int, ManualBoardSettings] = field(default_factory=dict) + + +class MiningModeConfig(Enum): + normal = MiningModeNormal + low = MiningModeLPM + high = MiningModeHPM + sleep = MiningModeSleep + power_tuning = MiningModePowerTune + hashrate_tuning = MiningModeHashrateTune + manual = MiningModeManual + + @classmethod + def default(cls): + return cls.normal() + + def __call__(self, *args, **kwargs): + return self.value(*args, **kwargs) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py new file mode 100644 index 00000000..d78635d4 --- /dev/null +++ b/pyasic/config/pools.py @@ -0,0 +1,56 @@ +# ------------------------------------------------------------------------------ +# 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 random +import string +from dataclasses import dataclass, field +from typing import Union + + +@dataclass +class Pool: + url: str + user: str + password: str + + +@dataclass +class PoolGroup: + pools: list[Pool] = field(default_factory=list) + quota: int = 1 + name: str = None + + def __post_init__(self): + if self.group_name is None: + self.group_name = "".join( + random.choice(string.ascii_uppercase + string.digits) for _ in range(6) + ) # generate random pool group name in case it isn't set + + +@dataclass +class PoolConfig: + groups: list[PoolGroup] = field(default_factory=list) + + @classmethod + def default(cls): + return cls() + + def simple(self, pools: list[Union[Pool, dict[str, str]]]): + group_pools = [] + for pool in pools: + if isinstance(pool, dict): + pool = Pool(**pool) + group_pools.append(pool) + self.groups = [PoolGroup(pools=group_pools)] diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py new file mode 100644 index 00000000..5e3682a0 --- /dev/null +++ b/pyasic/config/power_scaling.py @@ -0,0 +1,58 @@ +# ------------------------------------------------------------------------------ +# 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 dataclasses import dataclass, field +from enum import Enum + + +@dataclass +class PowerScalingShutdownEnabled: + mode: str = field(init=False, default="enabled") + duration: int = None + + +@dataclass +class PowerScalingShutdownDisabled: + mode: str = field(init=False, default="disabled") + + +class PowerScalingShutdown(Enum): + enabled = PowerScalingShutdownEnabled + disabled = PowerScalingShutdownDisabled + + +@dataclass +class PowerScalingEnabled: + mode: str = field(init=False, default="enabled") + power_step: int = None + minimum_power: int = None + shutdown_mode: PowerScalingShutdown = None + + +@dataclass +class PowerScalingDisabled: + mode: str = field(init=False, default="disabled") + + +class PowerScalingConfig(Enum): + enabled = PowerScalingEnabled + disabled = PowerScalingDisabled + + @classmethod + def default(cls): + return cls.disabled() + + def __call__(self, *args, **kwargs): + return self.value(*args, **kwargs) diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py new file mode 100644 index 00000000..68645ee8 --- /dev/null +++ b/pyasic/config/temperature.py @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# Copyright 2022 Upstream Data Inc - +# - +# Licensed under the Apache License, Version 2.0 (the "License"); - +# you may not use this file except in compliance with the License. - +# You may obtain a copy of the License at - +# - +# http://www.apache.org/licenses/LICENSE-2.0 - +# - +# Unless required by applicable law or agreed to in writing, software - +# distributed under the License is distributed on an "AS IS" BASIS, - +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - +# See the License for the specific language governing permissions and - +# limitations under the License. - +# ------------------------------------------------------------------------------ +from dataclasses import dataclass + + +@dataclass +class TemperatureConfig: + target: int = None + hot: int = None + danger: int = None + + @classmethod + def default(cls): + return cls() diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index 6921caed..f001da9f 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -18,7 +18,7 @@ import asyncio from typing import List, Optional, Union from pyasic.API import APIError -from pyasic.config import MinerConfig, X19PowerMode +from pyasic.config import MinerConfig from pyasic.data import Fan, HashBoard from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.miners.backends.bmminer import BMMiner From bca81f3bca310f2bd730be2d5ce6e9d301f2430a Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Fri, 8 Dec 2023 10:10:21 -0700 Subject: [PATCH 02/37] feature: add AM old and modern, and WM config implementation. --- pyasic/config/__init__.py | 46 ++++++++++++- pyasic/config/base.py | 65 +++++++++++++++++++ pyasic/config/fans.py | 26 +++----- pyasic/config/mining.py | 115 ++++++++++++++++++++------------- pyasic/config/pools.py | 104 +++++++++++++++++++++++++++-- pyasic/config/power_scaling.py | 18 +++--- pyasic/config/temperature.py | 4 +- 7 files changed, 297 insertions(+), 81 deletions(-) create mode 100644 pyasic/config/base.py diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 944bfbe3..12986043 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from dataclasses import asdict, dataclass +from dataclasses import dataclass from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig @@ -30,6 +30,48 @@ class MinerConfig: temperature: TemperatureConfig = TemperatureConfig.default() power_scaling: PowerScalingConfig = PowerScalingConfig.default() + def as_am_modern(self, user_suffix: str = None): + return { + **self.fan_mode.as_am_modern(), + "freq-level": "100", + **self.mining_mode.as_am_modern(), + **self.pools.as_am_modern(user_suffix=user_suffix), + **self.temperature.as_am_modern(), + **self.power_scaling.as_am_modern(), + } + + def as_wm(self, user_suffix: str = None): + return { + **self.fan_mode.as_wm(), + **self.mining_mode.as_wm(), + **self.pools.as_wm(user_suffix=user_suffix), + **self.temperature.as_wm(), + **self.power_scaling.as_wm(), + } + + def as_am_old(self, user_suffix: str = None): + return { + **self.fan_mode.as_am_old(), + **self.mining_mode.as_am_old(), + **self.pools.as_am_old(user_suffix=user_suffix), + **self.temperature.as_am_old(), + **self.power_scaling.as_am_old(), + } + if __name__ == "__main__": - print(asdict(MinerConfig())) + config = MinerConfig( + pools=PoolConfig.simple( + [ + { + "url": "stratum+tcp://stratum.test.io:3333", + "user": "user.test", + "password": "123", + } + ] + ), + mining_mode=MiningModeConfig.power_tuning(3000), + ) + print(config.as_wm()) + print(config.as_am_modern()) + print(config.as_am_old()) diff --git a/pyasic/config/base.py b/pyasic/config/base.py new file mode 100644 index 00000000..2a39477a --- /dev/null +++ b/pyasic/config/base.py @@ -0,0 +1,65 @@ +# ------------------------------------------------------------------------------ +# 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 enum import Enum + + +class MinerConfigOption(Enum): + def as_am_modern(self) -> dict: + return self.value.as_am_modern() + + def as_am_old(self) -> dict: + return self.value.as_am_old() + + def as_wm(self) -> dict: + return self.value.as_wm() + + def as_inno(self) -> dict: + return self.value.as_inno() + + def as_goldshell(self) -> dict: + return self.value.as_goldshell() + + def as_avalon(self) -> dict: + return self.value.as_avalon() + + def as_bos(self) -> dict: + return self.value.as_bos() + + def __call__(self, *args, **kwargs): + return self.value(*args, **kwargs) + + +class MinerConfigValue: + def as_am_modern(self) -> dict: + return {} + + def as_am_old(self) -> dict: + return {} + + def as_wm(self) -> dict: + return {} + + def as_inno(self) -> dict: + return {} + + def as_goldshell(self) -> dict: + return {} + + def as_avalon(self) -> dict: + return {} + + def as_bos(self) -> dict: + return {} diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index ac74b0a4..c26fbc30 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -14,24 +14,23 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field -from enum import Enum + +from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass -class FanModeNormal: +class FanModeNormal(MinerConfigValue): mode: str = field(init=False, default="auto") - @staticmethod - def as_am_modern(): + def as_am_modern(self): return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} - @staticmethod - def as_bos(): + def as_bos(self): return {"temp_control": {"mode": "auto"}} @dataclass -class FanModeManual: +class FanModeManual(MinerConfigValue): mode: str = field(init=False, default="manual") minimum_fans: int = 1 speed: int = 100 @@ -47,19 +46,17 @@ class FanModeManual: @dataclass -class FanModeImmersion: +class FanModeImmersion(MinerConfigValue): mode: str = field(init=False, default="immersion") - @staticmethod - def as_am_modern(): + def as_am_modern(self): return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"} - @staticmethod - def as_bos(): + def as_bos(self): return {"temp_control": {"mode": "manual"}, "fan_control": {"min_fans": 0}} -class FanModeConfig(Enum): +class FanModeConfig(MinerConfigOption): normal = FanModeNormal manual = FanModeManual immersion = FanModeImmersion @@ -67,6 +64,3 @@ class FanModeConfig(Enum): @classmethod def default(cls): return cls.normal() - - def __call__(self, *args, **kwargs): - return self.value(*args, **kwargs) diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index 6d779a2e..41ffb7f2 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -14,69 +14,97 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field -from enum import Enum + +from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass -class MiningModeNormal: +class MiningModeNormal(MinerConfigValue): mode: str = field(init=False, default="normal") - @staticmethod - def as_am_modern(): + def as_am_modern(self): + return {"miner-mode": "0"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModeSleep(MinerConfigValue): + mode: str = field(init=False, default="sleep") + + def as_am_modern(self): + return {"miner-mode": "1"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModeLPM(MinerConfigValue): + mode: str = field(init=False, default="low") + + def as_am_modern(self): + return {"miner-mode": "3"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModeHPM(MinerConfigValue): + mode: str = field(init=False, default="high") + + def as_am_modern(self): + return {"miner-mode": "0"} + + def as_wm(self): + return {"mode": self.mode} + + +@dataclass +class MiningModePowerTune(MinerConfigValue): + mode: str = field(init=False, default="power_tuning") + power: int + + def as_am_modern(self): + return {"miner-mode": "0"} + + def as_wm(self): + return {"mode": self.mode, self.mode: {"wattage": self.power}} + + +@dataclass +class MiningModeHashrateTune(MinerConfigValue): + mode: str = field(init=False, default="hashrate_tuning") + hashrate: int + + def as_am_modern(self): return {"miner-mode": "0"} @dataclass -class MiningModeSleep: - mode: str = field(init=False, default="sleep") - - @staticmethod - def as_am_modern(): - return {"miner-mode": "1"} - - -@dataclass -class MiningModeLPM: - mode: str = field(init=False, default="low") - - @staticmethod - def as_am_modern(): - return {"miner-mode": "3"} - - -@dataclass -class MiningModeHPM(MiningModeNormal): - mode: str = field(init=False, default="high") - - -@dataclass -class MiningModePowerTune(MiningModeNormal): - mode: str = field(init=False, default="power_tuning") - power: int - - -@dataclass -class MiningModeHashrateTune(MiningModeNormal): - mode: str = field(init=False, default="hashrate_tuning") - hashrate: int - - -@dataclass -class ManualBoardSettings: +class ManualBoardSettings(MinerConfigValue): freq: float volt: float + def as_am_modern(self): + return {"miner-mode": "0"} + @dataclass -class MiningModeManual(MiningModeNormal): +class MiningModeManual(MinerConfigValue): mode: str = field(init=False, default="manual") global_freq: float global_volt: float boards: dict[int, ManualBoardSettings] = field(default_factory=dict) + def as_am_modern(self): + return {"miner-mode": "0"} -class MiningModeConfig(Enum): + +class MiningModeConfig(MinerConfigOption): normal = MiningModeNormal low = MiningModeLPM high = MiningModeHPM @@ -88,6 +116,3 @@ class MiningModeConfig(Enum): @classmethod def default(cls): return cls.normal() - - def __call__(self, *args, **kwargs): - return self.value(*args, **kwargs) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index d78635d4..466dba5d 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -18,39 +18,129 @@ import string from dataclasses import dataclass, field from typing import Union +from pyasic.config.base import MinerConfigValue + @dataclass -class Pool: +class Pool(MinerConfigValue): url: str user: str password: str + def as_am_modern(self, user_suffix: str = None): + 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} + + def as_wm(self, idx: int, user_suffix: str = None): + if user_suffix is not None: + return { + f"pool_{idx}": self.url, + f"worker_{idx}": f"{self.user}{user_suffix}", + f"passwd_{idx}": self.password, + } + return { + f"pool_{idx}": self.url, + f"worker_{idx}": self.user, + f"passwd_{idx}": self.password, + } + + def as_am_old(self, idx: int, user_suffix: str = None): + if user_suffix is not None: + return { + f"_ant_pool{idx}url": self.url, + f"_ant_pool{idx}user": f"{self.user}{user_suffix}", + f"_ant_pool{idx}pw": self.password, + } + return { + f"_ant_pool{idx}url": self.url, + f"_ant_pool{idx}user": self.user, + f"_ant_pool{idx}pw": self.password, + } + @dataclass -class PoolGroup: +class PoolGroup(MinerConfigValue): pools: list[Pool] = field(default_factory=list) quota: int = 1 name: str = None def __post_init__(self): - if self.group_name is None: - self.group_name = "".join( + if self.name is None: + self.name = "".join( random.choice(string.ascii_uppercase + string.digits) for _ in range(6) ) # generate random pool group name in case it isn't set + def as_am_modern(self, user_suffix: str = None): + pools = [] + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix)) + else: + pools.append(Pool("", "", "").as_am_modern()) + idx += 1 + return pools + + def as_wm(self, user_suffix: str = None): + pools = {} + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.update( + **self.pools[idx].as_wm(idx=idx + 1, user_suffix=user_suffix) + ) + else: + pools.update(**Pool("", "", "").as_wm(idx=idx + 1)) + idx += 1 + return pools + + def as_am_old(self, user_suffix: str = None): + pools = {} + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.update( + **self.pools[idx].as_am_old(idx=idx + 1, user_suffix=user_suffix) + ) + else: + pools.update(**Pool("", "", "").as_am_old(idx=idx + 1)) + idx += 1 + return pools + @dataclass -class PoolConfig: +class PoolConfig(MinerConfigValue): groups: list[PoolGroup] = field(default_factory=list) @classmethod def default(cls): return cls() - def simple(self, pools: list[Union[Pool, dict[str, str]]]): + @classmethod + def simple(cls, pools: list[Union[Pool, dict[str, str]]]): group_pools = [] for pool in pools: if isinstance(pool, dict): pool = Pool(**pool) group_pools.append(pool) - self.groups = [PoolGroup(pools=group_pools)] + return cls(groups=[PoolGroup(pools=group_pools)]) + + def as_am_modern(self, user_suffix: str = None): + if len(self.groups) > 0: + return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_am_modern()} + + def as_wm(self, user_suffix: str = None): + if len(self.groups) > 0: + return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_wm()} + + def as_am_old(self, user_suffix: str = None): + if len(self.groups) > 0: + return self.groups[0].as_am_old(user_suffix=user_suffix) + return PoolGroup().as_am_old() diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index 5e3682a0..e2bf2ca3 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -14,27 +14,28 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field -from enum import Enum + +from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass -class PowerScalingShutdownEnabled: +class PowerScalingShutdownEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") duration: int = None @dataclass -class PowerScalingShutdownDisabled: +class PowerScalingShutdownDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") -class PowerScalingShutdown(Enum): +class PowerScalingShutdown(MinerConfigOption): enabled = PowerScalingShutdownEnabled disabled = PowerScalingShutdownDisabled @dataclass -class PowerScalingEnabled: +class PowerScalingEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") power_step: int = None minimum_power: int = None @@ -42,17 +43,14 @@ class PowerScalingEnabled: @dataclass -class PowerScalingDisabled: +class PowerScalingDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") -class PowerScalingConfig(Enum): +class PowerScalingConfig(MinerConfigOption): enabled = PowerScalingEnabled disabled = PowerScalingDisabled @classmethod def default(cls): return cls.disabled() - - def __call__(self, *args, **kwargs): - return self.value(*args, **kwargs) diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py index 68645ee8..0e2b416a 100644 --- a/pyasic/config/temperature.py +++ b/pyasic/config/temperature.py @@ -15,9 +15,11 @@ # ------------------------------------------------------------------------------ from dataclasses import dataclass +from pyasic.config.base import MinerConfigValue + @dataclass -class TemperatureConfig: +class TemperatureConfig(MinerConfigValue): target: int = None hot: int = None danger: int = None From 5a70a27f075b07e24e0b3ba24106801a2655f9a2 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Fri, 8 Dec 2023 10:11:43 -0700 Subject: [PATCH 03/37] reformat: remove some useless files. --- pyasic/config/miners/__init__.py | 15 --------------- pyasic/config/miners/bosminer.py | 15 --------------- 2 files changed, 30 deletions(-) delete mode 100644 pyasic/config/miners/__init__.py delete mode 100644 pyasic/config/miners/bosminer.py diff --git a/pyasic/config/miners/__init__.py b/pyasic/config/miners/__init__.py deleted file mode 100644 index d3221b3d..00000000 --- a/pyasic/config/miners/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ diff --git a/pyasic/config/miners/bosminer.py b/pyasic/config/miners/bosminer.py deleted file mode 100644 index d3221b3d..00000000 --- a/pyasic/config/miners/bosminer.py +++ /dev/null @@ -1,15 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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 791249bf3d6a0f96d4fd5c56c4a89fc342581739 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Fri, 8 Dec 2023 10:57:57 -0700 Subject: [PATCH 04/37] feature: add avalon and goldshell to miner config types. --- pyasic/config/__init__.py | 26 +++++++++++++++++--- pyasic/config/pools.py | 52 ++++++++++++++++++++++++++++++++++----- 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 12986043..f16434d6 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -58,6 +58,24 @@ class MinerConfig: **self.power_scaling.as_am_old(), } + def as_goldshell(self, user_suffix: str = None): + return { + **self.fan_mode.as_goldshell(), + **self.mining_mode.as_goldshell(), + **self.pools.as_goldshell(user_suffix=user_suffix), + **self.temperature.as_goldshell(), + **self.power_scaling.as_goldshell(), + } + + def as_avalon(self, user_suffix: str = None): + return { + **self.fan_mode.as_avalon(), + **self.mining_mode.as_avalon(), + **self.pools.as_avalon(user_suffix=user_suffix), + **self.temperature.as_avalon(), + **self.power_scaling.as_avalon(), + } + if __name__ == "__main__": config = MinerConfig( @@ -72,6 +90,8 @@ if __name__ == "__main__": ), mining_mode=MiningModeConfig.power_tuning(3000), ) - print(config.as_wm()) - print(config.as_am_modern()) - print(config.as_am_old()) + print("WM:", config.as_wm()) + print("AM Modern:", config.as_am_modern()) + print("AM Old:", config.as_am_old()) + print("GS:", config.as_goldshell()) + print("Avalon:", config.as_avalon()) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 466dba5d..6c6c01a9 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -62,6 +62,20 @@ class Pool(MinerConfigValue): f"_ant_pool{idx}pw": self.password, } + def as_goldshell(self, user_suffix: str = None): + 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} + + def as_avalon(self, user_suffix: str = None): + 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]) + @dataclass class PoolGroup(MinerConfigValue): @@ -75,7 +89,7 @@ class PoolGroup(MinerConfigValue): random.choice(string.ascii_uppercase + string.digits) for _ in range(6) ) # generate random pool group name in case it isn't set - def as_am_modern(self, user_suffix: str = None): + def as_am_modern(self, user_suffix: str = None) -> list: pools = [] idx = 0 while idx < 3: @@ -86,7 +100,7 @@ class PoolGroup(MinerConfigValue): idx += 1 return pools - def as_wm(self, user_suffix: str = None): + def as_wm(self, user_suffix: str = None) -> dict: pools = {} idx = 0 while idx < 3: @@ -99,7 +113,7 @@ class PoolGroup(MinerConfigValue): idx += 1 return pools - def as_am_old(self, user_suffix: str = None): + def as_am_old(self, user_suffix: str = None) -> dict: pools = {} idx = 0 while idx < 3: @@ -112,6 +126,22 @@ class PoolGroup(MinerConfigValue): idx += 1 return pools + def as_goldshell(self, user_suffix: str = None) -> list: + pools = [] + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix)) + else: + pools.append(Pool("", "", "").as_am_modern()) + idx += 1 + return pools + + def as_avalon(self, user_suffix: str = None) -> dict: + if len(self.pools) > 0: + return self.pools[0].as_avalon(user_suffix=user_suffix) + return Pool("", "", "").as_avalon() + @dataclass class PoolConfig(MinerConfigValue): @@ -130,17 +160,27 @@ class PoolConfig(MinerConfigValue): group_pools.append(pool) return cls(groups=[PoolGroup(pools=group_pools)]) - def as_am_modern(self, user_suffix: str = None): + def as_am_modern(self, user_suffix: str = None) -> dict: if len(self.groups) > 0: return {"pools": self.groups[0].as_am_modern(user_suffix=user_suffix)} return {"pools": PoolGroup().as_am_modern()} - def as_wm(self, user_suffix: str = None): + def as_wm(self, user_suffix: str = None) -> dict: if len(self.groups) > 0: return {"pools": self.groups[0].as_wm(user_suffix=user_suffix)} return {"pools": PoolGroup().as_wm()} - def as_am_old(self, user_suffix: str = None): + def as_am_old(self, user_suffix: str = None) -> dict: if len(self.groups) > 0: return self.groups[0].as_am_old(user_suffix=user_suffix) return PoolGroup().as_am_old() + + def as_goldshell(self, user_suffix: str = None) -> dict: + if len(self.groups) > 0: + return {"pools": self.groups[0].as_goldshell(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_goldshell()} + + def as_avalon(self, user_suffix: str = None) -> dict: + if len(self.groups) > 0: + return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)} + return {"pools": PoolGroup().as_avalon()} From 242517a36a09ae146157e668fb5e42c010ab67f4 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Fri, 8 Dec 2023 11:03:36 -0700 Subject: [PATCH 05/37] feature: add inno to config miner types. --- pyasic/config/__init__.py | 10 ++++++++++ pyasic/config/pools.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index f16434d6..e7625e7a 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -76,6 +76,15 @@ class MinerConfig: **self.power_scaling.as_avalon(), } + def as_inno(self, user_suffix: str = None): + return { + **self.fan_mode.as_inno(), + **self.mining_mode.as_inno(), + **self.pools.as_inno(user_suffix=user_suffix), + **self.temperature.as_inno(), + **self.power_scaling.as_inno(), + } + if __name__ == "__main__": config = MinerConfig( @@ -95,3 +104,4 @@ if __name__ == "__main__": print("AM Old:", config.as_am_old()) print("GS:", config.as_goldshell()) print("Avalon:", config.as_avalon()) + print("Inno:", config.as_inno()) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 6c6c01a9..a6214717 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -76,6 +76,19 @@ class Pool(MinerConfigValue): 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, user_suffix: str = None): + if user_suffix is not None: + return { + f"Pool{idx}": self.url, + f"UserName{idx}": f"{self.user}{user_suffix}", + f"Password{idx}": self.password, + } + return { + f"Pool{idx}": self.url, + f"UserName{idx}": self.user, + f"Password{idx}": self.password, + } + @dataclass class PoolGroup(MinerConfigValue): @@ -142,6 +155,19 @@ class PoolGroup(MinerConfigValue): return self.pools[0].as_avalon(user_suffix=user_suffix) return Pool("", "", "").as_avalon() + def as_inno(self, user_suffix: str = None) -> dict: + pools = {} + idx = 0 + while idx < 3: + if len(self.pools) > idx: + pools.update( + **self.pools[idx].as_inno(idx=idx + 1, user_suffix=user_suffix) + ) + else: + pools.update(**Pool("", "", "").as_inno(idx=idx + 1)) + idx += 1 + return pools + @dataclass class PoolConfig(MinerConfigValue): @@ -184,3 +210,8 @@ class PoolConfig(MinerConfigValue): if len(self.groups) > 0: return {"pools": self.groups[0].as_avalon(user_suffix=user_suffix)} return {"pools": PoolGroup().as_avalon()} + + def as_inno(self, user_suffix: str = None) -> dict: + if len(self.groups) > 0: + return self.groups[0].as_inno(user_suffix=user_suffix) + return PoolGroup().as_inno() From d7d1b845a702eb6f41a825ac5556491a6476146f Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sat, 9 Dec 2023 13:06:52 -0700 Subject: [PATCH 06/37] feature: add MinerConfig.from_api(). --- pyasic/config/__init__.py | 4 ++++ pyasic/config/pools.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index e7625e7a..e433217f 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -85,6 +85,10 @@ class MinerConfig: **self.power_scaling.as_inno(), } + @classmethod + def from_api(cls, api_pools: dict): + return cls(pools=PoolConfig.from_api(api_pools)) + if __name__ == "__main__": config = MinerConfig( diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index a6214717..d1bd3d1e 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -89,6 +89,9 @@ class Pool(MinerConfigValue): f"Password{idx}": self.password, } + @classmethod + def from_api(cls, api_pool: dict): + return cls(url=api_pool["URL"], user=api_pool["User"], password="x") @dataclass class PoolGroup(MinerConfigValue): @@ -168,6 +171,12 @@ class PoolGroup(MinerConfigValue): idx += 1 return pools + @classmethod + def from_api(cls, api_pool_list: list): + pools = [] + for pool in api_pool_list: + pools.append(Pool.from_api(pool)) + return cls(pools=pools) @dataclass class PoolConfig(MinerConfigValue): @@ -215,3 +224,10 @@ class PoolConfig(MinerConfigValue): if len(self.groups) > 0: return self.groups[0].as_inno(user_suffix=user_suffix) return PoolGroup().as_inno() + + @classmethod + def from_api(cls, api_pools: dict): + pool_data = api_pools["POOLS"] + pool_data = sorted(pool_data, key=lambda x: int(x["POOL"])) + + return cls([PoolGroup.from_api(pool_data)]) \ No newline at end of file From 5eaf876c6d345027f8505cae180e0501336a5a22 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Sat, 9 Dec 2023 13:24:44 -0700 Subject: [PATCH 07/37] feature: add bos to config miner types. --- pyasic/config/__init__.py | 37 ++++++++++++++++++++++++++++++++++++ pyasic/config/base.py | 4 ++-- pyasic/config/fans.py | 8 ++++---- pyasic/config/mining.py | 3 +++ pyasic/config/pools.py | 31 +++++++++++++++++++++++++++++- pyasic/config/temperature.py | 10 ++++++++++ 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index e433217f..1ed04b5b 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -15,6 +15,8 @@ # ------------------------------------------------------------------------------ from dataclasses import dataclass +import toml + from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig from pyasic.config.pools import PoolConfig @@ -85,11 +87,41 @@ class MinerConfig: **self.power_scaling.as_inno(), } + def as_bosminer(self, user_suffix: str = None): + + return { + **merge(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()), + **self.mining_mode.as_bosminer(), + **self.pools.as_bosminer(user_suffix=user_suffix), + **self.power_scaling.as_bosminer(), + } + @classmethod def from_api(cls, api_pools: dict): return cls(pools=PoolConfig.from_api(api_pools)) + +def merge(a: dict, b: dict): + ret = {} + for k in a: + v = a[k] + if k in b.keys(): + if isinstance(v, dict): + ret[k] = merge(a[k], b[k]) + elif isinstance(v, list): + ret[k] = [*v, *b[k]] + else: + ret[k] = v + else: + ret[k] = v + for k in b: + v = b[k] + if k not in ret.keys(): + ret[k] = v + return ret + + if __name__ == "__main__": config = MinerConfig( pools=PoolConfig.simple( @@ -102,6 +134,8 @@ if __name__ == "__main__": ] ), mining_mode=MiningModeConfig.power_tuning(3000), + temperature=TemperatureConfig(hot=100), + fan_mode=FanModeConfig.manual(minimum_fans=2, speed=70), ) print("WM:", config.as_wm()) print("AM Modern:", config.as_am_modern()) @@ -109,3 +143,6 @@ if __name__ == "__main__": print("GS:", config.as_goldshell()) print("Avalon:", config.as_avalon()) print("Inno:", config.as_inno()) + print("BOS+ .toml:", config.as_bosminer()) + print("BOS+ .toml as toml:") + print(toml.dumps(config.as_bosminer())) diff --git a/pyasic/config/base.py b/pyasic/config/base.py index 2a39477a..590d2778 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -35,7 +35,7 @@ class MinerConfigOption(Enum): def as_avalon(self) -> dict: return self.value.as_avalon() - def as_bos(self) -> dict: + def as_bosminer(self) -> dict: return self.value.as_bos() def __call__(self, *args, **kwargs): @@ -61,5 +61,5 @@ class MinerConfigValue: def as_avalon(self) -> dict: return {} - def as_bos(self) -> dict: + def as_bosminer(self) -> dict: return {} diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index c26fbc30..7a608056 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -25,7 +25,7 @@ class FanModeNormal(MinerConfigValue): def as_am_modern(self): return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} - def as_bos(self): + def as_bosminer(self): return {"temp_control": {"mode": "auto"}} @@ -38,7 +38,7 @@ class FanModeManual(MinerConfigValue): def as_am_modern(self): return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)} - def as_bos(self): + def as_bosminer(self): return { "temp_control": {"mode": "manual"}, "fan_control": {"min_fans": self.minimum_fans, "speed": self.speed}, @@ -52,8 +52,8 @@ class FanModeImmersion(MinerConfigValue): def as_am_modern(self): return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"} - def as_bos(self): - return {"temp_control": {"mode": "manual"}, "fan_control": {"min_fans": 0}} + def as_bosminer(self): + return {"temp_control": {"mode": "disabled"}} class FanModeConfig(MinerConfigOption): diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index 41ffb7f2..17b87214 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -73,6 +73,9 @@ class MiningModePowerTune(MinerConfigValue): def as_wm(self): return {"mode": self.mode, self.mode: {"wattage": self.power}} + def as_bosminer(self) -> dict: + return {"autotuning": {"enabled": True, "psu_power_limit": self.power}} + @dataclass class MiningModeHashrateTune(MinerConfigValue): diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index d1bd3d1e..660b5151 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -93,6 +93,16 @@ class Pool(MinerConfigValue): def from_api(cls, api_pool: dict): return cls(url=api_pool["URL"], user=api_pool["User"], password="x") + def as_bosminer(self, user_suffix: str = None): + 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} + + @dataclass class PoolGroup(MinerConfigValue): pools: list[Pool] = field(default_factory=list) @@ -178,6 +188,18 @@ class PoolGroup(MinerConfigValue): pools.append(Pool.from_api(pool)) return cls(pools=pools) + def as_bosminer(self, user_suffix: str = None) -> dict: + if len(self.pools) > 0: + return { + "name": self.name, + "quota": self.quota, + "pool": [ + pool.as_bosminer(user_suffix=user_suffix) for pool in self.pools + ], + } + return {"name": "Group", "quota": 1, "pool": [Pool.as_bosminer()]} + + @dataclass class PoolConfig(MinerConfigValue): groups: list[PoolGroup] = field(default_factory=list) @@ -230,4 +252,11 @@ class PoolConfig(MinerConfigValue): pool_data = api_pools["POOLS"] pool_data = sorted(pool_data, key=lambda x: int(x["POOL"])) - return cls([PoolGroup.from_api(pool_data)]) \ No newline at end of file + return cls([PoolGroup.from_api(pool_data)]) + + def as_bosminer(self, user_suffix: str = None) -> dict: + if len(self.groups) > 0: + return { + "group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups] + } + return {"group": [PoolGroup().as_bosminer()]} diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py index 0e2b416a..3bfe6d47 100644 --- a/pyasic/config/temperature.py +++ b/pyasic/config/temperature.py @@ -27,3 +27,13 @@ class TemperatureConfig(MinerConfigValue): @classmethod def default(cls): return cls() + + def as_bosminer(self) -> dict: + temp_cfg = {} + if self.target is not None: + temp_cfg["target_temp"] = self.target + if self.hot is not None: + temp_cfg["hot_temp"] = self.hot + if self.danger is not None: + temp_cfg["dangerous_temp"] = self.danger + return {"temp_control": temp_cfg} From 81b974f565579122e1d8eaebafafa6a372141f88 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sat, 9 Dec 2023 15:12:36 -0700 Subject: [PATCH 08/37] bug: fix bad indentation. --- pyasic/config/pools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 660b5151..2e288447 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -188,7 +188,7 @@ class PoolGroup(MinerConfigValue): pools.append(Pool.from_api(pool)) return cls(pools=pools) - def as_bosminer(self, user_suffix: str = None) -> dict: + def as_bosminer(self, user_suffix: str = None) -> dict: if len(self.pools) > 0: return { "name": self.name, From f892c3a0fddd0b99b986652296650ab0af2fcbb8 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sat, 9 Dec 2023 16:59:39 -0700 Subject: [PATCH 09/37] feature: Add from am_modern to config. --- pyasic/config/__init__.py | 8 ++++++ pyasic/config/fans.py | 11 +++++++++ pyasic/config/mining.py | 12 +++++++++ pyasic/config/pools.py | 52 +++++++++++++++++++++++++++------------ 4 files changed, 67 insertions(+), 16 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 1ed04b5b..7530de8f 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -100,6 +100,14 @@ class MinerConfig: def from_api(cls, api_pools: dict): return cls(pools=PoolConfig.from_api(api_pools)) + @classmethod + def from_am_modern(cls, web_conf: dict): + return cls( + pools=PoolConfig.from_am_modern(web_conf), + mining_mode=MiningModeConfig.from_am_modern(web_conf), + fan_mode=FanModeConfig.from_am_modern(web_conf) + ) + def merge(a: dict, b: dict): diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index 7a608056..85eaf16c 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -64,3 +64,14 @@ class FanModeConfig(MinerConfigOption): @classmethod def default(cls): return cls.normal() + + @classmethod + def from_am_modern(cls, web_conf: dict): + if web_conf.get("bitmain-fan-ctrl") is not None: + fan_manual = web_conf["bitmain-fan-ctrl"] + if fan_manual: + return cls.manual(speed=web_conf["bitmain-fan-pwm"]) + else: + return cls.normal() + else: + return cls.default() \ No newline at end of file diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index 17b87214..3603b64c 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -119,3 +119,15 @@ class MiningModeConfig(MinerConfigOption): @classmethod def default(cls): return cls.normal() + + @classmethod + def from_am_modern(cls, web_conf: dict): + if web_conf.get("bitmain-work-mode") is not None: + work_mode = web_conf["bitmain-work-mode"] + if int(work_mode) == 0: + return cls.normal() + elif int(work_mode) == 1: + return cls.sleep() + elif int(work_mode) == 3: + return cls.low() + return cls.default() \ No newline at end of file diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 2e288447..250f4d89 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -89,9 +89,6 @@ class Pool(MinerConfigValue): f"Password{idx}": self.password, } - @classmethod - def from_api(cls, api_pool: dict): - return cls(url=api_pool["URL"], user=api_pool["User"], password="x") def as_bosminer(self, user_suffix: str = None): if user_suffix is not None: @@ -102,6 +99,14 @@ class Pool(MinerConfigValue): } return {"url": self.url, "user": self.user, "pass": self.password} + @classmethod + def from_api(cls, api_pool: dict): + return cls(url=api_pool["URL"], user=api_pool["User"], password="x") + + @classmethod + def from_am_modern(cls, web_pool: dict): + return cls(url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]) + @dataclass class PoolGroup(MinerConfigValue): @@ -181,13 +186,6 @@ class PoolGroup(MinerConfigValue): idx += 1 return pools - @classmethod - def from_api(cls, api_pool_list: list): - pools = [] - for pool in api_pool_list: - pools.append(Pool.from_api(pool)) - return cls(pools=pools) - def as_bosminer(self, user_suffix: str = None) -> dict: if len(self.pools) > 0: return { @@ -199,6 +197,21 @@ class PoolGroup(MinerConfigValue): } return {"name": "Group", "quota": 1, "pool": [Pool.as_bosminer()]} + @classmethod + def from_api(cls, api_pool_list: list): + pools = [] + for pool in api_pool_list: + pools.append(Pool.from_api(pool)) + return cls(pools=pools) + + @classmethod + def from_am_modern(cls, web_pool_list: list): + pools = [] + for pool in web_pool_list: + pools.append(Pool.from_am_modern(pool)) + return cls(pools=pools) + + @dataclass class PoolConfig(MinerConfigValue): @@ -247,12 +260,6 @@ class PoolConfig(MinerConfigValue): return self.groups[0].as_inno(user_suffix=user_suffix) return PoolGroup().as_inno() - @classmethod - def from_api(cls, api_pools: dict): - pool_data = api_pools["POOLS"] - pool_data = sorted(pool_data, key=lambda x: int(x["POOL"])) - - return cls([PoolGroup.from_api(pool_data)]) def as_bosminer(self, user_suffix: str = None) -> dict: if len(self.groups) > 0: @@ -260,3 +267,16 @@ class PoolConfig(MinerConfigValue): "group": [g.as_bosminer(user_suffix=user_suffix) for g in self.groups] } return {"group": [PoolGroup().as_bosminer()]} + + @classmethod + def from_api(cls, api_pools: dict): + pool_data = api_pools["POOLS"] + pool_data = sorted(pool_data, key=lambda x: int(x["POOL"])) + + return cls([PoolGroup.from_api(pool_data)]) + + @classmethod + def from_am_modern(cls, web_conf: dict): + pool_data = web_conf["pools"] + + return cls([PoolGroup.from_am_modern(pool_data)]) From 83d0d09b0d0745c17f340c0021fa9478f280b411 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sat, 9 Dec 2023 17:35:47 -0700 Subject: [PATCH 10/37] feature: Add whatsminer get_config. --- pyasic/miners/backends/btminer.py | 55 +++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index 6cb76160..3d622464 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -15,12 +15,11 @@ # ------------------------------------------------------------------------------ import logging -import warnings from collections import namedtuple from typing import List, Optional, Tuple from pyasic.API.btminer import BTMinerAPI -from pyasic.config import MinerConfig +from pyasic.config import MinerConfig, MiningModeConfig from pyasic.data import Fan, HashBoard from pyasic.data.error_codes import MinerErrorData, WhatsminerError from pyasic.errors import APIError @@ -209,33 +208,53 @@ class BTMiner(BaseMiner): async def get_config(self) -> MinerConfig: pools = None summary = None - cfg = MinerConfig() - + status = None try: - data = await self.api.multicommand("pools", "summary") + data = await self.api.multicommand("pools", "summary", "status") pools = data["pools"][0] summary = data["summary"][0] + status = data["status"][0] except APIError as e: logging.warning(e) except LookupError: pass - if pools: - if "POOLS" in pools: - cfg = cfg.from_api(pools["POOLS"]) + if pools is not None: + cfg = MinerConfig.from_api(pools) else: - # somethings wrong with the miner - warnings.warn( - f"Failed to gather pool config for miner: {self}, miner did not return pool information." - ) - if summary: - if "SUMMARY" in summary: - if wattage := summary["SUMMARY"][0].get("Power Limit"): - cfg.autotuning_wattage = wattage + cfg = MinerConfig() + + is_mining = await self.is_mining(status) + if not is_mining: + cfg.mining_mode = MiningModeConfig.sleep() + return cfg + + if summary is not None: + mining_mode = None + try: + mining_mode = summary["SUMMARY"][0]["Power Mode"] + except LookupError: + pass + + if mining_mode == "High": + cfg.mining_mode = MiningModeConfig.high() + return cfg + elif mining_mode == "Low": + cfg.mining_mode = MiningModeConfig.low() + return cfg + try: + power_lim = summary["SUMMARY"][0]["Power Limit"] + except LookupError: + power_lim = None + + if power_lim is None: + cfg.mining_mode = MiningModeConfig.normal() + return cfg + + cfg.mining_mode = MiningModeConfig.power_tuning(power_lim) + return cfg - self.config = cfg - return self.config async def set_power_limit(self, wattage: int) -> bool: try: From bfdfa8a6ab81e03b1982f8e6898c7213964730ca Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 09:30:31 -0700 Subject: [PATCH 11/37] feature: Add AM modern send and get config. --- pyasic/miners/backends/antminer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index f001da9f..abd696b4 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -80,12 +80,12 @@ class AntminerModern(BMMiner): async def get_config(self) -> MinerConfig: data = await self.web.get_miner_conf() if data: - self.config = MinerConfig().from_raw(data) + self.config = MinerConfig.from_am_modern(data) return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: self.config = config - conf = config.as_x19(user_suffix=user_suffix) + conf = config.as_am_modern(user_suffix=user_suffix) data = await self.web.set_miner_conf(conf) if data: @@ -94,7 +94,7 @@ class AntminerModern(BMMiner): for i in range(7): data = await self.get_config() - if data.as_x19() == conf: + if data.as_am_modern() == conf: break await asyncio.sleep(1) From 03990941973b9928edcd743d547e57fcda8c16b9 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 09:45:34 -0700 Subject: [PATCH 12/37] feature: add AM old and goldshell configs. --- pyasic/config/__init__.py | 9 ++++++ pyasic/config/pools.py | 29 ++++++++++++-------- pyasic/miners/backends/antminer.py | 4 +-- pyasic/miners/backends/bfgminer_goldshell.py | 4 +-- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 7530de8f..24f55916 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -108,6 +108,15 @@ class MinerConfig: fan_mode=FanModeConfig.from_am_modern(web_conf) ) + @classmethod + def from_am_old(cls, web_conf: dict): + return cls.from_am_modern(web_conf) + + @classmethod + def from_goldshell(cls, web_conf: dict): + return cls( + pools=PoolConfig.from_am_modern(web_conf), + ) def merge(a: dict, b: dict): diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 250f4d89..5cae3c07 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -89,7 +89,6 @@ class Pool(MinerConfigValue): f"Password{idx}": self.password, } - def as_bosminer(self, user_suffix: str = None): if user_suffix is not None: return { @@ -105,7 +104,15 @@ class Pool(MinerConfigValue): @classmethod def from_am_modern(cls, web_pool: dict): - return cls(url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"]) + return cls( + url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] + ) + + @classmethod + def from_goldshell(cls, web_pool: dict): + return cls( + url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] + ) @dataclass @@ -158,15 +165,7 @@ class PoolGroup(MinerConfigValue): return pools def as_goldshell(self, user_suffix: str = None) -> list: - pools = [] - idx = 0 - while idx < 3: - if len(self.pools) > idx: - pools.append(self.pools[idx].as_am_modern(user_suffix=user_suffix)) - else: - pools.append(Pool("", "", "").as_am_modern()) - idx += 1 - return pools + return [pool.as_goldshell(user_suffix) for pool in self.pools] def as_avalon(self, user_suffix: str = None) -> dict: if len(self.pools) > 0: @@ -211,6 +210,9 @@ class PoolGroup(MinerConfigValue): pools.append(Pool.from_am_modern(pool)) return cls(pools=pools) + @classmethod + def from_goldshell(cls, web_pools: list): + return cls([Pool.from_goldshell(p) for p in web_pools]) @dataclass @@ -260,7 +262,6 @@ class PoolConfig(MinerConfigValue): return self.groups[0].as_inno(user_suffix=user_suffix) return PoolGroup().as_inno() - def as_bosminer(self, user_suffix: str = None) -> dict: if len(self.groups) > 0: return { @@ -280,3 +281,7 @@ class PoolConfig(MinerConfigValue): pool_data = web_conf["pools"] return cls([PoolGroup.from_am_modern(pool_data)]) + + @classmethod + def from_goldshell(cls, web_pools: list): + return cls([PoolGroup.from_goldshell(web_pools)]) diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index abd696b4..bbe967a4 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -349,11 +349,11 @@ class AntminerOld(CGMiner): async def get_config(self) -> MinerConfig: data = await self.web.get_miner_conf() if data: - self.config = MinerConfig().from_raw(data) + self.config = MinerConfig.from_am_old(data) return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: - await self.web.set_miner_conf(config.as_x17(user_suffix=user_suffix)) + await self.web.set_miner_conf(config.as_am_old(user_suffix=user_suffix)) async def get_mac(self) -> Union[str, None]: try: diff --git a/pyasic/miners/backends/bfgminer_goldshell.py b/pyasic/miners/backends/bfgminer_goldshell.py index 5d9e81dc..bf080e3c 100644 --- a/pyasic/miners/backends/bfgminer_goldshell.py +++ b/pyasic/miners/backends/bfgminer_goldshell.py @@ -64,7 +64,7 @@ class BFGMinerGoldshell(BFGMiner): self.data_locations = GOLDSHELL_DATA_LOC async def get_config(self) -> MinerConfig: - return MinerConfig().from_raw(await self.web.pools()) + return MinerConfig.from_goldshell(await self.web.pools()) async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: pools_data = await self.web.pools() @@ -80,7 +80,7 @@ class BFGMinerGoldshell(BFGMiner): self.config = config # send them back 1 at a time - for pool in config.as_goldshell(user_suffix=user_suffix): + for pool in config.as_goldshell(user_suffix=user_suffix)["pools"]: await self.web.newpool( url=pool["url"], user=pool["user"], password=pool["pass"] ) From 468fba346539fcaab3511f9d26c7792ffb95388b Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 09:49:24 -0700 Subject: [PATCH 13/37] feature: Add whatsminer set mode commands. --- pyasic/API/btminer.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pyasic/API/btminer.py b/pyasic/API/btminer.py index 369d3363..1ce805cc 100644 --- a/pyasic/API/btminer.py +++ b/pyasic/API/btminer.py @@ -487,6 +487,34 @@ class BTMinerAPI(BaseMinerAPI): """ return await self.send_privileged_command("set_low_power") + async def set_high_power(self) -> dict: + """Set low power mode on the miner using the API. +
+ Expand + + Set low power mode on the miner using the API, only works after + changing the password of the miner using the Whatsminer tool. + + Returns: + A reply informing of the status of setting low power mode. +
+ """ + return await self.send_privileged_command("set_high_power") + + async def set_normal_power(self) -> dict: + """Set low power mode on the miner using the API. +
+ Expand + + Set low power mode on the miner using the API, only works after + changing the password of the miner using the Whatsminer tool. + + Returns: + A reply informing of the status of setting low power mode. +
+ """ + return await self.send_privileged_command("set_normal_power") + async def update_firmware(self): # noqa - static """Not implemented.""" # to be determined if this will be added later From b22b506d55b5c117db5372eaf9c25d5573f584f8 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 09:55:05 -0700 Subject: [PATCH 14/37] feature: Add whatsminer send_config. --- pyasic/miners/backends/btminer.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index 3d622464..4c79a5f5 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -199,10 +199,18 @@ class BTMiner(BaseMiner): await self.api.update_pools(**pools_conf) except APIError: pass + try: - await self.api.adjust_power_limit(conf["wattage"]) + if conf["mode"] == "normal": + await self.api.set_normal_power() + elif conf["mode"] == "high": + await self.api.set_high_power() + elif conf["mode"] == "low": + await self.api.set_low_power() + elif conf["mode"] == "power_tuning": + await self.api.adjust_power_limit(conf["power_tuning"]["wattage"]) except APIError: - # cannot set wattage + # cannot update, no API access usually pass async def get_config(self) -> MinerConfig: @@ -252,7 +260,8 @@ class BTMiner(BaseMiner): return cfg cfg.mining_mode = MiningModeConfig.power_tuning(power_lim) - return cfg + self.config = cfg + return self.config From 9ee63cc3aba9f2f98a36d6891a4f333c6d62bb05 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 10:10:55 -0700 Subject: [PATCH 15/37] feature: Update get and send config methods for most miners, and add as_inno. --- pyasic/config/__init__.py | 11 ++++++----- pyasic/config/pools.py | 20 +++++++++++++++++++- pyasic/miners/backends/antminer.py | 6 +++--- pyasic/miners/backends/bfgminer.py | 2 +- pyasic/miners/backends/bfgminer_goldshell.py | 9 ++++++++- pyasic/miners/backends/bmminer.py | 2 +- pyasic/miners/backends/bosminer.py | 2 +- pyasic/miners/backends/btminer.py | 3 --- pyasic/miners/backends/cgminer.py | 9 ++++++--- pyasic/miners/backends/cgminer_avalon.py | 1 - pyasic/miners/backends/luxminer.py | 6 ------ 11 files changed, 45 insertions(+), 26 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 24f55916..5f932d77 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -88,7 +88,6 @@ class MinerConfig: } def as_bosminer(self, user_suffix: str = None): - return { **merge(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()), **self.mining_mode.as_bosminer(), @@ -105,7 +104,7 @@ class MinerConfig: return cls( pools=PoolConfig.from_am_modern(web_conf), mining_mode=MiningModeConfig.from_am_modern(web_conf), - fan_mode=FanModeConfig.from_am_modern(web_conf) + fan_mode=FanModeConfig.from_am_modern(web_conf), ) @classmethod @@ -114,9 +113,11 @@ class MinerConfig: @classmethod def from_goldshell(cls, web_conf: dict): - return cls( - pools=PoolConfig.from_am_modern(web_conf), - ) + return cls(pools=PoolConfig.from_am_modern(web_conf)) + + @classmethod + def from_inno(cls, web_pools: dict): + return cls(pools=PoolConfig.from_inno(web_pools)) def merge(a: dict, b: dict): diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 5cae3c07..8bb79379 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -108,12 +108,20 @@ class Pool(MinerConfigValue): url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) + # TODO: check if this is accurate, user/username, pass/password @classmethod def from_goldshell(cls, web_pool: dict): return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) + # TODO: check if this is accurate, user/username, pass/password + @classmethod + def from_inno(cls, web_pool: dict): + return cls( + url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] + ) + @dataclass class PoolGroup(MinerConfigValue): @@ -194,7 +202,7 @@ class PoolGroup(MinerConfigValue): pool.as_bosminer(user_suffix=user_suffix) for pool in self.pools ], } - return {"name": "Group", "quota": 1, "pool": [Pool.as_bosminer()]} + return {"name": "Group", "quota": 1, "pool": []} @classmethod def from_api(cls, api_pool_list: list): @@ -214,6 +222,10 @@ class PoolGroup(MinerConfigValue): def from_goldshell(cls, web_pools: list): return cls([Pool.from_goldshell(p) for p in web_pools]) + @classmethod + def from_inno(cls, web_pools: list): + return cls([Pool.from_inno(p) for p in web_pools]) + @dataclass class PoolConfig(MinerConfigValue): @@ -285,3 +297,9 @@ class PoolConfig(MinerConfigValue): @classmethod def from_goldshell(cls, web_pools: list): return cls([PoolGroup.from_goldshell(web_pools)]) + + @classmethod + def from_inno(cls, web_pools: list): + return cls([PoolGroup.from_inno(web_pools)]) + + diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index bbe967a4..dd532e86 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -18,7 +18,7 @@ import asyncio from typing import List, Optional, Union from pyasic.API import APIError -from pyasic.config import MinerConfig +from pyasic.config import MinerConfig, MiningModeConfig from pyasic.data import Fan, HashBoard from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.miners.backends.bmminer import BMMiner @@ -120,13 +120,13 @@ class AntminerModern(BMMiner): async def stop_mining(self) -> bool: cfg = await self.get_config() - cfg.miner_mode = X19PowerMode.Sleep + cfg.miner_mode = MiningModeConfig.sleep await self.send_config(cfg) return True async def resume_mining(self) -> bool: cfg = await self.get_config() - cfg.miner_mode = X19PowerMode.Normal + cfg.miner_mode = MiningModeConfig.normal await self.send_config(cfg) return True diff --git a/pyasic/miners/backends/bfgminer.py b/pyasic/miners/backends/bfgminer.py index 0f2229cb..a98d73d6 100644 --- a/pyasic/miners/backends/bfgminer.py +++ b/pyasic/miners/backends/bfgminer.py @@ -72,7 +72,7 @@ class BFGMiner(BaseMiner): except APIError: return self.config - self.config = MinerConfig().from_api(pools["POOLS"]) + self.config = MinerConfig.from_api(pools) return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: diff --git a/pyasic/miners/backends/bfgminer_goldshell.py b/pyasic/miners/backends/bfgminer_goldshell.py index bf080e3c..93c442c7 100644 --- a/pyasic/miners/backends/bfgminer_goldshell.py +++ b/pyasic/miners/backends/bfgminer_goldshell.py @@ -64,7 +64,14 @@ class BFGMinerGoldshell(BFGMiner): self.data_locations = GOLDSHELL_DATA_LOC async def get_config(self) -> MinerConfig: - return MinerConfig.from_goldshell(await self.web.pools()) + # get pool data + try: + pools = await self.web.pools() + except APIError: + return self.config + + self.config = MinerConfig.from_goldshell(pools) + return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: pools_data = await self.web.pools() diff --git a/pyasic/miners/backends/bmminer.py b/pyasic/miners/backends/bmminer.py index f4435be9..7b8bfbe5 100644 --- a/pyasic/miners/backends/bmminer.py +++ b/pyasic/miners/backends/bmminer.py @@ -104,7 +104,7 @@ class BMMiner(BaseMiner): except APIError: return self.config - self.config = MinerConfig().from_api(pools["POOLS"]) + self.config = MinerConfig.from_api(pools) return self.config async def reboot(self) -> bool: diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index 61004225..1cfa3a07 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -316,7 +316,7 @@ class BOSMiner(BaseMiner): (await conn.run("cat /etc/bosminer.toml")).stdout ) logging.debug(f"{self}: Converting config file.") - cfg = MinerConfig().from_raw(toml_data) + cfg = MinerConfig.from_raw(toml_data) self.config = cfg return self.config diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index 4c79a5f5..7a28c27a 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -197,10 +197,7 @@ class BTMiner(BaseMiner): try: await self.api.update_pools(**pools_conf) - except APIError: - pass - try: if conf["mode"] == "normal": await self.api.set_normal_power() elif conf["mode"] == "high": diff --git a/pyasic/miners/backends/cgminer.py b/pyasic/miners/backends/cgminer.py index 268c2149..e869cc46 100644 --- a/pyasic/miners/backends/cgminer.py +++ b/pyasic/miners/backends/cgminer.py @@ -143,10 +143,13 @@ class CGMiner(BaseMiner): return True async def get_config(self) -> MinerConfig: - api_pools = await self.api.pools() + # get pool data + try: + pools = await self.api.pools() + except APIError: + return self.config - if api_pools: - self.config = MinerConfig().from_api(api_pools["POOLS"]) + self.config = MinerConfig.from_api(pools) return self.config async def fault_light_off(self) -> bool: diff --git a/pyasic/miners/backends/cgminer_avalon.py b/pyasic/miners/backends/cgminer_avalon.py index ba128136..91c5a2e9 100644 --- a/pyasic/miners/backends/cgminer_avalon.py +++ b/pyasic/miners/backends/cgminer_avalon.py @@ -100,7 +100,6 @@ class CGMinerAvalon(CGMiner): return False async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: - """Configures miner with yaml config.""" self.config = config return None logging.debug(f"{self}: Sending config.") # noqa - This doesnt work... diff --git a/pyasic/miners/backends/luxminer.py b/pyasic/miners/backends/luxminer.py index 135ae046..ff8b1219 100644 --- a/pyasic/miners/backends/luxminer.py +++ b/pyasic/miners/backends/luxminer.py @@ -196,15 +196,9 @@ class LUXMiner(BaseMiner): return False async def get_config(self) -> MinerConfig: - """Gets the config for the miner and sets it as `self.config`. - - Returns: - The config from `self.config`. - """ return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: - """Configures miner with yaml config.""" pass async def set_power_limit(self, wattage: int) -> bool: From 4fc57832e1a959c45b56b06da4c970cc19cfe04a Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 10:14:57 -0700 Subject: [PATCH 16/37] feature: Finish fixing get and send config handlers for miners. --- pyasic/miners/innosilicon/cgminer/T3X/T3H.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pyasic/miners/innosilicon/cgminer/T3X/T3H.py b/pyasic/miners/innosilicon/cgminer/T3X/T3H.py index 5387b2b3..e7dc38f4 100644 --- a/pyasic/miners/innosilicon/cgminer/T3X/T3H.py +++ b/pyasic/miners/innosilicon/cgminer/T3X/T3H.py @@ -38,16 +38,13 @@ class CGMinerT3HPlus(CGMiner, T3HPlus): return False async def get_config(self, api_pools: dict = None) -> MinerConfig: - if not api_pools: - try: - api_pools = await self.api.pools() - except APIError as e: - logging.warning(e) + # get pool data + try: + pools = await self.api.pools() + except APIError: + return self.config - if api_pools: - if "POOLS" in api_pools.keys(): - cfg = MinerConfig().from_api(api_pools["POOLS"]) - self.config = cfg + self.config = MinerConfig.from_api(pools) return self.config async def reboot(self) -> bool: From 43200a735486df70fc7979bd504e3888401f6906 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 13:20:03 -0700 Subject: [PATCH 17/37] feature: Add bosminer.toml parser. --- pyasic/config/__init__.py | 17 ++++++++- pyasic/config/fans.py | 30 +++++++++++++++- pyasic/config/mining.py | 37 ++++++++++++++++--- pyasic/config/pools.py | 27 ++++++++++++++ pyasic/config/power_scaling.py | 57 +++++++++++++++++++++++++++++- pyasic/config/temperature.py | 10 ++++++ pyasic/miners/backends/bosminer.py | 7 +--- 7 files changed, 172 insertions(+), 13 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 5f932d77..9ff385ca 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -119,6 +119,15 @@ class MinerConfig: def from_inno(cls, web_pools: dict): return cls(pools=PoolConfig.from_inno(web_pools)) + @classmethod + def from_bosminer(cls, toml_conf: dict): + return cls( + pools=PoolConfig.from_bosminer(toml_conf), + mining_mode=MiningModeConfig.from_bosminer(toml_conf), + fan_mode=FanModeConfig.from_bosminer(toml_conf), + temperature=TemperatureConfig.from_bosminer(toml_conf), + power_scaling=PowerScalingConfig.from_bosminer(toml_conf), + ) def merge(a: dict, b: dict): ret = {} @@ -152,9 +161,11 @@ if __name__ == "__main__": ] ), mining_mode=MiningModeConfig.power_tuning(3000), - temperature=TemperatureConfig(hot=100), + temperature=TemperatureConfig(hot=100, danger=110), fan_mode=FanModeConfig.manual(minimum_fans=2, speed=70), + power_scaling=PowerScalingConfig.enabled(power_step=100, minimum_power=2400) ) + print(config) print("WM:", config.as_wm()) print("AM Modern:", config.as_am_modern()) print("AM Old:", config.as_am_old()) @@ -164,3 +175,7 @@ if __name__ == "__main__": print("BOS+ .toml:", config.as_bosminer()) print("BOS+ .toml as toml:") print(toml.dumps(config.as_bosminer())) + + bos_parsed = MinerConfig.from_bosminer(config.as_bosminer()) + print(bos_parsed) + print(bos_parsed == config) diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index 85eaf16c..1e628f01 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -35,6 +35,17 @@ class FanModeManual(MinerConfigValue): minimum_fans: int = 1 speed: int = 100 + @classmethod + def from_bosminer(cls, toml_fan_conf: dict): + cls_conf = {} + if toml_fan_conf.get("min_fans") is not None: + cls_conf["minimum_fans"] = toml_fan_conf["min_fans"] + if toml_fan_conf.get("speed") is not None: + cls_conf["speed"] = toml_fan_conf["speed"] + return cls(**cls_conf) + + + def as_am_modern(self): return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)} @@ -74,4 +85,21 @@ class FanModeConfig(MinerConfigOption): else: return cls.normal() else: - return cls.default() \ No newline at end of file + return cls.default() + + @classmethod + def from_bosminer(cls, toml_conf: dict): + if toml_conf.get("temp_control") is None: + return cls.default() + if toml_conf["temp_control"].get("mode") is None: + return cls.default() + + mode = toml_conf["temp_control"]["mode"] + if mode == "auto": + return cls.normal() + elif mode == "manual": + if toml_conf.get("fan_control"): + return cls.manual().from_bosminer(toml_conf["fan_control"]) + return cls.manual() + elif mode == "disabled": + return cls.immersion() diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index 3603b64c..dcbc3bb6 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -65,13 +65,15 @@ class MiningModeHPM(MinerConfigValue): @dataclass class MiningModePowerTune(MinerConfigValue): mode: str = field(init=False, default="power_tuning") - power: int + power: int = None def as_am_modern(self): return {"miner-mode": "0"} def as_wm(self): - return {"mode": self.mode, self.mode: {"wattage": self.power}} + if self.power is not None: + return {"mode": self.mode, self.mode: {"wattage": self.power}} + return {} def as_bosminer(self) -> dict: return {"autotuning": {"enabled": True, "psu_power_limit": self.power}} @@ -80,7 +82,7 @@ class MiningModePowerTune(MinerConfigValue): @dataclass class MiningModeHashrateTune(MinerConfigValue): mode: str = field(init=False, default="hashrate_tuning") - hashrate: int + hashrate: int = None def as_am_modern(self): return {"miner-mode": "0"} @@ -130,4 +132,31 @@ class MiningModeConfig(MinerConfigOption): return cls.sleep() elif int(work_mode) == 3: return cls.low() - return cls.default() \ No newline at end of file + return cls.default() + + @classmethod + def from_bosminer(cls, toml_conf: dict): + if toml_conf.get("autotuning") is None: + return cls.default() + autotuning_conf = toml_conf["autotuning"] + + if autotuning_conf.get("enabled") is None: + return cls.default() + if not autotuning_conf["enabled"]: + return cls.default() + + if autotuning_conf.get("psu_power_limit") is not None: + # old autotuning conf + return cls.power_tuning(autotuning_conf["psu_power_limit"]) + if autotuning_conf.get("mode") is not None: + # new autotuning conf + mode = autotuning_conf["mode"] + if mode == "power_target": + if autotuning_conf.get("power_target") is not None: + return cls.power_tuning(autotuning_conf["power_target"]) + return cls.power_tuning() + if mode == "hashrate_target": + if autotuning_conf.get("hashrate_target") is not None: + return cls.hashrate_tuning(autotuning_conf["hashrate_target"]) + return cls.hashrate_tuning() + diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 8bb79379..756f58be 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -122,6 +122,15 @@ class Pool(MinerConfigValue): url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) + @classmethod + def from_bosminer(cls, toml_pool_conf: dict): + return cls( + url=toml_pool_conf["url"], + user=toml_pool_conf["user"], + password=toml_pool_conf["pass"], + ) + + @dataclass class PoolGroup(MinerConfigValue): @@ -226,6 +235,16 @@ class PoolGroup(MinerConfigValue): def from_inno(cls, web_pools: list): return cls([Pool.from_inno(p) for p in web_pools]) + @classmethod + def from_bosminer(cls, toml_group_conf: dict): + if toml_group_conf.get("pool") is not None: + return cls( + name=toml_group_conf["name"], + quota=toml_group_conf["quota"], + pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]], + ) + return cls() + @dataclass class PoolConfig(MinerConfigValue): @@ -303,3 +322,11 @@ class PoolConfig(MinerConfigValue): return cls([PoolGroup.from_inno(web_pools)]) + @classmethod + def from_bosminer(cls, toml_conf: dict): + if toml_conf.get("group") is None: + return cls() + + return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]]) + + diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index e2bf2ca3..f6d3d3ae 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -23,24 +23,66 @@ class PowerScalingShutdownEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") duration: int = None + def as_bosminer(self) -> dict: + cfg = {"shutdown_enabled": True} + + if self.duration is not None: + cfg["shutdown_duration"] = self.duration + @dataclass class PowerScalingShutdownDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") + def as_bosminer(self) -> dict: + return {"shutdown_enabled": False} + class PowerScalingShutdown(MinerConfigOption): enabled = PowerScalingShutdownEnabled disabled = PowerScalingShutdownDisabled + @classmethod + def from_bosminer(cls, power_scaling_conf: dict): + sd_enabled = power_scaling_conf.get("shutdown_enabled") + if sd_enabled is not None: + if sd_enabled: + return cls.enabled(power_scaling_conf.get("shutdown_duration")) + else: + return cls.disabled() + return None + @dataclass class PowerScalingEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") power_step: int = None minimum_power: int = None - shutdown_mode: PowerScalingShutdown = None + shutdown_enabled: PowerScalingShutdown = None + @classmethod + def from_bosminer(cls, power_scaling_conf: dict): + power_step = power_scaling_conf.get("power_step") + min_power = power_scaling_conf.get("min_psu_power_limit") + sd_mode = PowerScalingShutdown.from_bosminer(power_scaling_conf) + + return cls( + power_step=power_step, minimum_power=min_power, shutdown_enabled=sd_mode + ) + + def as_bosminer(self) -> dict: + cfg = { + "enabled": True + } + if self.power_step is not None: + cfg["power_step"] = self.power_step + if self.minimum_power is not None: + cfg["min_psu_power_limit"] = self.minimum_power + + if self.shutdown_enabled is not None: + cfg = {**cfg, **self.shutdown_enabled.as_bosminer()} + + return {"power_scaling": cfg} @dataclass class PowerScalingDisabled(MinerConfigValue): @@ -54,3 +96,16 @@ class PowerScalingConfig(MinerConfigOption): @classmethod def default(cls): return cls.disabled() + + @classmethod + def from_bosminer(cls, toml_conf: dict): + power_scaling = toml_conf.get("power_scaling") + if power_scaling is not None: + enabled = power_scaling.get("enabled") + if enabled is not None: + if enabled: + return cls.enabled().from_bosminer(power_scaling) + else: + return cls.disabled() + + return cls.default() \ No newline at end of file diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py index 3bfe6d47..f353e507 100644 --- a/pyasic/config/temperature.py +++ b/pyasic/config/temperature.py @@ -37,3 +37,13 @@ class TemperatureConfig(MinerConfigValue): if self.danger is not None: temp_cfg["dangerous_temp"] = self.danger return {"temp_control": temp_cfg} + + @classmethod + def from_bosminer(cls, toml_conf: dict): + temp_control = toml_conf.get("temp_control") + if temp_control is not None: + return cls( + target=temp_control.get("target_temp"), + hot=temp_control.get("hot_temp"), + danger=temp_control.get("dangerous_temp"), + ) diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index 1cfa3a07..68d1ee83 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -297,11 +297,6 @@ class BOSMiner(BaseMiner): return False async def get_config(self) -> MinerConfig: - """Gets the config for the miner and sets it as `self.config`. - - Returns: - The config from `self.config`. - """ logging.debug(f"{self}: Getting config.") try: @@ -316,7 +311,7 @@ class BOSMiner(BaseMiner): (await conn.run("cat /etc/bosminer.toml")).stdout ) logging.debug(f"{self}: Converting config file.") - cfg = MinerConfig.from_raw(toml_data) + cfg = MinerConfig.from_bosminer(toml_data) self.config = cfg return self.config From 1ab39f5873cd0f177ccbdb269917c84c19f20e5a Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 17:40:39 -0700 Subject: [PATCH 18/37] bug: fix bosminer config parsing. --- pyasic/config/pools.py | 13 +++++++------ pyasic/miners/backends/antminer.py | 23 +++++++++++------------ pyasic/miners/backends/bosminer.py | 2 +- pyasic/miners/backends/cgminer_avalon.py | 21 +++++++++++---------- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index 756f58be..b478b20e 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -127,7 +127,7 @@ class Pool(MinerConfigValue): return cls( url=toml_pool_conf["url"], user=toml_pool_conf["user"], - password=toml_pool_conf["pass"], + password=toml_pool_conf["password"], ) @@ -135,7 +135,7 @@ class Pool(MinerConfigValue): @dataclass class PoolGroup(MinerConfigValue): pools: list[Pool] = field(default_factory=list) - quota: int = 1 + quota: int = None name: str = None def __post_init__(self): @@ -204,14 +204,15 @@ class PoolGroup(MinerConfigValue): def as_bosminer(self, user_suffix: str = None) -> dict: if len(self.pools) > 0: - return { + conf = { "name": self.name, - "quota": self.quota, "pool": [ pool.as_bosminer(user_suffix=user_suffix) for pool in self.pools ], } - return {"name": "Group", "quota": 1, "pool": []} + if self.quota is not None: + conf["quota"] = self.quota + return {"name": "Group", "pool": []} @classmethod def from_api(cls, api_pool_list: list): @@ -240,7 +241,7 @@ class PoolGroup(MinerConfigValue): if toml_group_conf.get("pool") is not None: return cls( name=toml_group_conf["name"], - quota=toml_group_conf["quota"], + quota=toml_group_conf.get("quota"), pools=[Pool.from_bosminer(p) for p in toml_group_conf["pool"]], ) return cls() diff --git a/pyasic/miners/backends/antminer.py b/pyasic/miners/backends/antminer.py index dd532e86..eaf81f6e 100644 --- a/pyasic/miners/backends/antminer.py +++ b/pyasic/miners/backends/antminer.py @@ -85,18 +85,16 @@ class AntminerModern(BMMiner): async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: self.config = config - conf = config.as_am_modern(user_suffix=user_suffix) - data = await self.web.set_miner_conf(conf) - - if data: - if data.get("code") == "M000": - return - - for i in range(7): - data = await self.get_config() - if data.as_am_modern() == conf: - break - await asyncio.sleep(1) + await self.web.set_miner_conf(config.as_am_modern(user_suffix=user_suffix)) + # if data: + # if data.get("code") == "M000": + # return + # + # for i in range(7): + # data = await self.get_config() + # if data == self.config: + # break + # await asyncio.sleep(1) async def fault_light_on(self) -> bool: data = await self.web.blink(blink=True) @@ -353,6 +351,7 @@ class AntminerOld(CGMiner): return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: + self.config = config await self.web.set_miner_conf(config.as_am_old(user_suffix=user_suffix)) async def get_mac(self) -> Union[str, None]: diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index 68d1ee83..49d3642d 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -617,7 +617,7 @@ class BOSMiner(BaseMiner): offset = 0 if 3 in b_names: offset = 1 - elif 6 in b_names: + elif 6 in b_names or 7 in b_names or 8 in b_names: offset = 6 for hb in boards: _id = int(hb["name"]) - offset diff --git a/pyasic/miners/backends/cgminer_avalon.py b/pyasic/miners/backends/cgminer_avalon.py index 91c5a2e9..67815ddf 100644 --- a/pyasic/miners/backends/cgminer_avalon.py +++ b/pyasic/miners/backends/cgminer_avalon.py @@ -100,16 +100,17 @@ class CGMinerAvalon(CGMiner): return False async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: - self.config = config - return None - logging.debug(f"{self}: Sending config.") # noqa - This doesnt work... - conf = config.as_avalon(user_suffix=user_suffix) - try: - data = await self.api.ascset( # noqa - 0, "setpool", f"root,root,{conf}" - ) # this should work but doesn't - except APIError: - pass + pass + # self.config = config + # return None + # logging.debug(f"{self}: Sending config.") # noqa - This doesnt work... + # conf = config.as_avalon(user_suffix=user_suffix) + # try: + # data = await self.api.ascset( # noqa + # 0, "setpool", f"root,root,{conf}" + # ) # this should work but doesn't + # except APIError: + # pass # return data @staticmethod From 47c2eb9f0ea212a221d01d113ccd79bdb3a64336 Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 20:10:11 -0700 Subject: [PATCH 19/37] feature: use betterproto + grpclib. --- pyasic/miners/backends/bosminer.py | 25 +- pyasic/web/bosminer/__init__.py | 271 +- pyasic/web/bosminer/proto/__init__.py | 54 - pyasic/web/bosminer/proto/bos/__init__.py | 15 - pyasic/web/bosminer/proto/bos/v1/__init__.py | 15 - pyasic/web/bosminer/proto/bos/v1/actions.py | 23 - .../web/bosminer/proto/bos/v1/actions_pb2.py | 56 - .../proto/bos/v1/authentication_pb2.py | 36 - .../web/bosminer/proto/bos/v1/common_pb2.py | 26 - .../proto/bos/v1/configuration_pb2.py | 40 - .../bosminer/proto/bos/v1/constraints_pb2.py | 44 - .../web/bosminer/proto/bos/v1/cooling_pb2.py | 56 - .../web/bosminer/proto/bos/v1/license_pb2.py | 42 - pyasic/web/bosminer/proto/bos/v1/miner_pb2.py | 84 - .../web/bosminer/proto/bos/v1/performance.py | 110 - .../bosminer/proto/bos/v1/performance_pb2.py | 112 - pyasic/web/bosminer/proto/bos/v1/pool_pb2.py | 75 - pyasic/web/bosminer/proto/bos/v1/units_pb2.py | 61 - pyasic/web/bosminer/proto/bos/v1/work_pb2.py | 47 - pyasic/web/bosminer/proto/bos/version_pb2.py | 47 - pyasic/web/bosminer/proto/braiins/__init__.py | 0 .../bosminer/proto/braiins/bos/__init__.py | 80 + .../bosminer/proto/braiins/bos/v1/__init__.py | 3030 +++++++++++++++++ 23 files changed, 3238 insertions(+), 1111 deletions(-) delete mode 100644 pyasic/web/bosminer/proto/bos/__init__.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/__init__.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/actions.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/actions_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/authentication_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/common_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/configuration_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/constraints_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/cooling_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/license_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/miner_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/performance.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/performance_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/pool_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/units_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/v1/work_pb2.py delete mode 100644 pyasic/web/bosminer/proto/bos/version_pb2.py create mode 100644 pyasic/web/bosminer/proto/braiins/__init__.py create mode 100644 pyasic/web/bosminer/proto/braiins/bos/__init__.py create mode 100644 pyasic/web/bosminer/proto/braiins/bos/v1/__init__.py diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index 49d3642d..d575c2ff 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -22,6 +22,7 @@ import toml from pyasic.API.bosminer import BOSMinerAPI from pyasic.config import MinerConfig +from pyasic.config.mining import MiningModePowerTune from pyasic.data import Fan, HashBoard from pyasic.data.error_codes import BraiinsOSError, MinerErrorData from pyasic.errors import APIError @@ -316,16 +317,25 @@ class BOSMiner(BaseMiner): return self.config async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None: - """Configures miner with yaml config.""" logging.debug(f"{self}: Sending config.") self.config = config - toml_conf = config.as_bos( - model=self.model.replace(" (BOS)", ""), user_suffix=user_suffix - ) + + if self.web.grpc is not None: + await self._send_config_grpc(config, user_suffix) + else: + await self._send_config_bosminer(config, user_suffix) + + async def _send_config_grpc(self, config: MinerConfig, user_suffix: str = None): + mining_mode = config.mining_mode + + mining_mode + + async def _send_config_bosminer(self, config: MinerConfig, user_suffix: str = None): + toml_conf = config.as_bosminer(user_suffix=user_suffix) try: conn = await self._get_ssh_connection() - except ConnectionError: - return None + except ConnectionError as e: + raise APIError("SSH connection failed when sending config.") from e async with conn: # BBB check because bitmain suxx bbb_check = await conn.run( @@ -352,12 +362,13 @@ class BOSMiner(BaseMiner): logging.debug(f"{self}: BBB restarting bosminer.") await conn.run("/etc/init.d/S99bosminer start") + async def set_power_limit(self, wattage: int) -> bool: try: cfg = await self.get_config() if cfg is None: return False - cfg.autotuning_wattage = wattage + cfg.mining_mode = MiningModePowerTune(wattage) await self.send_config(cfg) except Exception as e: logging.warning(f"{self} set_power_limit: {e}") diff --git a/pyasic/web/bosminer/__init__.py b/pyasic/web/bosminer/__init__.py index c23bd45a..76b34f86 100644 --- a/pyasic/web/bosminer/__init__.py +++ b/pyasic/web/bosminer/__init__.py @@ -14,46 +14,17 @@ # limitations under the License. - # ------------------------------------------------------------------------------ import json -from datetime import datetime, timedelta -from typing import List, Union +from datetime import timedelta +from typing import Union -import grpc_requests import httpx -from google.protobuf.message import Message -from grpc import RpcError +from grpclib.client import Channel from pyasic import APIError, settings from pyasic.web import BaseWebAPI -from pyasic.web.bosminer.proto import ( - get_auth_service_descriptors, - get_service_descriptors, -) -from pyasic.web.bosminer.proto.bos.v1.actions_pb2 import ( # noqa: this will be defined - SetLocateDeviceStatusRequest, -) -from pyasic.web.bosminer.proto.bos.v1.authentication_pb2 import ( # noqa: this will be defined - SetPasswordRequest, -) -from pyasic.web.bosminer.proto.bos.v1.common_pb2 import ( # noqa: this will be defined - SaveAction, -) -from pyasic.web.bosminer.proto.bos.v1.cooling_pb2 import ( # noqa: this will be defined - SetImmersionModeRequest, -) -from pyasic.web.bosminer.proto.bos.v1.miner_pb2 import ( # noqa: this will be defined - DisableHashboardsRequest, - EnableHashboardsRequest, -) -from pyasic.web.bosminer.proto.bos.v1.performance_pb2 import ( # noqa: this will be defined - DecrementHashrateTargetRequest, - DecrementPowerTargetRequest, - IncrementHashrateTargetRequest, - IncrementPowerTargetRequest, - SetDefaultHashrateTargetRequest, - SetDefaultPowerTargetRequest, - SetHashrateTargetRequest, - SetPowerTargetRequest, -) +from .proto.braiins.bos import * +from .proto.braiins.bos.v1 import * +from betterproto import Message class BOSMinerWebAPI(BaseWebAPI): @@ -286,6 +257,20 @@ class BOSMinerLuCIAPI: return await self.send_command("/cgi-bin/luci/admin/miner/api_status") +class BOSMinerGRPCStub( + ApiVersionServiceStub, + AuthenticationServiceStub, + CoolingServiceStub, + ConfigurationServiceStub, + MinerServiceStub, + PoolServiceStub, + LicenseServiceStub, + ActionsServiceStub, + PerformanceServiceStub, +): + pass + + class BOSMinerGRPCAPI: def __init__(self, ip: str, pwd: str): self.ip = ip @@ -321,25 +306,16 @@ class BOSMinerGRPCAPI: ignore_errors: bool = False, auth: bool = True, ) -> dict: - service, method = command.split("/") metadata = [] if auth: metadata.append(("authorization", await self.auth())) - async with grpc_requests.StubAsyncClient( - f"{self.ip}:50051", service_descriptors=get_service_descriptors() - ) as client: - await client.register_all_service() - try: - return await client.request( - service, - method, - request=message, - metadata=metadata, - ) - except RpcError as e: - if ignore_errors: - return {} - raise APIError(e._details) + async with Channel(self.ip, 50051) as c: + endpoint = getattr(BOSMinerGRPCStub(c), command) + if endpoint is None: + if not ignore_errors: + raise APIError(f"Command not found - {endpoint}") + return {} + return (await endpoint(message, metadata=metadata)).to_pydict() async def auth(self): if self._auth is not None and self._auth_time - datetime.now() < timedelta( @@ -350,100 +326,85 @@ class BOSMinerGRPCAPI: return self._auth async def _get_auth(self): - async with grpc_requests.StubAsyncClient( - f"{self.ip}:50051", service_descriptors=get_auth_service_descriptors() - ) as client: - await client.register_all_service() - method_meta = client.get_method_meta( - "braiins.bos.v1.AuthenticationService", "Login" - ) - _request = method_meta.method_type.request_parser( - {"username": self.username, "password": self.pwd}, - method_meta.input_type, - ) - metadata = await method_meta.handler(_request).initial_metadata() - - for key, value in metadata: - if key == "authorization": - self._auth = value + async with Channel(self.ip, 50051) as c: + req = LoginRequest(username=self.username, password=self.pwd) + async with c.request( + "/braiins.bos.v1.AuthenticationService/Login", + grpclib.const.Cardinality.UNARY_UNARY, + type(req), + LoginResponse, + ) as stream: + await stream.send_message(req, end=True) + await stream.recv_initial_metadata() + auth = stream.initial_metadata.get("authorization") + if auth is not None: + self._auth = auth self._auth_time = datetime.now() return self._auth async def get_api_version(self): return await self.send_command( - "braiins.bos.ApiVersionService/GetApiVersion", auth=False + "get_api_version", ApiVersionRequest(), auth=False ) async def start(self): - return await self.send_command("braiins.bos.v1.ActionsService/Start") + return await self.send_command("start", StartRequest()) async def stop(self): - return await self.send_command("braiins.bos.v1.ActionsService/Stop") + return await self.send_command("stop", StopRequest()) async def pause_mining(self): - return await self.send_command("braiins.bos.v1.ActionsService/PauseMining") + return await self.send_command("pause_mining", PauseMiningRequest()) async def resume_mining(self): - return await self.send_command("braiins.bos.v1.ActionsService/ResumeMining") + return await self.send_command("resume_mining", ResumeMiningRequest()) async def restart(self): - return await self.send_command("braiins.bos.v1.ActionsService/Restart") + return await self.send_command("restart", RestartRequest()) async def reboot(self): - return await self.send_command("braiins.bos.v1.ActionsService/Reboot") + return await self.send_command("reboot", RebootRequest()) async def set_locate_device_status(self, enable: bool): - message = SetLocateDeviceStatusRequest() - message.enable = enable return await self.send_command( - "braiins.bos.v1.ActionsService/SetLocateDeviceStatus", message=message + "set_locate_device_status", SetLocateDeviceStatusRequest(enable=enable) ) async def get_locate_device_status(self): - return await self.send_command( - "braiins.bos.v1.ActionsService/GetLocateDeviceStatus" - ) + return await self.send_command("get_locate_device_status") async def set_password(self, password: str = None): - message = SetPasswordRequest() - if password: - message.password = password return await self.send_command( - "braiins.bos.v1.AuthenticationService/SetPassword", message=message + "set_password", SetPasswordRequest(password=password) ) async def get_cooling_state(self): - return await self.send_command("braiins.bos.v1.CoolingService/GetCoolingState") + return await self.send_command("get_cooling_state", GetCoolingStateRequest()) async def set_immersion_mode( self, enable: bool, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = SetImmersionModeRequest() - message.enable = enable - message.save_action = save_action return await self.send_command( - "braiins.bos.v1.CoolingService/SetImmersionMode", message=message + "set_immersion_mode", + SetImmersionModeRequest( + enable_immersion_mode=enable, save_action=save_action + ), ) async def get_tuner_state(self): - return await self.send_command( - "braiins.bos.v1.PerformanceService/GetTunerState" - ) + return await self.send_command("get_tuner_state") async def list_target_profiles(self): - return await self.send_command( - "braiins.bos.v1.PerformanceService/ListTargetProfiles" - ) + return await self.send_command("list_target_profiles") async def set_default_power_target( self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY ): - message = SetDefaultPowerTargetRequest() - message.save_action = save_action return await self.send_command( - "braiins.bos.v1.PerformanceService/SetDefaultPowerTarget", message=message + "set_default_power_target", + message=SetDefaultPowerTargetRequest(save_action=save_action), ) async def set_power_target( @@ -451,11 +412,11 @@ class BOSMinerGRPCAPI: power_target: int, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = SetPowerTargetRequest() - message.power_target.watt = power_target - message.save_action = save_action return await self.send_command( - "braiins.bos.v1.PerformanceService/SetPowerTarget", message=message + "set_power_target", + SetPowerTargetRequest( + power_target=Power(watt=power_target), save_action=save_action + ), ) async def increment_power_target( @@ -463,12 +424,12 @@ class BOSMinerGRPCAPI: power_target_increment: int, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = IncrementPowerTargetRequest() - message.power_target_increment.watt = power_target_increment - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.PerformanceService/IncrementPowerTarget", message=message + "increment_power_target", + message=IncrementPowerTargetRequest( + power_target_increment=Power(watt=power_target_increment), + save_action=save_action, + ), ) async def decrement_power_target( @@ -476,37 +437,33 @@ class BOSMinerGRPCAPI: power_target_decrement: int, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = DecrementPowerTargetRequest() - message.power_target_decrement.watt = power_target_decrement - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.PerformanceService/DecrementPowerTarget", - message=message, + "decrement_power_target", + message=DecrementPowerTargetRequest( + power_target_decrement=Power(watt=power_target_decrement), + save_action=save_action, + ), ) async def set_default_hashrate_target( self, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY ): - message = SetDefaultHashrateTargetRequest() - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget", - message=message, + "set_default_hashrate_target", + message=SetDefaultHashrateTargetRequest(save_action=save_action), ) async def set_hashrate_target( self, - hashrate_target: int, + hashrate_target: float, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = SetHashrateTargetRequest() - message.hashrate_target.terahash_per_second = hashrate_target - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.PerformanceService/SetHashrateTarget", message=message + "set_hashrate_target", + SetHashrateTargetRequest( + hashrate_target=TeraHashrate(terahash_per_second=hashrate_target), + save_action=save_action, + ), ) async def increment_hashrate_target( @@ -514,15 +471,14 @@ class BOSMinerGRPCAPI: hashrate_target_increment: int, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = IncrementHashrateTargetRequest() - message.hashrate_target_increment.terahash_per_second = ( - hashrate_target_increment - ) - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.PerformanceService/IncrementHashrateTarget", - message=message, + "increment_hashrate_target", + IncrementHashrateTargetRequest( + hashrate_target_increment=TeraHashrate( + terahash_per_second=hashrate_target_increment + ), + save_action=save_action, + ), ) async def decrement_hashrate_target( @@ -530,18 +486,19 @@ class BOSMinerGRPCAPI: hashrate_target_decrement: int, save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = DecrementHashrateTargetRequest() - message.hashrate_target_decrement.terahash_per_second = ( - hashrate_target_decrement - ) - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.PerformanceService/DecrementHashrateTarget", - message=message, + "decrement_hashrate_target", + DecrementHashrateTargetRequest( + hashrate_target_decrement=TeraHashrate( + terahash_per_second=hashrate_target_decrement + ), + save_action=save_action, + ), ) - async def set_dps(self): + async def set_dps( + self, + ): raise NotImplementedError return await self.send_command("braiins.bos.v1.PerformanceService/SetDPS") @@ -553,11 +510,11 @@ class BOSMinerGRPCAPI: async def get_active_performance_mode(self): return await self.send_command( - "braiins.bos.v1.PerformanceService/GetActivePerformanceMode" + "get_active_performance_mode", GetPerformanceModeRequest() ) async def get_pool_groups(self): - return await self.send_command("braiins.bos.v1.PoolService/GetPoolGroups") + return await self.send_command("get_pool_groups", GetPoolGroupsRequest()) async def create_pool_group(self): raise NotImplementedError @@ -573,43 +530,39 @@ class BOSMinerGRPCAPI: async def get_miner_configuration(self): return await self.send_command( - "braiins.bos.v1.ConfigurationService/GetMinerConfiguration" + "get_miner_configuration", GetMinerConfigurationRequest() ) async def get_constraints(self): return await self.send_command( - "braiins.bos.v1.ConfigurationService/GetConstraints" + "get_constraints", GetConstraintsRequest() ) async def get_license_state(self): - return await self.send_command("braiins.bos.v1.LicenseService/GetLicenseState") + return await self.send_command("get_license_state", GetLicenseStateRequest()) async def get_miner_status(self): - return await self.send_command("braiins.bos.v1.MinerService/GetMinerStatus") + return await self.send_command("get_miner_status", GetMinerStatusRequest()) async def get_miner_details(self): - return await self.send_command("braiins.bos.v1.MinerService/GetMinerDetails") + return await self.send_command("get_miner_details", GetMinerDetailsRequest()) async def get_miner_stats(self): - return await self.send_command("braiins.bos.v1.MinerService/GetMinerStats") + return await self.send_command("get_miner_stats", GetMinerStatsRequest()) async def get_hashboards(self): - return await self.send_command("braiins.bos.v1.MinerService/GetHashboards") + return await self.send_command("get_hashboards", GetHashboardsRequest()) async def get_support_archive(self): - return await self.send_command("braiins.bos.v1.MinerService/GetSupportArchive") + return await self.send_command("get_support_archive", GetSupportArchiveRequest()) async def enable_hashboards( self, hashboard_ids: List[str], save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = EnableHashboardsRequest() - message.hashboard_ids[:] = hashboard_ids - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.MinerService/EnableHashboards", message=message + "enable_hashboards", EnableHashboardsRequest(hashboard_ids=hashboard_ids, save_action=save_action) ) async def disable_hashboards( @@ -617,10 +570,6 @@ class BOSMinerGRPCAPI: hashboard_ids: List[str], save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): - message = DisableHashboardsRequest() - message.hashboard_ids[:] = hashboard_ids - message.save_action = save_action - return await self.send_command( - "braiins.bos.v1.MinerService/DisableHashboards", message=message + "disable_hashboards", DisableHashboardsRequest(hashboard_ids=hashboard_ids, save_action=save_action) ) diff --git a/pyasic/web/bosminer/proto/__init__.py b/pyasic/web/bosminer/proto/__init__.py index 9ebaf8b9..e69de29b 100644 --- a/pyasic/web/bosminer/proto/__init__.py +++ b/pyasic/web/bosminer/proto/__init__.py @@ -1,54 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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 __future__ import annotations - -from .bos import version_pb2 -from .bos.v1 import ( - actions_pb2, - authentication_pb2, - common_pb2, - configuration_pb2, - constraints_pb2, - cooling_pb2, - license_pb2, - miner_pb2, - performance_pb2, - pool_pb2, - units_pb2, - work_pb2, -) - - -def get_service_descriptors(): - return [ - *version_pb2.DESCRIPTOR.services_by_name.values(), - *authentication_pb2.DESCRIPTOR.services_by_name.values(), - *actions_pb2.DESCRIPTOR.services_by_name.values(), - *common_pb2.DESCRIPTOR.services_by_name.values(), - *configuration_pb2.DESCRIPTOR.services_by_name.values(), - *constraints_pb2.DESCRIPTOR.services_by_name.values(), - *cooling_pb2.DESCRIPTOR.services_by_name.values(), - *license_pb2.DESCRIPTOR.services_by_name.values(), - *miner_pb2.DESCRIPTOR.services_by_name.values(), - *performance_pb2.DESCRIPTOR.services_by_name.values(), - *pool_pb2.DESCRIPTOR.services_by_name.values(), - *units_pb2.DESCRIPTOR.services_by_name.values(), - *work_pb2.DESCRIPTOR.services_by_name.values(), - ] - - -def get_auth_service_descriptors(): - return authentication_pb2.DESCRIPTOR.services_by_name.values() diff --git a/pyasic/web/bosminer/proto/bos/__init__.py b/pyasic/web/bosminer/proto/bos/__init__.py deleted file mode 100644 index d3221b3d..00000000 --- a/pyasic/web/bosminer/proto/bos/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ diff --git a/pyasic/web/bosminer/proto/bos/v1/__init__.py b/pyasic/web/bosminer/proto/bos/v1/__init__.py deleted file mode 100644 index d3221b3d..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ diff --git a/pyasic/web/bosminer/proto/bos/v1/actions.py b/pyasic/web/bosminer/proto/bos/v1/actions.py deleted file mode 100644 index 11af45b2..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/actions.py +++ /dev/null @@ -1,23 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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 enum import Enum - - -class SaveAction(Enum): - UNSPECIFIED = "SaveAction.SAVE_ACTION_UNSPECIFIED" - SAVE = "SaveAction.SAVE_ACTION_SAVE" - SAVE_AND_APPLY = "SaveAction.SAVE_ACTION_SAVE_AND_APPLY" - SAVE_AND_FORCE_APPLY = "SaveAction.SAVE_ACTION_SAVE_AND_FORCE_APPLY" diff --git a/pyasic/web/bosminer/proto/bos/v1/actions_pb2.py b/pyasic/web/bosminer/proto/bos/v1/actions_pb2.py deleted file mode 100644 index 4c36707d..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/actions_pb2.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/actions.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x14\x62os/v1/actions.proto\x12\x0e\x62raiins.bos.v1"\x0e\n\x0cStartRequest"(\n\rStartResponse\x12\x17\n\x0f\x61lready_running\x18\x01 \x01(\x08"\x10\n\x0eRestartRequest"*\n\x0fRestartResponse\x12\x17\n\x0f\x61lready_running\x18\x01 \x01(\x08"\x0f\n\rRebootRequest"\x10\n\x0eRebootResponse"\r\n\x0bStopRequest"\'\n\x0cStopResponse\x12\x17\n\x0f\x61lready_stopped\x18\x01 \x01(\x08"\x14\n\x12PauseMiningRequest"-\n\x13PauseMiningResponse\x12\x16\n\x0e\x61lready_paused\x18\x01 \x01(\x08"\x15\n\x13ResumeMiningRequest".\n\x14ResumeMiningResponse\x12\x16\n\x0e\x61lready_mining\x18\x01 \x01(\x08".\n\x1cSetLocateDeviceStatusRequest\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08"-\n\x1aLocateDeviceStatusResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08"\x1e\n\x1cGetLocateDeviceStatusRequest2\xc7\x05\n\x0e\x41\x63tionsService\x12\x44\n\x05Start\x12\x1c.braiins.bos.v1.StartRequest\x1a\x1d.braiins.bos.v1.StartResponse\x12\x41\n\x04Stop\x12\x1b.braiins.bos.v1.StopRequest\x1a\x1c.braiins.bos.v1.StopResponse\x12V\n\x0bPauseMining\x12".braiins.bos.v1.PauseMiningRequest\x1a#.braiins.bos.v1.PauseMiningResponse\x12Y\n\x0cResumeMining\x12#.braiins.bos.v1.ResumeMiningRequest\x1a$.braiins.bos.v1.ResumeMiningResponse\x12J\n\x07Restart\x12\x1e.braiins.bos.v1.RestartRequest\x1a\x1f.braiins.bos.v1.RestartResponse\x12G\n\x06Reboot\x12\x1d.braiins.bos.v1.RebootRequest\x1a\x1e.braiins.bos.v1.RebootResponse\x12q\n\x15SetLocateDeviceStatus\x12,.braiins.bos.v1.SetLocateDeviceStatusRequest\x1a*.braiins.bos.v1.LocateDeviceStatusResponse\x12q\n\x15GetLocateDeviceStatus\x12,.braiins.bos.v1.GetLocateDeviceStatusRequest\x1a*.braiins.bos.v1.LocateDeviceStatusResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.actions_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_STARTREQUEST"]._serialized_start = 40 - _globals["_STARTREQUEST"]._serialized_end = 54 - _globals["_STARTRESPONSE"]._serialized_start = 56 - _globals["_STARTRESPONSE"]._serialized_end = 96 - _globals["_RESTARTREQUEST"]._serialized_start = 98 - _globals["_RESTARTREQUEST"]._serialized_end = 114 - _globals["_RESTARTRESPONSE"]._serialized_start = 116 - _globals["_RESTARTRESPONSE"]._serialized_end = 158 - _globals["_REBOOTREQUEST"]._serialized_start = 160 - _globals["_REBOOTREQUEST"]._serialized_end = 175 - _globals["_REBOOTRESPONSE"]._serialized_start = 177 - _globals["_REBOOTRESPONSE"]._serialized_end = 193 - _globals["_STOPREQUEST"]._serialized_start = 195 - _globals["_STOPREQUEST"]._serialized_end = 208 - _globals["_STOPRESPONSE"]._serialized_start = 210 - _globals["_STOPRESPONSE"]._serialized_end = 249 - _globals["_PAUSEMININGREQUEST"]._serialized_start = 251 - _globals["_PAUSEMININGREQUEST"]._serialized_end = 271 - _globals["_PAUSEMININGRESPONSE"]._serialized_start = 273 - _globals["_PAUSEMININGRESPONSE"]._serialized_end = 318 - _globals["_RESUMEMININGREQUEST"]._serialized_start = 320 - _globals["_RESUMEMININGREQUEST"]._serialized_end = 341 - _globals["_RESUMEMININGRESPONSE"]._serialized_start = 343 - _globals["_RESUMEMININGRESPONSE"]._serialized_end = 389 - _globals["_SETLOCATEDEVICESTATUSREQUEST"]._serialized_start = 391 - _globals["_SETLOCATEDEVICESTATUSREQUEST"]._serialized_end = 437 - _globals["_LOCATEDEVICESTATUSRESPONSE"]._serialized_start = 439 - _globals["_LOCATEDEVICESTATUSRESPONSE"]._serialized_end = 484 - _globals["_GETLOCATEDEVICESTATUSREQUEST"]._serialized_start = 486 - _globals["_GETLOCATEDEVICESTATUSREQUEST"]._serialized_end = 516 - _globals["_ACTIONSSERVICE"]._serialized_start = 519 - _globals["_ACTIONSSERVICE"]._serialized_end = 1230 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/authentication_pb2.py b/pyasic/web/bosminer/proto/bos/v1/authentication_pb2.py deleted file mode 100644 index 245a60cd..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/authentication_pb2.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/authentication.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1b\x62os/v1/authentication.proto\x12\x0e\x62raiins.bos.v1"2\n\x0cLoginRequest\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t"\x0f\n\rLoginResponse"8\n\x12SetPasswordRequest\x12\x15\n\x08password\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x0b\n\t_password"\x15\n\x13SetPasswordResponse2\xb5\x01\n\x15\x41uthenticationService\x12\x44\n\x05Login\x12\x1c.braiins.bos.v1.LoginRequest\x1a\x1d.braiins.bos.v1.LoginResponse\x12V\n\x0bSetPassword\x12".braiins.bos.v1.SetPasswordRequest\x1a#.braiins.bos.v1.SetPasswordResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages( - DESCRIPTOR, "bos.v1.authentication_pb2", _globals -) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_LOGINREQUEST"]._serialized_start = 47 - _globals["_LOGINREQUEST"]._serialized_end = 97 - _globals["_LOGINRESPONSE"]._serialized_start = 99 - _globals["_LOGINRESPONSE"]._serialized_end = 114 - _globals["_SETPASSWORDREQUEST"]._serialized_start = 116 - _globals["_SETPASSWORDREQUEST"]._serialized_end = 172 - _globals["_SETPASSWORDRESPONSE"]._serialized_start = 174 - _globals["_SETPASSWORDRESPONSE"]._serialized_end = 195 - _globals["_AUTHENTICATIONSERVICE"]._serialized_start = 198 - _globals["_AUTHENTICATIONSERVICE"]._serialized_end = 379 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/common_pb2.py b/pyasic/web/bosminer/proto/bos/v1/common_pb2.py deleted file mode 100644 index 9dd91256..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/common_pb2.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/common.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b"\n\x13\x62os/v1/common.proto\x12\x0e\x62raiins.bos.v1*\x85\x01\n\nSaveAction\x12\x1b\n\x17SAVE_ACTION_UNSPECIFIED\x10\x00\x12\x14\n\x10SAVE_ACTION_SAVE\x10\x01\x12\x1e\n\x1aSAVE_ACTION_SAVE_AND_APPLY\x10\x02\x12$\n SAVE_ACTION_SAVE_AND_FORCE_APPLY\x10\x03\x62\x06proto3" -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.common_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_SAVEACTION"]._serialized_start = 40 - _globals["_SAVEACTION"]._serialized_end = 173 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/configuration_pb2.py b/pyasic/web/bosminer/proto/bos/v1/configuration_pb2.py deleted file mode 100644 index f44c4dd8..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/configuration_pb2.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/configuration.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from ...bos.v1 import cooling_pb2 as bos_dot_v1_dot_cooling__pb2 -from ...bos.v1 import performance_pb2 as bos_dot_v1_dot_performance__pb2 -from ...bos.v1 import pool_pb2 as bos_dot_v1_dot_pool__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1a\x62os/v1/configuration.proto\x12\x0e\x62raiins.bos.v1\x1a\x14\x62os/v1/cooling.proto\x1a\x18\x62os/v1/performance.proto\x1a\x11\x62os/v1/pool.proto"\x1e\n\x1cGetMinerConfigurationRequest"\xc6\x02\n\x1dGetMinerConfigurationResponse\x12;\n\x0bpool_groups\x18\x01 \x03(\x0b\x32&.braiins.bos.v1.PoolGroupConfiguration\x12\x39\n\x0btemperature\x18\x02 \x01(\x0b\x32$.braiins.bos.v1.CoolingConfiguration\x12\x31\n\x05tuner\x18\x03 \x01(\x0b\x32".braiins.bos.v1.TunerConfiguration\x12-\n\x03\x64ps\x18\x04 \x01(\x0b\x32 .braiins.bos.v1.DPSConfiguration\x12K\n\x10hashboard_config\x18\x05 \x01(\x0b\x32\x31.braiins.bos.v1.HashboardPerformanceConfiguration"\x17\n\x15GetConstraintsRequest"\x95\x02\n\x16GetConstraintsResponse\x12;\n\x11tuner_constraints\x18\x01 \x01(\x0b\x32 .braiins.bos.v1.TunerConstraints\x12?\n\x13\x63ooling_constraints\x18\x02 \x01(\x0b\x32".braiins.bos.v1.CoolingConstraints\x12\x37\n\x0f\x64ps_constraints\x18\x03 \x01(\x0b\x32\x1e.braiins.bos.v1.DPSConstraints\x12\x44\n\x16hashboards_constraints\x18\x04 \x01(\x0b\x32$.braiins.bos.v1.HashboardConstraints2\xed\x01\n\x14\x43onfigurationService\x12t\n\x15GetMinerConfiguration\x12,.braiins.bos.v1.GetMinerConfigurationRequest\x1a-.braiins.bos.v1.GetMinerConfigurationResponse\x12_\n\x0eGetConstraints\x12%.braiins.bos.v1.GetConstraintsRequest\x1a&.braiins.bos.v1.GetConstraintsResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages( - DESCRIPTOR, "bos.v1.configuration_pb2", _globals -) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_GETMINERCONFIGURATIONREQUEST"]._serialized_start = 113 - _globals["_GETMINERCONFIGURATIONREQUEST"]._serialized_end = 143 - _globals["_GETMINERCONFIGURATIONRESPONSE"]._serialized_start = 146 - _globals["_GETMINERCONFIGURATIONRESPONSE"]._serialized_end = 472 - _globals["_GETCONSTRAINTSREQUEST"]._serialized_start = 474 - _globals["_GETCONSTRAINTSREQUEST"]._serialized_end = 497 - _globals["_GETCONSTRAINTSRESPONSE"]._serialized_start = 500 - _globals["_GETCONSTRAINTSRESPONSE"]._serialized_end = 777 - _globals["_CONFIGURATIONSERVICE"]._serialized_start = 780 - _globals["_CONFIGURATIONSERVICE"]._serialized_end = 1017 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/constraints_pb2.py b/pyasic/web/bosminer/proto/bos/v1/constraints_pb2.py deleted file mode 100644 index 58ee8800..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/constraints_pb2.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/constraints.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x18\x62os/v1/constraints.proto\x12\x0e\x62raiins.bos.v1\x1a\x12\x62os/v1/units.proto">\n\x11UInt32Constraints\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\r\x12\x0b\n\x03min\x18\x02 \x01(\r\x12\x0b\n\x03max\x18\x03 \x01(\r">\n\x11\x44oubleConstraints\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x01\x12\x0b\n\x03min\x18\x02 \x01(\x01\x12\x0b\n\x03max\x18\x03 \x01(\x01"\x82\x01\n\x10PowerConstraints\x12&\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12"\n\x03min\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12"\n\x03max\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x9a\x01\n\x13HashrateConstraints\x12-\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12)\n\x03min\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12)\n\x03max\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x9a\x01\n\x16TemperatureConstraints\x12,\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12(\n\x03min\x18\x02 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12(\n\x03max\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature"$\n\x11\x42ooleanConstraint\x12\x0f\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x08"\x85\x01\n\x13\x44urationConstraints\x12&\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Hours\x12"\n\x03min\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Hours\x12"\n\x03max\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.Hours"\x92\x01\n\x14\x46requencyConstraints\x12*\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12&\n\x03min\x18\x02 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12&\n\x03max\x18\x03 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency"\x8a\x01\n\x12VoltageConstraints\x12(\n\x07\x64\x65\x66\x61ult\x18\x01 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12$\n\x03min\x18\x02 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12$\n\x03max\x18\x03 \x01(\x0b\x32\x17.braiins.bos.v1.Voltageb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.constraints_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_UINT32CONSTRAINTS"]._serialized_start = 64 - _globals["_UINT32CONSTRAINTS"]._serialized_end = 126 - _globals["_DOUBLECONSTRAINTS"]._serialized_start = 128 - _globals["_DOUBLECONSTRAINTS"]._serialized_end = 190 - _globals["_POWERCONSTRAINTS"]._serialized_start = 193 - _globals["_POWERCONSTRAINTS"]._serialized_end = 323 - _globals["_HASHRATECONSTRAINTS"]._serialized_start = 326 - _globals["_HASHRATECONSTRAINTS"]._serialized_end = 480 - _globals["_TEMPERATURECONSTRAINTS"]._serialized_start = 483 - _globals["_TEMPERATURECONSTRAINTS"]._serialized_end = 637 - _globals["_BOOLEANCONSTRAINT"]._serialized_start = 639 - _globals["_BOOLEANCONSTRAINT"]._serialized_end = 675 - _globals["_DURATIONCONSTRAINTS"]._serialized_start = 678 - _globals["_DURATIONCONSTRAINTS"]._serialized_end = 811 - _globals["_FREQUENCYCONSTRAINTS"]._serialized_start = 814 - _globals["_FREQUENCYCONSTRAINTS"]._serialized_end = 960 - _globals["_VOLTAGECONSTRAINTS"]._serialized_start = 963 - _globals["_VOLTAGECONSTRAINTS"]._serialized_end = 1101 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/cooling_pb2.py b/pyasic/web/bosminer/proto/bos/v1/cooling_pb2.py deleted file mode 100644 index d72f2ea0..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/cooling_pb2.py +++ /dev/null @@ -1,56 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/cooling.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from ...bos.v1 import common_pb2 as bos_dot_v1_dot_common__pb2 -from ...bos.v1 import constraints_pb2 as bos_dot_v1_dot_constraints__pb2 -from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x14\x62os/v1/cooling.proto\x12\x0e\x62raiins.bos.v1\x1a\x13\x62os/v1/common.proto\x1a\x18\x62os/v1/constraints.proto\x1a\x12\x62os/v1/units.proto"\xbc\x01\n\x0f\x43oolingAutoMode\x12\x37\n\x12target_temperature\x18\x01 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12\x34\n\x0fhot_temperature\x18\x02 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12:\n\x15\x64\x61ngerous_temperature\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature"\xb7\x01\n\x11\x43oolingManualMode\x12\x1c\n\x0f\x66\x61n_speed_ratio\x18\x01 \x01(\x01H\x00\x88\x01\x01\x12\x34\n\x0fhot_temperature\x18\x02 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12:\n\x15\x64\x61ngerous_temperature\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.TemperatureB\x12\n\x10_fan_speed_ratio"G\n\x13\x43oolingDisabledMode\x12\x1c\n\x0f\x66\x61n_speed_ratio\x18\x01 \x01(\x01H\x00\x88\x01\x01\x42\x12\n\x10_fan_speed_ratio"\xfb\x01\n\x14\x43oolingConfiguration\x12"\n\x15minimum_required_fans\x18\x01 \x01(\rH\x01\x88\x01\x01\x12/\n\x04\x61uto\x18\x02 \x01(\x0b\x32\x1f.braiins.bos.v1.CoolingAutoModeH\x00\x12\x33\n\x06manual\x18\x03 \x01(\x0b\x32!.braiins.bos.v1.CoolingManualModeH\x00\x12\x37\n\x08\x64isabled\x18\x04 \x01(\x0b\x32#.braiins.bos.v1.CoolingDisabledModeH\x00\x42\x06\n\x04modeB\x18\n\x16_minimum_required_fans"\x99\x03\n\x12\x43oolingConstraints\x12\x39\n\x14\x64\x65\x66\x61ult_cooling_mode\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.CoolingMode\x12\x42\n\x12target_temperature\x18\x02 \x01(\x0b\x32&.braiins.bos.v1.TemperatureConstraints\x12?\n\x0fhot_temperature\x18\x03 \x01(\x0b\x32&.braiins.bos.v1.TemperatureConstraints\x12\x45\n\x15\x64\x61ngerous_temperature\x18\x04 \x01(\x0b\x32&.braiins.bos.v1.TemperatureConstraints\x12:\n\x0f\x66\x61n_speed_ratio\x18\x05 \x01(\x0b\x32!.braiins.bos.v1.DoubleConstraints\x12@\n\x15minimum_required_fans\x18\x06 \x01(\x0b\x32!.braiins.bos.v1.UInt32Constraints"s\n\x08\x46\x61nState\x12\x15\n\x08position\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x0b\n\x03rpm\x18\x02 \x01(\r\x12\x1f\n\x12target_speed_ratio\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x0b\n\t_positionB\x15\n\x13_target_speed_ratio"\x8f\x01\n\x11TemperatureSensor\x12\x0f\n\x02id\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x30\n\x08location\x18\x02 \x01(\x0e\x32\x1e.braiins.bos.v1.SensorLocation\x12\x30\n\x0btemperature\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.TemperatureB\x05\n\x03_id"\x18\n\x16GetCoolingStateRequest"\x81\x01\n\x17GetCoolingStateResponse\x12&\n\x04\x66\x61ns\x18\x01 \x03(\x0b\x32\x18.braiins.bos.v1.FanState\x12>\n\x13highest_temperature\x18\x02 \x01(\x0b\x32!.braiins.bos.v1.TemperatureSensor"i\n\x17SetImmersionModeRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x1d\n\x15\x65nable_immersion_mode\x18\x02 \x01(\x08"2\n\x18SetImmersionModeResponse\x12\x16\n\x0eimmersion_mode\x18\x01 \x01(\x08*v\n\x0b\x43oolingMode\x12\x1c\n\x18\x43OOLING_MODE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x43OOLING_MODE_AUTO\x10\x01\x12\x17\n\x13\x43OOLING_MODE_MANUAL\x10\x02\x12\x19\n\x15\x43OOLING_MODE_DISABLED\x10\x03*d\n\x0eSensorLocation\x12\x1f\n\x1bSENSOR_LOCATION_UNSPECIFIED\x10\x00\x12\x18\n\x14SENSOR_LOCATION_CHIP\x10\x01\x12\x17\n\x13SENSOR_LOCATION_PCB\x10\x02\x32\xdb\x01\n\x0e\x43oolingService\x12\x62\n\x0fGetCoolingState\x12&.braiins.bos.v1.GetCoolingStateRequest\x1a\'.braiins.bos.v1.GetCoolingStateResponse\x12\x65\n\x10SetImmersionMode\x12\'.braiins.bos.v1.SetImmersionModeRequest\x1a(.braiins.bos.v1.SetImmersionModeResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.cooling_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_COOLINGMODE"]._serialized_start = 1803 - _globals["_COOLINGMODE"]._serialized_end = 1921 - _globals["_SENSORLOCATION"]._serialized_start = 1923 - _globals["_SENSORLOCATION"]._serialized_end = 2023 - _globals["_COOLINGAUTOMODE"]._serialized_start = 108 - _globals["_COOLINGAUTOMODE"]._serialized_end = 296 - _globals["_COOLINGMANUALMODE"]._serialized_start = 299 - _globals["_COOLINGMANUALMODE"]._serialized_end = 482 - _globals["_COOLINGDISABLEDMODE"]._serialized_start = 484 - _globals["_COOLINGDISABLEDMODE"]._serialized_end = 555 - _globals["_COOLINGCONFIGURATION"]._serialized_start = 558 - _globals["_COOLINGCONFIGURATION"]._serialized_end = 809 - _globals["_COOLINGCONSTRAINTS"]._serialized_start = 812 - _globals["_COOLINGCONSTRAINTS"]._serialized_end = 1221 - _globals["_FANSTATE"]._serialized_start = 1223 - _globals["_FANSTATE"]._serialized_end = 1338 - _globals["_TEMPERATURESENSOR"]._serialized_start = 1341 - _globals["_TEMPERATURESENSOR"]._serialized_end = 1484 - _globals["_GETCOOLINGSTATEREQUEST"]._serialized_start = 1486 - _globals["_GETCOOLINGSTATEREQUEST"]._serialized_end = 1510 - _globals["_GETCOOLINGSTATERESPONSE"]._serialized_start = 1513 - _globals["_GETCOOLINGSTATERESPONSE"]._serialized_end = 1642 - _globals["_SETIMMERSIONMODEREQUEST"]._serialized_start = 1644 - _globals["_SETIMMERSIONMODEREQUEST"]._serialized_end = 1749 - _globals["_SETIMMERSIONMODERESPONSE"]._serialized_start = 1751 - _globals["_SETIMMERSIONMODERESPONSE"]._serialized_end = 1801 - _globals["_COOLINGSERVICE"]._serialized_start = 2026 - _globals["_COOLINGSERVICE"]._serialized_end = 2245 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/license_pb2.py b/pyasic/web/bosminer/proto/bos/v1/license_pb2.py deleted file mode 100644 index f70f9ae1..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/license_pb2.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/license.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x14\x62os/v1/license.proto\x12\x0e\x62raiins.bos.v1\x1a\x12\x62os/v1/units.proto")\n\x0bNoneLicense\x12\x1a\n\x12time_to_restricted\x18\x01 \x01(\r"\x10\n\x0eLimitedLicense"\x9a\x01\n\x0cValidLicense\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.LicenseType\x12\x15\n\rcontract_name\x18\x02 \x01(\t\x12\x1a\n\x12time_to_restricted\x18\x03 \x01(\r\x12,\n\x07\x64\x65v_fee\x18\x04 \x01(\x0b\x32\x1b.braiins.bos.v1.BasesPoints"\x80\x01\n\x0e\x45xpiredLicense\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.LicenseType\x12\x15\n\rcontract_name\x18\x02 \x01(\t\x12,\n\x07\x64\x65v_fee\x18\x03 \x01(\x0b\x32\x1b.braiins.bos.v1.BasesPoints"\x18\n\x16GetLicenseStateRequest"\xe4\x01\n\x17GetLicenseStateResponse\x12+\n\x04none\x18\x01 \x01(\x0b\x32\x1b.braiins.bos.v1.NoneLicenseH\x00\x12\x31\n\x07limited\x18\x02 \x01(\x0b\x32\x1e.braiins.bos.v1.LimitedLicenseH\x00\x12-\n\x05valid\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.ValidLicenseH\x00\x12\x31\n\x07\x65xpired\x18\x04 \x01(\x0b\x32\x1e.braiins.bos.v1.ExpiredLicenseH\x00\x42\x07\n\x05state*_\n\x0bLicenseType\x12\x1c\n\x18LICENSE_TYPE_UNSPECIFIED\x10\x00\x12\x19\n\x15LICENSE_TYPE_STANDARD\x10\x01\x12\x17\n\x13LICENSE_TYPE_CUSTOM\x10\x02\x32t\n\x0eLicenseService\x12\x62\n\x0fGetLicenseState\x12&.braiins.bos.v1.GetLicenseStateRequest\x1a\'.braiins.bos.v1.GetLicenseStateResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.license_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_LICENSETYPE"]._serialized_start = 666 - _globals["_LICENSETYPE"]._serialized_end = 761 - _globals["_NONELICENSE"]._serialized_start = 60 - _globals["_NONELICENSE"]._serialized_end = 101 - _globals["_LIMITEDLICENSE"]._serialized_start = 103 - _globals["_LIMITEDLICENSE"]._serialized_end = 119 - _globals["_VALIDLICENSE"]._serialized_start = 122 - _globals["_VALIDLICENSE"]._serialized_end = 276 - _globals["_EXPIREDLICENSE"]._serialized_start = 279 - _globals["_EXPIREDLICENSE"]._serialized_end = 407 - _globals["_GETLICENSESTATEREQUEST"]._serialized_start = 409 - _globals["_GETLICENSESTATEREQUEST"]._serialized_end = 433 - _globals["_GETLICENSESTATERESPONSE"]._serialized_start = 436 - _globals["_GETLICENSESTATERESPONSE"]._serialized_end = 664 - _globals["_LICENSESERVICE"]._serialized_start = 763 - _globals["_LICENSESERVICE"]._serialized_end = 879 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/miner_pb2.py b/pyasic/web/bosminer/proto/bos/v1/miner_pb2.py deleted file mode 100644 index 314b07bd..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/miner_pb2.py +++ /dev/null @@ -1,84 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/miner.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 - -from ...bos.v1 import common_pb2 as bos_dot_v1_dot_common__pb2 -from ...bos.v1 import cooling_pb2 as bos_dot_v1_dot_cooling__pb2 -from ...bos.v1 import pool_pb2 as bos_dot_v1_dot_pool__pb2 -from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2 -from ...bos.v1 import work_pb2 as bos_dot_v1_dot_work__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x12\x62os/v1/miner.proto\x12\x0e\x62raiins.bos.v1\x1a\x13\x62os/v1/common.proto\x1a\x14\x62os/v1/cooling.proto\x1a\x11\x62os/v1/pool.proto\x1a\x12\x62os/v1/units.proto\x1a\x11\x62os/v1/work.proto\x1a\x1egoogle/protobuf/wrappers.proto"s\n\rMinerIdentity\x12)\n\x05\x62rand\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.MinerBrand\x12)\n\x05model\x18\x02 \x01(\x0e\x32\x1a.braiins.bos.v1.MinerModel\x12\x0c\n\x04name\x18\x03 \x01(\t">\n\nBosVersion\x12\x0f\n\x07\x63urrent\x18\x01 \x01(\t\x12\r\n\x05major\x18\x02 \x01(\t\x12\x10\n\x08\x62os_plus\x18\x03 \x01(\x08"\x17\n\x15GetMinerStatusRequest"E\n\x16GetMinerStatusResponse\x12+\n\x06status\x18\x01 \x01(\x0e\x32\x1b.braiins.bos.v1.MinerStatus"\x18\n\x16GetMinerDetailsRequest"\xdb\x02\n\x17GetMinerDetailsResponse\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\x35\n\x0eminer_identity\x18\x02 \x01(\x0b\x32\x1d.braiins.bos.v1.MinerIdentity\x12*\n\x08platform\x18\x03 \x01(\x0e\x32\x18.braiins.bos.v1.Platform\x12)\n\x08\x62os_mode\x18\x04 \x01(\x0e\x32\x17.braiins.bos.v1.BosMode\x12/\n\x0b\x62os_version\x18\x05 \x01(\x0b\x32\x1a.braiins.bos.v1.BosVersion\x12\x10\n\x08hostname\x18\x06 \x01(\t\x12\x13\n\x0bmac_address\x18\x07 \x01(\t\x12\x15\n\rsystem_uptime\x18\x08 \x01(\x04\x12\x36\n\x10sticker_hashrate\x18\t \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate"\x7f\n\x0fMinerPowerStats\x12\x37\n\x18\x61pproximated_consumption\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12\x33\n\nefficiency\x18\x02 \x01(\x0b\x32\x1f.braiins.bos.v1.PowerEfficiency"\x16\n\x14GetMinerStatsRequest"\xb2\x01\n\x15GetMinerStatsResponse\x12-\n\npool_stats\x18\x01 \x01(\x0b\x32\x19.braiins.bos.v1.PoolStats\x12\x34\n\x0bminer_stats\x18\x02 \x01(\x0b\x32\x1f.braiins.bos.v1.WorkSolverStats\x12\x34\n\x0bpower_stats\x18\x03 \x01(\x0b\x32\x1f.braiins.bos.v1.MinerPowerStats"\xe2\x02\n\tHashboard\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\x12\x31\n\x0b\x63hips_count\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x30\n\x0f\x63urrent_voltage\x18\x04 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12\x34\n\x11\x63urrent_frequency\x18\x05 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12<\n\x11highest_chip_temp\x18\x06 \x01(\x0b\x32!.braiins.bos.v1.TemperatureSensor\x12/\n\nboard_temp\x18\x07 \x01(\x0b\x32\x1b.braiins.bos.v1.Temperature\x12.\n\x05stats\x18\x08 \x01(\x0b\x32\x1f.braiins.bos.v1.WorkSolverStats"P\n\x18GetSupportArchiveRequest\x12\x34\n\x06\x66ormat\x18\x01 \x01(\x0e\x32$.braiins.bos.v1.SupportArchiveFormat"/\n\x19GetSupportArchiveResponse\x12\x12\n\nchunk_data\x18\x01 \x01(\x0c"\x16\n\x14GetHashboardsRequest"F\n\x15GetHashboardsResponse\x12-\n\nhashboards\x18\x01 \x03(\x0b\x32\x19.braiins.bos.v1.Hashboard"a\n\x17\x45nableHashboardsRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x15\n\rhashboard_ids\x18\x02 \x03(\t"T\n\x18\x45nableHashboardsResponse\x12\x38\n\nhashboards\x18\x01 \x03(\x0b\x32$.braiins.bos.v1.HashboardEnableState"b\n\x18\x44isableHashboardsRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x15\n\rhashboard_ids\x18\x02 \x03(\t"U\n\x19\x44isableHashboardsResponse\x12\x38\n\nhashboards\x18\x01 \x03(\x0b\x32$.braiins.bos.v1.HashboardEnableState"6\n\x14HashboardEnableState\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nis_enabled\x18\x02 \x01(\x08*\xba\x01\n\x08Platform\x12\x18\n\x14PLATFORM_UNSPECIFIED\x10\x00\x12\x13\n\x0fPLATFORM_AM1_S9\x10\x01\x12\x14\n\x10PLATFORM_AM2_S17\x10\x02\x12\x14\n\x10PLATFORM_AM3_BBB\x10\x03\x12\x14\n\x10PLATFORM_AM3_AML\x10\x04\x12 \n\x1cPLATFORM_STM32MP157C_II1_AM2\x10\x05\x12\x1b\n\x17PLATFORM_CVITEK_BM1_AM2\x10\x06*\x87\x01\n\x07\x42osMode\x12\x18\n\x14\x42OS_MODE_UNSPECIFIED\x10\x00\x12\x14\n\x10\x42OS_MODE_UPGRADE\x10\x01\x12\x15\n\x11\x42OS_MODE_RECOVERY\x10\x02\x12\x0f\n\x0b\x42OS_MODE_SD\x10\x03\x12\x11\n\rBOS_MODE_NAND\x10\x04\x12\x11\n\rBOS_MODE_EMMC\x10\x05*_\n\nMinerBrand\x12\x1b\n\x17MINER_BRAND_UNSPECIFIED\x10\x00\x12\x18\n\x14MINER_BRAND_ANTMINER\x10\x01\x12\x1a\n\x16MINER_BRAND_WHATSMINER\x10\x02*\x89\x05\n\nMinerModel\x12\x1b\n\x17MINER_MODEL_UNSPECIFIED\x10\x00\x12\x1b\n\x17MINER_MODEL_ANTMINER_S9\x10\x01\x12\x1c\n\x18MINER_MODEL_ANTMINER_X17\x10\x02\x12\x1c\n\x18MINER_MODEL_ANTMINER_S17\x10\x03\x12!\n\x1dMINER_MODEL_ANTMINER_S17_PLUS\x10\x04\x12 \n\x1cMINER_MODEL_ANTMINER_S17_PRO\x10\x05\x12\x1d\n\x19MINER_MODEL_ANTMINER_S17E\x10\x06\x12\x1c\n\x18MINER_MODEL_ANTMINER_T17\x10\x07\x12\x1d\n\x19MINER_MODEL_ANTMINER_T17E\x10\x08\x12!\n\x1dMINER_MODEL_ANTMINER_T17_PLUS\x10\t\x12\x1c\n\x18MINER_MODEL_ANTMINER_X19\x10\n\x12\x1c\n\x18MINER_MODEL_ANTMINER_S19\x10\x0b\x12 \n\x1cMINER_MODEL_ANTMINER_S19_PRO\x10\x0c\x12!\n\x1dMINER_MODEL_ANTMINER_S19_PLUS\x10\r\x12\x1d\n\x19MINER_MODEL_ANTMINER_S19J\x10\x0e\x12!\n\x1dMINER_MODEL_ANTMINER_S19J_PRO\x10\x0f\x12\x1d\n\x19MINER_MODEL_ANTMINER_S19A\x10\x10\x12!\n\x1dMINER_MODEL_ANTMINER_S19A_PRO\x10\x11\x12\x1e\n\x1aMINER_MODEL_ANTMINER_S19XP\x10\x12\x12\x1c\n\x18MINER_MODEL_ANTMINER_T19\x10\x13*\xb4\x01\n\x0bMinerStatus\x12\x1c\n\x18MINER_STATUS_UNSPECIFIED\x10\x00\x12\x1c\n\x18MINER_STATUS_NOT_STARTED\x10\x01\x12\x17\n\x13MINER_STATUS_NORMAL\x10\x02\x12\x17\n\x13MINER_STATUS_PAUSED\x10\x03\x12\x1a\n\x16MINER_STATUS_SUSPENDED\x10\x04\x12\x1b\n\x17MINER_STATUS_RESTRICTED\x10\x05*~\n\x14SupportArchiveFormat\x12&\n"SUPPORT_ARCHIVE_FORMAT_UNSPECIFIED\x10\x00\x12\x1e\n\x1aSUPPORT_ARCHIVE_FORMAT_ZIP\x10\x01\x12\x1e\n\x1aSUPPORT_ARCHIVE_FORMAT_BOS\x10\x02\x32\xce\x05\n\x0cMinerService\x12\x61\n\x0eGetMinerStatus\x12%.braiins.bos.v1.GetMinerStatusRequest\x1a&.braiins.bos.v1.GetMinerStatusResponse0\x01\x12\x62\n\x0fGetMinerDetails\x12&.braiins.bos.v1.GetMinerDetailsRequest\x1a\'.braiins.bos.v1.GetMinerDetailsResponse\x12\\\n\rGetMinerStats\x12$.braiins.bos.v1.GetMinerStatsRequest\x1a%.braiins.bos.v1.GetMinerStatsResponse\x12\\\n\rGetHashboards\x12$.braiins.bos.v1.GetHashboardsRequest\x1a%.braiins.bos.v1.GetHashboardsResponse\x12j\n\x11GetSupportArchive\x12(.braiins.bos.v1.GetSupportArchiveRequest\x1a).braiins.bos.v1.GetSupportArchiveResponse0\x01\x12\x65\n\x10\x45nableHashboards\x12\'.braiins.bos.v1.EnableHashboardsRequest\x1a(.braiins.bos.v1.EnableHashboardsResponse\x12h\n\x11\x44isableHashboards\x12(.braiins.bos.v1.DisableHashboardsRequest\x1a).braiins.bos.v1.DisableHashboardsResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.miner_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_PLATFORM"]._serialized_start = 2171 - _globals["_PLATFORM"]._serialized_end = 2357 - _globals["_BOSMODE"]._serialized_start = 2360 - _globals["_BOSMODE"]._serialized_end = 2495 - _globals["_MINERBRAND"]._serialized_start = 2497 - _globals["_MINERBRAND"]._serialized_end = 2592 - _globals["_MINERMODEL"]._serialized_start = 2595 - _globals["_MINERMODEL"]._serialized_end = 3244 - _globals["_MINERSTATUS"]._serialized_start = 3247 - _globals["_MINERSTATUS"]._serialized_end = 3427 - _globals["_SUPPORTARCHIVEFORMAT"]._serialized_start = 3429 - _globals["_SUPPORTARCHIVEFORMAT"]._serialized_end = 3555 - _globals["_MINERIDENTITY"]._serialized_start = 171 - _globals["_MINERIDENTITY"]._serialized_end = 286 - _globals["_BOSVERSION"]._serialized_start = 288 - _globals["_BOSVERSION"]._serialized_end = 350 - _globals["_GETMINERSTATUSREQUEST"]._serialized_start = 352 - _globals["_GETMINERSTATUSREQUEST"]._serialized_end = 375 - _globals["_GETMINERSTATUSRESPONSE"]._serialized_start = 377 - _globals["_GETMINERSTATUSRESPONSE"]._serialized_end = 446 - _globals["_GETMINERDETAILSREQUEST"]._serialized_start = 448 - _globals["_GETMINERDETAILSREQUEST"]._serialized_end = 472 - _globals["_GETMINERDETAILSRESPONSE"]._serialized_start = 475 - _globals["_GETMINERDETAILSRESPONSE"]._serialized_end = 822 - _globals["_MINERPOWERSTATS"]._serialized_start = 824 - _globals["_MINERPOWERSTATS"]._serialized_end = 951 - _globals["_GETMINERSTATSREQUEST"]._serialized_start = 953 - _globals["_GETMINERSTATSREQUEST"]._serialized_end = 975 - _globals["_GETMINERSTATSRESPONSE"]._serialized_start = 978 - _globals["_GETMINERSTATSRESPONSE"]._serialized_end = 1156 - _globals["_HASHBOARD"]._serialized_start = 1159 - _globals["_HASHBOARD"]._serialized_end = 1513 - _globals["_GETSUPPORTARCHIVEREQUEST"]._serialized_start = 1515 - _globals["_GETSUPPORTARCHIVEREQUEST"]._serialized_end = 1595 - _globals["_GETSUPPORTARCHIVERESPONSE"]._serialized_start = 1597 - _globals["_GETSUPPORTARCHIVERESPONSE"]._serialized_end = 1644 - _globals["_GETHASHBOARDSREQUEST"]._serialized_start = 1646 - _globals["_GETHASHBOARDSREQUEST"]._serialized_end = 1668 - _globals["_GETHASHBOARDSRESPONSE"]._serialized_start = 1670 - _globals["_GETHASHBOARDSRESPONSE"]._serialized_end = 1740 - _globals["_ENABLEHASHBOARDSREQUEST"]._serialized_start = 1742 - _globals["_ENABLEHASHBOARDSREQUEST"]._serialized_end = 1839 - _globals["_ENABLEHASHBOARDSRESPONSE"]._serialized_start = 1841 - _globals["_ENABLEHASHBOARDSRESPONSE"]._serialized_end = 1925 - _globals["_DISABLEHASHBOARDSREQUEST"]._serialized_start = 1927 - _globals["_DISABLEHASHBOARDSREQUEST"]._serialized_end = 2025 - _globals["_DISABLEHASHBOARDSRESPONSE"]._serialized_start = 2027 - _globals["_DISABLEHASHBOARDSRESPONSE"]._serialized_end = 2112 - _globals["_HASHBOARDENABLESTATE"]._serialized_start = 2114 - _globals["_HASHBOARDENABLESTATE"]._serialized_end = 2168 - _globals["_MINERSERVICE"]._serialized_start = 3558 - _globals["_MINERSERVICE"]._serialized_end = 4276 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/performance.py b/pyasic/web/bosminer/proto/bos/v1/performance.py deleted file mode 100644 index 53c147f8..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/performance.py +++ /dev/null @@ -1,110 +0,0 @@ -# ------------------------------------------------------------------------------ -# 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 dataclasses import asdict, dataclass -from typing import Union - - -@dataclass -class Frequency: - hertz: float - - -@dataclass -class Voltage: - volt: float - - -@dataclass -class Power: - watt: int - - -@dataclass -class TeraHashrate: - terahash_per_second: float - - -@dataclass -class HashboardPerformanceSettings: - id: str - frequency: Frequency - voltage: Voltage - - -@dataclass -class ManualPerformanceMode: - global_frequency: Frequency - global_voltage: Voltage - hashboards: list[HashboardPerformanceSettings] - - -@dataclass -class PowerTargetMode: - power_target: Power - - -@dataclass -class HashrateTargetMode: - hashrate_target: TeraHashrate - - -@dataclass -class TunerPerformanceMode: - target: Union[PowerTargetMode, HashrateTargetMode] - - -@dataclass -class PerformanceMode: - mode: Union[ManualPerformanceMode, TunerPerformanceMode] - - @classmethod - def create( - cls, - power_target: int = None, - hashrate_target: float = None, - manual_configuration: ManualPerformanceMode = None, - ): - provided_args = [power_target, hashrate_target, manual_configuration] - if sum(arg is not None for arg in provided_args) > 1: - raise ValueError( - "More than one keyword argument provided. Please use only power target, hashrate target, or manual config." - ) - elif sum(arg is not None for arg in provided_args) < 1: - raise ValueError( - "Please pass one of power target, hashrate target, or manual config." - ) - - if power_target is not None: - return cls( - mode=TunerPerformanceMode( - target=PowerTargetMode(power_target=Power(watt=power_target)) - ) - ) - elif hashrate_target is not None: - return cls( - mode=TunerPerformanceMode( - target=HashrateTargetMode( - hashrate_target=TeraHashrate( - terahash_per_second=hashrate_target - ) - ) - ) - ) - elif manual_configuration is not None: - return cls(mode=manual_configuration) - - def as_dict(self): - return asdict(self) diff --git a/pyasic/web/bosminer/proto/bos/v1/performance_pb2.py b/pyasic/web/bosminer/proto/bos/v1/performance_pb2.py deleted file mode 100644 index 02fbc0cd..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/performance_pb2.py +++ /dev/null @@ -1,112 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/performance.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 - -from ...bos.v1 import common_pb2 as bos_dot_v1_dot_common__pb2 -from ...bos.v1 import constraints_pb2 as bos_dot_v1_dot_constraints__pb2 -from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x18\x62os/v1/performance.proto\x12\x0e\x62raiins.bos.v1\x1a\x13\x62os/v1/common.proto\x1a\x18\x62os/v1/constraints.proto\x1a\x12\x62os/v1/units.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xdd\x01\n\x12TunerConfiguration\x12\x14\n\x07\x65nabled\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x32\n\ntuner_mode\x18\x02 \x01(\x0e\x32\x19.braiins.bos.v1.TunerModeH\x01\x88\x01\x01\x12+\n\x0cpower_target\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12\x35\n\x0fhashrate_target\x18\x04 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrateB\n\n\x08_enabledB\r\n\x0b_tuner_mode"\x88\x01\n\x10TunerConstraints\x12\x36\n\x0cpower_target\x18\x01 \x01(\x0b\x32 .braiins.bos.v1.PowerConstraints\x12<\n\x0fhashrate_target\x18\x02 \x01(\x0b\x32#.braiins.bos.v1.HashrateConstraints"\xe6\x02\n\x10\x44PSConfiguration\x12\x14\n\x07\x65nabled\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12)\n\npower_step\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12\x33\n\rhashrate_step\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12/\n\x10min_power_target\x18\x04 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12\x39\n\x13min_hashrate_target\x18\x05 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12\x1d\n\x10shutdown_enabled\x18\x06 \x01(\x08H\x01\x88\x01\x01\x12\x30\n\x11shutdown_duration\x18\x07 \x01(\x0b\x32\x15.braiins.bos.v1.HoursB\n\n\x08_enabledB\x13\n\x11_shutdown_enabled"\xbe\x01\n!HashboardPerformanceConfiguration\x12\x33\n\x10global_frequency\x18\x01 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12/\n\x0eglobal_voltage\x18\x02 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12\x33\n\nhashboards\x18\x03 \x03(\x0b\x32\x1f.braiins.bos.v1.HashboardConfig"\xfd\x02\n\x0e\x44PSConstraints\x12\x34\n\npower_step\x18\x01 \x01(\x0b\x32 .braiins.bos.v1.PowerConstraints\x12:\n\rhashrate_step\x18\x02 \x01(\x0b\x32#.braiins.bos.v1.HashrateConstraints\x12:\n\x10min_power_target\x18\x03 \x01(\x0b\x32 .braiins.bos.v1.PowerConstraints\x12@\n\x13min_hashrate_target\x18\x04 \x01(\x0b\x32#.braiins.bos.v1.HashrateConstraints\x12;\n\x10shutdown_enabled\x18\x05 \x01(\x0b\x32!.braiins.bos.v1.BooleanConstraint\x12>\n\x11shutdown_duration\x18\x06 \x01(\x0b\x32#.braiins.bos.v1.DurationConstraints"\xcf\x01\n\x14HashboardConstraints\x12\x15\n\rhashboard_ids\x18\x01 \x03(\t\x12\x32\n\x07\x65nabled\x18\x02 \x01(\x0b\x32!.braiins.bos.v1.BooleanConstraint\x12\x37\n\tfrequency\x18\x03 \x01(\x0b\x32$.braiins.bos.v1.FrequencyConstraints\x12\x33\n\x07voltage\x18\x04 \x01(\x0b\x32".braiins.bos.v1.VoltageConstraints"\xdd\x01\n\x12PowerTargetProfile\x12+\n\x07\x63reated\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12%\n\x06target\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12\x37\n\x11measured_hashrate\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12:\n\x1b\x65stimated_power_consumption\x18\x04 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\xe7\x01\n\x15HashrateTargetProfile\x12+\n\x07\x63reated\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x06target\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12\x37\n\x11measured_hashrate\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12:\n\x1b\x65stimated_power_consumption\x18\x04 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x16\n\x14GetTunerStateRequest"\xf6\x01\n\x15GetTunerStateResponse\x12\x37\n\x13overall_tuner_state\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.TunerState\x12G\n\x17power_target_mode_state\x18\x02 \x01(\x0b\x32$.braiins.bos.v1.PowerTargetModeStateH\x00\x12M\n\x1ahashrate_target_mode_state\x18\x03 \x01(\x0b\x32\'.braiins.bos.v1.HashrateTargetModeStateH\x00\x42\x0c\n\nmode_state"z\n\x14PowerTargetModeState\x12\x33\n\x07profile\x18\x01 \x01(\x0b\x32".braiins.bos.v1.PowerTargetProfile\x12-\n\x0e\x63urrent_target\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x87\x01\n\x17HashrateTargetModeState\x12\x36\n\x07profile\x18\x01 \x01(\x0b\x32%.braiins.bos.v1.HashrateTargetProfile\x12\x34\n\x0e\x63urrent_target\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x1b\n\x19ListTargetProfilesRequest"\xa8\x01\n\x1aListTargetProfilesResponse\x12\x41\n\x15power_target_profiles\x18\x01 \x03(\x0b\x32".braiins.bos.v1.PowerTargetProfile\x12G\n\x18hashrate_target_profiles\x18\x02 \x03(\x0b\x32%.braiins.bos.v1.HashrateTargetProfile"O\n\x1cSetDefaultPowerTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction"u\n\x15SetPowerTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12+\n\x0cpower_target\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x85\x01\n\x1bIncrementPowerTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x35\n\x16power_target_increment\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x85\x01\n\x1b\x44\x65\x63rementPowerTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x35\n\x16power_target_decrement\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power"E\n\x16SetPowerTargetResponse\x12+\n\x0cpower_target\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Power"R\n\x1fSetDefaultHashrateTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction"\x82\x01\n\x18SetHashrateTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x35\n\x0fhashrate_target\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x92\x01\n\x1eIncrementHashrateTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12?\n\x19hashrate_target_increment\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x92\x01\n\x1e\x44\x65\x63rementHashrateTargetRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12?\n\x19hashrate_target_decrement\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"R\n\x19SetHashrateTargetResponse\x12\x35\n\x0fhashrate_target\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"l\n\x0e\x44PSPowerTarget\x12)\n\npower_step\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Power\x12/\n\x10min_power_target\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.Power"\x83\x01\n\x11\x44PSHashrateTarget\x12\x33\n\rhashrate_step\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate\x12\x39\n\x13min_hashrate_target\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x8b\x01\n\tDPSTarget\x12\x36\n\x0cpower_target\x18\x01 \x01(\x0b\x32\x1e.braiins.bos.v1.DPSPowerTargetH\x00\x12<\n\x0fhashrate_target\x18\x02 \x01(\x0b\x32!.braiins.bos.v1.DPSHashrateTargetH\x00\x42\x08\n\x06target"\x8a\x02\n\rSetDPSRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x13\n\x06\x65nable\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12\x1c\n\x0f\x65nable_shutdown\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x35\n\x11shutdown_duration\x18\x04 \x01(\x0b\x32\x15.braiins.bos.v1.HoursH\x02\x88\x01\x01\x12)\n\x06target\x18\x05 \x01(\x0b\x32\x19.braiins.bos.v1.DPSTargetB\t\n\x07_enableB\x12\n\x10_enable_shutdownB\x14\n\x12_shutdown_duration"\xa5\x02\n\x0eSetDPSResponse\x12\x14\n\x07\x65nabled\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1d\n\x10shutdown_enabled\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12\x35\n\x11shutdown_duration\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.HoursH\x02\x88\x01\x01\x12\x34\n\x0cpower_target\x18\x04 \x01(\x0b\x32\x1e.braiins.bos.v1.DPSPowerTarget\x12:\n\x0fhashrate_target\x18\x05 \x01(\x0b\x32!.braiins.bos.v1.DPSHashrateTargetB\n\n\x08_enabledB\x13\n\x11_shutdown_enabledB\x14\n\x12_shutdown_duration"\x82\x01\n\x1cHashboardPerformanceSettings\x12\n\n\x02id\x18\x01 \x01(\t\x12,\n\tfrequency\x18\x02 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12(\n\x07voltage\x18\x03 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage"\x97\x01\n\x0fHashboardConfig\x12\n\n\x02id\x18\x01 \x01(\t\x12\x14\n\x07\x65nabled\x18\x02 \x01(\x08H\x00\x88\x01\x01\x12,\n\tfrequency\x18\x03 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12(\n\x07voltage\x18\x04 \x01(\x0b\x32\x17.braiins.bos.v1.VoltageB\n\n\x08_enabled"\xbf\x01\n\x15ManualPerformanceMode\x12\x33\n\x10global_frequency\x18\x01 \x01(\x0b\x32\x19.braiins.bos.v1.Frequency\x12/\n\x0eglobal_voltage\x18\x02 \x01(\x0b\x32\x17.braiins.bos.v1.Voltage\x12@\n\nhashboards\x18\x03 \x03(\x0b\x32,.braiins.bos.v1.HashboardPerformanceSettings">\n\x0fPowerTargetMode\x12+\n\x0cpower_target\x18\x01 \x01(\x0b\x32\x15.braiins.bos.v1.Power"K\n\x12HashrateTargetMode\x12\x35\n\x0fhashrate_target\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.TeraHashrate"\x98\x01\n\x14TunerPerformanceMode\x12\x37\n\x0cpower_target\x18\x01 \x01(\x0b\x32\x1f.braiins.bos.v1.PowerTargetModeH\x00\x12=\n\x0fhashrate_target\x18\x02 \x01(\x0b\x32".braiins.bos.v1.HashrateTargetModeH\x00\x42\x08\n\x06target"{\n\x19SetPerformanceModeRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12-\n\x04mode\x18\x02 \x01(\x0b\x32\x1f.braiins.bos.v1.PerformanceMode"\x93\x01\n\x0fPerformanceMode\x12<\n\x0bmanual_mode\x18\x01 \x01(\x0b\x32%.braiins.bos.v1.ManualPerformanceModeH\x00\x12:\n\ntuner_mode\x18\x02 \x01(\x0b\x32$.braiins.bos.v1.TunerPerformanceModeH\x00\x42\x06\n\x04mode"\x1b\n\x19GetPerformanceModeRequest*d\n\tTunerMode\x12\x1a\n\x16TUNER_MODE_UNSPECIFIED\x10\x00\x12\x1b\n\x17TUNER_MODE_POWER_TARGET\x10\x01\x12\x1e\n\x1aTUNER_MODE_HASHRATE_TARGET\x10\x02*\x8a\x01\n\nTunerState\x12\x1b\n\x17TUNER_STATE_UNSPECIFIED\x10\x00\x12\x18\n\x14TUNER_STATE_DISABLED\x10\x01\x12\x16\n\x12TUNER_STATE_STABLE\x10\x02\x12\x16\n\x12TUNER_STATE_TUNING\x10\x03\x12\x15\n\x11TUNER_STATE_ERROR\x10\x04\x32\xea\n\n\x12PerformanceService\x12\\\n\rGetTunerState\x12$.braiins.bos.v1.GetTunerStateRequest\x1a%.braiins.bos.v1.GetTunerStateResponse\x12k\n\x12ListTargetProfiles\x12).braiins.bos.v1.ListTargetProfilesRequest\x1a*.braiins.bos.v1.ListTargetProfilesResponse\x12m\n\x15SetDefaultPowerTarget\x12,.braiins.bos.v1.SetDefaultPowerTargetRequest\x1a&.braiins.bos.v1.SetPowerTargetResponse\x12_\n\x0eSetPowerTarget\x12%.braiins.bos.v1.SetPowerTargetRequest\x1a&.braiins.bos.v1.SetPowerTargetResponse\x12k\n\x14IncrementPowerTarget\x12+.braiins.bos.v1.IncrementPowerTargetRequest\x1a&.braiins.bos.v1.SetPowerTargetResponse\x12k\n\x14\x44\x65\x63rementPowerTarget\x12+.braiins.bos.v1.DecrementPowerTargetRequest\x1a&.braiins.bos.v1.SetPowerTargetResponse\x12v\n\x18SetDefaultHashrateTarget\x12/.braiins.bos.v1.SetDefaultHashrateTargetRequest\x1a).braiins.bos.v1.SetHashrateTargetResponse\x12h\n\x11SetHashrateTarget\x12(.braiins.bos.v1.SetHashrateTargetRequest\x1a).braiins.bos.v1.SetHashrateTargetResponse\x12t\n\x17IncrementHashrateTarget\x12..braiins.bos.v1.IncrementHashrateTargetRequest\x1a).braiins.bos.v1.SetHashrateTargetResponse\x12t\n\x17\x44\x65\x63rementHashrateTarget\x12..braiins.bos.v1.DecrementHashrateTargetRequest\x1a).braiins.bos.v1.SetHashrateTargetResponse\x12G\n\x06SetDPS\x12\x1d.braiins.bos.v1.SetDPSRequest\x1a\x1e.braiins.bos.v1.SetDPSResponse\x12`\n\x12SetPerformanceMode\x12).braiins.bos.v1.SetPerformanceModeRequest\x1a\x1f.braiins.bos.v1.PerformanceMode\x12\x66\n\x18GetActivePerformanceMode\x12).braiins.bos.v1.GetPerformanceModeRequest\x1a\x1f.braiins.bos.v1.PerformanceModeb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.performance_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_TUNERMODE"]._serialized_start = 6022 - _globals["_TUNERMODE"]._serialized_end = 6122 - _globals["_TUNERSTATE"]._serialized_start = 6125 - _globals["_TUNERSTATE"]._serialized_end = 6263 - _globals["_TUNERCONFIGURATION"]._serialized_start = 145 - _globals["_TUNERCONFIGURATION"]._serialized_end = 366 - _globals["_TUNERCONSTRAINTS"]._serialized_start = 369 - _globals["_TUNERCONSTRAINTS"]._serialized_end = 505 - _globals["_DPSCONFIGURATION"]._serialized_start = 508 - _globals["_DPSCONFIGURATION"]._serialized_end = 866 - _globals["_HASHBOARDPERFORMANCECONFIGURATION"]._serialized_start = 869 - _globals["_HASHBOARDPERFORMANCECONFIGURATION"]._serialized_end = 1059 - _globals["_DPSCONSTRAINTS"]._serialized_start = 1062 - _globals["_DPSCONSTRAINTS"]._serialized_end = 1443 - _globals["_HASHBOARDCONSTRAINTS"]._serialized_start = 1446 - _globals["_HASHBOARDCONSTRAINTS"]._serialized_end = 1653 - _globals["_POWERTARGETPROFILE"]._serialized_start = 1656 - _globals["_POWERTARGETPROFILE"]._serialized_end = 1877 - _globals["_HASHRATETARGETPROFILE"]._serialized_start = 1880 - _globals["_HASHRATETARGETPROFILE"]._serialized_end = 2111 - _globals["_GETTUNERSTATEREQUEST"]._serialized_start = 2113 - _globals["_GETTUNERSTATEREQUEST"]._serialized_end = 2135 - _globals["_GETTUNERSTATERESPONSE"]._serialized_start = 2138 - _globals["_GETTUNERSTATERESPONSE"]._serialized_end = 2384 - _globals["_POWERTARGETMODESTATE"]._serialized_start = 2386 - _globals["_POWERTARGETMODESTATE"]._serialized_end = 2508 - _globals["_HASHRATETARGETMODESTATE"]._serialized_start = 2511 - _globals["_HASHRATETARGETMODESTATE"]._serialized_end = 2646 - _globals["_LISTTARGETPROFILESREQUEST"]._serialized_start = 2648 - _globals["_LISTTARGETPROFILESREQUEST"]._serialized_end = 2675 - _globals["_LISTTARGETPROFILESRESPONSE"]._serialized_start = 2678 - _globals["_LISTTARGETPROFILESRESPONSE"]._serialized_end = 2846 - _globals["_SETDEFAULTPOWERTARGETREQUEST"]._serialized_start = 2848 - _globals["_SETDEFAULTPOWERTARGETREQUEST"]._serialized_end = 2927 - _globals["_SETPOWERTARGETREQUEST"]._serialized_start = 2929 - _globals["_SETPOWERTARGETREQUEST"]._serialized_end = 3046 - _globals["_INCREMENTPOWERTARGETREQUEST"]._serialized_start = 3049 - _globals["_INCREMENTPOWERTARGETREQUEST"]._serialized_end = 3182 - _globals["_DECREMENTPOWERTARGETREQUEST"]._serialized_start = 3185 - _globals["_DECREMENTPOWERTARGETREQUEST"]._serialized_end = 3318 - _globals["_SETPOWERTARGETRESPONSE"]._serialized_start = 3320 - _globals["_SETPOWERTARGETRESPONSE"]._serialized_end = 3389 - _globals["_SETDEFAULTHASHRATETARGETREQUEST"]._serialized_start = 3391 - _globals["_SETDEFAULTHASHRATETARGETREQUEST"]._serialized_end = 3473 - _globals["_SETHASHRATETARGETREQUEST"]._serialized_start = 3476 - _globals["_SETHASHRATETARGETREQUEST"]._serialized_end = 3606 - _globals["_INCREMENTHASHRATETARGETREQUEST"]._serialized_start = 3609 - _globals["_INCREMENTHASHRATETARGETREQUEST"]._serialized_end = 3755 - _globals["_DECREMENTHASHRATETARGETREQUEST"]._serialized_start = 3758 - _globals["_DECREMENTHASHRATETARGETREQUEST"]._serialized_end = 3904 - _globals["_SETHASHRATETARGETRESPONSE"]._serialized_start = 3906 - _globals["_SETHASHRATETARGETRESPONSE"]._serialized_end = 3988 - _globals["_DPSPOWERTARGET"]._serialized_start = 3990 - _globals["_DPSPOWERTARGET"]._serialized_end = 4098 - _globals["_DPSHASHRATETARGET"]._serialized_start = 4101 - _globals["_DPSHASHRATETARGET"]._serialized_end = 4232 - _globals["_DPSTARGET"]._serialized_start = 4235 - _globals["_DPSTARGET"]._serialized_end = 4374 - _globals["_SETDPSREQUEST"]._serialized_start = 4377 - _globals["_SETDPSREQUEST"]._serialized_end = 4643 - _globals["_SETDPSRESPONSE"]._serialized_start = 4646 - _globals["_SETDPSRESPONSE"]._serialized_end = 4939 - _globals["_HASHBOARDPERFORMANCESETTINGS"]._serialized_start = 4942 - _globals["_HASHBOARDPERFORMANCESETTINGS"]._serialized_end = 5072 - _globals["_HASHBOARDCONFIG"]._serialized_start = 5075 - _globals["_HASHBOARDCONFIG"]._serialized_end = 5226 - _globals["_MANUALPERFORMANCEMODE"]._serialized_start = 5229 - _globals["_MANUALPERFORMANCEMODE"]._serialized_end = 5420 - _globals["_POWERTARGETMODE"]._serialized_start = 5422 - _globals["_POWERTARGETMODE"]._serialized_end = 5484 - _globals["_HASHRATETARGETMODE"]._serialized_start = 5486 - _globals["_HASHRATETARGETMODE"]._serialized_end = 5561 - _globals["_TUNERPERFORMANCEMODE"]._serialized_start = 5564 - _globals["_TUNERPERFORMANCEMODE"]._serialized_end = 5716 - _globals["_SETPERFORMANCEMODEREQUEST"]._serialized_start = 5718 - _globals["_SETPERFORMANCEMODEREQUEST"]._serialized_end = 5841 - _globals["_PERFORMANCEMODE"]._serialized_start = 5844 - _globals["_PERFORMANCEMODE"]._serialized_end = 5991 - _globals["_GETPERFORMANCEMODEREQUEST"]._serialized_start = 5993 - _globals["_GETPERFORMANCEMODEREQUEST"]._serialized_end = 6020 - _globals["_PERFORMANCESERVICE"]._serialized_start = 6266 - _globals["_PERFORMANCESERVICE"]._serialized_end = 7652 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/pool_pb2.py b/pyasic/web/bosminer/proto/bos/v1/pool_pb2.py deleted file mode 100644 index 55913f07..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/pool_pb2.py +++ /dev/null @@ -1,75 +0,0 @@ -# -*- coding: utf-8 -*- - -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/pool.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from ...bos.v1 import common_pb2 as bos_dot_v1_dot_common__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x11\x62os/v1/pool.proto\x12\x0e\x62raiins.bos.v1\x1a\x13\x62os/v1/common.proto"\x16\n\x05Quota\x12\r\n\x05value\x18\x01 \x01(\r" \n\x0f\x46ixedShareRatio\x12\r\n\x05value\x18\x01 \x01(\x01"\xe4\x01\n\x16PoolGroupConfiguration\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12&\n\x05quota\x18\x03 \x01(\x0b\x32\x15.braiins.bos.v1.QuotaH\x00\x12<\n\x11\x66ixed_share_ratio\x18\x04 \x01(\x0b\x32\x1f.braiins.bos.v1.FixedShareRatioH\x00\x12\x30\n\x05pools\x18\x05 \x03(\x0b\x32!.braiins.bos.v1.PoolConfigurationB\x17\n\x15load_balance_strategy"\x81\x01\n\x11PoolConfiguration\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0c\n\x04user\x18\x03 \x01(\t\x12\x15\n\x08password\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x65nabled\x18\x05 \x01(\x08H\x01\x88\x01\x01\x42\x0b\n\t_passwordB\n\n\x08_enabled"\xb0\x01\n\tPoolGroup\x12\x0c\n\x04name\x18\x01 \x01(\t\x12&\n\x05quota\x18\x02 \x01(\x0b\x32\x15.braiins.bos.v1.QuotaH\x00\x12<\n\x11\x66ixed_share_ratio\x18\x03 \x01(\x0b\x32\x1f.braiins.bos.v1.FixedShareRatioH\x00\x12#\n\x05pools\x18\x04 \x03(\x0b\x32\x14.braiins.bos.v1.PoolB\n\n\x08strategy"\x88\x01\n\x04Pool\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0c\n\x04user\x18\x03 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\x12\r\n\x05\x61live\x18\x05 \x01(\x08\x12\x0e\n\x06\x61\x63tive\x18\x06 \x01(\x08\x12(\n\x05stats\x18\x07 \x01(\x0b\x32\x19.braiins.bos.v1.PoolStats"\x98\x01\n\tPoolStats\x12\x17\n\x0f\x61\x63\x63\x65pted_shares\x18\x01 \x01(\x04\x12\x17\n\x0frejected_shares\x18\x02 \x01(\x04\x12\x14\n\x0cstale_shares\x18\x03 \x01(\x04\x12\x17\n\x0flast_difficulty\x18\x04 \x01(\x04\x12\x12\n\nbest_share\x18\x05 \x01(\x04\x12\x16\n\x0egenerated_work\x18\x06 \x01(\x04"\x16\n\x14GetPoolGroupsRequest"G\n\x15GetPoolGroupsResponse\x12.\n\x0bpool_groups\x18\x01 \x03(\x0b\x32\x19.braiins.bos.v1.PoolGroup"\x80\x01\n\x16\x43reatePoolGroupRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x35\n\x05group\x18\x02 \x01(\x0b\x32&.braiins.bos.v1.PoolGroupConfiguration"P\n\x17\x43reatePoolGroupResponse\x12\x35\n\x05group\x18\x01 \x01(\x0b\x32&.braiins.bos.v1.PoolGroupConfiguration"\x80\x01\n\x16UpdatePoolGroupRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x35\n\x05group\x18\x02 \x01(\x0b\x32&.braiins.bos.v1.PoolGroupConfiguration"P\n\x17UpdatePoolGroupResponse\x12\x35\n\x05group\x18\x01 \x01(\x0b\x32&.braiins.bos.v1.PoolGroupConfiguration"V\n\x16RemovePoolGroupRequest\x12/\n\x0bsave_action\x18\x01 \x01(\x0e\x32\x1a.braiins.bos.v1.SaveAction\x12\x0b\n\x03uid\x18\x02 \x01(\t"\x19\n\x17RemovePoolGroupResponse2\x97\x03\n\x0bPoolService\x12\\\n\rGetPoolGroups\x12$.braiins.bos.v1.GetPoolGroupsRequest\x1a%.braiins.bos.v1.GetPoolGroupsResponse\x12\x62\n\x0f\x43reatePoolGroup\x12&.braiins.bos.v1.CreatePoolGroupRequest\x1a\'.braiins.bos.v1.CreatePoolGroupResponse\x12\x62\n\x0fUpdatePoolGroup\x12&.braiins.bos.v1.UpdatePoolGroupRequest\x1a\'.braiins.bos.v1.UpdatePoolGroupResponse\x12\x62\n\x0fRemovePoolGroup\x12&.braiins.bos.v1.RemovePoolGroupRequest\x1a\'.braiins.bos.v1.RemovePoolGroupResponseb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.pool_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_QUOTA"]._serialized_start = 58 - _globals["_QUOTA"]._serialized_end = 80 - _globals["_FIXEDSHARERATIO"]._serialized_start = 82 - _globals["_FIXEDSHARERATIO"]._serialized_end = 114 - _globals["_POOLGROUPCONFIGURATION"]._serialized_start = 117 - _globals["_POOLGROUPCONFIGURATION"]._serialized_end = 345 - _globals["_POOLCONFIGURATION"]._serialized_start = 348 - _globals["_POOLCONFIGURATION"]._serialized_end = 477 - _globals["_POOLGROUP"]._serialized_start = 480 - _globals["_POOLGROUP"]._serialized_end = 656 - _globals["_POOL"]._serialized_start = 659 - _globals["_POOL"]._serialized_end = 795 - _globals["_POOLSTATS"]._serialized_start = 798 - _globals["_POOLSTATS"]._serialized_end = 950 - _globals["_GETPOOLGROUPSREQUEST"]._serialized_start = 952 - _globals["_GETPOOLGROUPSREQUEST"]._serialized_end = 974 - _globals["_GETPOOLGROUPSRESPONSE"]._serialized_start = 976 - _globals["_GETPOOLGROUPSRESPONSE"]._serialized_end = 1047 - _globals["_CREATEPOOLGROUPREQUEST"]._serialized_start = 1050 - _globals["_CREATEPOOLGROUPREQUEST"]._serialized_end = 1178 - _globals["_CREATEPOOLGROUPRESPONSE"]._serialized_start = 1180 - _globals["_CREATEPOOLGROUPRESPONSE"]._serialized_end = 1260 - _globals["_UPDATEPOOLGROUPREQUEST"]._serialized_start = 1263 - _globals["_UPDATEPOOLGROUPREQUEST"]._serialized_end = 1391 - _globals["_UPDATEPOOLGROUPRESPONSE"]._serialized_start = 1393 - _globals["_UPDATEPOOLGROUPRESPONSE"]._serialized_end = 1473 - _globals["_REMOVEPOOLGROUPREQUEST"]._serialized_start = 1475 - _globals["_REMOVEPOOLGROUPREQUEST"]._serialized_end = 1561 - _globals["_REMOVEPOOLGROUPRESPONSE"]._serialized_start = 1563 - _globals["_REMOVEPOOLGROUPRESPONSE"]._serialized_end = 1588 - _globals["_POOLSERVICE"]._serialized_start = 1591 - _globals["_POOLSERVICE"]._serialized_end = 1998 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/units_pb2.py b/pyasic/web/bosminer/proto/bos/v1/units_pb2.py deleted file mode 100644 index 5ba78648..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/units_pb2.py +++ /dev/null @@ -1,61 +0,0 @@ -# -*- coding: utf-8 -*- - -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/units.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x12\x62os/v1/units.proto\x12\x0e\x62raiins.bos.v1"+\n\x0cMegaHashrate\x12\x1b\n\x13megahash_per_second\x18\x01 \x01(\x01"+\n\x0cGigaHashrate\x12\x1b\n\x13gigahash_per_second\x18\x01 \x01(\x01"+\n\x0cTeraHashrate\x12\x1b\n\x13terahash_per_second\x18\x01 \x01(\x01"\x1a\n\tFrequency\x12\r\n\x05hertz\x18\x01 \x01(\x01"\x17\n\x07Voltage\x12\x0c\n\x04volt\x18\x01 \x01(\x01"\x15\n\x05Power\x12\x0c\n\x04watt\x18\x01 \x01(\x04"-\n\x0fPowerEfficiency\x12\x1a\n\x12joule_per_terahash\x18\x01 \x01(\x01"\x1f\n\x0bTemperature\x12\x10\n\x08\x64\x65gree_c\x18\x01 \x01(\x01"\x1a\n\x0b\x42\x61sesPoints\x12\x0b\n\x03\x62sp\x18\x01 \x01(\r"\x16\n\x05Hours\x12\r\n\x05hours\x18\x01 \x01(\rb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.units_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_MEGAHASHRATE"]._serialized_start = 38 - _globals["_MEGAHASHRATE"]._serialized_end = 81 - _globals["_GIGAHASHRATE"]._serialized_start = 83 - _globals["_GIGAHASHRATE"]._serialized_end = 126 - _globals["_TERAHASHRATE"]._serialized_start = 128 - _globals["_TERAHASHRATE"]._serialized_end = 171 - _globals["_FREQUENCY"]._serialized_start = 173 - _globals["_FREQUENCY"]._serialized_end = 199 - _globals["_VOLTAGE"]._serialized_start = 201 - _globals["_VOLTAGE"]._serialized_end = 224 - _globals["_POWER"]._serialized_start = 226 - _globals["_POWER"]._serialized_end = 247 - _globals["_POWEREFFICIENCY"]._serialized_start = 249 - _globals["_POWEREFFICIENCY"]._serialized_end = 294 - _globals["_TEMPERATURE"]._serialized_start = 296 - _globals["_TEMPERATURE"]._serialized_end = 327 - _globals["_BASESPOINTS"]._serialized_start = 329 - _globals["_BASESPOINTS"]._serialized_end = 355 - _globals["_HOURS"]._serialized_start = 357 - _globals["_HOURS"]._serialized_end = 379 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/v1/work_pb2.py b/pyasic/web/bosminer/proto/bos/v1/work_pb2.py deleted file mode 100644 index 9bff6586..00000000 --- a/pyasic/web/bosminer/proto/bos/v1/work_pb2.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- - -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/v1/work.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from ...bos.v1 import units_pb2 as bos_dot_v1_dot_units__pb2 - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x11\x62os/v1/work.proto\x12\x0e\x62raiins.bos.v1\x1a\x12\x62os/v1/units.proto"\xef\x03\n\x0cRealHashrate\x12-\n\x07last_5s\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12.\n\x08last_15s\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12.\n\x08last_30s\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12-\n\x07last_1m\x18\x04 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12-\n\x07last_5m\x18\x05 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12.\n\x08last_15m\x18\x06 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12.\n\x08last_30m\x18\x07 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12-\n\x07last_1h\x18\x08 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12.\n\x08last_24h\x18\t \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12\x33\n\rsince_restart\x18\n \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate"\xde\x01\n\x0fWorkSolverStats\x12\x33\n\rreal_hashrate\x18\x01 \x01(\x0b\x32\x1c.braiins.bos.v1.RealHashrate\x12\x36\n\x10nominal_hashrate\x18\x02 \x01(\x0b\x32\x1c.braiins.bos.v1.GigaHashrate\x12\x34\n\x0e\x65rror_hashrate\x18\x03 \x01(\x0b\x32\x1c.braiins.bos.v1.MegaHashrate\x12\x14\n\x0c\x66ound_blocks\x18\x04 \x01(\r\x12\x12\n\nbest_share\x18\x05 \x01(\x04\x62\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.v1.work_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_REALHASHRATE"]._serialized_start = 58 - _globals["_REALHASHRATE"]._serialized_end = 553 - _globals["_WORKSOLVERSTATS"]._serialized_start = 556 - _globals["_WORKSOLVERSTATS"]._serialized_end = 778 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/bos/version_pb2.py b/pyasic/web/bosminer/proto/bos/version_pb2.py deleted file mode 100644 index aed5ad35..00000000 --- a/pyasic/web/bosminer/proto/bos/version_pb2.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- - -# ------------------------------------------------------------------------------ -# 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. - -# ------------------------------------------------------------------------------ - -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: bos/version.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder - -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x11\x62os/version.proto\x12\x0b\x62raiins.bos"U\n\nApiVersion\x12\r\n\x05major\x18\x01 \x01(\x04\x12\r\n\x05minor\x18\x02 \x01(\x04\x12\r\n\x05patch\x18\x03 \x01(\x04\x12\x0b\n\x03pre\x18\x04 \x01(\t\x12\r\n\x05\x62uild\x18\x05 \x01(\t"\x13\n\x11\x41piVersionRequest2]\n\x11\x41piVersionService\x12H\n\rGetApiVersion\x12\x1e.braiins.bos.ApiVersionRequest\x1a\x17.braiins.bos.ApiVersionb\x06proto3' -) - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "bos.version_pb2", _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals["_APIVERSION"]._serialized_start = 34 - _globals["_APIVERSION"]._serialized_end = 119 - _globals["_APIVERSIONREQUEST"]._serialized_start = 121 - _globals["_APIVERSIONREQUEST"]._serialized_end = 140 - _globals["_APIVERSIONSERVICE"]._serialized_start = 142 - _globals["_APIVERSIONSERVICE"]._serialized_end = 235 -# @@protoc_insertion_point(module_scope) diff --git a/pyasic/web/bosminer/proto/braiins/__init__.py b/pyasic/web/bosminer/proto/braiins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pyasic/web/bosminer/proto/braiins/bos/__init__.py b/pyasic/web/bosminer/proto/braiins/bos/__init__.py new file mode 100644 index 00000000..f0341c49 --- /dev/null +++ b/pyasic/web/bosminer/proto/braiins/bos/__init__.py @@ -0,0 +1,80 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: bos/version.proto +# plugin: python-betterproto +# This file has been @generated + +from dataclasses import dataclass +from typing import ( + TYPE_CHECKING, + Dict, + Optional, +) + +import betterproto +import grpclib +from betterproto.grpc.grpclib_server import ServiceBase + + +if TYPE_CHECKING: + import grpclib.server + from betterproto.grpc.grpclib_client import MetadataLike + from grpclib.metadata import Deadline + + +@dataclass(eq=False, repr=False) +class ApiVersion(betterproto.Message): + """LATEST_API_VERSION=1.0.0-beta.4""" + + major: int = betterproto.uint64_field(1) + minor: int = betterproto.uint64_field(2) + patch: int = betterproto.uint64_field(3) + pre: str = betterproto.string_field(4) + build: str = betterproto.string_field(5) + + +@dataclass(eq=False, repr=False) +class ApiVersionRequest(betterproto.Message): + pass + + +class ApiVersionServiceStub(betterproto.ServiceStub): + async def get_api_version( + self, + api_version_request: "ApiVersionRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "ApiVersion": + return await self._unary_unary( + "/braiins.bos.ApiVersionService/GetApiVersion", + api_version_request, + ApiVersion, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class ApiVersionServiceBase(ServiceBase): + async def get_api_version( + self, api_version_request: "ApiVersionRequest" + ) -> "ApiVersion": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_api_version( + self, stream: "grpclib.server.Stream[ApiVersionRequest, ApiVersion]" + ) -> None: + request = await stream.recv_message() + response = await self.get_api_version(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.ApiVersionService/GetApiVersion": grpclib.const.Handler( + self.__rpc_get_api_version, + grpclib.const.Cardinality.UNARY_UNARY, + ApiVersionRequest, + ApiVersion, + ), + } diff --git a/pyasic/web/bosminer/proto/braiins/bos/v1/__init__.py b/pyasic/web/bosminer/proto/braiins/bos/v1/__init__.py new file mode 100644 index 00000000..eecc5c83 --- /dev/null +++ b/pyasic/web/bosminer/proto/braiins/bos/v1/__init__.py @@ -0,0 +1,3030 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: bos/v1/actions.proto, bos/v1/authentication.proto, bos/v1/common.proto, bos/v1/configuration.proto, bos/v1/constraints.proto, bos/v1/cooling.proto, bos/v1/license.proto, bos/v1/miner.proto, bos/v1/performance.proto, bos/v1/pool.proto, bos/v1/units.proto, bos/v1/work.proto +# plugin: python-betterproto +# This file has been @generated +import warnings +from dataclasses import dataclass +from datetime import datetime +from typing import ( + TYPE_CHECKING, + AsyncIterator, + Dict, + List, + Optional, +) + +import betterproto +import grpclib +from betterproto.grpc.grpclib_server import ServiceBase + + +if TYPE_CHECKING: + import grpclib.server + from betterproto.grpc.grpclib_client import MetadataLike + from grpclib.metadata import Deadline + + +class SaveAction(betterproto.Enum): + """Save action for different operations""" + + SAVE_ACTION_UNSPECIFIED = 0 + SAVE_ACTION_SAVE = 1 + SAVE_ACTION_SAVE_AND_APPLY = 2 + SAVE_ACTION_SAVE_AND_FORCE_APPLY = 3 + + +class CoolingMode(betterproto.Enum): + COOLING_MODE_UNSPECIFIED = 0 + COOLING_MODE_AUTO = 1 + COOLING_MODE_MANUAL = 2 + COOLING_MODE_DISABLED = 3 + + +class SensorLocation(betterproto.Enum): + SENSOR_LOCATION_UNSPECIFIED = 0 + SENSOR_LOCATION_CHIP = 1 + SENSOR_LOCATION_PCB = 2 + + +class TunerMode(betterproto.Enum): + TUNER_MODE_UNSPECIFIED = 0 + TUNER_MODE_POWER_TARGET = 1 + TUNER_MODE_HASHRATE_TARGET = 2 + + +class TunerState(betterproto.Enum): + TUNER_STATE_UNSPECIFIED = 0 + TUNER_STATE_DISABLED = 1 + TUNER_STATE_STABLE = 2 + TUNER_STATE_TUNING = 3 + TUNER_STATE_ERROR = 4 + + +class LicenseType(betterproto.Enum): + LICENSE_TYPE_UNSPECIFIED = 0 + LICENSE_TYPE_STANDARD = 1 + LICENSE_TYPE_CUSTOM = 2 + + +class Platform(betterproto.Enum): + """Supported platforms""" + + PLATFORM_UNSPECIFIED = 0 + PLATFORM_AM1_S9 = 1 + PLATFORM_AM2_S17 = 2 + PLATFORM_AM3_BBB = 3 + PLATFORM_AM3_AML = 4 + PLATFORM_STM32MP157C_II1_AM2 = 5 + PLATFORM_CVITEK_BM1_AM2 = 6 + + +class BosMode(betterproto.Enum): + """BOS modes enumeration""" + + BOS_MODE_UNSPECIFIED = 0 + BOS_MODE_UPGRADE = 1 + BOS_MODE_RECOVERY = 2 + BOS_MODE_SD = 3 + BOS_MODE_NAND = 4 + BOS_MODE_EMMC = 5 + + +class MinerBrand(betterproto.Enum): + MINER_BRAND_UNSPECIFIED = 0 + MINER_BRAND_ANTMINER = 1 + MINER_BRAND_WHATSMINER = 2 + + +class MinerModel(betterproto.Enum): + """Deprecated: This enumeration is not longer maintained""" + + MINER_MODEL_UNSPECIFIED = 0 + MINER_MODEL_ANTMINER_S9 = 1 + MINER_MODEL_ANTMINER_X17 = 2 + MINER_MODEL_ANTMINER_S17 = 3 + MINER_MODEL_ANTMINER_S17_PLUS = 4 + MINER_MODEL_ANTMINER_S17_PRO = 5 + MINER_MODEL_ANTMINER_S17E = 6 + MINER_MODEL_ANTMINER_T17 = 7 + MINER_MODEL_ANTMINER_T17E = 8 + MINER_MODEL_ANTMINER_T17_PLUS = 9 + MINER_MODEL_ANTMINER_X19 = 10 + MINER_MODEL_ANTMINER_S19 = 11 + MINER_MODEL_ANTMINER_S19_PRO = 12 + MINER_MODEL_ANTMINER_S19_PLUS = 13 + MINER_MODEL_ANTMINER_S19J = 14 + MINER_MODEL_ANTMINER_S19J_PRO = 15 + MINER_MODEL_ANTMINER_S19A = 16 + MINER_MODEL_ANTMINER_S19A_PRO = 17 + MINER_MODEL_ANTMINER_S19XP = 18 + MINER_MODEL_ANTMINER_T19 = 19 + MINER_MODEL_ANTMINER_S19J_PRO_PLUS = 20 + + +class MinerStatus(betterproto.Enum): + MINER_STATUS_UNSPECIFIED = 0 + MINER_STATUS_NOT_STARTED = 1 + MINER_STATUS_NORMAL = 2 + MINER_STATUS_PAUSED = 3 + MINER_STATUS_SUSPENDED = 4 + MINER_STATUS_RESTRICTED = 5 + + +class SupportArchiveFormat(betterproto.Enum): + """Enumeration for support archive format""" + + SUPPORT_ARCHIVE_FORMAT_UNSPECIFIED = 0 + SUPPORT_ARCHIVE_FORMAT_ZIP = 1 + """Compressed zip format""" + + SUPPORT_ARCHIVE_FORMAT_BOS = 2 + """BOS custom format""" + + +@dataclass(eq=False, repr=False) +class StartRequest(betterproto.Message): + """Request for start bosminer action.""" + + pass + + +@dataclass(eq=False, repr=False) +class StartResponse(betterproto.Message): + """Response for start bosminer action.""" + + already_running: bool = betterproto.bool_field(1) + """Flag that bosminer was already running""" + + +@dataclass(eq=False, repr=False) +class RestartRequest(betterproto.Message): + """Request for restart bosminer action.""" + + pass + + +@dataclass(eq=False, repr=False) +class RestartResponse(betterproto.Message): + """Response for restart bosminer action.""" + + already_running: bool = betterproto.bool_field(1) + """Flag that bosminer was already running""" + + +@dataclass(eq=False, repr=False) +class RebootRequest(betterproto.Message): + """Request for reboot bosminer action.""" + + pass + + +@dataclass(eq=False, repr=False) +class RebootResponse(betterproto.Message): + """Response for reboot bosminer action.""" + + pass + + +@dataclass(eq=False, repr=False) +class StopRequest(betterproto.Message): + """Request for stop bosminer action.""" + + pass + + +@dataclass(eq=False, repr=False) +class StopResponse(betterproto.Message): + """Response for stop bosminer action.""" + + already_stopped: bool = betterproto.bool_field(1) + """Flag that bosminer was already stopped""" + + +@dataclass(eq=False, repr=False) +class PauseMiningRequest(betterproto.Message): + """Request for pause mining action.""" + + pass + + +@dataclass(eq=False, repr=False) +class PauseMiningResponse(betterproto.Message): + """Response for pause mining action.""" + + already_paused: bool = betterproto.bool_field(1) + """Flag that miner mining was already paused""" + + +@dataclass(eq=False, repr=False) +class ResumeMiningRequest(betterproto.Message): + """Response for resume mining action.""" + + pass + + +@dataclass(eq=False, repr=False) +class ResumeMiningResponse(betterproto.Message): + """Response for resume mining action.""" + + already_mining: bool = betterproto.bool_field(1) + """Flag that miner was already mining""" + + +@dataclass(eq=False, repr=False) +class SetLocateDeviceStatusRequest(betterproto.Message): + """Request message to enable/disable locate device""" + + enable: bool = betterproto.bool_field(1) + + +@dataclass(eq=False, repr=False) +class LocateDeviceStatusResponse(betterproto.Message): + """Response with locate device status""" + + enabled: bool = betterproto.bool_field(1) + + +@dataclass(eq=False, repr=False) +class GetLocateDeviceStatusRequest(betterproto.Message): + """Request for locate device status action.""" + + pass + + +@dataclass(eq=False, repr=False) +class LoginRequest(betterproto.Message): + """Request for login action.""" + + username: str = betterproto.string_field(1) + password: str = betterproto.string_field(2) + + +@dataclass(eq=False, repr=False) +class LoginResponse(betterproto.Message): + """Response for login action.""" + + pass + + +@dataclass(eq=False, repr=False) +class SetPasswordRequest(betterproto.Message): + """Request for set password action.""" + + password: Optional[str] = betterproto.string_field( + 1, optional=True, group="_password" + ) + + +@dataclass(eq=False, repr=False) +class SetPasswordResponse(betterproto.Message): + """Response for set password action.""" + + pass + + +@dataclass(eq=False, repr=False) +class MegaHashrate(betterproto.Message): + megahash_per_second: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class GigaHashrate(betterproto.Message): + gigahash_per_second: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class TeraHashrate(betterproto.Message): + terahash_per_second: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class Frequency(betterproto.Message): + hertz: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class Voltage(betterproto.Message): + volt: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class Power(betterproto.Message): + watt: int = betterproto.uint64_field(1) + + +@dataclass(eq=False, repr=False) +class PowerEfficiency(betterproto.Message): + joule_per_terahash: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class Temperature(betterproto.Message): + degree_c: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class BasesPoints(betterproto.Message): + """Structure representing Basis Points""" + + bsp: int = betterproto.uint32_field(1) + """ + A basis point is one hundredth of 1 percentage point. For example: 1bps = + 0.01%, 250bps = 2.5% + """ + + +@dataclass(eq=False, repr=False) +class Hours(betterproto.Message): + hours: int = betterproto.uint32_field(1) + + +@dataclass(eq=False, repr=False) +class UInt32Constraints(betterproto.Message): + default: int = betterproto.uint32_field(1) + min: int = betterproto.uint32_field(2) + max: int = betterproto.uint32_field(3) + + +@dataclass(eq=False, repr=False) +class DoubleConstraints(betterproto.Message): + default: float = betterproto.double_field(1) + min: float = betterproto.double_field(2) + max: float = betterproto.double_field(3) + + +@dataclass(eq=False, repr=False) +class PowerConstraints(betterproto.Message): + default: "Power" = betterproto.message_field(1) + min: "Power" = betterproto.message_field(2) + max: "Power" = betterproto.message_field(3) + + +@dataclass(eq=False, repr=False) +class HashrateConstraints(betterproto.Message): + default: "TeraHashrate" = betterproto.message_field(1) + min: "TeraHashrate" = betterproto.message_field(2) + max: "TeraHashrate" = betterproto.message_field(3) + + +@dataclass(eq=False, repr=False) +class TemperatureConstraints(betterproto.Message): + default: "Temperature" = betterproto.message_field(1) + min: "Temperature" = betterproto.message_field(2) + max: "Temperature" = betterproto.message_field(3) + + +@dataclass(eq=False, repr=False) +class BooleanConstraint(betterproto.Message): + default: bool = betterproto.bool_field(1) + + +@dataclass(eq=False, repr=False) +class DurationConstraints(betterproto.Message): + default: "Hours" = betterproto.message_field(1) + min: "Hours" = betterproto.message_field(2) + max: "Hours" = betterproto.message_field(3) + + +@dataclass(eq=False, repr=False) +class FrequencyConstraints(betterproto.Message): + default: "Frequency" = betterproto.message_field(1) + min: "Frequency" = betterproto.message_field(2) + max: "Frequency" = betterproto.message_field(3) + + +@dataclass(eq=False, repr=False) +class VoltageConstraints(betterproto.Message): + default: "Voltage" = betterproto.message_field(1) + min: "Voltage" = betterproto.message_field(2) + max: "Voltage" = betterproto.message_field(3) + + +@dataclass(eq=False, repr=False) +class CoolingAutoMode(betterproto.Message): + """ + The temperature control modes. Miner software tries to regulate the fan + speed so that miner temperature is approximately at the target temperature. + The allowed temperature range is 0-200 degree Celsius. + """ + + target_temperature: "Temperature" = betterproto.message_field(1) + """Temperature that the miner will try to maintain""" + + hot_temperature: "Temperature" = betterproto.message_field(2) + """Temperature threshold at which the fans start to run at 100%.""" + + dangerous_temperature: "Temperature" = betterproto.message_field(3) + """ + Temperature threshold at which BOSMiner shuts down in order to prevent + overheating and damaging the miner. + """ + + +@dataclass(eq=False, repr=False) +class CoolingManualMode(betterproto.Message): + """ + Fans are kept at a fixed, user-defined speed, no matter the temperature. + """ + + fan_speed_ratio: Optional[float] = betterproto.double_field( + 1, optional=True, group="_fan_speed_ratio" + ) + """ + User defined fan speed expressed as a ratio between 0.0 and 1.0 where 0.0 + means completely turned off and 1.0 means running at full speed possible + """ + + hot_temperature: "Temperature" = betterproto.message_field(2) + """Temperature threshold at which the fans start to run at 100%.""" + + dangerous_temperature: "Temperature" = betterproto.message_field(3) + """ + Temperature threshold at which BOSMiner shuts down in order to prevent + overheating and damaging the miner. + """ + + +@dataclass(eq=False, repr=False) +class CoolingDisabledMode(betterproto.Message): + """Disable temperature control. May be dangerous.""" + + fan_speed_ratio: Optional[float] = betterproto.double_field( + 1, optional=True, group="_fan_speed_ratio" + ) + """ + User defined fan speed expressed as a ratio between 0.0 and 1.0 where 0.0 + means completely turned off and 1.0 means running at full speed possible + """ + + +@dataclass(eq=False, repr=False) +class CoolingConfiguration(betterproto.Message): + minimum_required_fans: Optional[int] = betterproto.uint32_field( + 1, optional=True, group="_minimum_required_fans" + ) + auto: "CoolingAutoMode" = betterproto.message_field(2, group="mode") + manual: "CoolingManualMode" = betterproto.message_field(3, group="mode") + disabled: "CoolingDisabledMode" = betterproto.message_field(4, group="mode") + + +@dataclass(eq=False, repr=False) +class CoolingConstraints(betterproto.Message): + default_cooling_mode: "CoolingMode" = betterproto.enum_field(1) + target_temperature: "TemperatureConstraints" = betterproto.message_field(2) + hot_temperature: "TemperatureConstraints" = betterproto.message_field(3) + dangerous_temperature: "TemperatureConstraints" = betterproto.message_field(4) + fan_speed_ratio: "DoubleConstraints" = betterproto.message_field(5) + minimum_required_fans: "UInt32Constraints" = betterproto.message_field(6) + + +@dataclass(eq=False, repr=False) +class FanState(betterproto.Message): + """Structure which contain info about one specific miner fan.""" + + position: Optional[int] = betterproto.uint32_field( + 1, optional=True, group="_position" + ) + """Fan positions/ID""" + + rpm: int = betterproto.uint32_field(2) + """Actual fan RPM (Revolutions/Rotation Per Minute)""" + + target_speed_ratio: Optional[float] = betterproto.double_field( + 3, optional=True, group="_target_speed_ratio" + ) + """Actual fan speed ratio(PWM) in range 0.0 - 1.0""" + + +@dataclass(eq=False, repr=False) +class TemperatureSensor(betterproto.Message): + id: Optional[int] = betterproto.uint32_field(1, optional=True, group="_id") + """Sensor id""" + + location: "SensorLocation" = betterproto.enum_field(2) + """Sensor location""" + + temperature: "Temperature" = betterproto.message_field(3) + """Temperature""" + + +@dataclass(eq=False, repr=False) +class GetCoolingStateRequest(betterproto.Message): + """Request to get current temperature and fans measurements""" + + pass + + +@dataclass(eq=False, repr=False) +class GetCoolingStateResponse(betterproto.Message): + """Response to get current fan states and temperature measurements""" + + fans: List["FanState"] = betterproto.message_field(1) + """All Fans state""" + + highest_temperature: "TemperatureSensor" = betterproto.message_field(2) + """Sensor with current highest temperature""" + + +@dataclass(eq=False, repr=False) +class SetImmersionModeRequest(betterproto.Message): + """Request to set immersion mode""" + + save_action: "SaveAction" = betterproto.enum_field(1) + enable_immersion_mode: bool = betterproto.bool_field(2) + """Flag to enable or disable immersion mode""" + + +@dataclass(eq=False, repr=False) +class SetImmersionModeResponse(betterproto.Message): + """Response for set immersion mode action.""" + + immersion_mode: bool = betterproto.bool_field(1) + """The resulting immersion mode""" + + +@dataclass(eq=False, repr=False) +class TunerConfiguration(betterproto.Message): + enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled") + """Flag if tuner is enabled""" + + tuner_mode: Optional["TunerMode"] = betterproto.enum_field( + 2, optional=True, group="_tuner_mode" + ) + """Tuner mode""" + + power_target: "Power" = betterproto.message_field(3) + """Tuner power target""" + + hashrate_target: "TeraHashrate" = betterproto.message_field(4) + """Tuner hashrate target""" + + +@dataclass(eq=False, repr=False) +class TunerConstraints(betterproto.Message): + power_target: "PowerConstraints" = betterproto.message_field(1) + hashrate_target: "HashrateConstraints" = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class DpsConfiguration(betterproto.Message): + enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled") + """Flag if Dynamic Performance Scaling is enabled""" + + power_step: "Power" = betterproto.message_field(2) + """Dynamic Performance Scaling power step""" + + hashrate_step: "TeraHashrate" = betterproto.message_field(3) + """Dynamic Performance Scaling hashrate step""" + + min_power_target: "Power" = betterproto.message_field(4) + """Dynamic Performance Scaling minimal power target""" + + min_hashrate_target: "TeraHashrate" = betterproto.message_field(5) + """Dynamic Performance Scaling minimal hashrate target""" + + shutdown_enabled: Optional[bool] = betterproto.bool_field( + 6, optional=True, group="_shutdown_enabled" + ) + """Flag if shutdown for Dynamic Performance Scaling is enabled""" + + shutdown_duration: "Hours" = betterproto.message_field(7) + """Dynamic Performance Scaling shutdown duration""" + + +@dataclass(eq=False, repr=False) +class HashboardPerformanceConfiguration(betterproto.Message): + global_frequency: "Frequency" = betterproto.message_field(1) + """Common frequency for all HB""" + + global_voltage: "Voltage" = betterproto.message_field(2) + """Common voltage for all HB""" + + hashboards: List["HashboardConfig"] = betterproto.message_field(3) + """ + Per hashboard frequency/voltage. It has higher priority than global one + """ + + +@dataclass(eq=False, repr=False) +class DpsConstraints(betterproto.Message): + power_step: "PowerConstraints" = betterproto.message_field(1) + """Dynamic Performance Scaling power step constraints""" + + hashrate_step: "HashrateConstraints" = betterproto.message_field(2) + """Dynamic Performance Scaling hashrate step constraints""" + + min_power_target: "PowerConstraints" = betterproto.message_field(3) + """Dynamic Performance Scaling minimal power target constraints""" + + min_hashrate_target: "HashrateConstraints" = betterproto.message_field(4) + """Dynamic Performance Scaling minimal hashrate target constraints""" + + shutdown_enabled: "BooleanConstraint" = betterproto.message_field(5) + """Dynamic Performance Scaling enabled shutdown default value""" + + shutdown_duration: "DurationConstraints" = betterproto.message_field(6) + """Dynamic Performance Scaling shutdown duration constraints""" + + +@dataclass(eq=False, repr=False) +class HashboardConstraints(betterproto.Message): + hashboard_ids: List[str] = betterproto.string_field(1) + """List of possible HB indices""" + + enabled: "BooleanConstraint" = betterproto.message_field(2) + """Default value for flag if hashboards are enabled""" + + frequency: "FrequencyConstraints" = betterproto.message_field(3) + """HB frequency constraints""" + + voltage: "VoltageConstraints" = betterproto.message_field(4) + """HB frequency constraints""" + + +@dataclass(eq=False, repr=False) +class PowerTargetProfile(betterproto.Message): + """Structure to handle power target profile""" + + created: datetime = betterproto.message_field(1) + """Creation timestamp""" + + target: "Power" = betterproto.message_field(2) + """Tuned power target""" + + measured_hashrate: "GigaHashrate" = betterproto.message_field(3) + """Measured hashrate""" + + estimated_power_consumption: "Power" = betterproto.message_field(4) + """Estimated power consumption""" + + +@dataclass(eq=False, repr=False) +class HashrateTargetProfile(betterproto.Message): + """Structure to handle hashrate target profile""" + + created: datetime = betterproto.message_field(1) + """Creation timestamp""" + + target: "TeraHashrate" = betterproto.message_field(2) + """Tuned hashrate target""" + + measured_hashrate: "GigaHashrate" = betterproto.message_field(3) + """Measured hashrate""" + + estimated_power_consumption: "Power" = betterproto.message_field(4) + """Estimated power consumption""" + + +@dataclass(eq=False, repr=False) +class GetTunerStateRequest(betterproto.Message): + """Request for getting the current performance data""" + + pass + + +@dataclass(eq=False, repr=False) +class GetTunerStateResponse(betterproto.Message): + """Response with the current tuner details""" + + overall_tuner_state: "TunerState" = betterproto.enum_field(1) + """Tuner state""" + + power_target_mode_state: "PowerTargetModeState" = betterproto.message_field( + 2, group="mode_state" + ) + hashrate_target_mode_state: "HashrateTargetModeState" = betterproto.message_field( + 3, group="mode_state" + ) + + +@dataclass(eq=False, repr=False) +class PowerTargetModeState(betterproto.Message): + profile: "PowerTargetProfile" = betterproto.message_field(1) + """current power target profile""" + + current_target: "Power" = betterproto.message_field(2) + """Current power target""" + + +@dataclass(eq=False, repr=False) +class HashrateTargetModeState(betterproto.Message): + profile: "HashrateTargetProfile" = betterproto.message_field(1) + """Currently used profile""" + + current_target: "TeraHashrate" = betterproto.message_field(2) + """Current hashrate target""" + + +@dataclass(eq=False, repr=False) +class ListTargetProfilesRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class ListTargetProfilesResponse(betterproto.Message): + power_target_profiles: List["PowerTargetProfile"] = betterproto.message_field(1) + """Tuner profiles for power target mode""" + + hashrate_target_profiles: List["HashrateTargetProfile"] = betterproto.message_field( + 2 + ) + + +@dataclass(eq=False, repr=False) +class SetDefaultPowerTargetRequest(betterproto.Message): + """Request for set default power target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + +@dataclass(eq=False, repr=False) +class SetPowerTargetRequest(betterproto.Message): + """Request for set absolute power target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + power_target: "Power" = betterproto.message_field(2) + """Absolute value of power target""" + + +@dataclass(eq=False, repr=False) +class IncrementPowerTargetRequest(betterproto.Message): + """Request for increment power target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + power_target_increment: "Power" = betterproto.message_field(2) + """Incremental value of power target""" + + +@dataclass(eq=False, repr=False) +class DecrementPowerTargetRequest(betterproto.Message): + """Request for decrement power target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + power_target_decrement: "Power" = betterproto.message_field(2) + """Decremental value of power target""" + + +@dataclass(eq=False, repr=False) +class SetPowerTargetResponse(betterproto.Message): + """Response for set power target action.""" + + power_target: "Power" = betterproto.message_field(1) + """New value of power target""" + + +@dataclass(eq=False, repr=False) +class SetDefaultHashrateTargetRequest(betterproto.Message): + """Request for set default hashrate target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + +@dataclass(eq=False, repr=False) +class SetHashrateTargetRequest(betterproto.Message): + """Request for set absolute hashrate target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + hashrate_target: "TeraHashrate" = betterproto.message_field(2) + """Absolute value of hashrate target""" + + +@dataclass(eq=False, repr=False) +class IncrementHashrateTargetRequest(betterproto.Message): + """Request for increment hashrate target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + hashrate_target_increment: "TeraHashrate" = betterproto.message_field(2) + """Incremental value of hashrate target""" + + +@dataclass(eq=False, repr=False) +class DecrementHashrateTargetRequest(betterproto.Message): + """Request for decrement hashrate target action.""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + hashrate_target_decrement: "TeraHashrate" = betterproto.message_field(2) + """Decremental value of hashrate target""" + + +@dataclass(eq=False, repr=False) +class SetHashrateTargetResponse(betterproto.Message): + """Response for set hashrate target action.""" + + hashrate_target: "TeraHashrate" = betterproto.message_field(1) + """New value of hashrate target""" + + +@dataclass(eq=False, repr=False) +class DpsPowerTarget(betterproto.Message): + power_step: "Power" = betterproto.message_field(1) + """Dynamic Performance Scaling power step""" + + min_power_target: "Power" = betterproto.message_field(2) + """Dynamic Performance Scaling minimal power target""" + + +@dataclass(eq=False, repr=False) +class DpsHashrateTarget(betterproto.Message): + hashrate_step: "TeraHashrate" = betterproto.message_field(1) + """Dynamic Performance Scaling hashrate step""" + + min_hashrate_target: "TeraHashrate" = betterproto.message_field(2) + """Dynamic Performance Scaling minimal hashrate target""" + + +@dataclass(eq=False, repr=False) +class DpsTarget(betterproto.Message): + power_target: "DpsPowerTarget" = betterproto.message_field(1, group="target") + """Power target settings for Dynamic Performance Scaling""" + + hashrate_target: "DpsHashrateTarget" = betterproto.message_field(2, group="target") + """Hashrate target settings for Dynamic Performance Scaling""" + + +@dataclass(eq=False, repr=False) +class SetDpsRequest(betterproto.Message): + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + enable: Optional[bool] = betterproto.bool_field(2, optional=True, group="_enable") + """Flag if Dynamic Performance Scaling should be enabled""" + + enable_shutdown: Optional[bool] = betterproto.bool_field( + 3, optional=True, group="_enable_shutdown" + ) + """Flag if shutdown for Dynamic Performance Scaling should be enabled""" + + shutdown_duration: Optional["Hours"] = betterproto.message_field( + 4, optional=True, group="_shutdown_duration" + ) + """Dynamic Performance Scaling shutdown duration""" + + target: "DpsTarget" = betterproto.message_field(5) + """Dynamic Performance Scaling target""" + + +@dataclass(eq=False, repr=False) +class SetDpsResponse(betterproto.Message): + enabled: Optional[bool] = betterproto.bool_field(1, optional=True, group="_enabled") + """Flag if Dynamic Performance Scaling is enabled""" + + shutdown_enabled: Optional[bool] = betterproto.bool_field( + 2, optional=True, group="_shutdown_enabled" + ) + """Flag if shutdown for Dynamic Performance Scaling should be enabled""" + + shutdown_duration: Optional["Hours"] = betterproto.message_field( + 3, optional=True, group="_shutdown_duration" + ) + """Dynamic Performance Scaling shutdown duration""" + + power_target: "DpsPowerTarget" = betterproto.message_field(4) + """Dynamic Performance Scaling Power target""" + + hashrate_target: "DpsHashrateTarget" = betterproto.message_field(5) + """Dynamic Performance Scaling hashrate target""" + + +@dataclass(eq=False, repr=False) +class HashboardPerformanceSettings(betterproto.Message): + id: str = betterproto.string_field(1) + """Hashboard id""" + + frequency: "Frequency" = betterproto.message_field(2) + """Hashboard frequency""" + + voltage: "Voltage" = betterproto.message_field(3) + """Hashboard voltage""" + + +@dataclass(eq=False, repr=False) +class HashboardConfig(betterproto.Message): + id: str = betterproto.string_field(1) + """Hashboard id""" + + enabled: Optional[bool] = betterproto.bool_field(2, optional=True, group="_enabled") + """Flag if HB si enabled""" + + frequency: "Frequency" = betterproto.message_field(3) + """Hashboard frequency""" + + voltage: "Voltage" = betterproto.message_field(4) + """Hashboard voltage""" + + +@dataclass(eq=False, repr=False) +class ManualPerformanceMode(betterproto.Message): + global_frequency: "Frequency" = betterproto.message_field(1) + """Global hashboard frequency""" + + global_voltage: "Voltage" = betterproto.message_field(2) + """Global hashboard voltage""" + + hashboards: List["HashboardPerformanceSettings"] = betterproto.message_field(3) + """ + Per hashboard frequency/voltage. It has higher priority than global one + """ + + +@dataclass(eq=False, repr=False) +class PowerTargetMode(betterproto.Message): + power_target: "Power" = betterproto.message_field(1) + """Power target""" + + +@dataclass(eq=False, repr=False) +class HashrateTargetMode(betterproto.Message): + hashrate_target: "TeraHashrate" = betterproto.message_field(1) + """Hashrate target""" + + +@dataclass(eq=False, repr=False) +class TunerPerformanceMode(betterproto.Message): + power_target: "PowerTargetMode" = betterproto.message_field(1, group="target") + """Tuner power target""" + + hashrate_target: "HashrateTargetMode" = betterproto.message_field(2, group="target") + """Tuner hashrate target""" + + +@dataclass(eq=False, repr=False) +class SetPerformanceModeRequest(betterproto.Message): + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + mode: "PerformanceMode" = betterproto.message_field(2) + + +@dataclass(eq=False, repr=False) +class PerformanceMode(betterproto.Message): + manual_mode: "ManualPerformanceMode" = betterproto.message_field(1, group="mode") + tuner_mode: "TunerPerformanceMode" = betterproto.message_field(2, group="mode") + + +@dataclass(eq=False, repr=False) +class GetPerformanceModeRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class RemoveTunedProfilesRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class RemoveTunedProfilesResponse(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class Quota(betterproto.Message): + """Structure for quota load balance strategy""" + + value: int = betterproto.uint32_field(1) + + +@dataclass(eq=False, repr=False) +class FixedShareRatio(betterproto.Message): + """ + Structure for fixed share ratio load balance strategy Fixed share ratio is + value between 0.0 to 1.0 where 1.0 represents that all work is generated + from the group + """ + + value: float = betterproto.double_field(1) + + +@dataclass(eq=False, repr=False) +class PoolGroupConfiguration(betterproto.Message): + """Structure handle configured mining group""" + + uid: str = betterproto.string_field(1) + """Group id""" + + name: str = betterproto.string_field(2) + """Group name""" + + quota: "Quota" = betterproto.message_field(3, group="load_balance_strategy") + fixed_share_ratio: "FixedShareRatio" = betterproto.message_field( + 4, group="load_balance_strategy" + ) + pools: List["PoolConfiguration"] = betterproto.message_field(5) + """Group pools""" + + +@dataclass(eq=False, repr=False) +class PoolConfiguration(betterproto.Message): + """Structure handle information about configured pool""" + + uid: str = betterproto.string_field(1) + """Pool connection id""" + + url: str = betterproto.string_field(2) + """Pool connection URL""" + + user: str = betterproto.string_field(3) + """Pool connection user""" + + password: Optional[str] = betterproto.string_field( + 4, optional=True, group="_password" + ) + """Pool connection password if set""" + + enabled: Optional[bool] = betterproto.bool_field(5, optional=True, group="_enabled") + """Flag if pool connection is enabled""" + + +@dataclass(eq=False, repr=False) +class PoolGroup(betterproto.Message): + """Structure handle all pool group details""" + + name: str = betterproto.string_field(1) + """Group name""" + + quota: "Quota" = betterproto.message_field(2, group="strategy") + fixed_share_ratio: "FixedShareRatio" = betterproto.message_field( + 3, group="strategy" + ) + pools: List["Pool"] = betterproto.message_field(4) + """Group pools""" + + +@dataclass(eq=False, repr=False) +class Pool(betterproto.Message): + """Structure handle information about configured pool""" + + uid: str = betterproto.string_field(1) + """Pool connection id""" + + url: str = betterproto.string_field(2) + """Pool connection URL""" + + user: str = betterproto.string_field(3) + """Pool connection user""" + + enabled: bool = betterproto.bool_field(4) + """Flag if pool connection is enabled""" + + alive: bool = betterproto.bool_field(5) + """Flag if pool is alive""" + + active: bool = betterproto.bool_field(6) + """Flag if pool is active (running)""" + + stats: "PoolStats" = betterproto.message_field(7) + """Pool stats""" + + +@dataclass(eq=False, repr=False) +class PoolStats(betterproto.Message): + """Structure handle pool statistics""" + + accepted_shares: int = betterproto.uint64_field(1) + """Accepted shares""" + + rejected_shares: int = betterproto.uint64_field(2) + """Rejected shares""" + + stale_shares: int = betterproto.uint64_field(3) + """Stale shares""" + + last_difficulty: int = betterproto.uint64_field(4) + """Last difficulty""" + + best_share: int = betterproto.uint64_field(5) + """Best share""" + + generated_work: int = betterproto.uint64_field(6) + """Generated work""" + + +@dataclass(eq=False, repr=False) +class GetPoolGroupsRequest(betterproto.Message): + """Request to get pool groups data""" + + pass + + +@dataclass(eq=False, repr=False) +class GetPoolGroupsResponse(betterproto.Message): + """Response on getting pool group data""" + + pool_groups: List["PoolGroup"] = betterproto.message_field(1) + """All pool groups details""" + + +@dataclass(eq=False, repr=False) +class CreatePoolGroupRequest(betterproto.Message): + """ + Request for pool group create action group.uid must not be specified (it + will be generated) group.pools[].uid must not be specified (it will be + generated) + """ + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + group: "PoolGroupConfiguration" = betterproto.message_field(2) + """Group configuration""" + + +@dataclass(eq=False, repr=False) +class CreatePoolGroupResponse(betterproto.Message): + """Response for pool group create action""" + + group: "PoolGroupConfiguration" = betterproto.message_field(1) + """Group configuration""" + + +@dataclass(eq=False, repr=False) +class UpdatePoolGroupRequest(betterproto.Message): + """ + Request for pool group update action group.uid must be specified and + represents unique id of group which will be updated group.pools[].uid must + not be specified (it will be generated) + """ + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + group: "PoolGroupConfiguration" = betterproto.message_field(2) + """New group configuration""" + + +@dataclass(eq=False, repr=False) +class UpdatePoolGroupResponse(betterproto.Message): + """Response for pool group update action""" + + group: "PoolGroupConfiguration" = betterproto.message_field(1) + """Group configuration""" + + +@dataclass(eq=False, repr=False) +class RemovePoolGroupRequest(betterproto.Message): + """Request for pool group remove action""" + + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + uid: str = betterproto.string_field(2) + """Group id""" + + +@dataclass(eq=False, repr=False) +class RemovePoolGroupResponse(betterproto.Message): + """Response for pool group remove action""" + + pass + + +@dataclass(eq=False, repr=False) +class GetMinerConfigurationRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetMinerConfigurationResponse(betterproto.Message): + """Structure to return miner configuration""" + + pool_groups: List["PoolGroupConfiguration"] = betterproto.message_field(1) + """Pool groups configuration""" + + temperature: "CoolingConfiguration" = betterproto.message_field(2) + """Cooling configuration""" + + tuner: "TunerConfiguration" = betterproto.message_field(3) + """Tuner configuration""" + + dps: "DpsConfiguration" = betterproto.message_field(4) + """DPS configuration""" + + hashboard_config: "HashboardPerformanceConfiguration" = betterproto.message_field(5) + """Hashchain configuration""" + + +@dataclass(eq=False, repr=False) +class GetConstraintsRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetConstraintsResponse(betterproto.Message): + tuner_constraints: "TunerConstraints" = betterproto.message_field(1) + """Tuner constraints""" + + cooling_constraints: "CoolingConstraints" = betterproto.message_field(2) + """Cooling constraints""" + + dps_constraints: "DpsConstraints" = betterproto.message_field(3) + """Dynamic Performance scaling constraints""" + + hashboards_constraints: "HashboardConstraints" = betterproto.message_field(4) + """Hashboard constraints""" + + +@dataclass(eq=False, repr=False) +class NoneLicense(betterproto.Message): + time_to_restricted: int = betterproto.uint32_field(1) + """ + BOS Initialization timeout - number of seconds elapsed since bosminer start + i.e., number of seconds BOS will start mining in restricted mode burning + 15% of hashrate + """ + + +@dataclass(eq=False, repr=False) +class LimitedLicense(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class ValidLicense(betterproto.Message): + type: "LicenseType" = betterproto.enum_field(1) + """License type""" + + contract_name: str = betterproto.string_field(2) + """Contract name""" + + time_to_restricted: int = betterproto.uint32_field(3) + """ + Current license expiration - number of seconds since the moment the license + was received i.e., number of seconds BOS will start mining in restricted + mode burning 15% of hashrate + """ + + dev_fee: "BasesPoints" = betterproto.message_field(4) + """Defines how much DevFee should be generated by the device.""" + + +@dataclass(eq=False, repr=False) +class ExpiredLicense(betterproto.Message): + type: "LicenseType" = betterproto.enum_field(1) + """License type""" + + contract_name: str = betterproto.string_field(2) + """Contract name""" + + dev_fee: "BasesPoints" = betterproto.message_field(3) + """Defines how much DevFee should be generated by the device.""" + + +@dataclass(eq=False, repr=False) +class GetLicenseStateRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetLicenseStateResponse(betterproto.Message): + none: "NoneLicense" = betterproto.message_field(1, group="state") + limited: "LimitedLicense" = betterproto.message_field(2, group="state") + valid: "ValidLicense" = betterproto.message_field(3, group="state") + expired: "ExpiredLicense" = betterproto.message_field(4, group="state") + + +@dataclass(eq=False, repr=False) +class RealHashrate(betterproto.Message): + """Structure which contains HB real hashrate stats.""" + + last_5_s: "GigaHashrate" = betterproto.message_field(1) + last_15_s: "GigaHashrate" = betterproto.message_field(2) + last_30_s: "GigaHashrate" = betterproto.message_field(3) + last_1_m: "GigaHashrate" = betterproto.message_field(4) + last_5_m: "GigaHashrate" = betterproto.message_field(5) + last_15_m: "GigaHashrate" = betterproto.message_field(6) + last_30_m: "GigaHashrate" = betterproto.message_field(7) + last_1_h: "GigaHashrate" = betterproto.message_field(8) + last_24_h: "GigaHashrate" = betterproto.message_field(9) + since_restart: "GigaHashrate" = betterproto.message_field(10) + + +@dataclass(eq=False, repr=False) +class WorkSolverStats(betterproto.Message): + """Structure to handle HB state""" + + real_hashrate: "RealHashrate" = betterproto.message_field(1) + """Real hashrate""" + + nominal_hashrate: "GigaHashrate" = betterproto.message_field(2) + """Nominal hashrate""" + + error_hashrate: "MegaHashrate" = betterproto.message_field(3) + """Error hashrate""" + + found_blocks: int = betterproto.uint32_field(4) + """Found blocks""" + + best_share: int = betterproto.uint64_field(5) + """Best share""" + + +@dataclass(eq=False, repr=False) +class MinerIdentity(betterproto.Message): + brand: "MinerBrand" = betterproto.enum_field(1) + model: "MinerModel" = betterproto.enum_field(2) + """ + Deprecated: Use miner_model instead. This field is no longer supported. + """ + + name: str = betterproto.string_field(3) + miner_model: str = betterproto.string_field(4) + + def __post_init__(self) -> None: + super().__post_init__() + if self.is_set("model"): + warnings.warn("MinerIdentity.model is deprecated", DeprecationWarning) + + +@dataclass(eq=False, repr=False) +class BosVersion(betterproto.Message): + """Structure to handle BOS version""" + + current: str = betterproto.string_field(1) + major: str = betterproto.string_field(2) + bos_plus: bool = betterproto.bool_field(3) + + +@dataclass(eq=False, repr=False) +class GetMinerStatusRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetMinerStatusResponse(betterproto.Message): + status: "MinerStatus" = betterproto.enum_field(1) + + +@dataclass(eq=False, repr=False) +class GetMinerDetailsRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetMinerDetailsResponse(betterproto.Message): + """Structure to handle system details""" + + uid: str = betterproto.string_field(1) + """Miner ID""" + + miner_identity: "MinerIdentity" = betterproto.message_field(2) + """Miner identity""" + + platform: "Platform" = betterproto.enum_field(3) + """Miner platform""" + + bos_mode: "BosMode" = betterproto.enum_field(4) + """BOS Mode""" + + bos_version: "BosVersion" = betterproto.message_field(5) + """OS Version""" + + hostname: str = betterproto.string_field(6) + """Hostname""" + + mac_address: str = betterproto.string_field(7) + """MAC address""" + + system_uptime: int = betterproto.uint64_field(8) + """Deprecated: Use system_uptime_s""" + + sticker_hashrate: "GigaHashrate" = betterproto.message_field(9) + """Miner hashrate declared by manufacturer""" + + bosminer_uptime_s: int = betterproto.uint64_field(10) + """Bosminer uptime. `0` means bosminer is not running""" + + system_uptime_s: int = betterproto.uint64_field(11) + """System uptime""" + + def __post_init__(self) -> None: + super().__post_init__() + if self.is_set("system_uptime"): + warnings.warn( + "GetMinerDetailsResponse.system_uptime is deprecated", + DeprecationWarning, + ) + + +@dataclass(eq=False, repr=False) +class MinerPowerStats(betterproto.Message): + approximated_consumption: "Power" = betterproto.message_field(1) + """Miner approximated power consumption.""" + + efficiency: "PowerEfficiency" = betterproto.message_field(2) + """Miner power efficiency""" + + +@dataclass(eq=False, repr=False) +class GetMinerStatsRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetMinerStatsResponse(betterproto.Message): + pool_stats: "PoolStats" = betterproto.message_field(1) + """Aggregate pool statistic""" + + miner_stats: "WorkSolverStats" = betterproto.message_field(2) + """Miner hashrate statistics""" + + power_stats: "MinerPowerStats" = betterproto.message_field(3) + """Miner power stats""" + + +@dataclass(eq=False, repr=False) +class Hashboard(betterproto.Message): + """Structure to handle HB details""" + + id: str = betterproto.string_field(1) + """Hashboard ID""" + + enabled: bool = betterproto.bool_field(2) + """Flag if HB is enabled""" + + chips_count: Optional[int] = betterproto.message_field( + 3, wraps=betterproto.TYPE_UINT32 + ) + """Number of chips""" + + current_voltage: "Voltage" = betterproto.message_field(4) + """Current HB voltage in volts""" + + current_frequency: "Frequency" = betterproto.message_field(5) + """Current HB frequency in Hz""" + + highest_chip_temp: "TemperatureSensor" = betterproto.message_field(6) + """Highest chip temperature""" + + board_temp: "Temperature" = betterproto.message_field(7) + """Board temperature""" + + stats: "WorkSolverStats" = betterproto.message_field(8) + """Hashboard stats""" + + +@dataclass(eq=False, repr=False) +class GetSupportArchiveRequest(betterproto.Message): + format: "SupportArchiveFormat" = betterproto.enum_field(1) + """Support archive format.""" + + +@dataclass(eq=False, repr=False) +class GetSupportArchiveResponse(betterproto.Message): + chunk_data: bytes = betterproto.bytes_field(1) + """Support archive data""" + + +@dataclass(eq=False, repr=False) +class GetHashboardsRequest(betterproto.Message): + pass + + +@dataclass(eq=False, repr=False) +class GetHashboardsResponse(betterproto.Message): + hashboards: List["Hashboard"] = betterproto.message_field(1) + """All HB details""" + + +@dataclass(eq=False, repr=False) +class EnableHashboardsRequest(betterproto.Message): + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + hashboard_ids: List[str] = betterproto.string_field(2) + """IDs of HB that should be enabled""" + + +@dataclass(eq=False, repr=False) +class EnableHashboardsResponse(betterproto.Message): + hashboards: List["HashboardEnableState"] = betterproto.message_field(1) + """Hashboards enable state""" + + +@dataclass(eq=False, repr=False) +class DisableHashboardsRequest(betterproto.Message): + save_action: "SaveAction" = betterproto.enum_field(1) + """Save action""" + + hashboard_ids: List[str] = betterproto.string_field(2) + """IDs of HB that should be disabled""" + + +@dataclass(eq=False, repr=False) +class DisableHashboardsResponse(betterproto.Message): + hashboards: List["HashboardEnableState"] = betterproto.message_field(1) + """Hashboards disable state""" + + +@dataclass(eq=False, repr=False) +class HashboardEnableState(betterproto.Message): + id: str = betterproto.string_field(1) + """Hashboard ID""" + + is_enabled: bool = betterproto.bool_field(2) + """Flag if hashboard is enabled or not""" + + +class ActionsServiceStub(betterproto.ServiceStub): + async def start( + self, + start_request: "StartRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "StartResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/Start", + start_request, + StartResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def stop( + self, + stop_request: "StopRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "StopResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/Stop", + stop_request, + StopResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def pause_mining( + self, + pause_mining_request: "PauseMiningRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "PauseMiningResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/PauseMining", + pause_mining_request, + PauseMiningResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def resume_mining( + self, + resume_mining_request: "ResumeMiningRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "ResumeMiningResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/ResumeMining", + resume_mining_request, + ResumeMiningResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def restart( + self, + restart_request: "RestartRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "RestartResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/Restart", + restart_request, + RestartResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def reboot( + self, + reboot_request: "RebootRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "RebootResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/Reboot", + reboot_request, + RebootResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_locate_device_status( + self, + set_locate_device_status_request: "SetLocateDeviceStatusRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "LocateDeviceStatusResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/SetLocateDeviceStatus", + set_locate_device_status_request, + LocateDeviceStatusResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def get_locate_device_status( + self, + get_locate_device_status_request: "GetLocateDeviceStatusRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "LocateDeviceStatusResponse": + return await self._unary_unary( + "/braiins.bos.v1.ActionsService/GetLocateDeviceStatus", + get_locate_device_status_request, + LocateDeviceStatusResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class AuthenticationServiceStub(betterproto.ServiceStub): + async def login( + self, + login_request: "LoginRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "LoginResponse": + return await self._unary_unary( + "/braiins.bos.v1.AuthenticationService/Login", + login_request, + LoginResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_password( + self, + set_password_request: "SetPasswordRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetPasswordResponse": + return await self._unary_unary( + "/braiins.bos.v1.AuthenticationService/SetPassword", + set_password_request, + SetPasswordResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class CoolingServiceStub(betterproto.ServiceStub): + async def get_cooling_state( + self, + get_cooling_state_request: "GetCoolingStateRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetCoolingStateResponse": + return await self._unary_unary( + "/braiins.bos.v1.CoolingService/GetCoolingState", + get_cooling_state_request, + GetCoolingStateResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_immersion_mode( + self, + set_immersion_mode_request: "SetImmersionModeRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetImmersionModeResponse": + return await self._unary_unary( + "/braiins.bos.v1.CoolingService/SetImmersionMode", + set_immersion_mode_request, + SetImmersionModeResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class PerformanceServiceStub(betterproto.ServiceStub): + async def get_tuner_state( + self, + get_tuner_state_request: "GetTunerStateRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetTunerStateResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/GetTunerState", + get_tuner_state_request, + GetTunerStateResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def list_target_profiles( + self, + list_target_profiles_request: "ListTargetProfilesRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "ListTargetProfilesResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/ListTargetProfiles", + list_target_profiles_request, + ListTargetProfilesResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_default_power_target( + self, + set_default_power_target_request: "SetDefaultPowerTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetPowerTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/SetDefaultPowerTarget", + set_default_power_target_request, + SetPowerTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_power_target( + self, + set_power_target_request: "SetPowerTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetPowerTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/SetPowerTarget", + set_power_target_request, + SetPowerTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def increment_power_target( + self, + increment_power_target_request: "IncrementPowerTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetPowerTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/IncrementPowerTarget", + increment_power_target_request, + SetPowerTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def decrement_power_target( + self, + decrement_power_target_request: "DecrementPowerTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetPowerTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/DecrementPowerTarget", + decrement_power_target_request, + SetPowerTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_default_hashrate_target( + self, + set_default_hashrate_target_request: "SetDefaultHashrateTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetHashrateTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget", + set_default_hashrate_target_request, + SetHashrateTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_hashrate_target( + self, + set_hashrate_target_request: "SetHashrateTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetHashrateTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/SetHashrateTarget", + set_hashrate_target_request, + SetHashrateTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def increment_hashrate_target( + self, + increment_hashrate_target_request: "IncrementHashrateTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetHashrateTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/IncrementHashrateTarget", + increment_hashrate_target_request, + SetHashrateTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def decrement_hashrate_target( + self, + decrement_hashrate_target_request: "DecrementHashrateTargetRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetHashrateTargetResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/DecrementHashrateTarget", + decrement_hashrate_target_request, + SetHashrateTargetResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_dps( + self, + set_dps_request: "SetDpsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "SetDpsResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/SetDPS", + set_dps_request, + SetDpsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def set_performance_mode( + self, + set_performance_mode_request: "SetPerformanceModeRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "PerformanceMode": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/SetPerformanceMode", + set_performance_mode_request, + PerformanceMode, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def get_active_performance_mode( + self, + get_performance_mode_request: "GetPerformanceModeRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "PerformanceMode": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/GetActivePerformanceMode", + get_performance_mode_request, + PerformanceMode, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def remove_tuned_profiles( + self, + remove_tuned_profiles_request: "RemoveTunedProfilesRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "RemoveTunedProfilesResponse": + return await self._unary_unary( + "/braiins.bos.v1.PerformanceService/RemoveTunedProfiles", + remove_tuned_profiles_request, + RemoveTunedProfilesResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class PoolServiceStub(betterproto.ServiceStub): + async def get_pool_groups( + self, + get_pool_groups_request: "GetPoolGroupsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetPoolGroupsResponse": + return await self._unary_unary( + "/braiins.bos.v1.PoolService/GetPoolGroups", + get_pool_groups_request, + GetPoolGroupsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def create_pool_group( + self, + create_pool_group_request: "CreatePoolGroupRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "CreatePoolGroupResponse": + return await self._unary_unary( + "/braiins.bos.v1.PoolService/CreatePoolGroup", + create_pool_group_request, + CreatePoolGroupResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def update_pool_group( + self, + update_pool_group_request: "UpdatePoolGroupRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "UpdatePoolGroupResponse": + return await self._unary_unary( + "/braiins.bos.v1.PoolService/UpdatePoolGroup", + update_pool_group_request, + UpdatePoolGroupResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def remove_pool_group( + self, + remove_pool_group_request: "RemovePoolGroupRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "RemovePoolGroupResponse": + return await self._unary_unary( + "/braiins.bos.v1.PoolService/RemovePoolGroup", + remove_pool_group_request, + RemovePoolGroupResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class ConfigurationServiceStub(betterproto.ServiceStub): + async def get_miner_configuration( + self, + get_miner_configuration_request: "GetMinerConfigurationRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetMinerConfigurationResponse": + return await self._unary_unary( + "/braiins.bos.v1.ConfigurationService/GetMinerConfiguration", + get_miner_configuration_request, + GetMinerConfigurationResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def get_constraints( + self, + get_constraints_request: "GetConstraintsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetConstraintsResponse": + return await self._unary_unary( + "/braiins.bos.v1.ConfigurationService/GetConstraints", + get_constraints_request, + GetConstraintsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class LicenseServiceStub(betterproto.ServiceStub): + async def get_license_state( + self, + get_license_state_request: "GetLicenseStateRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetLicenseStateResponse": + return await self._unary_unary( + "/braiins.bos.v1.LicenseService/GetLicenseState", + get_license_state_request, + GetLicenseStateResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class MinerServiceStub(betterproto.ServiceStub): + async def get_miner_status( + self, + get_miner_status_request: "GetMinerStatusRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> AsyncIterator["GetMinerStatusResponse"]: + async for response in self._unary_stream( + "/braiins.bos.v1.MinerService/GetMinerStatus", + get_miner_status_request, + GetMinerStatusResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ): + yield response + + async def get_miner_details( + self, + get_miner_details_request: "GetMinerDetailsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetMinerDetailsResponse": + return await self._unary_unary( + "/braiins.bos.v1.MinerService/GetMinerDetails", + get_miner_details_request, + GetMinerDetailsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def get_miner_stats( + self, + get_miner_stats_request: "GetMinerStatsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetMinerStatsResponse": + return await self._unary_unary( + "/braiins.bos.v1.MinerService/GetMinerStats", + get_miner_stats_request, + GetMinerStatsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def get_hashboards( + self, + get_hashboards_request: "GetHashboardsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "GetHashboardsResponse": + return await self._unary_unary( + "/braiins.bos.v1.MinerService/GetHashboards", + get_hashboards_request, + GetHashboardsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def get_support_archive( + self, + get_support_archive_request: "GetSupportArchiveRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> AsyncIterator["GetSupportArchiveResponse"]: + async for response in self._unary_stream( + "/braiins.bos.v1.MinerService/GetSupportArchive", + get_support_archive_request, + GetSupportArchiveResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ): + yield response + + async def enable_hashboards( + self, + enable_hashboards_request: "EnableHashboardsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "EnableHashboardsResponse": + return await self._unary_unary( + "/braiins.bos.v1.MinerService/EnableHashboards", + enable_hashboards_request, + EnableHashboardsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + async def disable_hashboards( + self, + disable_hashboards_request: "DisableHashboardsRequest", + *, + timeout: Optional[float] = None, + deadline: Optional["Deadline"] = None, + metadata: Optional["MetadataLike"] = None + ) -> "DisableHashboardsResponse": + return await self._unary_unary( + "/braiins.bos.v1.MinerService/DisableHashboards", + disable_hashboards_request, + DisableHashboardsResponse, + timeout=timeout, + deadline=deadline, + metadata=metadata, + ) + + +class ActionsServiceBase(ServiceBase): + async def start(self, start_request: "StartRequest") -> "StartResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def stop(self, stop_request: "StopRequest") -> "StopResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def pause_mining( + self, pause_mining_request: "PauseMiningRequest" + ) -> "PauseMiningResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def resume_mining( + self, resume_mining_request: "ResumeMiningRequest" + ) -> "ResumeMiningResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def restart(self, restart_request: "RestartRequest") -> "RestartResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def reboot(self, reboot_request: "RebootRequest") -> "RebootResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_locate_device_status( + self, set_locate_device_status_request: "SetLocateDeviceStatusRequest" + ) -> "LocateDeviceStatusResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def get_locate_device_status( + self, get_locate_device_status_request: "GetLocateDeviceStatusRequest" + ) -> "LocateDeviceStatusResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_start( + self, stream: "grpclib.server.Stream[StartRequest, StartResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.start(request) + await stream.send_message(response) + + async def __rpc_stop( + self, stream: "grpclib.server.Stream[StopRequest, StopResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.stop(request) + await stream.send_message(response) + + async def __rpc_pause_mining( + self, stream: "grpclib.server.Stream[PauseMiningRequest, PauseMiningResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.pause_mining(request) + await stream.send_message(response) + + async def __rpc_resume_mining( + self, stream: "grpclib.server.Stream[ResumeMiningRequest, ResumeMiningResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.resume_mining(request) + await stream.send_message(response) + + async def __rpc_restart( + self, stream: "grpclib.server.Stream[RestartRequest, RestartResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.restart(request) + await stream.send_message(response) + + async def __rpc_reboot( + self, stream: "grpclib.server.Stream[RebootRequest, RebootResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.reboot(request) + await stream.send_message(response) + + async def __rpc_set_locate_device_status( + self, + stream: "grpclib.server.Stream[SetLocateDeviceStatusRequest, LocateDeviceStatusResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.set_locate_device_status(request) + await stream.send_message(response) + + async def __rpc_get_locate_device_status( + self, + stream: "grpclib.server.Stream[GetLocateDeviceStatusRequest, LocateDeviceStatusResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_locate_device_status(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.ActionsService/Start": grpclib.const.Handler( + self.__rpc_start, + grpclib.const.Cardinality.UNARY_UNARY, + StartRequest, + StartResponse, + ), + "/braiins.bos.v1.ActionsService/Stop": grpclib.const.Handler( + self.__rpc_stop, + grpclib.const.Cardinality.UNARY_UNARY, + StopRequest, + StopResponse, + ), + "/braiins.bos.v1.ActionsService/PauseMining": grpclib.const.Handler( + self.__rpc_pause_mining, + grpclib.const.Cardinality.UNARY_UNARY, + PauseMiningRequest, + PauseMiningResponse, + ), + "/braiins.bos.v1.ActionsService/ResumeMining": grpclib.const.Handler( + self.__rpc_resume_mining, + grpclib.const.Cardinality.UNARY_UNARY, + ResumeMiningRequest, + ResumeMiningResponse, + ), + "/braiins.bos.v1.ActionsService/Restart": grpclib.const.Handler( + self.__rpc_restart, + grpclib.const.Cardinality.UNARY_UNARY, + RestartRequest, + RestartResponse, + ), + "/braiins.bos.v1.ActionsService/Reboot": grpclib.const.Handler( + self.__rpc_reboot, + grpclib.const.Cardinality.UNARY_UNARY, + RebootRequest, + RebootResponse, + ), + "/braiins.bos.v1.ActionsService/SetLocateDeviceStatus": grpclib.const.Handler( + self.__rpc_set_locate_device_status, + grpclib.const.Cardinality.UNARY_UNARY, + SetLocateDeviceStatusRequest, + LocateDeviceStatusResponse, + ), + "/braiins.bos.v1.ActionsService/GetLocateDeviceStatus": grpclib.const.Handler( + self.__rpc_get_locate_device_status, + grpclib.const.Cardinality.UNARY_UNARY, + GetLocateDeviceStatusRequest, + LocateDeviceStatusResponse, + ), + } + + +class AuthenticationServiceBase(ServiceBase): + async def login(self, login_request: "LoginRequest") -> "LoginResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_password( + self, set_password_request: "SetPasswordRequest" + ) -> "SetPasswordResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_login( + self, stream: "grpclib.server.Stream[LoginRequest, LoginResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.login(request) + await stream.send_message(response) + + async def __rpc_set_password( + self, stream: "grpclib.server.Stream[SetPasswordRequest, SetPasswordResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.set_password(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.AuthenticationService/Login": grpclib.const.Handler( + self.__rpc_login, + grpclib.const.Cardinality.UNARY_UNARY, + LoginRequest, + LoginResponse, + ), + "/braiins.bos.v1.AuthenticationService/SetPassword": grpclib.const.Handler( + self.__rpc_set_password, + grpclib.const.Cardinality.UNARY_UNARY, + SetPasswordRequest, + SetPasswordResponse, + ), + } + + +class CoolingServiceBase(ServiceBase): + async def get_cooling_state( + self, get_cooling_state_request: "GetCoolingStateRequest" + ) -> "GetCoolingStateResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_immersion_mode( + self, set_immersion_mode_request: "SetImmersionModeRequest" + ) -> "SetImmersionModeResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_cooling_state( + self, + stream: "grpclib.server.Stream[GetCoolingStateRequest, GetCoolingStateResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_cooling_state(request) + await stream.send_message(response) + + async def __rpc_set_immersion_mode( + self, + stream: "grpclib.server.Stream[SetImmersionModeRequest, SetImmersionModeResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.set_immersion_mode(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.CoolingService/GetCoolingState": grpclib.const.Handler( + self.__rpc_get_cooling_state, + grpclib.const.Cardinality.UNARY_UNARY, + GetCoolingStateRequest, + GetCoolingStateResponse, + ), + "/braiins.bos.v1.CoolingService/SetImmersionMode": grpclib.const.Handler( + self.__rpc_set_immersion_mode, + grpclib.const.Cardinality.UNARY_UNARY, + SetImmersionModeRequest, + SetImmersionModeResponse, + ), + } + + +class PerformanceServiceBase(ServiceBase): + async def get_tuner_state( + self, get_tuner_state_request: "GetTunerStateRequest" + ) -> "GetTunerStateResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def list_target_profiles( + self, list_target_profiles_request: "ListTargetProfilesRequest" + ) -> "ListTargetProfilesResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_default_power_target( + self, set_default_power_target_request: "SetDefaultPowerTargetRequest" + ) -> "SetPowerTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_power_target( + self, set_power_target_request: "SetPowerTargetRequest" + ) -> "SetPowerTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def increment_power_target( + self, increment_power_target_request: "IncrementPowerTargetRequest" + ) -> "SetPowerTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def decrement_power_target( + self, decrement_power_target_request: "DecrementPowerTargetRequest" + ) -> "SetPowerTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_default_hashrate_target( + self, set_default_hashrate_target_request: "SetDefaultHashrateTargetRequest" + ) -> "SetHashrateTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_hashrate_target( + self, set_hashrate_target_request: "SetHashrateTargetRequest" + ) -> "SetHashrateTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def increment_hashrate_target( + self, increment_hashrate_target_request: "IncrementHashrateTargetRequest" + ) -> "SetHashrateTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def decrement_hashrate_target( + self, decrement_hashrate_target_request: "DecrementHashrateTargetRequest" + ) -> "SetHashrateTargetResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_dps(self, set_dps_request: "SetDpsRequest") -> "SetDpsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def set_performance_mode( + self, set_performance_mode_request: "SetPerformanceModeRequest" + ) -> "PerformanceMode": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def get_active_performance_mode( + self, get_performance_mode_request: "GetPerformanceModeRequest" + ) -> "PerformanceMode": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def remove_tuned_profiles( + self, remove_tuned_profiles_request: "RemoveTunedProfilesRequest" + ) -> "RemoveTunedProfilesResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_tuner_state( + self, + stream: "grpclib.server.Stream[GetTunerStateRequest, GetTunerStateResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_tuner_state(request) + await stream.send_message(response) + + async def __rpc_list_target_profiles( + self, + stream: "grpclib.server.Stream[ListTargetProfilesRequest, ListTargetProfilesResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.list_target_profiles(request) + await stream.send_message(response) + + async def __rpc_set_default_power_target( + self, + stream: "grpclib.server.Stream[SetDefaultPowerTargetRequest, SetPowerTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.set_default_power_target(request) + await stream.send_message(response) + + async def __rpc_set_power_target( + self, + stream: "grpclib.server.Stream[SetPowerTargetRequest, SetPowerTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.set_power_target(request) + await stream.send_message(response) + + async def __rpc_increment_power_target( + self, + stream: "grpclib.server.Stream[IncrementPowerTargetRequest, SetPowerTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.increment_power_target(request) + await stream.send_message(response) + + async def __rpc_decrement_power_target( + self, + stream: "grpclib.server.Stream[DecrementPowerTargetRequest, SetPowerTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.decrement_power_target(request) + await stream.send_message(response) + + async def __rpc_set_default_hashrate_target( + self, + stream: "grpclib.server.Stream[SetDefaultHashrateTargetRequest, SetHashrateTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.set_default_hashrate_target(request) + await stream.send_message(response) + + async def __rpc_set_hashrate_target( + self, + stream: "grpclib.server.Stream[SetHashrateTargetRequest, SetHashrateTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.set_hashrate_target(request) + await stream.send_message(response) + + async def __rpc_increment_hashrate_target( + self, + stream: "grpclib.server.Stream[IncrementHashrateTargetRequest, SetHashrateTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.increment_hashrate_target(request) + await stream.send_message(response) + + async def __rpc_decrement_hashrate_target( + self, + stream: "grpclib.server.Stream[DecrementHashrateTargetRequest, SetHashrateTargetResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.decrement_hashrate_target(request) + await stream.send_message(response) + + async def __rpc_set_dps( + self, stream: "grpclib.server.Stream[SetDpsRequest, SetDpsResponse]" + ) -> None: + request = await stream.recv_message() + response = await self.set_dps(request) + await stream.send_message(response) + + async def __rpc_set_performance_mode( + self, + stream: "grpclib.server.Stream[SetPerformanceModeRequest, PerformanceMode]", + ) -> None: + request = await stream.recv_message() + response = await self.set_performance_mode(request) + await stream.send_message(response) + + async def __rpc_get_active_performance_mode( + self, + stream: "grpclib.server.Stream[GetPerformanceModeRequest, PerformanceMode]", + ) -> None: + request = await stream.recv_message() + response = await self.get_active_performance_mode(request) + await stream.send_message(response) + + async def __rpc_remove_tuned_profiles( + self, + stream: "grpclib.server.Stream[RemoveTunedProfilesRequest, RemoveTunedProfilesResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.remove_tuned_profiles(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.PerformanceService/GetTunerState": grpclib.const.Handler( + self.__rpc_get_tuner_state, + grpclib.const.Cardinality.UNARY_UNARY, + GetTunerStateRequest, + GetTunerStateResponse, + ), + "/braiins.bos.v1.PerformanceService/ListTargetProfiles": grpclib.const.Handler( + self.__rpc_list_target_profiles, + grpclib.const.Cardinality.UNARY_UNARY, + ListTargetProfilesRequest, + ListTargetProfilesResponse, + ), + "/braiins.bos.v1.PerformanceService/SetDefaultPowerTarget": grpclib.const.Handler( + self.__rpc_set_default_power_target, + grpclib.const.Cardinality.UNARY_UNARY, + SetDefaultPowerTargetRequest, + SetPowerTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/SetPowerTarget": grpclib.const.Handler( + self.__rpc_set_power_target, + grpclib.const.Cardinality.UNARY_UNARY, + SetPowerTargetRequest, + SetPowerTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/IncrementPowerTarget": grpclib.const.Handler( + self.__rpc_increment_power_target, + grpclib.const.Cardinality.UNARY_UNARY, + IncrementPowerTargetRequest, + SetPowerTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/DecrementPowerTarget": grpclib.const.Handler( + self.__rpc_decrement_power_target, + grpclib.const.Cardinality.UNARY_UNARY, + DecrementPowerTargetRequest, + SetPowerTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/SetDefaultHashrateTarget": grpclib.const.Handler( + self.__rpc_set_default_hashrate_target, + grpclib.const.Cardinality.UNARY_UNARY, + SetDefaultHashrateTargetRequest, + SetHashrateTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/SetHashrateTarget": grpclib.const.Handler( + self.__rpc_set_hashrate_target, + grpclib.const.Cardinality.UNARY_UNARY, + SetHashrateTargetRequest, + SetHashrateTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/IncrementHashrateTarget": grpclib.const.Handler( + self.__rpc_increment_hashrate_target, + grpclib.const.Cardinality.UNARY_UNARY, + IncrementHashrateTargetRequest, + SetHashrateTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/DecrementHashrateTarget": grpclib.const.Handler( + self.__rpc_decrement_hashrate_target, + grpclib.const.Cardinality.UNARY_UNARY, + DecrementHashrateTargetRequest, + SetHashrateTargetResponse, + ), + "/braiins.bos.v1.PerformanceService/SetDPS": grpclib.const.Handler( + self.__rpc_set_dps, + grpclib.const.Cardinality.UNARY_UNARY, + SetDpsRequest, + SetDpsResponse, + ), + "/braiins.bos.v1.PerformanceService/SetPerformanceMode": grpclib.const.Handler( + self.__rpc_set_performance_mode, + grpclib.const.Cardinality.UNARY_UNARY, + SetPerformanceModeRequest, + PerformanceMode, + ), + "/braiins.bos.v1.PerformanceService/GetActivePerformanceMode": grpclib.const.Handler( + self.__rpc_get_active_performance_mode, + grpclib.const.Cardinality.UNARY_UNARY, + GetPerformanceModeRequest, + PerformanceMode, + ), + "/braiins.bos.v1.PerformanceService/RemoveTunedProfiles": grpclib.const.Handler( + self.__rpc_remove_tuned_profiles, + grpclib.const.Cardinality.UNARY_UNARY, + RemoveTunedProfilesRequest, + RemoveTunedProfilesResponse, + ), + } + + +class PoolServiceBase(ServiceBase): + async def get_pool_groups( + self, get_pool_groups_request: "GetPoolGroupsRequest" + ) -> "GetPoolGroupsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def create_pool_group( + self, create_pool_group_request: "CreatePoolGroupRequest" + ) -> "CreatePoolGroupResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def update_pool_group( + self, update_pool_group_request: "UpdatePoolGroupRequest" + ) -> "UpdatePoolGroupResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def remove_pool_group( + self, remove_pool_group_request: "RemovePoolGroupRequest" + ) -> "RemovePoolGroupResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_pool_groups( + self, + stream: "grpclib.server.Stream[GetPoolGroupsRequest, GetPoolGroupsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_pool_groups(request) + await stream.send_message(response) + + async def __rpc_create_pool_group( + self, + stream: "grpclib.server.Stream[CreatePoolGroupRequest, CreatePoolGroupResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.create_pool_group(request) + await stream.send_message(response) + + async def __rpc_update_pool_group( + self, + stream: "grpclib.server.Stream[UpdatePoolGroupRequest, UpdatePoolGroupResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.update_pool_group(request) + await stream.send_message(response) + + async def __rpc_remove_pool_group( + self, + stream: "grpclib.server.Stream[RemovePoolGroupRequest, RemovePoolGroupResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.remove_pool_group(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.PoolService/GetPoolGroups": grpclib.const.Handler( + self.__rpc_get_pool_groups, + grpclib.const.Cardinality.UNARY_UNARY, + GetPoolGroupsRequest, + GetPoolGroupsResponse, + ), + "/braiins.bos.v1.PoolService/CreatePoolGroup": grpclib.const.Handler( + self.__rpc_create_pool_group, + grpclib.const.Cardinality.UNARY_UNARY, + CreatePoolGroupRequest, + CreatePoolGroupResponse, + ), + "/braiins.bos.v1.PoolService/UpdatePoolGroup": grpclib.const.Handler( + self.__rpc_update_pool_group, + grpclib.const.Cardinality.UNARY_UNARY, + UpdatePoolGroupRequest, + UpdatePoolGroupResponse, + ), + "/braiins.bos.v1.PoolService/RemovePoolGroup": grpclib.const.Handler( + self.__rpc_remove_pool_group, + grpclib.const.Cardinality.UNARY_UNARY, + RemovePoolGroupRequest, + RemovePoolGroupResponse, + ), + } + + +class ConfigurationServiceBase(ServiceBase): + async def get_miner_configuration( + self, get_miner_configuration_request: "GetMinerConfigurationRequest" + ) -> "GetMinerConfigurationResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def get_constraints( + self, get_constraints_request: "GetConstraintsRequest" + ) -> "GetConstraintsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_miner_configuration( + self, + stream: "grpclib.server.Stream[GetMinerConfigurationRequest, GetMinerConfigurationResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_miner_configuration(request) + await stream.send_message(response) + + async def __rpc_get_constraints( + self, + stream: "grpclib.server.Stream[GetConstraintsRequest, GetConstraintsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_constraints(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.ConfigurationService/GetMinerConfiguration": grpclib.const.Handler( + self.__rpc_get_miner_configuration, + grpclib.const.Cardinality.UNARY_UNARY, + GetMinerConfigurationRequest, + GetMinerConfigurationResponse, + ), + "/braiins.bos.v1.ConfigurationService/GetConstraints": grpclib.const.Handler( + self.__rpc_get_constraints, + grpclib.const.Cardinality.UNARY_UNARY, + GetConstraintsRequest, + GetConstraintsResponse, + ), + } + + +class LicenseServiceBase(ServiceBase): + async def get_license_state( + self, get_license_state_request: "GetLicenseStateRequest" + ) -> "GetLicenseStateResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_license_state( + self, + stream: "grpclib.server.Stream[GetLicenseStateRequest, GetLicenseStateResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_license_state(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.LicenseService/GetLicenseState": grpclib.const.Handler( + self.__rpc_get_license_state, + grpclib.const.Cardinality.UNARY_UNARY, + GetLicenseStateRequest, + GetLicenseStateResponse, + ), + } + + +class MinerServiceBase(ServiceBase): + async def get_miner_status( + self, get_miner_status_request: "GetMinerStatusRequest" + ) -> AsyncIterator["GetMinerStatusResponse"]: + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + yield GetMinerStatusResponse() + + async def get_miner_details( + self, get_miner_details_request: "GetMinerDetailsRequest" + ) -> "GetMinerDetailsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def get_miner_stats( + self, get_miner_stats_request: "GetMinerStatsRequest" + ) -> "GetMinerStatsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def get_hashboards( + self, get_hashboards_request: "GetHashboardsRequest" + ) -> "GetHashboardsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def get_support_archive( + self, get_support_archive_request: "GetSupportArchiveRequest" + ) -> AsyncIterator["GetSupportArchiveResponse"]: + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + yield GetSupportArchiveResponse() + + async def enable_hashboards( + self, enable_hashboards_request: "EnableHashboardsRequest" + ) -> "EnableHashboardsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def disable_hashboards( + self, disable_hashboards_request: "DisableHashboardsRequest" + ) -> "DisableHashboardsResponse": + raise grpclib.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def __rpc_get_miner_status( + self, + stream: "grpclib.server.Stream[GetMinerStatusRequest, GetMinerStatusResponse]", + ) -> None: + request = await stream.recv_message() + await self._call_rpc_handler_server_stream( + self.get_miner_status, + stream, + request, + ) + + async def __rpc_get_miner_details( + self, + stream: "grpclib.server.Stream[GetMinerDetailsRequest, GetMinerDetailsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_miner_details(request) + await stream.send_message(response) + + async def __rpc_get_miner_stats( + self, + stream: "grpclib.server.Stream[GetMinerStatsRequest, GetMinerStatsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_miner_stats(request) + await stream.send_message(response) + + async def __rpc_get_hashboards( + self, + stream: "grpclib.server.Stream[GetHashboardsRequest, GetHashboardsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.get_hashboards(request) + await stream.send_message(response) + + async def __rpc_get_support_archive( + self, + stream: "grpclib.server.Stream[GetSupportArchiveRequest, GetSupportArchiveResponse]", + ) -> None: + request = await stream.recv_message() + await self._call_rpc_handler_server_stream( + self.get_support_archive, + stream, + request, + ) + + async def __rpc_enable_hashboards( + self, + stream: "grpclib.server.Stream[EnableHashboardsRequest, EnableHashboardsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.enable_hashboards(request) + await stream.send_message(response) + + async def __rpc_disable_hashboards( + self, + stream: "grpclib.server.Stream[DisableHashboardsRequest, DisableHashboardsResponse]", + ) -> None: + request = await stream.recv_message() + response = await self.disable_hashboards(request) + await stream.send_message(response) + + def __mapping__(self) -> Dict[str, grpclib.const.Handler]: + return { + "/braiins.bos.v1.MinerService/GetMinerStatus": grpclib.const.Handler( + self.__rpc_get_miner_status, + grpclib.const.Cardinality.UNARY_STREAM, + GetMinerStatusRequest, + GetMinerStatusResponse, + ), + "/braiins.bos.v1.MinerService/GetMinerDetails": grpclib.const.Handler( + self.__rpc_get_miner_details, + grpclib.const.Cardinality.UNARY_UNARY, + GetMinerDetailsRequest, + GetMinerDetailsResponse, + ), + "/braiins.bos.v1.MinerService/GetMinerStats": grpclib.const.Handler( + self.__rpc_get_miner_stats, + grpclib.const.Cardinality.UNARY_UNARY, + GetMinerStatsRequest, + GetMinerStatsResponse, + ), + "/braiins.bos.v1.MinerService/GetHashboards": grpclib.const.Handler( + self.__rpc_get_hashboards, + grpclib.const.Cardinality.UNARY_UNARY, + GetHashboardsRequest, + GetHashboardsResponse, + ), + "/braiins.bos.v1.MinerService/GetSupportArchive": grpclib.const.Handler( + self.__rpc_get_support_archive, + grpclib.const.Cardinality.UNARY_STREAM, + GetSupportArchiveRequest, + GetSupportArchiveResponse, + ), + "/braiins.bos.v1.MinerService/EnableHashboards": grpclib.const.Handler( + self.__rpc_enable_hashboards, + grpclib.const.Cardinality.UNARY_UNARY, + EnableHashboardsRequest, + EnableHashboardsResponse, + ), + "/braiins.bos.v1.MinerService/DisableHashboards": grpclib.const.Handler( + self.__rpc_disable_hashboards, + grpclib.const.Cardinality.UNARY_UNARY, + DisableHashboardsRequest, + DisableHashboardsResponse, + ), + } From af15c4fbd16283c225581ff17108cd6c07419e4e Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 20:25:27 -0700 Subject: [PATCH 20/37] bug: pin working betterproto version. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 664b17b4..c3858536 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ grpc-requests = "^0.1.12" passlib = "^1.7.4" pyaml = "^23.9.7" toml = "^0.10.2" +betterproto = "2.0.0b6" [tool.poetry.group.dev] optional = true From bb182bb22d2e0192f77abff9ef4f93a276e0809f Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sun, 10 Dec 2023 20:28:06 -0700 Subject: [PATCH 21/37] bug: fix some issues with return types and missing return statements. --- pyasic/config/__init__.py | 2 +- pyasic/config/power_scaling.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 9ff385ca..d0b377a9 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -116,7 +116,7 @@ class MinerConfig: return cls(pools=PoolConfig.from_am_modern(web_conf)) @classmethod - def from_inno(cls, web_pools: dict): + def from_inno(cls, web_pools: list): return cls(pools=PoolConfig.from_inno(web_pools)) @classmethod diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index f6d3d3ae..f7d9fa15 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -29,6 +29,8 @@ class PowerScalingShutdownEnabled(MinerConfigValue): if self.duration is not None: cfg["shutdown_duration"] = self.duration + return cfg + @dataclass class PowerScalingShutdownDisabled(MinerConfigValue): From f162529883a45a16c9892b23a33f890dc5fa8d31 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 11:40:46 -0700 Subject: [PATCH 22/37] feature: allow dps conversion for bos grpc. --- pyasic/config/__init__.py | 13 ++++++++++++- pyasic/config/base.py | 8 +++++++- pyasic/config/pools.py | 7 +++---- pyasic/config/power_scaling.py | 34 +++++++++++++++++++++++++++++---- pyasic/web/bosminer/__init__.py | 24 +++++++++++++++-------- 5 files changed, 68 insertions(+), 18 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index d0b377a9..bfdc6970 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -95,6 +95,15 @@ class MinerConfig: **self.power_scaling.as_bosminer(), } + def as_bos_grpc(self, user_suffix: str = None): + return { + **self.fan_mode.as_bos_grpc(), + **self.temperature.as_bos_grpc(), + **self.mining_mode.as_bos_grpc(), + **self.pools.as_bos_grpc(user_suffix=user_suffix), + **self.power_scaling.as_bos_grpc(), + } + @classmethod def from_api(cls, api_pools: dict): return cls(pools=PoolConfig.from_api(api_pools)) @@ -129,6 +138,7 @@ class MinerConfig: power_scaling=PowerScalingConfig.from_bosminer(toml_conf), ) + def merge(a: dict, b: dict): ret = {} for k in a: @@ -163,7 +173,7 @@ if __name__ == "__main__": mining_mode=MiningModeConfig.power_tuning(3000), temperature=TemperatureConfig(hot=100, danger=110), fan_mode=FanModeConfig.manual(minimum_fans=2, speed=70), - power_scaling=PowerScalingConfig.enabled(power_step=100, minimum_power=2400) + power_scaling=PowerScalingConfig.enabled(power_step=100, minimum_power=2400), ) print(config) print("WM:", config.as_wm()) @@ -175,6 +185,7 @@ if __name__ == "__main__": print("BOS+ .toml:", config.as_bosminer()) print("BOS+ .toml as toml:") print(toml.dumps(config.as_bosminer())) + print(config.as_bos_grpc()) bos_parsed = MinerConfig.from_bosminer(config.as_bosminer()) print(bos_parsed) diff --git a/pyasic/config/base.py b/pyasic/config/base.py index 590d2778..e43b851f 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -36,7 +36,10 @@ class MinerConfigOption(Enum): return self.value.as_avalon() def as_bosminer(self) -> dict: - return self.value.as_bos() + return self.value.as_bosminer() + + def as_bos_grpc(self) -> dict: + return self.value.as_bos_grpc() def __call__(self, *args, **kwargs): return self.value(*args, **kwargs) @@ -63,3 +66,6 @@ class MinerConfigValue: def as_bosminer(self) -> dict: return {} + + def as_bos_grpc(self) -> dict: + return {} diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index b478b20e..d9ccc826 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -131,7 +131,6 @@ class Pool(MinerConfigValue): ) - @dataclass class PoolGroup(MinerConfigValue): pools: list[Pool] = field(default_factory=list) @@ -301,6 +300,9 @@ class PoolConfig(MinerConfigValue): } return {"group": [PoolGroup().as_bosminer()]} + def as_bos_grpc(self, user_suffix: str = None) -> dict: + return {} + @classmethod def from_api(cls, api_pools: dict): pool_data = api_pools["POOLS"] @@ -322,12 +324,9 @@ class PoolConfig(MinerConfigValue): def from_inno(cls, web_pools: list): return cls([PoolGroup.from_inno(web_pools)]) - @classmethod def from_bosminer(cls, toml_conf: dict): if toml_conf.get("group") is None: return cls() return cls([PoolGroup.from_bosminer(g) for g in toml_conf["group"]]) - - diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index f7d9fa15..0ba31122 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -16,6 +16,7 @@ from dataclasses import dataclass, field from pyasic.config.base import MinerConfigOption, MinerConfigValue +from pyasic.web.bosminer.proto.braiins.bos.v1 import DpsPowerTarget, DpsTarget, Hours @dataclass @@ -31,6 +32,14 @@ class PowerScalingShutdownEnabled(MinerConfigValue): return cfg + def as_bos_grpc(self) -> dict: + cfg = {"enable_shutdown ": True} + + if self.duration is not None: + cfg["shutdown_duration"] = Hours(self.duration) + + return cfg + @dataclass class PowerScalingShutdownDisabled(MinerConfigValue): @@ -39,6 +48,9 @@ class PowerScalingShutdownDisabled(MinerConfigValue): def as_bosminer(self) -> dict: return {"shutdown_enabled": False} + def as_bos_grpc(self) -> dict: + return {"enable_shutdown ": False} + class PowerScalingShutdown(MinerConfigOption): enabled = PowerScalingShutdownEnabled @@ -73,9 +85,7 @@ class PowerScalingEnabled(MinerConfigValue): ) def as_bosminer(self) -> dict: - cfg = { - "enabled": True - } + cfg = {"enabled": True} if self.power_step is not None: cfg["power_step"] = self.power_step if self.minimum_power is not None: @@ -86,6 +96,22 @@ class PowerScalingEnabled(MinerConfigValue): return {"power_scaling": cfg} + def as_bos_grpc(self): + cfg = {"enable": True} + target_conf = {} + if self.power_step is not None: + target_conf["power_step"] = self.power_step + if self.minimum_power is not None: + target_conf["min_power_target"] = self.minimum_power + + cfg["target"] = DpsTarget(power_target=DpsPowerTarget(**target_conf)) + + if self.shutdown_enabled is not None: + cfg = {**cfg, **self.shutdown_enabled.as_bos_grpc()} + + return {"dps": cfg} + + @dataclass class PowerScalingDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") @@ -110,4 +136,4 @@ class PowerScalingConfig(MinerConfigOption): else: return cls.disabled() - return cls.default() \ No newline at end of file + return cls.default() diff --git a/pyasic/web/bosminer/__init__.py b/pyasic/web/bosminer/__init__.py index 76b34f86..aad5de2c 100644 --- a/pyasic/web/bosminer/__init__.py +++ b/pyasic/web/bosminer/__init__.py @@ -18,13 +18,15 @@ from datetime import timedelta from typing import Union import httpx +from betterproto import Message from grpclib.client import Channel -from pyasic import APIError, settings +from pyasic import settings +from pyasic.errors import APIError from pyasic.web import BaseWebAPI + from .proto.braiins.bos import * from .proto.braiins.bos.v1 import * -from betterproto import Message class BOSMinerWebAPI(BaseWebAPI): @@ -534,9 +536,7 @@ class BOSMinerGRPCAPI: ) async def get_constraints(self): - return await self.send_command( - "get_constraints", GetConstraintsRequest() - ) + return await self.send_command("get_constraints", GetConstraintsRequest()) async def get_license_state(self): return await self.send_command("get_license_state", GetLicenseStateRequest()) @@ -554,7 +554,9 @@ class BOSMinerGRPCAPI: return await self.send_command("get_hashboards", GetHashboardsRequest()) async def get_support_archive(self): - return await self.send_command("get_support_archive", GetSupportArchiveRequest()) + return await self.send_command( + "get_support_archive", GetSupportArchiveRequest() + ) async def enable_hashboards( self, @@ -562,7 +564,10 @@ class BOSMinerGRPCAPI: save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): return await self.send_command( - "enable_hashboards", EnableHashboardsRequest(hashboard_ids=hashboard_ids, save_action=save_action) + "enable_hashboards", + EnableHashboardsRequest( + hashboard_ids=hashboard_ids, save_action=save_action + ), ) async def disable_hashboards( @@ -571,5 +576,8 @@ class BOSMinerGRPCAPI: save_action: SaveAction = SaveAction.SAVE_ACTION_SAVE_AND_APPLY, ): return await self.send_command( - "disable_hashboards", DisableHashboardsRequest(hashboard_ids=hashboard_ids, save_action=save_action) + "disable_hashboards", + DisableHashboardsRequest( + hashboard_ids=hashboard_ids, save_action=save_action + ), ) From c919b0031243a18622aad8b54a040d13ceaa85de Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 13:40:10 -0700 Subject: [PATCH 23/37] feature: allow config conversion to and from dict. --- pyasic/config/__init__.py | 83 +++++++++++++++++++++------------- pyasic/config/base.py | 19 ++++++++ pyasic/config/fans.py | 47 +++++++++++++++---- pyasic/config/mining.py | 75 ++++++++++++++++++++++++------ pyasic/config/pools.py | 63 ++++++++++++++++++-------- pyasic/config/power_scaling.py | 52 ++++++++++++++++++++- pyasic/config/temperature.py | 11 ++++- 7 files changed, 274 insertions(+), 76 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index bfdc6970..9fb75a2a 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -13,14 +13,14 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from dataclasses import dataclass +from dataclasses import asdict, dataclass import toml from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig from pyasic.config.pools import PoolConfig -from pyasic.config.power_scaling import PowerScalingConfig +from pyasic.config.power_scaling import PowerScalingConfig, PowerScalingShutdown from pyasic.config.temperature import TemperatureConfig @@ -32,7 +32,10 @@ class MinerConfig: temperature: TemperatureConfig = TemperatureConfig.default() power_scaling: PowerScalingConfig = PowerScalingConfig.default() - def as_am_modern(self, user_suffix: str = None): + def as_dict(self) -> dict: + return asdict(self) + + def as_am_modern(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_am_modern(), "freq-level": "100", @@ -42,7 +45,7 @@ class MinerConfig: **self.power_scaling.as_am_modern(), } - def as_wm(self, user_suffix: str = None): + def as_wm(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_wm(), **self.mining_mode.as_wm(), @@ -51,7 +54,7 @@ class MinerConfig: **self.power_scaling.as_wm(), } - def as_am_old(self, user_suffix: str = None): + def as_am_old(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_am_old(), **self.mining_mode.as_am_old(), @@ -60,7 +63,7 @@ class MinerConfig: **self.power_scaling.as_am_old(), } - def as_goldshell(self, user_suffix: str = None): + def as_goldshell(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_goldshell(), **self.mining_mode.as_goldshell(), @@ -69,7 +72,7 @@ class MinerConfig: **self.power_scaling.as_goldshell(), } - def as_avalon(self, user_suffix: str = None): + def as_avalon(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_avalon(), **self.mining_mode.as_avalon(), @@ -78,7 +81,7 @@ class MinerConfig: **self.power_scaling.as_avalon(), } - def as_inno(self, user_suffix: str = None): + def as_inno(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_inno(), **self.mining_mode.as_inno(), @@ -87,7 +90,7 @@ class MinerConfig: **self.power_scaling.as_inno(), } - def as_bosminer(self, user_suffix: str = None): + def as_bosminer(self, user_suffix: str = None) -> dict: return { **merge(self.fan_mode.as_bosminer(), self.temperature.as_bosminer()), **self.mining_mode.as_bosminer(), @@ -95,7 +98,7 @@ class MinerConfig: **self.power_scaling.as_bosminer(), } - def as_bos_grpc(self, user_suffix: str = None): + def as_bos_grpc(self, user_suffix: str = None) -> dict: return { **self.fan_mode.as_bos_grpc(), **self.temperature.as_bos_grpc(), @@ -105,11 +108,21 @@ class MinerConfig: } @classmethod - def from_api(cls, api_pools: dict): + def from_dict(cls, dict_conf: dict) -> "MinerConfig": + return cls( + pools=PoolConfig.from_dict(dict_conf.get("pools")), + mining_mode=MiningModeConfig.from_dict(dict_conf.get("mining_mode")), + fan_mode=FanModeConfig.from_dict(dict_conf.get("fan_mode")), + temperature=TemperatureConfig.from_dict(dict_conf.get("temperature")), + power_scaling=PowerScalingConfig.from_dict(dict_conf.get("power_scaling")), + ) + + @classmethod + def from_api(cls, api_pools: dict) -> "MinerConfig": return cls(pools=PoolConfig.from_api(api_pools)) @classmethod - def from_am_modern(cls, web_conf: dict): + def from_am_modern(cls, web_conf: dict) -> "MinerConfig": return cls( pools=PoolConfig.from_am_modern(web_conf), mining_mode=MiningModeConfig.from_am_modern(web_conf), @@ -117,19 +130,19 @@ class MinerConfig: ) @classmethod - def from_am_old(cls, web_conf: dict): + def from_am_old(cls, web_conf: dict) -> "MinerConfig": return cls.from_am_modern(web_conf) @classmethod - def from_goldshell(cls, web_conf: dict): + def from_goldshell(cls, web_conf: dict) -> "MinerConfig": return cls(pools=PoolConfig.from_am_modern(web_conf)) @classmethod - def from_inno(cls, web_pools: list): + def from_inno(cls, web_pools: list) -> "MinerConfig": return cls(pools=PoolConfig.from_inno(web_pools)) @classmethod - def from_bosminer(cls, toml_conf: dict): + def from_bosminer(cls, toml_conf: dict) -> "MinerConfig": return cls( pools=PoolConfig.from_bosminer(toml_conf), mining_mode=MiningModeConfig.from_bosminer(toml_conf), @@ -139,7 +152,7 @@ class MinerConfig: ) -def merge(a: dict, b: dict): +def merge(a: dict, b: dict) -> dict: ret = {} for k in a: v = a[k] @@ -173,20 +186,26 @@ if __name__ == "__main__": mining_mode=MiningModeConfig.power_tuning(3000), temperature=TemperatureConfig(hot=100, danger=110), fan_mode=FanModeConfig.manual(minimum_fans=2, speed=70), - power_scaling=PowerScalingConfig.enabled(power_step=100, minimum_power=2400), + power_scaling=PowerScalingConfig.enabled( + power_step=100, + minimum_power=2400, + shutdown_enabled=PowerScalingShutdown.enabled(duration=3), + ), ) print(config) - print("WM:", config.as_wm()) - print("AM Modern:", config.as_am_modern()) - print("AM Old:", config.as_am_old()) - print("GS:", config.as_goldshell()) - print("Avalon:", config.as_avalon()) - print("Inno:", config.as_inno()) - print("BOS+ .toml:", config.as_bosminer()) - print("BOS+ .toml as toml:") - print(toml.dumps(config.as_bosminer())) - print(config.as_bos_grpc()) - - bos_parsed = MinerConfig.from_bosminer(config.as_bosminer()) - print(bos_parsed) - print(bos_parsed == config) + # print("WM:", config.as_wm()) + # print("AM Modern:", config.as_am_modern()) + # print("AM Old:", config.as_am_old()) + # print("GS:", config.as_goldshell()) + # print("Avalon:", config.as_avalon()) + # print("Inno:", config.as_inno()) + # print("BOS+ .toml:", config.as_bosminer()) + # print("BOS+ .toml as toml:") + # print(toml.dumps(config.as_bosminer())) + # print(config.as_bos_grpc()) + dict_config = config.as_dict() + parsed_conf = MinerConfig.from_dict(dict_config) + print(parsed_conf) + # bos_parsed = MinerConfig.from_bosminer(config.as_bosminer()) + # print(bos_parsed) + # print(bos_parsed == config) diff --git a/pyasic/config/base.py b/pyasic/config/base.py index e43b851f..b082228f 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -13,10 +13,17 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ +from abc import ABC, abstractmethod +from dataclasses import asdict, dataclass from enum import Enum +from typing import Union class MinerConfigOption(Enum): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]): + return cls.default() + def as_am_modern(self) -> dict: return self.value.as_am_modern() @@ -44,8 +51,20 @@ class MinerConfigOption(Enum): def __call__(self, *args, **kwargs): return self.value(*args, **kwargs) + @classmethod + def default(cls): + pass + +@dataclass class MinerConfigValue: + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]): + return cls() + + def as_dict(self): + return asdict(self) + def as_am_modern(self) -> dict: return {} diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index 1e628f01..ca91972b 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -14,6 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field +from typing import Union from pyasic.config.base import MinerConfigOption, MinerConfigValue @@ -22,10 +23,14 @@ from pyasic.config.base import MinerConfigOption, MinerConfigValue class FanModeNormal(MinerConfigValue): mode: str = field(init=False, default="auto") - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeNormal": + return cls() + + def as_am_modern(self) -> dict: return {"bitmain-fan-ctrl": False, "bitmain-fan-pwn": "100"} - def as_bosminer(self): + def as_bosminer(self) -> dict: return {"temp_control": {"mode": "auto"}} @@ -36,7 +41,16 @@ class FanModeManual(MinerConfigValue): speed: int = 100 @classmethod - def from_bosminer(cls, toml_fan_conf: dict): + def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeManual": + cls_conf = {} + if dict_conf.get("min_fans") is not None: + cls_conf["minimum_fans"] = dict_conf["minimum_fans"] + if dict_conf.get("speed") is not None: + cls_conf["speed"] = dict_conf["speed"] + return cls(**cls_conf) + + @classmethod + def from_bosminer(cls, toml_fan_conf: dict) -> "FanModeManual": cls_conf = {} if toml_fan_conf.get("min_fans") is not None: cls_conf["minimum_fans"] = toml_fan_conf["min_fans"] @@ -44,12 +58,10 @@ class FanModeManual(MinerConfigValue): cls_conf["speed"] = toml_fan_conf["speed"] return cls(**cls_conf) - - - def as_am_modern(self): + def as_am_modern(self) -> dict: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": str(self.speed)} - def as_bosminer(self): + def as_bosminer(self) -> dict: return { "temp_control": {"mode": "manual"}, "fan_control": {"min_fans": self.minimum_fans, "speed": self.speed}, @@ -60,10 +72,14 @@ class FanModeManual(MinerConfigValue): class FanModeImmersion(MinerConfigValue): mode: str = field(init=False, default="immersion") - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeImmersion": + return cls() + + def as_am_modern(self) -> dict: return {"bitmain-fan-ctrl": True, "bitmain-fan-pwn": "0"} - def as_bosminer(self): + def as_bosminer(self) -> dict: return {"temp_control": {"mode": "disabled"}} @@ -76,6 +92,19 @@ class FanModeConfig(MinerConfigOption): def default(cls): return cls.normal() + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]): + if dict_conf is None: + return cls.default() + + mode = dict_conf.get("mode") + if mode is None: + return cls.default() + + clsattr = getattr(cls, mode) + if clsattr is not None: + return clsattr().from_dict(dict_conf) + @classmethod def from_am_modern(cls, web_conf: dict): if web_conf.get("bitmain-fan-ctrl") is not None: diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index dcbc3bb6..16525768 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -14,6 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field +from typing import Union from pyasic.config.base import MinerConfigOption, MinerConfigValue @@ -22,10 +23,14 @@ from pyasic.config.base import MinerConfigOption, MinerConfigValue class MiningModeNormal(MinerConfigValue): mode: str = field(init=False, default="normal") - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeNormal": + return cls() + + def as_am_modern(self) -> dict: return {"miner-mode": "0"} - def as_wm(self): + def as_wm(self) -> dict: return {"mode": self.mode} @@ -33,10 +38,14 @@ class MiningModeNormal(MinerConfigValue): class MiningModeSleep(MinerConfigValue): mode: str = field(init=False, default="sleep") - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeSleep": + return cls() + + def as_am_modern(self) -> dict: return {"miner-mode": "1"} - def as_wm(self): + def as_wm(self) -> dict: return {"mode": self.mode} @@ -44,10 +53,14 @@ class MiningModeSleep(MinerConfigValue): class MiningModeLPM(MinerConfigValue): mode: str = field(init=False, default="low") - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeLPM": + return cls() + + def as_am_modern(self) -> dict: return {"miner-mode": "3"} - def as_wm(self): + def as_wm(self) -> dict: return {"mode": self.mode} @@ -55,10 +68,14 @@ class MiningModeLPM(MinerConfigValue): class MiningModeHPM(MinerConfigValue): mode: str = field(init=False, default="high") + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeHPM": + return cls() + def as_am_modern(self): return {"miner-mode": "0"} - def as_wm(self): + def as_wm(self) -> dict: return {"mode": self.mode} @@ -67,10 +84,14 @@ class MiningModePowerTune(MinerConfigValue): mode: str = field(init=False, default="power_tuning") power: int = None - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModePowerTune": + return cls(dict_conf.get("power")) + + def as_am_modern(self) -> dict: return {"miner-mode": "0"} - def as_wm(self): + def as_wm(self) -> dict: if self.power is not None: return {"mode": self.mode, self.mode: {"wattage": self.power}} return {} @@ -84,7 +105,11 @@ class MiningModeHashrateTune(MinerConfigValue): mode: str = field(init=False, default="hashrate_tuning") hashrate: int = None - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeHashrateTune": + return cls(dict_conf.get("hashrate")) + + def as_am_modern(self) -> dict: return {"miner-mode": "0"} @@ -93,7 +118,11 @@ class ManualBoardSettings(MinerConfigValue): freq: float volt: float - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "ManualBoardSettings": + return cls(freq=dict_conf["freq"], volt=dict_conf["volt"]) + + def as_am_modern(self) -> dict: return {"miner-mode": "0"} @@ -105,7 +134,15 @@ class MiningModeManual(MinerConfigValue): global_volt: float boards: dict[int, ManualBoardSettings] = field(default_factory=dict) - def as_am_modern(self): + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "MiningModeManual": + return cls( + global_freq=dict_conf["global_freq"], + global_volt=dict_conf["global_volt"], + boards={i: ManualBoardSettings.from_dict(dict_conf[i]) for i in dict_conf}, + ) + + def as_am_modern(self) -> dict: return {"miner-mode": "0"} @@ -122,6 +159,19 @@ class MiningModeConfig(MinerConfigOption): def default(cls): return cls.normal() + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]): + if dict_conf is None: + return cls.default() + + mode = dict_conf.get("mode") + if mode is None: + return cls.default() + + clsattr = getattr(cls, mode) + if clsattr is not None: + return clsattr().from_dict(dict_conf) + @classmethod def from_am_modern(cls, web_conf: dict): if web_conf.get("bitmain-work-mode") is not None: @@ -159,4 +209,3 @@ class MiningModeConfig(MinerConfigOption): if autotuning_conf.get("hashrate_target") is not None: return cls.hashrate_tuning(autotuning_conf["hashrate_target"]) return cls.hashrate_tuning() - diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index d9ccc826..d2835a91 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -94,36 +94,42 @@ class Pool(MinerConfigValue): return { "url": self.url, "user": f"{self.user}{user_suffix}", - "pass": self.password, + "password": self.password, } - return {"url": self.url, "user": self.user, "pass": self.password} + return {"url": self.url, "user": self.user, "password": self.password} @classmethod - def from_api(cls, api_pool: dict): + def from_dict(cls, dict_conf: Union[dict, None]) -> "Pool": + return cls( + url=dict_conf["url"], user=dict_conf["user"], password=dict_conf["password"] + ) + + @classmethod + def from_api(cls, api_pool: dict) -> "Pool": return cls(url=api_pool["URL"], user=api_pool["User"], password="x") @classmethod - def from_am_modern(cls, web_pool: dict): + def from_am_modern(cls, web_pool: dict) -> "Pool": return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) # TODO: check if this is accurate, user/username, pass/password @classmethod - def from_goldshell(cls, web_pool: dict): + def from_goldshell(cls, web_pool: dict) -> "Pool": return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) # TODO: check if this is accurate, user/username, pass/password @classmethod - def from_inno(cls, web_pool: dict): + def from_inno(cls, web_pool: dict) -> "Pool": return cls( url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) @classmethod - def from_bosminer(cls, toml_pool_conf: dict): + def from_bosminer(cls, toml_pool_conf: dict) -> "Pool": return cls( url=toml_pool_conf["url"], user=toml_pool_conf["user"], @@ -211,32 +217,44 @@ class PoolGroup(MinerConfigValue): } if self.quota is not None: conf["quota"] = self.quota + return conf return {"name": "Group", "pool": []} @classmethod - def from_api(cls, api_pool_list: list): + def from_dict(cls, dict_conf: Union[dict, None]) -> "PoolGroup": + cls_conf = {} + + if dict_conf.get("quota") is not None: + cls_conf["quota"] = dict_conf["quota"] + if dict_conf.get("name") is not None: + cls_conf["name"] = dict_conf["name"] + cls_conf["pools"] = [Pool.from_dict(p) for p in dict_conf["pools"]] + return cls(**cls_conf) + + @classmethod + def from_api(cls, api_pool_list: list) -> "PoolGroup": pools = [] for pool in api_pool_list: pools.append(Pool.from_api(pool)) return cls(pools=pools) @classmethod - def from_am_modern(cls, web_pool_list: list): + def from_am_modern(cls, web_pool_list: list) -> "PoolGroup": pools = [] for pool in web_pool_list: pools.append(Pool.from_am_modern(pool)) return cls(pools=pools) @classmethod - def from_goldshell(cls, web_pools: list): + def from_goldshell(cls, web_pools: list) -> "PoolGroup": return cls([Pool.from_goldshell(p) for p in web_pools]) @classmethod - def from_inno(cls, web_pools: list): + def from_inno(cls, web_pools: list) -> "PoolGroup": return cls([Pool.from_inno(p) for p in web_pools]) @classmethod - def from_bosminer(cls, toml_group_conf: dict): + def from_bosminer(cls, toml_group_conf: dict) -> "PoolGroup": if toml_group_conf.get("pool") is not None: return cls( name=toml_group_conf["name"], @@ -251,11 +269,18 @@ class PoolConfig(MinerConfigValue): groups: list[PoolGroup] = field(default_factory=list) @classmethod - def default(cls): + def default(cls) -> "PoolConfig": return cls() @classmethod - def simple(cls, pools: list[Union[Pool, dict[str, str]]]): + def from_dict(cls, dict_conf: Union[dict, None]) -> "PoolConfig": + if dict_conf is None: + return cls.default() + + return cls(groups=[PoolGroup.from_dict(g) for g in dict_conf["groups"]]) + + @classmethod + def simple(cls, pools: list[Union[Pool, dict[str, str]]]) -> "PoolConfig": group_pools = [] for pool in pools: if isinstance(pool, dict): @@ -304,28 +329,28 @@ class PoolConfig(MinerConfigValue): return {} @classmethod - def from_api(cls, api_pools: dict): + def from_api(cls, api_pools: dict) -> "PoolConfig": pool_data = api_pools["POOLS"] pool_data = sorted(pool_data, key=lambda x: int(x["POOL"])) return cls([PoolGroup.from_api(pool_data)]) @classmethod - def from_am_modern(cls, web_conf: dict): + def from_am_modern(cls, web_conf: dict) -> "PoolConfig": pool_data = web_conf["pools"] return cls([PoolGroup.from_am_modern(pool_data)]) @classmethod - def from_goldshell(cls, web_pools: list): + def from_goldshell(cls, web_pools: list) -> "PoolConfig": return cls([PoolGroup.from_goldshell(web_pools)]) @classmethod - def from_inno(cls, web_pools: list): + def from_inno(cls, web_pools: list) -> "PoolConfig": return cls([PoolGroup.from_inno(web_pools)]) @classmethod - def from_bosminer(cls, toml_conf: dict): + def from_bosminer(cls, toml_conf: dict) -> "PoolConfig": if toml_conf.get("group") is None: return cls() diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index 0ba31122..e776b49c 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -14,6 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass, field +from typing import Union from pyasic.config.base import MinerConfigOption, MinerConfigValue from pyasic.web.bosminer.proto.braiins.bos.v1 import DpsPowerTarget, DpsTarget, Hours @@ -24,6 +25,10 @@ class PowerScalingShutdownEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") duration: int = None + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "PowerScalingShutdownEnabled": + return cls(duration=dict_conf.get("duration")) + def as_bosminer(self) -> dict: cfg = {"shutdown_enabled": True} @@ -45,6 +50,10 @@ class PowerScalingShutdownEnabled(MinerConfigValue): class PowerScalingShutdownDisabled(MinerConfigValue): mode: str = field(init=False, default="disabled") + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "PowerScalingShutdownDisabled": + return cls() + def as_bosminer(self) -> dict: return {"shutdown_enabled": False} @@ -56,6 +65,19 @@ class PowerScalingShutdown(MinerConfigOption): enabled = PowerScalingShutdownEnabled disabled = PowerScalingShutdownDisabled + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]): + if dict_conf is None: + return cls.default() + + mode = dict_conf.get("mode") + if mode is None: + return cls.default() + + clsattr = getattr(cls, mode) + if clsattr is not None: + return clsattr().from_dict(dict_conf) + @classmethod def from_bosminer(cls, power_scaling_conf: dict): sd_enabled = power_scaling_conf.get("shutdown_enabled") @@ -75,7 +97,7 @@ class PowerScalingEnabled(MinerConfigValue): shutdown_enabled: PowerScalingShutdown = None @classmethod - def from_bosminer(cls, power_scaling_conf: dict): + def from_bosminer(cls, power_scaling_conf: dict) -> "PowerScalingEnabled": power_step = power_scaling_conf.get("power_step") min_power = power_scaling_conf.get("min_psu_power_limit") sd_mode = PowerScalingShutdown.from_bosminer(power_scaling_conf) @@ -84,6 +106,19 @@ class PowerScalingEnabled(MinerConfigValue): power_step=power_step, minimum_power=min_power, shutdown_enabled=sd_mode ) + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]) -> "PowerScalingEnabled": + cls_conf = { + "power_step": dict_conf.get("power_step"), + "minimum_power": dict_conf.get("minimum_power"), + } + shutdown_enabled = dict_conf.get("shutdown_enabled") + if shutdown_enabled is not None: + cls_conf["shutdown_enabled"] = PowerScalingShutdown.from_dict( + shutdown_enabled + ) + return cls(**cls_conf) + def as_bosminer(self) -> dict: cfg = {"enabled": True} if self.power_step is not None: @@ -96,7 +131,7 @@ class PowerScalingEnabled(MinerConfigValue): return {"power_scaling": cfg} - def as_bos_grpc(self): + def as_bos_grpc(self) -> dict: cfg = {"enable": True} target_conf = {} if self.power_step is not None: @@ -125,6 +160,19 @@ class PowerScalingConfig(MinerConfigOption): def default(cls): return cls.disabled() + @classmethod + def from_dict(cls, dict_conf: Union[dict, None]): + if dict_conf is None: + return cls.default() + + mode = dict_conf.get("mode") + if mode is None: + return cls.default() + + clsattr = getattr(cls, mode) + if clsattr is not None: + return clsattr().from_dict(dict_conf) + @classmethod def from_bosminer(cls, toml_conf: dict): power_scaling = toml_conf.get("power_scaling") diff --git a/pyasic/config/temperature.py b/pyasic/config/temperature.py index f353e507..d875104b 100644 --- a/pyasic/config/temperature.py +++ b/pyasic/config/temperature.py @@ -14,6 +14,7 @@ # limitations under the License. - # ------------------------------------------------------------------------------ from dataclasses import dataclass +from typing import Union from pyasic.config.base import MinerConfigValue @@ -39,7 +40,15 @@ class TemperatureConfig(MinerConfigValue): return {"temp_control": temp_cfg} @classmethod - def from_bosminer(cls, toml_conf: dict): + def from_dict(cls, dict_conf: Union[dict, None]) -> "TemperatureConfig": + return cls( + target=dict_conf.get("target"), + hot=dict_conf.get("hot"), + danger=dict_conf.get("danger"), + ) + + @classmethod + def from_bosminer(cls, toml_conf: dict) -> "TemperatureConfig": temp_control = toml_conf.get("temp_control") if temp_control is not None: return cls( From 27368a9bd28a1b0f23376f686002a1b4cc8f930b Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 13:48:26 -0700 Subject: [PATCH 24/37] bug: fix some issues, and remove unused imports. --- pyasic/config/__init__.py | 2 -- pyasic/config/base.py | 1 - pyasic/config/power_scaling.py | 4 +++- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 9fb75a2a..116b57bf 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -15,8 +15,6 @@ # ------------------------------------------------------------------------------ from dataclasses import asdict, dataclass -import toml - from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig from pyasic.config.pools import PoolConfig diff --git a/pyasic/config/base.py b/pyasic/config/base.py index b082228f..3a5bfe8d 100644 --- a/pyasic/config/base.py +++ b/pyasic/config/base.py @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ -from abc import ABC, abstractmethod from dataclasses import asdict, dataclass from enum import Enum from typing import Union diff --git a/pyasic/config/power_scaling.py b/pyasic/config/power_scaling.py index e776b49c..7df2fffe 100644 --- a/pyasic/config/power_scaling.py +++ b/pyasic/config/power_scaling.py @@ -94,7 +94,9 @@ class PowerScalingEnabled(MinerConfigValue): mode: str = field(init=False, default="enabled") power_step: int = None minimum_power: int = None - shutdown_enabled: PowerScalingShutdown = None + shutdown_enabled: Union[ + PowerScalingShutdownEnabled, PowerScalingShutdownDisabled + ] = None @classmethod def from_bosminer(cls, power_scaling_conf: dict) -> "PowerScalingEnabled": From 8d68dd9dac72d3f58e6ecb4a79370387473aa23f Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 14:06:22 -0700 Subject: [PATCH 25/37] refactor: re-order config keys --- 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 116b57bf..20635e51 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -25,9 +25,9 @@ from pyasic.config.temperature import TemperatureConfig @dataclass class MinerConfig: pools: PoolConfig = PoolConfig.default() - mining_mode: MiningModeConfig = MiningModeConfig.default() fan_mode: FanModeConfig = FanModeConfig.default() temperature: TemperatureConfig = TemperatureConfig.default() + mining_mode: MiningModeConfig = MiningModeConfig.default() power_scaling: PowerScalingConfig = PowerScalingConfig.default() def as_dict(self) -> dict: From f6b0b64d868f0805417a8b8fa45da0e1181d6702 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 14:07:17 -0700 Subject: [PATCH 26/37] bug: set default quota to 1. --- pyasic/config/pools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index d2835a91..a66a3177 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -140,7 +140,7 @@ class Pool(MinerConfigValue): @dataclass class PoolGroup(MinerConfigValue): pools: list[Pool] = field(default_factory=list) - quota: int = None + quota: int = 1 name: str = None def __post_init__(self): From 6941d9f3492f25adfe72099eb24aa887f0633f06 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 15:08:57 -0700 Subject: [PATCH 27/37] bug: add default case for work mode when there is no work mode returned from bitmain. --- pyasic/config/mining.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyasic/config/mining.py b/pyasic/config/mining.py index 16525768..010d556e 100644 --- a/pyasic/config/mining.py +++ b/pyasic/config/mining.py @@ -176,6 +176,8 @@ class MiningModeConfig(MinerConfigOption): def from_am_modern(cls, web_conf: dict): if web_conf.get("bitmain-work-mode") is not None: work_mode = web_conf["bitmain-work-mode"] + if work_mode == "": + return cls.default() if int(work_mode) == 0: return cls.normal() elif int(work_mode) == 1: From 3a43cd293c68e01dbcd7e149ba203250cc7d91f1 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 15:18:23 -0700 Subject: [PATCH 28/37] bug: Fix improper naming of fan mode. --- pyasic/config/fans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyasic/config/fans.py b/pyasic/config/fans.py index ca91972b..6b7daaf0 100644 --- a/pyasic/config/fans.py +++ b/pyasic/config/fans.py @@ -21,7 +21,7 @@ from pyasic.config.base import MinerConfigOption, MinerConfigValue @dataclass class FanModeNormal(MinerConfigValue): - mode: str = field(init=False, default="auto") + mode: str = field(init=False, default="normal") @classmethod def from_dict(cls, dict_conf: Union[dict, None]) -> "FanModeNormal": From 02056b8c88f5860e3f68ee4a6770fcc311a03305 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Mon, 11 Dec 2023 15:36:02 -0700 Subject: [PATCH 29/37] refactor: remove config prints. --- pyasic/config/__init__.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 20635e51..57ed62d4 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -168,42 +168,3 @@ def merge(a: dict, b: dict) -> dict: if k not in ret.keys(): ret[k] = v return ret - - -if __name__ == "__main__": - config = MinerConfig( - pools=PoolConfig.simple( - [ - { - "url": "stratum+tcp://stratum.test.io:3333", - "user": "user.test", - "password": "123", - } - ] - ), - mining_mode=MiningModeConfig.power_tuning(3000), - temperature=TemperatureConfig(hot=100, danger=110), - fan_mode=FanModeConfig.manual(minimum_fans=2, speed=70), - power_scaling=PowerScalingConfig.enabled( - power_step=100, - minimum_power=2400, - shutdown_enabled=PowerScalingShutdown.enabled(duration=3), - ), - ) - print(config) - # print("WM:", config.as_wm()) - # print("AM Modern:", config.as_am_modern()) - # print("AM Old:", config.as_am_old()) - # print("GS:", config.as_goldshell()) - # print("Avalon:", config.as_avalon()) - # print("Inno:", config.as_inno()) - # print("BOS+ .toml:", config.as_bosminer()) - # print("BOS+ .toml as toml:") - # print(toml.dumps(config.as_bosminer())) - # print(config.as_bos_grpc()) - dict_config = config.as_dict() - parsed_conf = MinerConfig.from_dict(dict_config) - print(parsed_conf) - # bos_parsed = MinerConfig.from_bosminer(config.as_bosminer()) - # print(bos_parsed) - # print(bos_parsed == config) From dc22df0280d32621607a9b355327bc76d0cb665b Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Tue, 12 Dec 2023 08:54:24 -0700 Subject: [PATCH 30/37] refactor: remove innosilicon pool comment, as it is correct. --- pyasic/config/pools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyasic/config/pools.py b/pyasic/config/pools.py index a66a3177..e90c1fa6 100644 --- a/pyasic/config/pools.py +++ b/pyasic/config/pools.py @@ -121,7 +121,6 @@ class Pool(MinerConfigValue): url=web_pool["url"], user=web_pool["user"], password=web_pool["pass"] ) - # TODO: check if this is accurate, user/username, pass/password @classmethod def from_inno(cls, web_pool: dict) -> "Pool": return cls( From 02234f3d1efb7b66ec7ca05b7c032e6a7681e65e Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Tue, 12 Dec 2023 09:25:43 -0700 Subject: [PATCH 31/37] feature: improve dict merging speed --- pyasic/config/__init__.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 57ed62d4..09ec96b5 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -13,8 +13,11 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ +from copy import deepcopy from dataclasses import asdict, dataclass +import toml + from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig from pyasic.config.pools import PoolConfig @@ -151,20 +154,11 @@ class MinerConfig: def merge(a: dict, b: dict) -> dict: - ret = {} - for k in a: - v = a[k] - if k in b.keys(): - if isinstance(v, dict): - ret[k] = merge(a[k], b[k]) - elif isinstance(v, list): - ret[k] = [*v, *b[k]] - else: - ret[k] = v + result = deepcopy(a) + for b_key, b_val in b.items(): + a_val = result.get(b_key) + if isinstance(a_val, dict) and isinstance(b_val, dict): + result[b_key] = merge(a_val, b_val) else: - ret[k] = v - for k in b: - v = b[k] - if k not in ret.keys(): - ret[k] = v - return ret + result[b_key] = deepcopy(b_val) + return result From 073e048726d317c09e1b9e5f5d1825f106ca9e50 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Tue, 12 Dec 2023 13:11:49 -0700 Subject: [PATCH 32/37] bug: fix bosminer config missing format information. --- pyasic/config/__init__.py | 2 -- pyasic/miners/backends/bosminer.py | 16 ++++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pyasic/config/__init__.py b/pyasic/config/__init__.py index 09ec96b5..5d45939a 100644 --- a/pyasic/config/__init__.py +++ b/pyasic/config/__init__.py @@ -16,8 +16,6 @@ from copy import deepcopy from dataclasses import asdict, dataclass -import toml - from pyasic.config.fans import FanModeConfig from pyasic.config.mining import MiningModeConfig from pyasic.config.pools import PoolConfig diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index d575c2ff..b193c16b 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -15,6 +15,7 @@ # ------------------------------------------------------------------------------ import asyncio import logging +import time from collections import namedtuple from typing import List, Optional, Tuple, Union @@ -326,12 +327,20 @@ class BOSMiner(BaseMiner): await self._send_config_bosminer(config, user_suffix) async def _send_config_grpc(self, config: MinerConfig, user_suffix: str = None): + raise NotImplementedError mining_mode = config.mining_mode - mining_mode - async def _send_config_bosminer(self, config: MinerConfig, user_suffix: str = None): - toml_conf = config.as_bosminer(user_suffix=user_suffix) + toml_conf = toml.dumps( + { + "format": { + "version": "1.2+", + "generator": "pyasic", + "timestamp": int(time.time()), + }, + **config.as_bosminer(user_suffix=user_suffix), + } + ) try: conn = await self._get_ssh_connection() except ConnectionError as e: @@ -362,7 +371,6 @@ class BOSMiner(BaseMiner): logging.debug(f"{self}: BBB restarting bosminer.") await conn.run("/etc/init.d/S99bosminer start") - async def set_power_limit(self, wattage: int) -> bool: try: cfg = await self.get_config() From 67aed79330153b4c957d3f0d3ea7a38ed9ff1074 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Tue, 12 Dec 2023 13:21:50 -0700 Subject: [PATCH 33/37] bug: fix mode spec in bosminer config. --- pyasic/miners/backends/bosminer.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index b193c16b..6b0a9507 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -322,9 +322,12 @@ class BOSMiner(BaseMiner): self.config = config if self.web.grpc is not None: - await self._send_config_grpc(config, user_suffix) - else: - await self._send_config_bosminer(config, user_suffix) + try: + await self._send_config_grpc(config, user_suffix) + return + except: + pass + await self._send_config_bosminer(config, user_suffix) async def _send_config_grpc(self, config: MinerConfig, user_suffix: str = None): raise NotImplementedError @@ -336,6 +339,7 @@ class BOSMiner(BaseMiner): "format": { "version": "1.2+", "generator": "pyasic", + "model": f"{self.make.replace('Miner', 'miner')} {self.model.replace(' (BOS)', '').replace('j', 'J')}", "timestamp": int(time.time()), }, **config.as_bosminer(user_suffix=user_suffix), From 497ffb5bc0ec424a3d696a8a38b7b84f4a6cd4be Mon Sep 17 00:00:00 2001 From: checksum0 Date: Tue, 12 Dec 2023 23:32:12 -0300 Subject: [PATCH 34/37] Add all the currently known Whatsminer M6X machines (#77) * Create new BTMiner M6X backend class to represent Whatsminer new M6X generation * Add all new known types of Whatsminer M6X * Ensure all new types are imported in their respective __init__.py * Create all BTMiner API class for known types of new M6X generation * Ensure all new BTMiner API class are imported in __init__.py * Fix erroneous M6X models data * Ensure M6X miners are imported and add them to their MinerTypes dictionary in miner_factory.py --- pyasic/miners/backends/__init__.py | 2 +- pyasic/miners/backends/whatsminer.py | 6 ++ pyasic/miners/miner_factory.py | 19 ++++++ pyasic/miners/types/whatsminer/M6X/M60.py | 66 +++++++++++++++++++ pyasic/miners/types/whatsminer/M6X/M60S.py | 64 ++++++++++++++++++ pyasic/miners/types/whatsminer/M6X/M63.py | 52 +++++++++++++++ pyasic/miners/types/whatsminer/M6X/M63S.py | 54 +++++++++++++++ pyasic/miners/types/whatsminer/M6X/M66.py | 43 ++++++++++++ pyasic/miners/types/whatsminer/M6X/M66S.py | 53 +++++++++++++++ .../miners/types/whatsminer/M6X/__init__.py | 52 +++++++++++++++ pyasic/miners/types/whatsminer/__init__.py | 1 + pyasic/miners/whatsminer/btminer/M6X/M60.py | 39 +++++++++++ pyasic/miners/whatsminer/btminer/M6X/M60S.py | 39 +++++++++++ pyasic/miners/whatsminer/btminer/M6X/M63.py | 34 ++++++++++ pyasic/miners/whatsminer/btminer/M6X/M63S.py | 34 ++++++++++ pyasic/miners/whatsminer/btminer/M6X/M66.py | 29 ++++++++ pyasic/miners/whatsminer/btminer/M6X/M66S.py | 34 ++++++++++ .../miners/whatsminer/btminer/M6X/__init__.py | 52 +++++++++++++++ pyasic/miners/whatsminer/btminer/__init__.py | 1 + 19 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 pyasic/miners/types/whatsminer/M6X/M60.py create mode 100644 pyasic/miners/types/whatsminer/M6X/M60S.py create mode 100644 pyasic/miners/types/whatsminer/M6X/M63.py create mode 100644 pyasic/miners/types/whatsminer/M6X/M63S.py create mode 100644 pyasic/miners/types/whatsminer/M6X/M66.py create mode 100644 pyasic/miners/types/whatsminer/M6X/M66S.py create mode 100644 pyasic/miners/types/whatsminer/M6X/__init__.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/M60.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/M60S.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/M63.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/M63S.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/M66.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/M66S.py create mode 100644 pyasic/miners/whatsminer/btminer/M6X/__init__.py diff --git a/pyasic/miners/backends/__init__.py b/pyasic/miners/backends/__init__.py index c34fe09f..fab68b27 100644 --- a/pyasic/miners/backends/__init__.py +++ b/pyasic/miners/backends/__init__.py @@ -25,4 +25,4 @@ from .hiveon import Hiveon from .luxminer import LUXMiner from .vnish import VNish from .epic import ePIC -from .whatsminer import M2X, M3X, M5X +from .whatsminer import M2X, M3X, M5X, M6X diff --git a/pyasic/miners/backends/whatsminer.py b/pyasic/miners/backends/whatsminer.py index d7057071..a40d5631 100644 --- a/pyasic/miners/backends/whatsminer.py +++ b/pyasic/miners/backends/whatsminer.py @@ -16,6 +16,12 @@ from pyasic.miners.backends.btminer import BTMiner +class M6X(BTMiner): + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.supports_autotuning = True + + class M5X(BTMiner): def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index 841df63d..777842c7 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -288,6 +288,25 @@ MINER_CLASSES = { "M56SVH30": BTMinerM56SVH30, "M56S+VJ30": BTMinerM56SPlusVJ30, "M59VH30": BTMinerM59VH30, + "M60VK10": BTMinerM60VK10, + "M60VK20": BTMinerM60VK20, + "M60VK30": BTMinerM60VK30, + "M60VK40": BTMinerM60VK40, + "M60SVK10": BTMinerM60SVK10, + "M60SVK20": BTMinerM60SVK20, + "M60SVK30": BTMinerM60SVK30, + "M60SVK40": BTMinerM60SVK40, + "M63VK10": BTMinerM63VK10, + "M63VK20": BTMinerM63VK20, + "M63VK30": BTMinerM63VK30, + "M63SVK10": BTMinerM63SVK10, + "M63SVK20": BTMinerM63SVK20, + "M63SVK30": BTMinerM63SVK30, + "M66VK20": BTMinerM66VK20, + "M66VK30": BTMinerM66VK30, + "M66SVK20": BTMinerM66SVK20, + "M66SVK30": BTMinerM66SVK30, + "M66SVK40": BTMinerM66SVK40, }, MinerTypes.AVALONMINER: { None: CGMinerAvalon, diff --git a/pyasic/miners/types/whatsminer/M6X/M60.py b/pyasic/miners/types/whatsminer/M6X/M60.py new file mode 100644 index 00000000..77649cd4 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/M60.py @@ -0,0 +1,66 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 warnings + +from pyasic.miners.makes import WhatsMiner + +class M60VK10(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60 VK10" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60 VK10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 + + +class M60VK20(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60 VK20" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60 VK20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 + + +class M60VK30(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60 VK30" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60 VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 + + +class M60VK40(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60 VK40" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60 VK40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/M6X/M60S.py b/pyasic/miners/types/whatsminer/M6X/M60S.py new file mode 100644 index 00000000..2ec049e8 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/M60S.py @@ -0,0 +1,64 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 warnings + +from pyasic.miners.makes import WhatsMiner + +class M60SVK10(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60S VK10" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60S VK10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 + + +class M60SVK20(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60S VK20" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60S VK20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 + + +class M60SVK30(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60S VK30" + self.ideal_hashboards = 3 + self.nominal_chips = 78 + self.fan_count = 2 + + +class M60SVK40(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M60S VK40" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M60S VK40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 2 \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/M6X/M63.py b/pyasic/miners/types/whatsminer/M6X/M63.py new file mode 100644 index 00000000..28310657 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/M63.py @@ -0,0 +1,52 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 warnings + +from pyasic.miners.makes import WhatsMiner + +class M63VK10(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M63 VK10" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M63 VK10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 + + +class M63VK20(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M63 VK20" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M63 VK20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 + + +class M63VK30(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M63 VK30" + self.nominal_chips = 68 + self.ideal_hashboards = 4 + self.fan_count = 0 \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/M6X/M63S.py b/pyasic/miners/types/whatsminer/M6X/M63S.py new file mode 100644 index 00000000..704670e9 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/M63S.py @@ -0,0 +1,54 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 warnings + +from pyasic.miners.makes import WhatsMiner + +class M63SVK10(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M63S VK10" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M63S VK10, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 + + +class M63SVK20(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M63S VK20" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M63S VK20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 + + +class M63SVK30(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M63S VK30" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M63S VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/M6X/M66.py b/pyasic/miners/types/whatsminer/M6X/M66.py new file mode 100644 index 00000000..2fc312e3 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/M66.py @@ -0,0 +1,43 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 warnings + +from pyasic.miners.makes import WhatsMiner + + +class M66VK20(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M66 VK20" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M66 VK20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 + + +class M66VK30(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M66 VK30" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M66 VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/M6X/M66S.py b/pyasic/miners/types/whatsminer/M6X/M66S.py new file mode 100644 index 00000000..b6128d15 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/M66S.py @@ -0,0 +1,53 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 warnings + +from pyasic.miners.makes import WhatsMiner + + +class M66SVK20(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M66S VK20" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M66S VK20, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 + + +class M66SVK30(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M66S VK30" + self.nominal_chips = 96 + self.ideal_hashboards = 4 + self.fan_count = 0 + + +class M66SVK40(WhatsMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "M66S VK40" + self.nominal_chips = 0 + warnings.warn( + "Unknown chip count for miner type M66 VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." + ) + self.fan_count = 0 \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/M6X/__init__.py b/pyasic/miners/types/whatsminer/M6X/__init__.py new file mode 100644 index 00000000..f55c8525 --- /dev/null +++ b/pyasic/miners/types/whatsminer/M6X/__init__.py @@ -0,0 +1,52 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 .M60 import ( + M60VK10, + M60VK20, + M60VK30, + M60VK40, +) + +from .M60S import ( + M60SVK10, + M60SVK20, + M60SVK30, + M60SVK40, +) + +from .M63 import ( + M63VK10, + M63VK20, + M63VK30, +) + +from .M63S import ( + M63SVK10, + M63SVK20, + M63SVK30, +) + +from .M66 import ( + M66VK20, + M66VK30, +) + +from .M66S import ( + M66SVK20, + M66SVK30, + M66SVK40, +) \ No newline at end of file diff --git a/pyasic/miners/types/whatsminer/__init__.py b/pyasic/miners/types/whatsminer/__init__.py index 550f58d8..ddcba8b5 100644 --- a/pyasic/miners/types/whatsminer/__init__.py +++ b/pyasic/miners/types/whatsminer/__init__.py @@ -17,3 +17,4 @@ from .M2X import * from .M3X import * from .M5X import * +from .M6X import * diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60.py b/pyasic/miners/whatsminer/btminer/M6X/M60.py new file mode 100644 index 00000000..d5290cef --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/M60.py @@ -0,0 +1,39 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 M6X +from pyasic.miners.types import ( + M60VK10, + M60VK20, + M60VK30, + M60VK40, +) + + +class BTMinerM60VK10(M6X, M60VK10): + pass + + +class BTMinerM60VK20(M6X, M60VK20): + pass + + +class BTMinerM60VK30(M6X, M60VK30): + pass + + +class BTMinerM60VK40(M6X, M60VK40): + pass diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60S.py b/pyasic/miners/whatsminer/btminer/M6X/M60S.py new file mode 100644 index 00000000..ebf05df6 --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/M60S.py @@ -0,0 +1,39 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 M6X +from pyasic.miners.types import ( + M60SVK10, + M60SVK20, + M60SVK30, + M60SVK40, +) + + +class BTMinerM60SVK10(M6X, M60SVK10): + pass + + +class BTMinerM60SVK20(M6X, M60SVK20): + pass + + +class BTMinerM60SVK30(M6X, M60SVK30): + pass + + +class BTMinerM60SVK40(M6X, M60SVK40): + pass diff --git a/pyasic/miners/whatsminer/btminer/M6X/M63.py b/pyasic/miners/whatsminer/btminer/M6X/M63.py new file mode 100644 index 00000000..a34aeb29 --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/M63.py @@ -0,0 +1,34 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 M6X +from pyasic.miners.types import ( + M63VK10, + M63VK20, + M63VK30, +) + + +class BTMinerM63VK10(M6X, M63VK10): + pass + + +class BTMinerM63VK20(M6X, M63VK20): + pass + + +class BTMinerM63VK30(M6X, M63VK30): + pass diff --git a/pyasic/miners/whatsminer/btminer/M6X/M63S.py b/pyasic/miners/whatsminer/btminer/M6X/M63S.py new file mode 100644 index 00000000..bc36d8a9 --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/M63S.py @@ -0,0 +1,34 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 M6X +from pyasic.miners.types import ( + M63SVK10, + M63SVK20, + M63SVK30, +) + + +class BTMinerM63SVK10(M6X, M63SVK10): + pass + + +class BTMinerM63SVK20(M6X, M63SVK20): + pass + + +class BTMinerM63SVK30(M6X, M63SVK30): + pass diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66.py b/pyasic/miners/whatsminer/btminer/M6X/M66.py new file mode 100644 index 00000000..c57b85ea --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/M66.py @@ -0,0 +1,29 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 M6X +from pyasic.miners.types import ( + M66VK20, + M66VK30, +) + + +class BTMinerM66VK20(M6X, M66VK20): + pass + + +class BTMinerM66VK30(M6X, M66VK30): + pass diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66S.py b/pyasic/miners/whatsminer/btminer/M6X/M66S.py new file mode 100644 index 00000000..4d5eb86b --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/M66S.py @@ -0,0 +1,34 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 M6X +from pyasic.miners.types import ( + M66SVK20, + M66SVK30, + M66SVK40, +) + + +class BTMinerM66SVK20(M6X, M66SVK20): + pass + + +class BTMinerM66SVK30(M6X, M66SVK30): + pass + + +class BTMinerM66SVK40(M6X, M66SVK40): + pass \ No newline at end of file diff --git a/pyasic/miners/whatsminer/btminer/M6X/__init__.py b/pyasic/miners/whatsminer/btminer/M6X/__init__.py new file mode 100644 index 00000000..d2e8fee4 --- /dev/null +++ b/pyasic/miners/whatsminer/btminer/M6X/__init__.py @@ -0,0 +1,52 @@ +# ------------------------------------------------------------------------------ +# Copyright 2023 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 .M60 import ( + BTMinerM60VK10, + BTMinerM60VK20, + BTMinerM60VK30, + BTMinerM60VK40, +) + +from .M60S import ( + BTMinerM60SVK10, + BTMinerM60SVK20, + BTMinerM60SVK30, + BTMinerM60SVK40, +) + +from .M63 import ( + BTMinerM63VK10, + BTMinerM63VK20, + BTMinerM63VK30, +) + +from .M63S import ( + BTMinerM63SVK10, + BTMinerM63SVK20, + BTMinerM63SVK30, +) + +from .M66 import ( + BTMinerM66VK20, + BTMinerM66VK30, +) + +from .M66S import ( + BTMinerM66SVK20, + BTMinerM66SVK30, + BTMinerM66SVK40, +) \ No newline at end of file diff --git a/pyasic/miners/whatsminer/btminer/__init__.py b/pyasic/miners/whatsminer/btminer/__init__.py index 550f58d8..e12b787c 100644 --- a/pyasic/miners/whatsminer/btminer/__init__.py +++ b/pyasic/miners/whatsminer/btminer/__init__.py @@ -17,3 +17,4 @@ from .M2X import * from .M3X import * from .M5X import * +from .M6X import * \ No newline at end of file From 4201905fddf46c2af7a97a07f91978eabf6efae5 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Wed, 13 Dec 2023 10:18:28 -0700 Subject: [PATCH 35/37] bug: fix some tasks not being cancelled properly in miner factory. --- pyasic/miners/miner_factory.py | 101 +++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index 777842c7..3c758e11 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -306,7 +306,7 @@ MINER_CLASSES = { "M66VK30": BTMinerM66VK30, "M66SVK20": BTMinerM66SVK20, "M66SVK30": BTMinerM66SVK30, - "M66SVK40": BTMinerM66SVK40, + "M66SVK40": BTMinerM66SVK40, }, MinerTypes.AVALONMINER: { None: CGMinerAvalon, @@ -389,24 +389,18 @@ MINER_CLASSES = { async def concurrent_get_first_result(tasks: list, verification_func: Callable): - while True: - await asyncio.sleep(0) - if len(tasks) == 0: - return - for task in tasks: - if task.done(): - try: - result = await task - except asyncio.CancelledError: - for t in tasks: - t.cancel() - raise - else: - if not verification_func(result): - continue - for t in tasks: - t.cancel() - return result + res = None + for fut in asyncio.as_completed(tasks): + res = await fut + if verification_func(res): + break + for t in tasks: + t.cancel() + try: + await t + except asyncio.CancelledError: + pass + return res class MinerFactory: @@ -453,7 +447,7 @@ class MinerFactory: task, timeout=settings.get("factory_get_timeout", 3) ) except asyncio.TimeoutError: - task.cancel() + continue else: if miner_type is not None: break @@ -481,7 +475,7 @@ class MinerFactory: task, timeout=settings.get("factory_get_timeout", 3) ) except asyncio.TimeoutError: - task.cancel() + pass boser_enabled = None if miner_type == MinerTypes.BRAIINS_OS: @@ -507,19 +501,30 @@ class MinerFactory: return await concurrent_get_first_result(tasks, lambda x: x is not None) async def _get_miner_web(self, ip: str): - urls = [f"http://{ip}/", f"https://{ip}/"] - async with httpx.AsyncClient( - transport=settings.transport(verify=False) - ) as session: - tasks = [asyncio.create_task(self._web_ping(session, url)) for url in urls] + tasks = [] + try: + urls = [f"http://{ip}/", f"https://{ip}/"] + async with httpx.AsyncClient( + transport=settings.transport(verify=False) + ) as session: + tasks = [ + asyncio.create_task(self._web_ping(session, url)) for url in urls + ] - text, resp = await concurrent_get_first_result( - tasks, - lambda x: x[0] is not None - and self._parse_web_type(x[0], x[1]) is not None, - ) - if text is not None: - return self._parse_web_type(text, resp) + text, resp = await concurrent_get_first_result( + tasks, + lambda x: x[0] is not None + and self._parse_web_type(x[0], x[1]) is not None, + ) + if text is not None: + return self._parse_web_type(text, resp) + except asyncio.CancelledError: + for t in tasks: + t.cancel() + try: + await t + except asyncio.CancelledError: + pass @staticmethod async def _web_ping( @@ -565,15 +570,27 @@ class MinerFactory: return MinerTypes.INNOSILICON async def _get_miner_socket(self, ip: str): - commands = ["version", "devdetails"] - tasks = [asyncio.create_task(self._socket_ping(ip, cmd)) for cmd in commands] + tasks = [] + try: + commands = ["version", "devdetails"] + tasks = [ + asyncio.create_task(self._socket_ping(ip, cmd)) for cmd in commands + ] - data = await concurrent_get_first_result( - tasks, lambda x: x is not None and self._parse_socket_type(x) is not None - ) - if data is not None: - d = self._parse_socket_type(data) - return d + data = await concurrent_get_first_result( + tasks, + lambda x: x is not None and self._parse_socket_type(x) is not None, + ) + if data is not None: + d = self._parse_socket_type(data) + return d + except asyncio.CancelledError: + for t in tasks: + t.cancel() + try: + await t + except asyncio.CancelledError: + pass @staticmethod async def _socket_ping(ip: str, cmd: str) -> Optional[str]: @@ -911,7 +928,7 @@ class MinerFactory: return miner_model except (TypeError, LookupError): pass - + async def get_miner_model_epic(self, ip: str) -> Optional[str]: sock_json_data = await self.send_web_command(ip, ":4028/capabilities") try: From 201cfd7ef9ec707be99595d0b3fa97bd46434ae1 Mon Sep 17 00:00:00 2001 From: UpstreamData Date: Wed, 13 Dec 2023 11:15:03 -0700 Subject: [PATCH 36/37] docs: update documentation to be more readable on the main page. --- docs/config/miner_config.md | 16 --- docs/index.md | 263 +++++++++++++----------------------- docs/miners/functions.md | 91 +++++++++++++ mkdocs.yml | 1 + 4 files changed, 189 insertions(+), 182 deletions(-) create mode 100644 docs/miners/functions.md diff --git a/docs/config/miner_config.md b/docs/config/miner_config.md index f38f65d2..378ce96d 100644 --- a/docs/config/miner_config.md +++ b/docs/config/miner_config.md @@ -6,19 +6,3 @@ options: show_root_heading: false heading_level: 4 - -## Pool Groups - -::: pyasic.config._PoolGroup - handler: python - options: - show_root_heading: false - heading_level: 4 - -## Pools - -::: pyasic.config._Pool - handler: python - options: - show_root_heading: false - heading_level: 4 diff --git a/docs/index.md b/docs/index.md index 50029188..c9cf7e55 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,17 +8,20 @@ [![GitHub](https://img.shields.io/github/license/UpstreamData/pyasic)](https://github.com/UpstreamData/pyasic/blob/master/LICENSE.txt) [![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/UpstreamData/pyasic)](https://www.codefactor.io/repository/github/upstreamdata/pyasic) +--- ## Intro -Welcome to pyasic! Pyasic uses an asynchronous method of communicating with asic miners on your network, which makes it super fast. +--- +Welcome to `pyasic`! `pyasic` uses an asynchronous method of communicating with ASIC miners on your network, which makes it super fast. -[Supported Miner Types](miners/supported_types.md) +[Click here to view supported miner types](miners/supported_types.md) -Getting started with pyasic is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP. +--- +## Getting started +--- +Getting started with `pyasic` is easy. First, find your miner (or miners) on the network by scanning for them or getting the correct class automatically for them if you know the IP. -
- -## Scanning for miners -To scan for miners in pyasic, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command. +##### Scanning for miners +To scan for miners in `pyasic`, we use the class [`MinerNetwork`][pyasic.network.MinerNetwork], which abstracts the search, communication, identification, setup, and return of a miner to 1 command. The command [`MinerNetwork.scan()`][pyasic.network.MinerNetwork.scan] returns a list that contains any miners found. ```python import asyncio # asyncio for handling the async part @@ -32,16 +35,15 @@ async def scan_miners(): # define async scan function to allow awaiting # scan for miners asynchronously # this will return the correct type of miners if they are supported with all functionality. - miners = await network.scan_network_for_miners() + miners = await network.scan() print(miners) if __name__ == "__main__": asyncio.run(scan_miners()) # run the scan asynchronously with asyncio.run() ``` -
- -## Creating miners based on IP +--- +##### Creating miners based on IP If you already know the IP address of your miner or miners, you can use the [`MinerFactory`][pyasic.miners.miner_factory.MinerFactory] to communicate and identify the miners, or an abstraction of its functionality, [`get_miner()`][pyasic.miners.get_miner]. The function [`get_miner()`][pyasic.miners.get_miner] will return any miner it found at the IP address specified, or an `UnknownMiner` if it cannot identify the miner. ```python @@ -58,6 +60,8 @@ async def get_miners(): # define async scan function to allow awaiting print(miner_1, miner_2) # can also gather these, since they are async + # gathering them will get them both at the same time + # this makes it much faster to get a lot of miners at a time tasks = [get_miner("192.168.1.75"), get_miner("192.168.1.76")] miners = await asyncio.gather(*tasks) print(miners) @@ -67,13 +71,14 @@ if __name__ == "__main__": asyncio.run(get_miners()) # get the miners asynchronously with asyncio.run() ``` -
- -## Getting data from miners - -Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built in function in each miner called `get_data()`. +--- +## Data gathering +--- +Once you have your miner(s) identified, you will likely want to get data from the miner(s). You can do this using a built-in function in each miner called `get_data()`. This function will return an instance of the dataclass [`MinerData`][pyasic.data.MinerData] with all data it can gather from the miner. Each piece of data in a [`MinerData`][pyasic.data.MinerData] instance can be referenced by getting it as an attribute, such as [`MinerData().hashrate`][pyasic.data.MinerData]. + +##### One miner ```python import asyncio from pyasic import get_miner @@ -88,7 +93,8 @@ async def gather_miner_data(): if __name__ == "__main__": asyncio.run(gather_miner_data()) ``` - +--- +##### Multiple miners You can do something similar with multiple miners, with only needing to make a small change to get all the data at once. ```python import asyncio # asyncio for handling the async part @@ -96,8 +102,8 @@ from pyasic.network import MinerNetwork # miner network handles the scanning async def gather_miner_data(): # define async scan function to allow awaiting - network = MinerNetwork("192.168.1.50") - miners = await network.scan_network_for_miners() + network = MinerNetwork.from_subnet("192.168.1.50/24") + miners = await network.scan() # we need to asyncio.gather() all the miners get_data() functions to make them run together all_miner_data = await asyncio.gather(*[miner.get_data() for miner in miners]) @@ -109,157 +115,56 @@ if __name__ == "__main__": asyncio.run(gather_miner_data()) ``` -
- -## Controlling miners via pyasic -Every miner class in pyasic must implement all the control functions defined in [`BaseMiner`][pyasic.miners.BaseMiner]. +--- +## Miner control +--- +`pyasic` exposes a standard interface for each miner using control functions. +Every miner class in `pyasic` must implement all the control functions defined in [`BaseMiner`][pyasic.miners.BaseMiner]. These functions are -[`check_light`](#check-light), -[`fault_light_off`](#fault-light-off), -[`fault_light_on`](#fault-light-on), -[`get_config`](#get-config), -[`get_data`](#get-data), -[`get_errors`](#get-errors), -[`get_hostname`](#get-hostname), -[`get_model`](#get-model), -[`reboot`](#reboot), -[`restart_backend`](#restart-backend), -[`stop_mining`](#stop-mining), -[`resume_mining`](#resume-mining), -[`is_mining`](#is-mining), -[`send_config`](#send-config), and -[`set_power_limit`](#set-power-limit). +[`check_light`][pyasic.miners.BaseMiner.check_light], +[`fault_light_off`][pyasic.miners.BaseMiner.fault_light_off], +[`fault_light_on`][pyasic.miners.BaseMiner.fault_light_on], +[`get_config`][pyasic.miners.BaseMiner.get_config], +[`get_data`][pyasic.miners.BaseMiner.get_data], +[`get_errors`][pyasic.miners.BaseMiner.get_errors], +[`get_hostname`][pyasic.miners.BaseMiner.get_hostname], +[`get_model`][pyasic.miners.BaseMiner.get_model], +[`reboot`][pyasic.miners.BaseMiner.reboot], +[`restart_backend`][pyasic.miners.BaseMiner.restart_backend], +[`stop_mining`][pyasic.miners.BaseMiner.stop_mining], +[`resume_mining`][pyasic.miners.BaseMiner.resume_mining], +[`is_mining`][pyasic.miners.BaseMiner.is_mining], +[`send_config`][pyasic.miners.BaseMiner.send_config], and +[`set_power_limit`][pyasic.miners.BaseMiner.set_power_limit]. -
+##### Usage +```python +import asyncio +from pyasic import get_miner -### Check Light -::: pyasic.miners.BaseMiner.check_light - handler: python - options: - heading_level: 4 -
+async def set_fault_light(): + miner = await get_miner("192.168.1.20") -### Fault Light Off -::: pyasic.miners.BaseMiner.fault_light_off - handler: python - options: - heading_level: 4 + # call control function + await miner.fault_light_on() -
+if __name__ == "__main__": + asyncio.run(set_fault_light()) +``` -### Fault Light On -::: pyasic.miners.BaseMiner.fault_light_on - handler: python - options: - heading_level: 4 +--- +## Helper dataclasses +--- -
+##### [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData] -### Get Config -::: pyasic.miners.BaseMiner.get_config - handler: python - options: - heading_level: 4 +`pyasic` implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`. -
+--- -### Get Data -::: pyasic.miners.BaseMiner.get_data - handler: python - options: - heading_level: 4 - -
- -### Get Errors -::: pyasic.miners.BaseMiner.get_errors - handler: python - options: - heading_level: 4 - -
- -### Get Hostname -::: pyasic.miners.BaseMiner.get_hostname - handler: python - options: - heading_level: 4 - -
- -### Get Model -::: pyasic.miners.BaseMiner.get_model - handler: python - options: - heading_level: 4 - -
- -### Reboot -::: pyasic.miners.BaseMiner.reboot - handler: python - options: - heading_level: 4 - -
- -### Restart Backend -::: pyasic.miners.BaseMiner.restart_backend - handler: python - options: - heading_level: 4 - -
- -### Stop Mining -::: pyasic.miners.BaseMiner.stop_mining - handler: python - options: - heading_level: 4 - -
- -### Resume Mining -::: pyasic.miners.BaseMiner.resume_mining - handler: python - options: - heading_level: 4 - -
- -### Is Mining -::: pyasic.miners.BaseMiner.is_mining - handler: python - options: - heading_level: 4 - -
- -### Send Config -::: pyasic.miners.BaseMiner.send_config - handler: python - options: - heading_level: 4 - -
- -### Set Power Limit -::: pyasic.miners.BaseMiner.set_power_limit - handler: python - options: - heading_level: 4 - -
- -## [`MinerConfig`][pyasic.config.MinerConfig] and [`MinerData`][pyasic.data.MinerData] - -Pyasic implements a few dataclasses as helpers to make data return types consistent across different miners and miner APIs. The different fields of these dataclasses can all be viewed with the classmethod `cls.fields()`. - -
- -### [`MinerData`][pyasic.data.MinerData] +##### [`MinerData`][pyasic.data.MinerData] [`MinerData`][pyasic.data.MinerData] is a return from the [`get_data()`](#get-data) function, and is used to have a consistent dataset across all returns. @@ -278,19 +183,40 @@ list_of_miner_data = [d1, d2] average_data = sum(list_of_miner_data, start=MinerData("0.0.0.0"))/len(list_of_miner_data) ``` +--- -
+##### [`MinerConfig`][pyasic.config.MinerConfig] -### [`MinerConfig`][pyasic.config.MinerConfig] - -[`MinerConfig`][pyasic.config.MinerConfig] is pyasic's way to represent a configuration file from a miner. -It is the return from [`get_config()`](#get-config). +[`MinerConfig`][pyasic.config.MinerConfig] is `pyasic`'s way to represent a configuration file from a miner. +It is designed to unionize the configuration of all supported miner types, and is the return from [`get_config()`](#get-config). Each miner has a unique way to convert the [`MinerConfig`][pyasic.config.MinerConfig] to their specific type, there are helper functions in the class. In most cases these helper functions should not be used, as [`send_config()`](#send-config) takes a [`MinerConfig`][pyasic.config.MinerConfig] and will do the conversion to the right type for you. +You can use the [`MinerConfig`][pyasic.config.MinerConfig] as follows: +```python +import asyncio +from pyasic import get_miner + + +async def set_fault_light(): + miner = await get_miner("192.168.1.20") + + # get config + cfg = await miner.get_config() + + # send config + await miner.send_config(cfg) + +if __name__ == "__main__": + asyncio.run(set_fault_light()) + +``` + +--- ## Settings -`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the [`pyasic.settings`][pyasic.settings] module, used as follows: +--- +`pyasic` has settings designed to make using large groups of miners easier. You can set the default password for all types of miners using the `pyasic.settings` module, used as follows: ```python from pyasic import settings @@ -298,18 +224,23 @@ from pyasic import settings settings.update("default_antminer_password", "my_pwd") ``` -Here are of all the settings, and their default values: +##### Default values: ``` "network_ping_retries": 1, "network_ping_timeout": 3, "network_scan_threads": 300, "factory_get_retries": 1, +"factory_get_timeout": 3, "get_data_retries": 1, +"api_function_timeout": 5, "default_whatsminer_password": "admin", "default_innosilicon_password": "admin", "default_antminer_password": "root", "default_bosminer_password": "root", "default_vnish_password": "admin", -"default_epic_password": "letmein", "default_goldshell_password": "123456789", + +# ADVANCED +# Only use this if you know what you are doing +"socket_linger_time": 1000, ``` diff --git a/docs/miners/functions.md b/docs/miners/functions.md new file mode 100644 index 00000000..5bfbd02c --- /dev/null +++ b/docs/miners/functions.md @@ -0,0 +1,91 @@ +## Control functionality + +### Check Light +::: pyasic.miners.BaseMiner.check_light + handler: python + options: + heading_level: 4 + +### Fault Light Off +::: pyasic.miners.BaseMiner.fault_light_off + handler: python + options: + heading_level: 4 + +### Fault Light On +::: pyasic.miners.BaseMiner.fault_light_on + handler: python + options: + heading_level: 4 + +### Get Config +::: pyasic.miners.BaseMiner.get_config + handler: python + options: + heading_level: 4 + +### Get Data +::: pyasic.miners.BaseMiner.get_data + handler: python + options: + heading_level: 4 + +### Get Errors +::: pyasic.miners.BaseMiner.get_errors + handler: python + options: + heading_level: 4 + +### Get Hostname +::: pyasic.miners.BaseMiner.get_hostname + handler: python + options: + heading_level: 4 + +### Get Model +::: pyasic.miners.BaseMiner.get_model + handler: python + options: + heading_level: 4 + +### Reboot +::: pyasic.miners.BaseMiner.reboot + handler: python + options: + heading_level: 4 + +### Restart Backend +::: pyasic.miners.BaseMiner.restart_backend + handler: python + options: + heading_level: 4 + +### Stop Mining +::: pyasic.miners.BaseMiner.stop_mining + handler: python + options: + heading_level: 4 + +### Resume Mining +::: pyasic.miners.BaseMiner.resume_mining + handler: python + options: + heading_level: 4 + +### Is Mining +::: pyasic.miners.BaseMiner.is_mining + handler: python + options: + heading_level: 4 + +### Send Config +::: pyasic.miners.BaseMiner.send_config + handler: python + options: + heading_level: 4 + +### Set Power Limit +::: pyasic.miners.BaseMiner.set_power_limit + handler: python + options: + heading_level: 4 diff --git a/mkdocs.yml b/mkdocs.yml index baf0af4c..646fb63c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -4,6 +4,7 @@ nav: - Introduction: "index.md" - Miners: - Supported Miners: "miners/supported_types.md" + - Standard Functionality: "miners/functions.md" - Miner Factory: "miners/miner_factory.md" - Network: - Miner Network: "network/miner_network.md" From 4459de22609be98def091013880d29c966257e6f Mon Sep 17 00:00:00 2001 From: Upstream Data Date: Sat, 16 Dec 2023 10:54:51 -0700 Subject: [PATCH 37/37] feature: add support for S19kProNoPIC BOS. Reformat. --- pyasic/API/__init__.py | 4 +- pyasic/miners/antminer/bosminer/X19/S19.py | 6 +- .../miners/antminer/bosminer/X19/__init__.py | 1 + pyasic/miners/antminer/epic/X19/S19.py | 16 ++- pyasic/miners/backends/bosminer.py | 1 - pyasic/miners/backends/btminer.py | 3 - pyasic/miners/backends/epic.py | 100 ++++++++++-------- pyasic/miners/miner_factory.py | 1 + pyasic/miners/types/antminer/X19/S19.py | 11 ++ pyasic/miners/types/antminer/X19/__init__.py | 1 + .../miners/types/whatsminer/M3X/M30S_Plus.py | 2 + pyasic/miners/types/whatsminer/M3X/M31H.py | 2 + pyasic/miners/types/whatsminer/M3X/M31L.py | 1 + .../miners/types/whatsminer/M3X/M33S_Plus.py | 3 +- pyasic/miners/types/whatsminer/M3X/M39.py | 1 + pyasic/miners/types/whatsminer/M5X/M50.py | 2 + pyasic/miners/types/whatsminer/M6X/M60.py | 5 +- pyasic/miners/types/whatsminer/M6X/M60S.py | 5 +- pyasic/miners/types/whatsminer/M6X/M63.py | 5 +- pyasic/miners/types/whatsminer/M6X/M63S.py | 5 +- pyasic/miners/types/whatsminer/M6X/M66.py | 4 +- pyasic/miners/types/whatsminer/M6X/M66S.py | 2 +- .../miners/types/whatsminer/M6X/__init__.py | 42 ++------ pyasic/miners/whatsminer/btminer/M6X/M60.py | 7 +- pyasic/miners/whatsminer/btminer/M6X/M60S.py | 7 +- pyasic/miners/whatsminer/btminer/M6X/M66S.py | 8 +- .../miners/whatsminer/btminer/M6X/__init__.py | 42 ++------ pyasic/miners/whatsminer/btminer/__init__.py | 2 +- pyasic/web/epic.py | 35 +++--- 29 files changed, 144 insertions(+), 180 deletions(-) diff --git a/pyasic/API/__init__.py b/pyasic/API/__init__.py index e4b9443a..77ae8456 100644 --- a/pyasic/API/__init__.py +++ b/pyasic/API/__init__.py @@ -213,11 +213,11 @@ If you are sure you want to use this command please use API.send_command("{comma # append that data if there is more, and then onto the main loop. # the password timeout might need to be longer than 1, but it seems to work for now. ret_data = await asyncio.wait_for(reader.read(1), timeout=1) - except (asyncio.TimeoutError): + except asyncio.TimeoutError: return b"{}" try: ret_data += await asyncio.wait_for(reader.read(4096), timeout=timeout) - except (ConnectionAbortedError): + except ConnectionAbortedError: return b"{}" # loop to receive all the data diff --git a/pyasic/miners/antminer/bosminer/X19/S19.py b/pyasic/miners/antminer/bosminer/X19/S19.py index ef90c7ec..c6e277f5 100644 --- a/pyasic/miners/antminer/bosminer/X19/S19.py +++ b/pyasic/miners/antminer/bosminer/X19/S19.py @@ -15,7 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import BOSMiner -from pyasic.miners.types import S19, S19j, S19jNoPIC, S19jPro, S19Pro +from pyasic.miners.types import S19, S19j, S19jNoPIC, S19jPro, S19kProNoPIC, S19Pro class BOSMinerS19(BOSMiner, S19): @@ -36,3 +36,7 @@ class BOSMinerS19jNoPIC(BOSMiner, S19jNoPIC): class BOSMinerS19jPro(BOSMiner, S19jPro): pass + + +class BOSMinerS19kProNoPIC(BOSMiner, S19kProNoPIC): + pass diff --git a/pyasic/miners/antminer/bosminer/X19/__init__.py b/pyasic/miners/antminer/bosminer/X19/__init__.py index 5be29194..fd5cf770 100644 --- a/pyasic/miners/antminer/bosminer/X19/__init__.py +++ b/pyasic/miners/antminer/bosminer/X19/__init__.py @@ -19,6 +19,7 @@ from .S19 import ( BOSMinerS19j, BOSMinerS19jNoPIC, BOSMinerS19jPro, + BOSMinerS19kProNoPIC, BOSMinerS19Pro, ) from .T19 import BOSMinerT19 diff --git a/pyasic/miners/antminer/epic/X19/S19.py b/pyasic/miners/antminer/epic/X19/S19.py index d6c4da95..5a5fd934 100644 --- a/pyasic/miners/antminer/epic/X19/S19.py +++ b/pyasic/miners/antminer/epic/X19/S19.py @@ -15,34 +15,32 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import ePIC -from pyasic.miners.types import ( - S19, - S19Pro, - S19j, - S19jPro, - S19jProPlus, - S19kPro, - S19XP, -) +from pyasic.miners.types import S19, S19XP, S19j, S19jPro, S19jProPlus, S19kPro, S19Pro class ePICS19(ePIC, S19): pass + class ePICS19Pro(ePIC, S19Pro): pass + class ePICS19j(ePIC, S19j): pass + class ePICS19jPro(ePIC, S19jPro): pass + class ePICS19jProPlus(ePIC, S19jProPlus): pass + class ePICS19kPro(ePIC, S19kPro): pass + class ePICS19XP(ePIC, S19XP): pass diff --git a/pyasic/miners/backends/bosminer.py b/pyasic/miners/backends/bosminer.py index 6b0a9507..c9124a7f 100644 --- a/pyasic/miners/backends/bosminer.py +++ b/pyasic/miners/backends/bosminer.py @@ -557,7 +557,6 @@ class BOSMiner(BaseMiner): async def get_hashrate( self, api_summary: dict = None, graphql_hashrate: dict = None ) -> Optional[float]: - # get hr from graphql if not graphql_hashrate: try: diff --git a/pyasic/miners/backends/btminer.py b/pyasic/miners/backends/btminer.py index 7a28c27a..8e167cfb 100644 --- a/pyasic/miners/backends/btminer.py +++ b/pyasic/miners/backends/btminer.py @@ -260,8 +260,6 @@ class BTMiner(BaseMiner): self.config = cfg return self.config - - async def set_power_limit(self, wattage: int) -> bool: try: await self.api.adjust_power_limit(wattage) @@ -411,7 +409,6 @@ class BTMiner(BaseMiner): pass async def get_hashboards(self, api_devs: dict = None) -> List[HashBoard]: - hashboards = [ HashBoard(slot=i, expected_chips=self.nominal_chips) for i in range(self.ideal_hashboards) diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py index 9966661e..9d4894ed 100644 --- a/pyasic/miners/backends/epic.py +++ b/pyasic/miners/backends/epic.py @@ -14,16 +14,14 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from typing import Optional +from typing import List, Optional, Tuple, Union +from pyasic.data import Fan, HashBoard +from pyasic.data.error_codes import MinerErrorData, X19Error from pyasic.errors import APIError from pyasic.logger import logger from pyasic.miners.backends.bmminer import BMMiner from pyasic.web.epic import ePICWebAPI -from pyasic.data import Fan, HashBoard -from typing import List, Optional, Tuple, Union -from pyasic.data.error_codes import MinerErrorData, X19Error - EPIC_DATA_LOC = { "mac": {"cmd": "get_mac", "kwargs": {"web_summary": {"web": "network"}}}, @@ -36,12 +34,21 @@ EPIC_DATA_LOC = { "cmd": "get_nominal_hashrate", "kwargs": {"web_summary": {"web": "summary"}}, }, - "hashboards": {"cmd": "get_hashboards", "kwargs": {"web_summary": {"web": "summary"}, "web_hashrate": {"web": "hashrate"}}}, + "hashboards": { + "cmd": "get_hashboards", + "kwargs": { + "web_summary": {"web": "summary"}, + "web_hashrate": {"web": "hashrate"}, + }, + }, "env_temp": {"cmd": "get_env_temp", "kwargs": {}}, "wattage": {"cmd": "get_wattage", "kwargs": {"web_summary": {"web": "summary"}}}, "fans": {"cmd": "get_fans", "kwargs": {"web_summary": {"web": "summary"}}}, "fan_psu": {"cmd": "get_fan_psu", "kwargs": {}}, - "fault_light": {"cmd": "get_fault_light", "kwargs": {"web_summary": {"web": "summary"}}}, + "fault_light": { + "cmd": "get_fault_light", + "kwargs": {"web_summary": {"web": "summary"}}, + }, "pools": {"cmd": "get_pools", "kwargs": {"web_summary": {"web": "summary"}}}, "is_mining": {"cmd": "is_mining", "kwargs": {}}, "uptime": {"cmd": "get_uptime", "kwargs": {"web_summary": {"web": "summary"}}}, @@ -148,12 +155,11 @@ class ePIC(BMMiner): if web_summary["HBs"] != None: for hb in web_summary["HBs"]: hashrate += hb["Hashrate"][0] - return round( - float(float(hashrate/ 1000000)), 2) + return round(float(float(hashrate / 1000000)), 2) except (LookupError, ValueError, TypeError) as e: logger.error(e) pass - + async def get_nominal_hashrate(self, web_summary: dict = None) -> Optional[float]: # get hr from API if not web_summary: @@ -170,16 +176,14 @@ class ePIC(BMMiner): if hb["Hashrate"][1] == 0: ideal = 1.0 else: - ideal = hb["Hashrate"][1]/100 - - hashrate += hb["Hashrate"][0]/ideal - return round( - float(float(hashrate/ 1000000)), 2) + ideal = hb["Hashrate"][1] / 100 + + hashrate += hb["Hashrate"][0] / ideal + return round(float(float(hashrate / 1000000)), 2) except (IndexError, KeyError, ValueError, TypeError) as e: logger.error(e) pass - async def get_fw_ver(self, web_summary: dict = None) -> Optional[str]: if not web_summary: web_summary = await self.web.summary() @@ -208,8 +212,10 @@ class ePIC(BMMiner): except (LookupError, ValueError, TypeError): fans.append(Fan()) return fans - - async def get_hashboards(self, web_summary: dict = None, web_hashrate: dict= None) -> List[HashBoard]: + + async def get_hashboards( + self, web_summary: dict = None, web_hashrate: dict = None + ) -> List[HashBoard]: if not web_summary: try: web_summary = await self.web.summary() @@ -220,51 +226,53 @@ class ePIC(BMMiner): web_hashrate = await self.web.hashrate() except APIError: pass - hb_list = [HashBoard(slot=i, expected_chips=self.nominal_chips) for i in range(self.ideal_hashboards)] + hb_list = [ + HashBoard(slot=i, expected_chips=self.nominal_chips) + for i in range(self.ideal_hashboards) + ] if web_summary["HBs"] != None: for hb in web_summary["HBs"]: for hr in web_hashrate: if hr["Index"] == hb["Index"]: num_of_chips = len(hr["Data"]) hashrate = hb["Hashrate"][0] - #Update the Hashboard object + # Update the Hashboard object hb_list[hr["Index"]].expected_chips = num_of_chips hb_list[hr["Index"]].missing = False - hb_list[hr["Index"]].hashrate = round(hashrate/1000000,2) + hb_list[hr["Index"]].hashrate = round(hashrate / 1000000, 2) hb_list[hr["Index"]].chips = num_of_chips hb_list[hr["Index"]].temp = hb["Temperature"] return hb_list async def is_mining(self, *args, **kwargs) -> Optional[bool]: return None - + async def get_pools(self, web_summary: dict = None) -> List[dict]: - groups = [] + groups = [] - if not web_summary: - try: - web_summary = await self.api.summary() - except APIError: - pass + if not web_summary: + try: + web_summary = await self.api.summary() + except APIError: + pass - if web_summary: - try: - pools = {} - for i, pool in enumerate(web_summary["StratumConfigs"]): - pools[f"pool_{i + 1}_url"] = ( - pool["pool"] - .replace("stratum+tcp://", "") - .replace("stratum2+tcp://", "") - ) - pools[f"pool_{i + 1}_user"] = pool["login"] - pools["quota"] = pool["Quota"] if pool.get("Quota") else "0" + if web_summary: + try: + pools = {} + for i, pool in enumerate(web_summary["StratumConfigs"]): + pools[f"pool_{i + 1}_url"] = ( + pool["pool"] + .replace("stratum+tcp://", "") + .replace("stratum2+tcp://", "") + ) + pools[f"pool_{i + 1}_user"] = pool["login"] + pools["quota"] = pool["Quota"] if pool.get("Quota") else "0" - groups.append(pools) - except KeyError: - pass - return groups + groups.append(pools) + except KeyError: + pass + return groups - async def get_uptime(self, web_summary: dict = None) -> Optional[int]: if not web_summary: web_summary = await self.web.summary() @@ -275,7 +283,7 @@ class ePIC(BMMiner): except KeyError: pass return None - + async def get_fault_light(self, web_summary: dict = None) -> bool: if not web_summary: web_summary = await self.web.summary() @@ -286,7 +294,7 @@ class ePIC(BMMiner): except KeyError: pass return False - + async def get_errors(self, web_summary: dict = None) -> List[MinerErrorData]: if not web_summary: web_summary = await self.web.summary() diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py index 3c758e11..4321e1a9 100644 --- a/pyasic/miners/miner_factory.py +++ b/pyasic/miners/miner_factory.py @@ -352,6 +352,7 @@ MINER_CLASSES = { "ANTMINER S19J PRO": BOSMinerS19jPro, "ANTMINER S19J PRO NOPIC": BOSMinerS19jPro, "ANTMINER T19": BOSMinerT19, + "ANTMINER S19K PRO NOPIC": BOSMinerS19kProNoPIC, }, MinerTypes.VNISH: { None: VNish, diff --git a/pyasic/miners/types/antminer/X19/S19.py b/pyasic/miners/types/antminer/X19/S19.py index 4f4d8614..442fbceb 100644 --- a/pyasic/miners/types/antminer/X19/S19.py +++ b/pyasic/miners/types/antminer/X19/S19.py @@ -124,6 +124,7 @@ class S19jPro(AntMiner): # noqa - ignore ABC method implementation self.nominal_chips = 126 self.fan_count = 4 + class S19jProPlus(AntMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -132,6 +133,7 @@ class S19jProPlus(AntMiner): # noqa - ignore ABC method implementation self.nominal_chips = 120 self.fan_count = 4 + class S19kPro(AntMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -148,3 +150,12 @@ class S19L(AntMiner): # noqa - ignore ABC method implementation self.model = "S19L" self.nominal_chips = 76 self.fan_count = 4 + + +class S19kProNoPIC(AntMiner): # noqa - ignore ABC method implementation + def __init__(self, ip: str, api_ver: str = "0.0.0"): + super().__init__(ip, api_ver) + self.ip = ip + self.model = "S19k Pro No PIC" + self.nominal_chips = 77 + self.fan_count = 4 diff --git a/pyasic/miners/types/antminer/X19/__init__.py b/pyasic/miners/types/antminer/X19/__init__.py index e388a3de..bd0c0074 100644 --- a/pyasic/miners/types/antminer/X19/__init__.py +++ b/pyasic/miners/types/antminer/X19/__init__.py @@ -26,6 +26,7 @@ from .S19 import ( S19jPro, S19jProPlus, S19kPro, + S19kProNoPIC, S19NoPIC, S19Plus, S19Pro, diff --git a/pyasic/miners/types/whatsminer/M3X/M30S_Plus.py b/pyasic/miners/types/whatsminer/M3X/M30S_Plus.py index 555a358f..7ff3d08c 100644 --- a/pyasic/miners/types/whatsminer/M3X/M30S_Plus.py +++ b/pyasic/miners/types/whatsminer/M3X/M30S_Plus.py @@ -213,6 +213,7 @@ class M30SPlusVF30(WhatsMiner): # noqa - ignore ABC method implementation self.nominal_chips = 117 self.fan_count = 2 + class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -221,6 +222,7 @@ class M30SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation self.nominal_chips = 82 self.fan_count = 2 + class M30SPlusVG30(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) diff --git a/pyasic/miners/types/whatsminer/M3X/M31H.py b/pyasic/miners/types/whatsminer/M3X/M31H.py index 269d99f7..fa4fe57b 100644 --- a/pyasic/miners/types/whatsminer/M3X/M31H.py +++ b/pyasic/miners/types/whatsminer/M3X/M31H.py @@ -18,6 +18,7 @@ import warnings from pyasic.miners.makes import WhatsMiner + class M31HV10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -26,6 +27,7 @@ class M31HV10(WhatsMiner): # noqa - ignore ABC method implementation self.nominal_chips = 114 self.fan_count = 0 + class M31HV40(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) diff --git a/pyasic/miners/types/whatsminer/M3X/M31L.py b/pyasic/miners/types/whatsminer/M3X/M31L.py index 437c4224..d14b4b2d 100644 --- a/pyasic/miners/types/whatsminer/M3X/M31L.py +++ b/pyasic/miners/types/whatsminer/M3X/M31L.py @@ -18,6 +18,7 @@ import warnings from pyasic.miners.makes import WhatsMiner + class M31LV10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) diff --git a/pyasic/miners/types/whatsminer/M3X/M33S_Plus.py b/pyasic/miners/types/whatsminer/M3X/M33S_Plus.py index f0de8f2b..e127e8a9 100644 --- a/pyasic/miners/types/whatsminer/M3X/M33S_Plus.py +++ b/pyasic/miners/types/whatsminer/M3X/M33S_Plus.py @@ -18,6 +18,7 @@ import warnings from pyasic.miners.makes import WhatsMiner + class M33SPlusVG20(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -44,7 +45,7 @@ class M33SPlusVH30(WhatsMiner): # noqa - ignore ABC method implementation self.ip = ip self.model = "M33S+ VH30" self.ideal_hashboards = 4 - self.nominal_chips = 0 # slot1 116, slot2 106, slot3 116, slot4 106 + self.nominal_chips = 0 # slot1 116, slot2 106, slot3 116, slot4 106 warnings.warn( "Unknown chip count for miner type M30S+ VH30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." ) diff --git a/pyasic/miners/types/whatsminer/M3X/M39.py b/pyasic/miners/types/whatsminer/M3X/M39.py index 73a74fdc..bc5d29a5 100644 --- a/pyasic/miners/types/whatsminer/M3X/M39.py +++ b/pyasic/miners/types/whatsminer/M3X/M39.py @@ -18,6 +18,7 @@ import warnings from pyasic.miners.makes import WhatsMiner + class M39V10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) diff --git a/pyasic/miners/types/whatsminer/M5X/M50.py b/pyasic/miners/types/whatsminer/M5X/M50.py index 675b0b1f..35082fa8 100644 --- a/pyasic/miners/types/whatsminer/M5X/M50.py +++ b/pyasic/miners/types/whatsminer/M5X/M50.py @@ -18,6 +18,7 @@ import warnings from pyasic.miners.makes import WhatsMiner + class M50VE30(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -27,6 +28,7 @@ class M50VE30(WhatsMiner): # noqa - ignore ABC method implementation self.nominal_chips = 255 self.fan_count = 2 + class M50VG30(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) diff --git a/pyasic/miners/types/whatsminer/M6X/M60.py b/pyasic/miners/types/whatsminer/M6X/M60.py index 77649cd4..31b33da2 100644 --- a/pyasic/miners/types/whatsminer/M6X/M60.py +++ b/pyasic/miners/types/whatsminer/M6X/M60.py @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ - + import warnings from pyasic.miners.makes import WhatsMiner + class M60VK10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -63,4 +64,4 @@ class M60VK40(WhatsMiner): # noqa - ignore ABC method implementation warnings.warn( "Unknown chip count for miner type M60 VK40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." ) - self.fan_count = 2 \ No newline at end of file + self.fan_count = 2 diff --git a/pyasic/miners/types/whatsminer/M6X/M60S.py b/pyasic/miners/types/whatsminer/M6X/M60S.py index 2ec049e8..1b46bbb6 100644 --- a/pyasic/miners/types/whatsminer/M6X/M60S.py +++ b/pyasic/miners/types/whatsminer/M6X/M60S.py @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ - + import warnings from pyasic.miners.makes import WhatsMiner + class M60SVK10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -61,4 +62,4 @@ class M60SVK40(WhatsMiner): # noqa - ignore ABC method implementation warnings.warn( "Unknown chip count for miner type M60S VK40, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." ) - self.fan_count = 2 \ No newline at end of file + self.fan_count = 2 diff --git a/pyasic/miners/types/whatsminer/M6X/M63.py b/pyasic/miners/types/whatsminer/M6X/M63.py index 28310657..d176d058 100644 --- a/pyasic/miners/types/whatsminer/M6X/M63.py +++ b/pyasic/miners/types/whatsminer/M6X/M63.py @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ - + import warnings from pyasic.miners.makes import WhatsMiner + class M63VK10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -49,4 +50,4 @@ class M63VK30(WhatsMiner): # noqa - ignore ABC method implementation self.model = "M63 VK30" self.nominal_chips = 68 self.ideal_hashboards = 4 - self.fan_count = 0 \ No newline at end of file + self.fan_count = 0 diff --git a/pyasic/miners/types/whatsminer/M6X/M63S.py b/pyasic/miners/types/whatsminer/M6X/M63S.py index 704670e9..36508fda 100644 --- a/pyasic/miners/types/whatsminer/M6X/M63S.py +++ b/pyasic/miners/types/whatsminer/M6X/M63S.py @@ -13,11 +13,12 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ - + import warnings from pyasic.miners.makes import WhatsMiner + class M63SVK10(WhatsMiner): # noqa - ignore ABC method implementation def __init__(self, ip: str, api_ver: str = "0.0.0"): super().__init__(ip, api_ver) @@ -51,4 +52,4 @@ class M63SVK30(WhatsMiner): # noqa - ignore ABC method implementation warnings.warn( "Unknown chip count for miner type M63S VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." ) - self.fan_count = 0 \ No newline at end of file + self.fan_count = 0 diff --git a/pyasic/miners/types/whatsminer/M6X/M66.py b/pyasic/miners/types/whatsminer/M6X/M66.py index 2fc312e3..43483715 100644 --- a/pyasic/miners/types/whatsminer/M6X/M66.py +++ b/pyasic/miners/types/whatsminer/M6X/M66.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and - # limitations under the License. - # ------------------------------------------------------------------------------ - + import warnings from pyasic.miners.makes import WhatsMiner @@ -40,4 +40,4 @@ class M66VK30(WhatsMiner): # noqa - ignore ABC method implementation warnings.warn( "Unknown chip count for miner type M66 VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." ) - self.fan_count = 0 \ No newline at end of file + self.fan_count = 0 diff --git a/pyasic/miners/types/whatsminer/M6X/M66S.py b/pyasic/miners/types/whatsminer/M6X/M66S.py index b6128d15..ee9467a1 100644 --- a/pyasic/miners/types/whatsminer/M6X/M66S.py +++ b/pyasic/miners/types/whatsminer/M6X/M66S.py @@ -50,4 +50,4 @@ class M66SVK40(WhatsMiner): # noqa - ignore ABC method implementation warnings.warn( "Unknown chip count for miner type M66 VK30, please open an issue on GitHub (https://github.com/UpstreamData/pyasic)." ) - self.fan_count = 0 \ No newline at end of file + self.fan_count = 0 diff --git a/pyasic/miners/types/whatsminer/M6X/__init__.py b/pyasic/miners/types/whatsminer/M6X/__init__.py index f55c8525..315db2b7 100644 --- a/pyasic/miners/types/whatsminer/M6X/__init__.py +++ b/pyasic/miners/types/whatsminer/M6X/__init__.py @@ -14,39 +14,9 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .M60 import ( - M60VK10, - M60VK20, - M60VK30, - M60VK40, -) - -from .M60S import ( - M60SVK10, - M60SVK20, - M60SVK30, - M60SVK40, -) - -from .M63 import ( - M63VK10, - M63VK20, - M63VK30, -) - -from .M63S import ( - M63SVK10, - M63SVK20, - M63SVK30, -) - -from .M66 import ( - M66VK20, - M66VK30, -) - -from .M66S import ( - M66SVK20, - M66SVK30, - M66SVK40, -) \ No newline at end of file +from .M60 import M60VK10, M60VK20, M60VK30, M60VK40 +from .M60S import M60SVK10, M60SVK20, M60SVK30, M60SVK40 +from .M63 import M63VK10, M63VK20, M63VK30 +from .M63S import M63SVK10, M63SVK20, M63SVK30 +from .M66 import M66VK20, M66VK30 +from .M66S import M66SVK20, M66SVK30, M66SVK40 diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60.py b/pyasic/miners/whatsminer/btminer/M6X/M60.py index d5290cef..8e26a71e 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M60.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M60.py @@ -15,12 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import M6X -from pyasic.miners.types import ( - M60VK10, - M60VK20, - M60VK30, - M60VK40, -) +from pyasic.miners.types import M60VK10, M60VK20, M60VK30, M60VK40 class BTMinerM60VK10(M6X, M60VK10): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M60S.py b/pyasic/miners/whatsminer/btminer/M6X/M60S.py index ebf05df6..35ba788e 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M60S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M60S.py @@ -15,12 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import M6X -from pyasic.miners.types import ( - M60SVK10, - M60SVK20, - M60SVK30, - M60SVK40, -) +from pyasic.miners.types import M60SVK10, M60SVK20, M60SVK30, M60SVK40 class BTMinerM60SVK10(M6X, M60SVK10): diff --git a/pyasic/miners/whatsminer/btminer/M6X/M66S.py b/pyasic/miners/whatsminer/btminer/M6X/M66S.py index 4d5eb86b..5f456fcd 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/M66S.py +++ b/pyasic/miners/whatsminer/btminer/M6X/M66S.py @@ -15,11 +15,7 @@ # ------------------------------------------------------------------------------ from pyasic.miners.backends import M6X -from pyasic.miners.types import ( - M66SVK20, - M66SVK30, - M66SVK40, -) +from pyasic.miners.types import M66SVK20, M66SVK30, M66SVK40 class BTMinerM66SVK20(M6X, M66SVK20): @@ -31,4 +27,4 @@ class BTMinerM66SVK30(M6X, M66SVK30): class BTMinerM66SVK40(M6X, M66SVK40): - pass \ No newline at end of file + pass diff --git a/pyasic/miners/whatsminer/btminer/M6X/__init__.py b/pyasic/miners/whatsminer/btminer/M6X/__init__.py index d2e8fee4..34dcf363 100644 --- a/pyasic/miners/whatsminer/btminer/M6X/__init__.py +++ b/pyasic/miners/whatsminer/btminer/M6X/__init__.py @@ -14,39 +14,9 @@ # limitations under the License. - # ------------------------------------------------------------------------------ -from .M60 import ( - BTMinerM60VK10, - BTMinerM60VK20, - BTMinerM60VK30, - BTMinerM60VK40, -) - -from .M60S import ( - BTMinerM60SVK10, - BTMinerM60SVK20, - BTMinerM60SVK30, - BTMinerM60SVK40, -) - -from .M63 import ( - BTMinerM63VK10, - BTMinerM63VK20, - BTMinerM63VK30, -) - -from .M63S import ( - BTMinerM63SVK10, - BTMinerM63SVK20, - BTMinerM63SVK30, -) - -from .M66 import ( - BTMinerM66VK20, - BTMinerM66VK30, -) - -from .M66S import ( - BTMinerM66SVK20, - BTMinerM66SVK30, - BTMinerM66SVK40, -) \ No newline at end of file +from .M60 import BTMinerM60VK10, BTMinerM60VK20, BTMinerM60VK30, BTMinerM60VK40 +from .M60S import BTMinerM60SVK10, BTMinerM60SVK20, BTMinerM60SVK30, BTMinerM60SVK40 +from .M63 import BTMinerM63VK10, BTMinerM63VK20, BTMinerM63VK30 +from .M63S import BTMinerM63SVK10, BTMinerM63SVK20, BTMinerM63SVK30 +from .M66 import BTMinerM66VK20, BTMinerM66VK30 +from .M66S import BTMinerM66SVK20, BTMinerM66SVK30, BTMinerM66SVK40 diff --git a/pyasic/miners/whatsminer/btminer/__init__.py b/pyasic/miners/whatsminer/btminer/__init__.py index e12b787c..ddcba8b5 100644 --- a/pyasic/miners/whatsminer/btminer/__init__.py +++ b/pyasic/miners/whatsminer/btminer/__init__.py @@ -17,4 +17,4 @@ from .M2X import * from .M3X import * from .M5X import * -from .M6X import * \ No newline at end of file +from .M6X import * diff --git a/pyasic/web/epic.py b/pyasic/web/epic.py index a270278a..2784edee 100644 --- a/pyasic/web/epic.py +++ b/pyasic/web/epic.py @@ -20,8 +20,9 @@ from typing import Union import httpx from pyasic import settings -from pyasic.web import BaseWebAPI from pyasic.errors import APIError, APIWarning +from pyasic.web import BaseWebAPI + class ePICWebAPI(BaseWebAPI): def __init__(self, ip: str) -> None: @@ -31,22 +32,24 @@ class ePICWebAPI(BaseWebAPI): self.token = None async def send_command( - self, - command: Union[str, bytes], - ignore_errors: bool = False, - allow_warning: bool = True, - post: bool = False, - **parameters: Union[str, int, bool], + self, + command: Union[str, bytes], + ignore_errors: bool = False, + allow_warning: bool = True, + post: bool = False, + **parameters: Union[str, int, bool], ) -> dict: if post or parameters != {}: post = True - + async with httpx.AsyncClient(transport=settings.transport()) as client: for i in range(settings.get("get_data_retries", 1) + 1): try: if post: - epic_param = {"param": parameters.get("parameters"), - "password": self.pwd} + epic_param = { + "param": parameters.get("parameters"), + "password": self.pwd, + } response = await client.post( f"http://{self.ip}:4028/{command}", timeout=5, @@ -56,14 +59,17 @@ class ePICWebAPI(BaseWebAPI): response = await client.get( f"http://{self.ip}:4028/{command}", timeout=5, - ) if not response.status_code == 200: continue json_data = response.json() if json_data: # The API can return a fail status if the miner cannot return the requested data. Catch this and pass - if "result" in json_data and json_data["result"] is False and not post: + if ( + "result" in json_data + and json_data["result"] is False + and not post + ): if not i > settings.get("get_data_retries", 1): continue if not ignore_errors: @@ -102,13 +108,12 @@ class ePICWebAPI(BaseWebAPI): async def summary(self): return await self.send_command("summary") - + async def hashrate(self): return await self.send_command("hashrate") - + async def network(self): return await self.send_command("network") async def capabilities(self): return await self.send_command("capabilities") -