diff --git a/docs/generate_miners.py b/docs/generate_miners.py
index 22ecfc4d..1e196268 100644
--- a/docs/generate_miners.py
+++ b/docs/generate_miners.py
@@ -31,6 +31,8 @@ def backend_str(backend: MinerTypes) -> str:
return "Stock Firmware Avalonminers"
case MinerTypes.VNISH:
return "Vnish Firmware Miners"
+ case MinerTypes.EPIC:
+ return "ePIC Firmware Miners"
case MinerTypes.BRAIINS_OS:
return "BOS+ Firmware Miners"
case MinerTypes.HIVEON:
diff --git a/docs/index.md b/docs/index.md
index f766ebb4..50029188 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -310,5 +310,6 @@ Here are of all the settings, and their default values:
"default_antminer_password": "root",
"default_bosminer_password": "root",
"default_vnish_password": "admin",
+"default_epic_password": "letmein",
"default_goldshell_password": "123456789",
```
diff --git a/docs/miners/antminer/X19.md b/docs/miners/antminer/X19.md
index 40daa590..7ea38d07 100644
--- a/docs/miners/antminer/X19.md
+++ b/docs/miners/antminer/X19.md
@@ -197,3 +197,38 @@
show_root_heading: false
heading_level: 4
+## S19 (ePIC)
+::: pyasic.miners.antminer.epic.X19.S19.ePICS19
+ handler: python
+ options:
+ show_root_heading: false
+ heading_level: 4
+
+## S19 Pro (ePIC)
+::: pyasic.miners.antminer.epic.X19.S19.ePICS19Pro
+ handler: python
+ options:
+ show_root_heading: false
+ heading_level: 4
+
+## S19j (ePIC)
+::: pyasic.miners.antminer.epic.X19.S19.ePICS19j
+ handler: python
+ options:
+ show_root_heading: false
+ heading_level: 4
+
+## S19j Pro (ePIC)
+::: pyasic.miners.antminer.epic.X19.S19.ePICS19jPro
+ handler: python
+ options:
+ show_root_heading: false
+ heading_level: 4
+
+## S19 XP (ePIC)
+::: pyasic.miners.antminer.epic.X19.S19.ePICS19XP
+ handler: python
+ options:
+ show_root_heading: false
+ heading_level: 4
+
diff --git a/docs/miners/backends/epic.md b/docs/miners/backends/epic.md
new file mode 100644
index 00000000..0d30c625
--- /dev/null
+++ b/docs/miners/backends/epic.md
@@ -0,0 +1,8 @@
+# pyasic
+## ePIC Backend
+
+::: pyasic.miners.backends.epic.ePIC
+ handler: python
+ options:
+ show_root_heading: false
+ heading_level: 4
diff --git a/docs/miners/supported_types.md b/docs/miners/supported_types.md
index c0780a2d..83cb4a1c 100644
--- a/docs/miners/supported_types.md
+++ b/docs/miners/supported_types.md
@@ -445,6 +445,21 @@ details {
+ePIC Firmware Miners:
+
+
+
HiveOS Firmware Miners:
diff --git a/mkdocs.yml b/mkdocs.yml
index 635a4007..baf0af4c 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -29,6 +29,7 @@ nav:
- CGMiner: "miners/backends/cgminer.md"
- LUXMiner: "miners/backends/luxminer.md"
- VNish: "miners/backends/vnish.md"
+ - ePIC: "miners/backends/epic.md"
- Hiveon: "miners/backends/hiveon.md"
- Classes:
- Antminer X3: "miners/antminer/X3.md"
diff --git a/pyasic/miners/antminer/__init__.py b/pyasic/miners/antminer/__init__.py
index 20d32e7d..1d2bd719 100644
--- a/pyasic/miners/antminer/__init__.py
+++ b/pyasic/miners/antminer/__init__.py
@@ -20,3 +20,4 @@ from .cgminer import *
from .hiveon import *
from .luxos import *
from .vnish import *
+from .epic import *
diff --git a/pyasic/miners/antminer/epic/X19/S19.py b/pyasic/miners/antminer/epic/X19/S19.py
new file mode 100644
index 00000000..6ad74210
--- /dev/null
+++ b/pyasic/miners/antminer/epic/X19/S19.py
@@ -0,0 +1,40 @@
+# ------------------------------------------------------------------------------
+# Copyright 2022 Upstream Data Inc -
+# -
+# Licensed under the Apache License, Version 2.0 (the "License"); -
+# you may not use this file except in compliance with the License. -
+# You may obtain a copy of the License at -
+# -
+# http://www.apache.org/licenses/LICENSE-2.0 -
+# -
+# Unless required by applicable law or agreed to in writing, software -
+# distributed under the License is distributed on an "AS IS" BASIS, -
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
+# See the License for the specific language governing permissions and -
+# limitations under the License. -
+# ------------------------------------------------------------------------------
+
+from pyasic.miners.backends import ePIC
+from pyasic.miners.types import (
+ S19,
+ S19Pro,
+ S19j,
+ S19jPro,
+ S19XP,
+)
+
+
+class ePICS19(ePIC, S19):
+ pass
+
+class ePICS19Pro(ePIC, S19Pro):
+ pass
+
+class ePICS19j(ePIC, S19j):
+ pass
+
+class ePICS19jPro(ePIC, S19jPro):
+ pass
+
+class ePICS19XP(ePIC, S19XP):
+ pass
diff --git a/pyasic/miners/antminer/epic/X19/__init__.py b/pyasic/miners/antminer/epic/X19/__init__.py
new file mode 100644
index 00000000..87bad9d7
--- /dev/null
+++ b/pyasic/miners/antminer/epic/X19/__init__.py
@@ -0,0 +1,23 @@
+# ------------------------------------------------------------------------------
+# Copyright 2022 Upstream Data Inc -
+# -
+# Licensed under the Apache License, Version 2.0 (the "License"); -
+# you may not use this file except in compliance with the License. -
+# You may obtain a copy of the License at -
+# -
+# http://www.apache.org/licenses/LICENSE-2.0 -
+# -
+# Unless required by applicable law or agreed to in writing, software -
+# distributed under the License is distributed on an "AS IS" BASIS, -
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
+# See the License for the specific language governing permissions and -
+# limitations under the License. -
+# ------------------------------------------------------------------------------
+
+from .S19 import (
+ ePICS19,
+ ePICS19Pro,
+ ePICS19j,
+ ePICS19jPro,
+ ePICS19XP,
+)
diff --git a/pyasic/miners/antminer/epic/__init__.py b/pyasic/miners/antminer/epic/__init__.py
new file mode 100644
index 00000000..36309a49
--- /dev/null
+++ b/pyasic/miners/antminer/epic/__init__.py
@@ -0,0 +1,17 @@
+# ------------------------------------------------------------------------------
+# 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 .X19 import *
diff --git a/pyasic/miners/backends/__init__.py b/pyasic/miners/backends/__init__.py
index b18d909f..c34fe09f 100644
--- a/pyasic/miners/backends/__init__.py
+++ b/pyasic/miners/backends/__init__.py
@@ -24,4 +24,5 @@ from .cgminer_avalon import CGMinerAvalon
from .hiveon import Hiveon
from .luxminer import LUXMiner
from .vnish import VNish
+from .epic import ePIC
from .whatsminer import M2X, M3X, M5X
diff --git a/pyasic/miners/backends/epic.py b/pyasic/miners/backends/epic.py
new file mode 100644
index 00000000..9966661e
--- /dev/null
+++ b/pyasic/miners/backends/epic.py
@@ -0,0 +1,302 @@
+# ------------------------------------------------------------------------------
+# Copyright 2022 Upstream Data Inc -
+# -
+# Licensed under the Apache License, Version 2.0 (the "License"); -
+# you may not use this file except in compliance with the License. -
+# You may obtain a copy of the License at -
+# -
+# http://www.apache.org/licenses/LICENSE-2.0 -
+# -
+# Unless required by applicable law or agreed to in writing, software -
+# distributed under the License is distributed on an "AS IS" BASIS, -
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
+# See the License for the specific language governing permissions and -
+# limitations under the License. -
+# ------------------------------------------------------------------------------
+
+from typing import Optional
+
+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"}}},
+ "model": {"cmd": "get_model", "kwargs": {}},
+ "api_ver": {"cmd": "get_api_ver", "kwargs": {"api_version": {"api": "version"}}},
+ "fw_ver": {"cmd": "get_fw_ver", "kwargs": {"web_summary": {"web": "summary"}}},
+ "hostname": {"cmd": "get_hostname", "kwargs": {"web_summary": {"web": "summary"}}},
+ "hashrate": {"cmd": "get_hashrate", "kwargs": {"web_summary": {"web": "summary"}}},
+ "nominal_hashrate": {
+ "cmd": "get_nominal_hashrate",
+ "kwargs": {"web_summary": {"web": "summary"}},
+ },
+ "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"}}},
+ "pools": {"cmd": "get_pools", "kwargs": {"web_summary": {"web": "summary"}}},
+ "is_mining": {"cmd": "is_mining", "kwargs": {}},
+ "uptime": {"cmd": "get_uptime", "kwargs": {"web_summary": {"web": "summary"}}},
+ "errors": {"cmd": "get_errors", "kwargs": {"web_summary": {"web": "summary"}}},
+}
+
+
+class ePIC(BMMiner):
+ def __init__(self, ip: str, api_ver: str = "0.0.0") -> None:
+ super().__init__(ip, api_ver)
+ # interfaces
+ self.web = ePICWebAPI(ip)
+
+ # static data
+ self.api_type = "ePIC"
+ # data gathering locations
+ self.data_locations = EPIC_DATA_LOC
+
+ async def get_model(self) -> Optional[str]:
+ if self.model is not None:
+ return self.model + " (ePIC)"
+ return "? (ePIC)"
+
+ async def restart_backend(self) -> bool:
+ data = await self.web.restart_epic()
+ if data:
+ try:
+ return data["success"]
+ except KeyError:
+ pass
+ return False
+
+ async def stop_mining(self) -> bool:
+ data = await self.web.stop_mining()
+ if data:
+ try:
+ return data["success"]
+ except KeyError:
+ pass
+ return False
+
+ async def resume_mining(self) -> bool:
+ data = await self.web.resume_mining()
+ if data:
+ try:
+ return data["success"]
+ except KeyError:
+ pass
+ return False
+
+ async def reboot(self) -> bool:
+ data = await self.web.reboot()
+ if data:
+ try:
+ return data["success"]
+ except KeyError:
+ pass
+ return False
+
+ async def get_mac(self, web_summary: dict = None) -> str:
+ if not web_summary:
+ web_summary = await self.web.network()
+ if web_summary:
+ try:
+ for network in web_summary:
+ mac = web_summary[network]["mac_address"]
+ return mac
+ except KeyError:
+ pass
+
+ async def get_hostname(self, web_summary: dict = None) -> str:
+ if not web_summary:
+ web_summary = await self.web.summary()
+ if web_summary:
+ try:
+ hostname = web_summary["Hostname"]
+ return hostname
+ except KeyError:
+ pass
+
+ async def get_wattage(self, web_summary: dict = None) -> Optional[int]:
+ if not web_summary:
+ web_summary = await self.web.summary()
+
+ if web_summary:
+ try:
+ wattage = web_summary["Power Supply Stats"]["Input Power"]
+ wattage = round(wattage)
+ return wattage
+ except KeyError:
+ pass
+
+ async def get_hashrate(self, web_summary: dict = None) -> Optional[float]:
+ # get hr from API
+ if not web_summary:
+ try:
+ web_summary = await self.web.summary()
+ except APIError:
+ pass
+
+ if web_summary:
+ try:
+ hashrate = 0
+ if web_summary["HBs"] != None:
+ for hb in web_summary["HBs"]:
+ hashrate += hb["Hashrate"][0]
+ 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:
+ try:
+ web_summary = await self.web.summary()
+ except APIError:
+ pass
+
+ if web_summary:
+ try:
+ hashrate = 0
+ if web_summary["HBs"] != None:
+ for hb in web_summary["HBs"]:
+ 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)
+ 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()
+
+ if web_summary:
+ try:
+ fw_ver = web_summary["Software"]
+ fw_ver = fw_ver.split(" ")[1].replace("v", "")
+ return fw_ver
+ except KeyError:
+ pass
+
+ async def get_fans(self, web_summary: dict = None) -> List[Fan]:
+ if not web_summary:
+ try:
+ web_summary = await self.web.summary()
+ except APIError:
+ pass
+
+ fans = []
+
+ if web_summary:
+ for fan in web_summary["Fans Rpm"]:
+ try:
+ fans.append(Fan(web_summary["Fans Rpm"][fan]))
+ except (LookupError, ValueError, TypeError):
+ fans.append(Fan())
+ return fans
+
+ 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()
+ except APIError:
+ pass
+ if not web_hashrate:
+ try:
+ 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)]
+ 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
+ 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"]].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 = []
+
+ 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"
+
+ 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()
+ if web_summary:
+ try:
+ uptime = web_summary["Session"]["Uptime"]
+ return uptime
+ 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()
+ if web_summary:
+ try:
+ light = web_summary["Misc"]["Locate Miner State"]
+ return light
+ 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()
+ errors = []
+ if web_summary:
+ try:
+ error = web_summary["Status"]["Last Error"]
+ if error != None:
+ errors.append(X19Error(str(error)))
+ return errors
+ except KeyError:
+ pass
+ return errors
diff --git a/pyasic/miners/miner_factory.py b/pyasic/miners/miner_factory.py
index 6cf5542e..a7cb2941 100644
--- a/pyasic/miners/miner_factory.py
+++ b/pyasic/miners/miner_factory.py
@@ -37,6 +37,7 @@ from pyasic.miners.backends import (
Hiveon,
LUXMiner,
VNish,
+ ePIC,
)
from pyasic.miners.base import AnyMiner
from pyasic.miners.goldshell import *
@@ -55,6 +56,7 @@ class MinerTypes(enum.Enum):
VNISH = 6
HIVEON = 7
LUX_OS = 8
+ EPIC = 9
MINER_CLASSES = {
@@ -346,6 +348,14 @@ MINER_CLASSES = {
"ANTMINER S19A PRO": VNishS19aPro,
"ANTMINER T19": VNishT19,
},
+ MinerTypes.EPIC: {
+ None: ePIC,
+ "ANTMINER S19": ePICS19,
+ "ANTMINER S19 PRO": ePICS19Pro,
+ "ANTMINER S19J": ePICS19j,
+ "ANTMINER S19J PRO": ePICS19jPro,
+ "ANTMINER S19 XP": ePICS19XP,
+ },
MinerTypes.HIVEON: {
None: Hiveon,
"ANTMINER T9": HiveonT9,
@@ -437,6 +447,7 @@ class MinerFactory:
MinerTypes.GOLDSHELL: self.get_miner_model_goldshell,
MinerTypes.BRAIINS_OS: self.get_miner_model_braiins_os,
MinerTypes.VNISH: self.get_miner_model_vnish,
+ MinerTypes.EPIC: self.get_miner_model_epic,
MinerTypes.HIVEON: self.get_miner_model_hiveon,
MinerTypes.LUX_OS: self.get_miner_model_luxos,
}
@@ -525,6 +536,8 @@ class MinerFactory:
return MinerTypes.GOLDSHELL
if "AnthillOS" in web_text:
return MinerTypes.VNISH
+ if "Miner Web Dashboard" in web_text:
+ return MinerTypes.EPIC
if "Avalon" in web_text:
return MinerTypes.AVALONMINER
if "DragonMint" in web_text:
@@ -877,6 +890,14 @@ 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:
+ miner_model = sock_json_data["Model"]
+ return miner_model
+ except (TypeError, LookupError):
+ pass
async def get_miner_model_hiveon(self, ip: str) -> Optional[str]:
sock_json_data = await self.send_api_command(ip, "version")
diff --git a/pyasic/web/epic.py b/pyasic/web/epic.py
new file mode 100644
index 00000000..680cd4db
--- /dev/null
+++ b/pyasic/web/epic.py
@@ -0,0 +1,120 @@
+# ------------------------------------------------------------------------------
+# 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 json
+import warnings
+from typing import Union
+
+import httpx
+
+from pyasic import settings
+from pyasic.web import BaseWebAPI
+from pyasic.errors import APIError, APIWarning
+
+
+class ePICWebAPI(BaseWebAPI):
+ def __init__(self, ip: str) -> None:
+ super().__init__(ip)
+ self.username = "root"
+ self.pwd = settings.get("default_epic_password", "letmein")
+ self.token = None
+
+ async def send_command(
+ self,
+ command: Union[str, bytes],
+ ignore_errors: bool = False,
+ allow_warning: bool = True,
+ **parameters: Union[str, int, bool],
+ ) -> dict:
+ async with httpx.AsyncClient() as client:
+ is_get = False
+ for i in range(settings.get("get_data_retries", 1)):
+ try:
+ if parameters.get("post"):
+ parameters.pop("post")
+ epic_param = {"param": parameters.get("parameters"), "password": self.pwd}
+ response = await client.post(
+ f"http://{self.ip}:4028/{command}",
+ timeout=5,
+ json=epic_param,
+ )
+ elif not parameters == {}:
+ response = await client.post(
+ f"http://{self.ip}:4028/{command}",
+ timeout=5,
+ json=parameters,
+ )
+ else:
+ is_get = True
+ 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 is_get and not ignore_errors:
+ raise APIError(json_data["error"])
+ return json_data
+ return {"success": True}
+ except httpx.HTTPError:
+ pass
+ except json.JSONDecodeError:
+ pass
+ except AttributeError:
+ pass
+
+ async def multicommand(
+ self, *commands: str, ignore_errors: bool = False, allow_warning: bool = True
+ ) -> dict:
+ data = {k: None for k in commands}
+ data["multicommand"] = True
+ for command in commands:
+ data[command] = await self.send_command(command)
+ return data
+
+ async def restart_epic(self) -> dict:
+ return await self.send_command("softreboot", post=True, parameters=None)
+
+ async def reboot(self) -> dict:
+ return await self.send_command("reboot", post=True, parameters=None)
+
+ async def pause_mining(self) -> dict:
+ return await self.send_command("miner", post=True, parameters="Stop")
+
+ async def resume_mining(self) -> dict:
+ return await self.send_command("miner", post=True, parameters="Autostart")
+
+ async def stop_mining(self) -> dict:
+ return await self.send_command("miner", post=True, parameters="Stop")
+
+ async def start_mining(self) -> dict:
+ return await self.send_command("miner", post=True, parameters="Autostart")
+
+ 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")
+