Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87016670d4 | ||
|
|
8701bbe4e2 | ||
|
|
7d1f125b0b | ||
|
|
e433902bd5 | ||
|
|
a653772968 |
@@ -31,6 +31,8 @@ def backend_str(backend: MinerTypes) -> str:
|
|||||||
return "Stock Firmware Avalonminers"
|
return "Stock Firmware Avalonminers"
|
||||||
case MinerTypes.VNISH:
|
case MinerTypes.VNISH:
|
||||||
return "Vnish Firmware Miners"
|
return "Vnish Firmware Miners"
|
||||||
|
case MinerTypes.EPIC:
|
||||||
|
return "ePIC Firmware Miners"
|
||||||
case MinerTypes.BRAIINS_OS:
|
case MinerTypes.BRAIINS_OS:
|
||||||
return "BOS+ Firmware Miners"
|
return "BOS+ Firmware Miners"
|
||||||
case MinerTypes.HIVEON:
|
case MinerTypes.HIVEON:
|
||||||
|
|||||||
@@ -310,5 +310,6 @@ Here are of all the settings, and their default values:
|
|||||||
"default_antminer_password": "root",
|
"default_antminer_password": "root",
|
||||||
"default_bosminer_password": "root",
|
"default_bosminer_password": "root",
|
||||||
"default_vnish_password": "admin",
|
"default_vnish_password": "admin",
|
||||||
|
"default_epic_password": "letmein",
|
||||||
"default_goldshell_password": "123456789",
|
"default_goldshell_password": "123456789",
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -197,3 +197,38 @@
|
|||||||
show_root_heading: false
|
show_root_heading: false
|
||||||
heading_level: 4
|
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>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
<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>
|
<summary>HiveOS Firmware Miners:</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@@ -1,11 +1,23 @@
|
|||||||
# pyasic
|
# pyasic
|
||||||
## settings
|
## settings
|
||||||
|
|
||||||
::: pyasic.settings
|
All settings here are global settings for all of pyasic. Set these settings with `update(key, value)`.
|
||||||
handler: python
|
|
||||||
options:
|
Settings options:
|
||||||
show_root_heading: false
|
- `network_ping_retries`
|
||||||
heading_level: 4
|
- `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
|
### get
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ nav:
|
|||||||
- Miner Factory: "miners/miner_factory.md"
|
- Miner Factory: "miners/miner_factory.md"
|
||||||
- Network:
|
- Network:
|
||||||
- Miner Network: "network/miner_network.md"
|
- Miner Network: "network/miner_network.md"
|
||||||
- Miner Network Range: "network/miner_network_range.md"
|
|
||||||
- Dataclasses:
|
- Dataclasses:
|
||||||
- Miner Data: "data/miner_data.md"
|
- Miner Data: "data/miner_data.md"
|
||||||
- Error Codes: "data/error_codes.md"
|
- Error Codes: "data/error_codes.md"
|
||||||
@@ -30,6 +29,7 @@ nav:
|
|||||||
- CGMiner: "miners/backends/cgminer.md"
|
- CGMiner: "miners/backends/cgminer.md"
|
||||||
- LUXMiner: "miners/backends/luxminer.md"
|
- LUXMiner: "miners/backends/luxminer.md"
|
||||||
- VNish: "miners/backends/vnish.md"
|
- VNish: "miners/backends/vnish.md"
|
||||||
|
- ePIC: "miners/backends/epic.md"
|
||||||
- Hiveon: "miners/backends/hiveon.md"
|
- Hiveon: "miners/backends/hiveon.md"
|
||||||
- Classes:
|
- Classes:
|
||||||
- Antminer X3: "miners/antminer/X3.md"
|
- Antminer X3: "miners/antminer/X3.md"
|
||||||
|
|||||||
@@ -20,3 +20,4 @@ from .cgminer import *
|
|||||||
from .hiveon import *
|
from .hiveon import *
|
||||||
from .luxos import *
|
from .luxos import *
|
||||||
from .vnish 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 .hiveon import Hiveon
|
||||||
from .luxminer import LUXMiner
|
from .luxminer import LUXMiner
|
||||||
from .vnish import VNish
|
from .vnish import VNish
|
||||||
|
from .epic import ePIC
|
||||||
from .whatsminer import M2X, M3X, M5X
|
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,
|
Hiveon,
|
||||||
LUXMiner,
|
LUXMiner,
|
||||||
VNish,
|
VNish,
|
||||||
|
ePIC,
|
||||||
)
|
)
|
||||||
from pyasic.miners.base import AnyMiner
|
from pyasic.miners.base import AnyMiner
|
||||||
from pyasic.miners.goldshell import *
|
from pyasic.miners.goldshell import *
|
||||||
@@ -55,6 +56,7 @@ class MinerTypes(enum.Enum):
|
|||||||
VNISH = 6
|
VNISH = 6
|
||||||
HIVEON = 7
|
HIVEON = 7
|
||||||
LUX_OS = 8
|
LUX_OS = 8
|
||||||
|
EPIC = 9
|
||||||
|
|
||||||
|
|
||||||
MINER_CLASSES = {
|
MINER_CLASSES = {
|
||||||
@@ -346,6 +348,14 @@ MINER_CLASSES = {
|
|||||||
"ANTMINER S19A PRO": VNishS19aPro,
|
"ANTMINER S19A PRO": VNishS19aPro,
|
||||||
"ANTMINER T19": VNishT19,
|
"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: {
|
MinerTypes.HIVEON: {
|
||||||
None: Hiveon,
|
None: Hiveon,
|
||||||
"ANTMINER T9": HiveonT9,
|
"ANTMINER T9": HiveonT9,
|
||||||
@@ -437,6 +447,7 @@ class MinerFactory:
|
|||||||
MinerTypes.GOLDSHELL: self.get_miner_model_goldshell,
|
MinerTypes.GOLDSHELL: self.get_miner_model_goldshell,
|
||||||
MinerTypes.BRAIINS_OS: self.get_miner_model_braiins_os,
|
MinerTypes.BRAIINS_OS: self.get_miner_model_braiins_os,
|
||||||
MinerTypes.VNISH: self.get_miner_model_vnish,
|
MinerTypes.VNISH: self.get_miner_model_vnish,
|
||||||
|
MinerTypes.EPIC: self.get_miner_model_epic,
|
||||||
MinerTypes.HIVEON: self.get_miner_model_hiveon,
|
MinerTypes.HIVEON: self.get_miner_model_hiveon,
|
||||||
MinerTypes.LUX_OS: self.get_miner_model_luxos,
|
MinerTypes.LUX_OS: self.get_miner_model_luxos,
|
||||||
}
|
}
|
||||||
@@ -525,6 +536,8 @@ class MinerFactory:
|
|||||||
return MinerTypes.GOLDSHELL
|
return MinerTypes.GOLDSHELL
|
||||||
if "AnthillOS" in web_text:
|
if "AnthillOS" in web_text:
|
||||||
return MinerTypes.VNISH
|
return MinerTypes.VNISH
|
||||||
|
if "Miner Web Dashboard" in web_text:
|
||||||
|
return MinerTypes.EPIC
|
||||||
if "Avalon" in web_text:
|
if "Avalon" in web_text:
|
||||||
return MinerTypes.AVALONMINER
|
return MinerTypes.AVALONMINER
|
||||||
if "DragonMint" in web_text:
|
if "DragonMint" in web_text:
|
||||||
@@ -877,6 +890,14 @@ class MinerFactory:
|
|||||||
return miner_model
|
return miner_model
|
||||||
except (TypeError, LookupError):
|
except (TypeError, LookupError):
|
||||||
pass
|
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]:
|
async def get_miner_model_hiveon(self, ip: str) -> Optional[str]:
|
||||||
sock_json_data = await self.send_api_command(ip, "version")
|
sock_json_data = await self.send_api_command(ip, "version")
|
||||||
|
|||||||
120
pyasic/web/epic.py
Normal file
120
pyasic/web/epic.py
Normal file
@@ -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")
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pyasic"
|
name = "pyasic"
|
||||||
version = "0.41.0"
|
version = "0.42.0"
|
||||||
description = "A simplified and standardized interface for Bitcoin ASICs."
|
description = "A simplified and standardized interface for Bitcoin ASICs."
|
||||||
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
authors = ["UpstreamData <brett@upstreamdata.ca>"]
|
||||||
repository = "https://github.com/UpstreamData/pyasic"
|
repository = "https://github.com/UpstreamData/pyasic"
|
||||||
|
|||||||
Reference in New Issue
Block a user