Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbd73881d4 | ||
|
|
68c4dadb63 | ||
|
|
87016670d4 | ||
|
|
8701bbe4e2 | ||
|
|
7d1f125b0b | ||
|
|
e433902bd5 | ||
|
|
a653772968 |
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
8
docs/miners/backends/epic.md
Normal file
8
docs/miners/backends/epic.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# pyasic
|
||||
## ePIC Backend
|
||||
|
||||
::: pyasic.miners.backends.epic.ePIC
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
@@ -445,6 +445,21 @@ details {
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>ePIC Firmware Miners:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
<summary>X19 Series:</summary>
|
||||
<ul>
|
||||
<li><a href="../antminer/X19#s19-epic">S19 (ePIC)</a></li>
|
||||
<li><a href="../antminer/X19#s19-pro-epic">S19 Pro (ePIC)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-epic">S19j (ePIC)</a></li>
|
||||
<li><a href="../antminer/X19#s19j-pro-epic">S19j Pro (ePIC)</a></li>
|
||||
<li><a href="../antminer/X19#s19-xp-epic">S19 XP (ePIC)</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</ul>
|
||||
</details>
|
||||
<details>
|
||||
<summary>HiveOS Firmware Miners:</summary>
|
||||
<ul>
|
||||
<details>
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
# pyasic
|
||||
## settings
|
||||
|
||||
::: pyasic.settings
|
||||
handler: python
|
||||
options:
|
||||
show_root_heading: false
|
||||
heading_level: 4
|
||||
All settings here are global settings for all of pyasic. Set these settings with `update(key, value)`.
|
||||
|
||||
Settings options:
|
||||
- `network_ping_retries`
|
||||
- `network_ping_timeout`
|
||||
- `network_scan_threads`
|
||||
- `factory_get_retries`
|
||||
- `factory_get_timeout`
|
||||
- `get_data_retries`
|
||||
- `api_function_timeout`
|
||||
- `default_whatsminer_password`
|
||||
- `default_innosilicon_password`
|
||||
- `default_antminer_password`
|
||||
- `default_bosminer_password`
|
||||
- `default_vnish_password`
|
||||
- `default_goldshell_password`
|
||||
- `socket_linger_time`
|
||||
|
||||
|
||||
### get
|
||||
|
||||
@@ -7,7 +7,6 @@ nav:
|
||||
- Miner Factory: "miners/miner_factory.md"
|
||||
- Network:
|
||||
- Miner Network: "network/miner_network.md"
|
||||
- Miner Network Range: "network/miner_network_range.md"
|
||||
- Dataclasses:
|
||||
- Miner Data: "data/miner_data.md"
|
||||
- Error Codes: "data/error_codes.md"
|
||||
@@ -30,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"
|
||||
|
||||
@@ -20,3 +20,4 @@ from .cgminer import *
|
||||
from .hiveon import *
|
||||
from .luxos import *
|
||||
from .vnish import *
|
||||
from .epic import *
|
||||
|
||||
40
pyasic/miners/antminer/epic/X19/S19.py
Normal file
40
pyasic/miners/antminer/epic/X19/S19.py
Normal file
@@ -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
|
||||
23
pyasic/miners/antminer/epic/X19/__init__.py
Normal file
23
pyasic/miners/antminer/epic/X19/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright 2022 Upstream Data Inc -
|
||||
# -
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); -
|
||||
# you may not use this file except in compliance with the License. -
|
||||
# You may obtain a copy of the License at -
|
||||
# -
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 -
|
||||
# -
|
||||
# Unless required by applicable law or agreed to in writing, software -
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, -
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -
|
||||
# See the License for the specific language governing permissions and -
|
||||
# limitations under the License. -
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
from .S19 import (
|
||||
ePICS19,
|
||||
ePICS19Pro,
|
||||
ePICS19j,
|
||||
ePICS19jPro,
|
||||
ePICS19XP,
|
||||
)
|
||||
17
pyasic/miners/antminer/epic/__init__.py
Normal file
17
pyasic/miners/antminer/epic/__init__.py
Normal file
@@ -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 *
|
||||
@@ -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
|
||||
|
||||
302
pyasic/miners/backends/epic.py
Normal file
302
pyasic/miners/backends/epic.py
Normal file
@@ -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
|
||||
@@ -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")
|
||||
|
||||
114
pyasic/web/epic.py
Normal file
114
pyasic/web/epic.py
Normal file
@@ -0,0 +1,114 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
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}
|
||||
response = await client.post(
|
||||
f"http://{self.ip}:4028/{command}",
|
||||
timeout=5,
|
||||
json=epic_param,
|
||||
)
|
||||
else:
|
||||
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 not i > settings.get("get_data_retries", 1):
|
||||
continue
|
||||
if not ignore_errors:
|
||||
raise APIError(json_data["error"])
|
||||
return json_data
|
||||
return {"success": True}
|
||||
except (httpx.HTTPError, json.JSONDecodeError, 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")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "pyasic"
|
||||
version = "0.41.0"
|
||||
version = "0.42.1"
|
||||
description = "A simplified and standardized interface for Bitcoin ASICs."
|
||||
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||
repository = "https://github.com/UpstreamData/pyasic"
|
||||
|
||||
Reference in New Issue
Block a user