Compare commits

..

15 Commits

Author SHA1 Message Date
Upstream Data
5fec3052f6 version: bump version number 2024-12-02 09:22:18 -07:00
Upstream Data
437ee774ab bug: type convert to int for vnish config 2024-12-02 09:22:00 -07:00
Upstream Data
aed9e0e406 version: bump version number 2024-12-02 09:14:05 -07:00
Upstream Data
be96428823 bug: skip vnish boards with 0 freq 2024-12-02 09:13:46 -07:00
Upstream Data
446881b237 version: bump version number 2024-12-02 09:00:15 -07:00
Upstream Data
ceab8e55b5 feature: add send_config support for vnish 2024-12-02 08:59:58 -07:00
Upstream Data
e12f85c94d version: bump version number 2024-11-28 15:09:28 -07:00
Upstream Data
0c85f53177 bug: fix some issues with pool URLs 2024-11-28 15:05:04 -07:00
Upstream Data
0b524f9bd0 version: bump version number 2024-11-28 14:40:01 -07:00
Upstream Data
95db852636 docs: update docs 2024-11-28 14:39:46 -07:00
Upstream Data
93fa02412a feature: add support for Hiveon S19j Pro 2024-11-28 14:38:30 -07:00
Upstream Data
edb77e9cd8 bug: fix hiveon identification 2024-11-28 14:33:52 -07:00
Upstream Data
cbf7eeb08d version: bump version number 2024-11-26 08:26:26 -07:00
Upstream Data
d34b35a82d bug: add luxos.supports_shutdown value 2024-11-26 08:25:25 -07:00
Upstream Data
5d57f35475 bug: fix possible case where unidentified miner type can raise and error 2024-11-25 13:00:49 -07:00
25 changed files with 390 additions and 43 deletions

View File

@@ -53,6 +53,8 @@ def backend_str(backend: MinerTypes) -> str:
return "Stock Firmware BitAxe Miners" return "Stock Firmware BitAxe Miners"
case MinerTypes.ICERIVER: case MinerTypes.ICERIVER:
return "Stock Firmware IceRiver Miners" return "Stock Firmware IceRiver Miners"
case MinerTypes.HAMMER:
return "Stock Firmware Hammer Miners"
def create_url_str(mtype: str): def create_url_str(mtype: str):

View File

@@ -358,6 +358,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## S19j Pro (Stock)
::: pyasic.miners.antminer.hiveon.X19.S19.HiveonS19jPro
handler: python
options:
show_root_heading: false
heading_level: 4
## S19 (LuxOS) ## S19 (LuxOS)
::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19 ::: pyasic.miners.antminer.luxos.X19.S19.LUXMinerS19
handler: python handler: python

View File

@@ -15,6 +15,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## D7 (Stock)
::: pyasic.miners.antminer.bmminer.X7.D7.BMMinerD7
handler: python
options:
show_root_heading: false
heading_level: 4
## L7 (VNish) ## L7 (VNish)
::: pyasic.miners.antminer.vnish.X7.L7.VnishL7 ::: pyasic.miners.antminer.vnish.X7.L7.VnishL7
handler: python handler: python

View File

@@ -8,6 +8,13 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## D9 (Stock)
::: pyasic.miners.antminer.bmminer.X9.D9.BMMinerD9
handler: python
options:
show_root_heading: false
heading_level: 4
## S9 (Stock) ## S9 (Stock)
::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9 ::: pyasic.miners.antminer.bmminer.X9.S9.BMMinerS9
handler: python handler: python

View File

@@ -22,3 +22,10 @@
show_root_heading: false show_root_heading: false
heading_level: 4 heading_level: 4
## Gamma (Stock)
::: pyasic.miners.bitaxe.espminer.BM.BM1370.BitAxeGamma
handler: python
options:
show_root_heading: false
heading_level: 4

10
docs/miners/hammer/DX.md Normal file
View File

@@ -0,0 +1,10 @@
# pyasic
## DX Models
## D10 (Stock)
::: pyasic.miners.hammer.blackminer.DX.D10.HammerD10
handler: python
options:
show_root_heading: false
heading_level: 4

View File

@@ -37,12 +37,14 @@ details {
<ul> <ul>
<li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li> <li><a href="../antminer/X7#l7-stock">L7 (Stock)</a></li>
<li><a href="../antminer/X7#k7-stock">K7 (Stock)</a></li> <li><a href="../antminer/X7#k7-stock">K7 (Stock)</a></li>
<li><a href="../antminer/X7#d7-stock">D7 (Stock)</a></li>
</ul> </ul>
</details> </details>
<details> <details>
<summary>X9 Series:</summary> <summary>X9 Series:</summary>
<ul> <ul>
<li><a href="../antminer/X9#e9pro-stock">E9Pro (Stock)</a></li> <li><a href="../antminer/X9#e9pro-stock">E9Pro (Stock)</a></li>
<li><a href="../antminer/X9#d9-stock">D9 (Stock)</a></li>
<li><a href="../antminer/X9#s9-stock">S9 (Stock)</a></li> <li><a href="../antminer/X9#s9-stock">S9 (Stock)</a></li>
<li><a href="../antminer/X9#s9i-stock">S9i (Stock)</a></li> <li><a href="../antminer/X9#s9i-stock">S9i (Stock)</a></li>
<li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li> <li><a href="../antminer/X9#s9j-stock">S9j (Stock)</a></li>
@@ -581,6 +583,12 @@ details {
<li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li> <li><a href="../antminer/X9#t9-stock">T9 (Stock)</a></li>
</ul> </ul>
</details> </details>
<details>
<summary>X19 Series:</summary>
<ul>
<li><a href="../antminer/X19#s19j-pro-stock">S19j Pro (Stock)</a></li>
</ul>
</details>
</ul> </ul>
</details> </details>
<details> <details>
@@ -672,6 +680,7 @@ details {
<li><a href="../bitaxe/BM#supra-stock">Supra (Stock)</a></li> <li><a href="../bitaxe/BM#supra-stock">Supra (Stock)</a></li>
<li><a href="../bitaxe/BM#ultra-stock">Ultra (Stock)</a></li> <li><a href="../bitaxe/BM#ultra-stock">Ultra (Stock)</a></li>
<li><a href="../bitaxe/BM#max-stock">Max (Stock)</a></li> <li><a href="../bitaxe/BM#max-stock">Max (Stock)</a></li>
<li><a href="../bitaxe/BM#gamma-stock">Gamma (Stock)</a></li>
</ul> </ul>
</details> </details>
</ul> </ul>
@@ -694,4 +703,15 @@ details {
</ul> </ul>
</details> </details>
</ul> </ul>
</details>
<details>
<summary>Stock Firmware Hammer Miners:</summary>
<ul>
<details>
<summary>DX Series:</summary>
<ul>
<li><a href="../hammer/DX#d10-stock">D10 (Stock)</a></li>
</ul>
</details>
</ul>
</details> </details>

View File

@@ -16,7 +16,7 @@
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from pyasic.config.fans import FanMode, FanModeConfig from pyasic.config.fans import FanMode, FanModeConfig, FanModeNormal
from pyasic.config.mining import MiningMode, MiningModeConfig from pyasic.config.mining import MiningMode, MiningModeConfig
from pyasic.config.mining.scaling import ScalingConfig from pyasic.config.mining.scaling import ScalingConfig
from pyasic.config.pools import PoolConfig from pyasic.config.pools import PoolConfig
@@ -159,6 +159,19 @@ class MinerConfig(BaseModel):
**self.pools.as_luxos(user_suffix=user_suffix), **self.pools.as_luxos(user_suffix=user_suffix),
} }
def as_vnish(self, user_suffix: str = None) -> dict:
main_cfg = {
"miner": {
**self.fan_mode.as_vnish(),
**self.temperature.as_vnish(),
**self.mining_mode.as_vnish(),
**self.pools.as_vnish(user_suffix=user_suffix),
}
}
if isinstance(self.fan_mode, FanModeNormal):
main_cfg["miner"]["cooling"]["mode"]["param"] = self.temperature.target
return main_cfg
def as_hammer(self, *args, **kwargs) -> dict: def as_hammer(self, *args, **kwargs) -> dict:
return self.as_am_modern(*args, **kwargs) return self.as_am_modern(*args, **kwargs)

View File

@@ -87,6 +87,18 @@ class FanModeNormal(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}} return {"fanset": {"speed": -1, "min_fans": self.minimum_fans}}
def as_vnish(self) -> dict:
return {
"cooling": {
"fan_min_count": self.minimum_fans,
"fan_min_duty": self.minimum_speed,
"mode": {
"name": "auto",
"param": None, # Target temp, must be set later...
},
}
}
class FanModeManual(MinerConfigValue): class FanModeManual(MinerConfigValue):
mode: str = Field(init=False, default="manual") mode: str = Field(init=False, default="manual")
@@ -150,6 +162,18 @@ class FanModeManual(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}} return {"fanset": {"speed": self.speed, "min_fans": self.minimum_fans}}
def as_vnish(self) -> dict:
return {
"cooling": {
"fan_min_count": self.minimum_fans,
"fan_min_duty": self.speed,
"mode": {
"name": "manual",
"param": self.speed, # Speed value
},
}
}
class FanModeImmersion(MinerConfigValue): class FanModeImmersion(MinerConfigValue):
mode: str = Field(init=False, default="immersion") mode: str = Field(init=False, default="immersion")
@@ -175,6 +199,9 @@ class FanModeImmersion(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"fanset": {"speed": 0, "min_fans": 0}} return {"fanset": {"speed": 0, "min_fans": 0}}
def as_vnish(self) -> dict:
return {"cooling": {"mode": {"name": "immers"}}}
class FanModeConfig(MinerConfigOption): class FanModeConfig(MinerConfigOption):
normal = FanModeNormal normal = FanModeNormal

View File

@@ -357,6 +357,9 @@ class ManualBoardSettings(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {"freq": self.freq}
class MiningModeManual(MinerConfigValue): class MiningModeManual(MinerConfigValue):
mode: str = field(init=False, default="manual") mode: str = field(init=False, default="manual")
@@ -378,6 +381,12 @@ class MiningModeManual(MinerConfigValue):
return {"miner-mode": "0"} return {"miner-mode": "0"}
return {"miner-mode": 0} return {"miner-mode": 0}
def as_vnish(self) -> dict:
return {
"chains": [b.as_vnish() for b in self.boards.values() if b.freq != 0],
"globals": {"freq": int(self.global_freq), "volt": int(self.global_volt)},
}
@classmethod @classmethod
def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual": def from_vnish(cls, web_overclock_settings: dict) -> "MiningModeManual":
# will raise KeyError if it cant find the settings, values cannot be empty # will raise KeyError if it cant find the settings, values cannot be empty

View File

@@ -146,6 +146,15 @@ class Pool(MinerConfigValue):
url=self.url, user=self.user, password=self.password, enabled=True url=self.url, user=self.user, password=self.password, enabled=True
) )
def as_vnish(self, user_suffix: str = None) -> dict:
if user_suffix is not None:
return {
"url": self.url,
"user": f"{self.user}{user_suffix}",
"pass": self.password,
}
return {"url": self.url, "user": self.user, "pass": self.password}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "Pool": def from_dict(cls, dict_conf: dict | None) -> "Pool":
return cls( return cls(
@@ -338,6 +347,9 @@ class PoolGroup(MinerConfigValue):
pools=[p.as_boser() for p in self.pools], pools=[p.as_boser() for p in self.pools],
) )
def as_vnish(self, user_suffix: str = None) -> dict:
return {"pools": [p.as_vnish(user_suffix=user_suffix) for p in self.pools]}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "PoolGroup": def from_dict(cls, dict_conf: dict | None) -> "PoolGroup":
cls_conf = {} cls_conf = {}
@@ -530,6 +542,9 @@ class PoolConfig(MinerConfigValue):
def as_luxos(self, user_suffix: str = None) -> dict: def as_luxos(self, user_suffix: str = None) -> dict:
return {} return {}
def as_vnish(self, user_suffix: str = None) -> dict:
return self.groups[0].as_vnish(user_suffix=user_suffix)
@classmethod @classmethod
def from_api(cls, api_pools: dict) -> "PoolConfig": def from_api(cls, api_pools: dict) -> "PoolConfig":
try: try:

View File

@@ -56,6 +56,9 @@ class TemperatureConfig(MinerConfigValue):
def as_luxos(self) -> dict: def as_luxos(self) -> dict:
return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]} return {"tempctrlset": [self.target or "", self.hot or "", self.danger or ""]}
def as_vnish(self) -> dict:
return {"misc": {"restart_temp": self.danger}}
@classmethod @classmethod
def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig": def from_dict(cls, dict_conf: dict | None) -> "TemperatureConfig":
return cls( return cls(
@@ -95,9 +98,16 @@ class TemperatureConfig(MinerConfigValue):
@classmethod @classmethod
def from_vnish(cls, web_settings: dict) -> "TemperatureConfig": def from_vnish(cls, web_settings: dict) -> "TemperatureConfig":
try:
dangerous_temp = web_settings["misc"]["restart_temp"]
except KeyError:
dangerous_temp = None
try: try:
if web_settings["miner"]["cooling"]["mode"]["name"] == "auto": if web_settings["miner"]["cooling"]["mode"]["name"] == "auto":
return cls(target=web_settings["miner"]["cooling"]["mode"]["param"]) return cls(
target=web_settings["miner"]["cooling"]["mode"]["param"],
danger=dangerous_temp,
)
except KeyError: except KeyError:
pass pass
return cls() return cls()

View File

@@ -3,6 +3,7 @@ from typing import Optional
from urllib.parse import urlparse from urllib.parse import urlparse
from pydantic import BaseModel, computed_field, model_serializer from pydantic import BaseModel, computed_field, model_serializer
from typing_extensions import Self
class Scheme(Enum): class Scheme(Enum):
@@ -28,8 +29,10 @@ class PoolUrl(BaseModel):
return f"{self.scheme.value}://{self.host}:{self.port}" return f"{self.scheme.value}://{self.host}:{self.port}"
@classmethod @classmethod
def from_str(cls, url: str) -> "PoolUrl": def from_str(cls, url: str) -> Self | None:
parsed_url = urlparse(url) parsed_url = urlparse(url)
if not parsed_url.hostname:
return None
if not parsed_url.scheme.strip() == "": if not parsed_url.scheme.strip() == "":
scheme = Scheme(parsed_url.scheme) scheme = Scheme(parsed_url.scheme)
else: else:
@@ -57,15 +60,15 @@ class PoolMetrics(BaseModel):
pool_stale_percent: Percentage of stale shares by the pool. pool_stale_percent: Percentage of stale shares by the pool.
""" """
url: PoolUrl url: PoolUrl | None
accepted: int = None accepted: int | None = None
rejected: int = None rejected: int | None = None
get_failures: int = None get_failures: int | None = None
remote_failures: int = None remote_failures: int | None = None
active: bool = None active: bool | None = None
alive: bool = None alive: bool | None = None
index: int = None index: int | None = None
user: str = None user: str | None = None
@computed_field # type: ignore[misc] @computed_field # type: ignore[misc]
@property @property

View File

@@ -0,0 +1,99 @@
# ------------------------------------------------------------------------------
# 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 Hiveon
from pyasic.miners.device.models import (
S19,
S19L,
S19XP,
S19a,
S19aPro,
S19Hydro,
S19i,
S19j,
S19jNoPIC,
S19jPro,
S19KPro,
S19Plus,
S19Pro,
S19ProHydro,
S19ProPlus,
S19ProPlusHydro,
)
class HiveonS19(Hiveon, S19):
pass
class HiveonS19Plus(Hiveon, S19Plus):
pass
class HiveonS19i(Hiveon, S19i):
pass
class HiveonS19Pro(Hiveon, S19Pro):
pass
class HiveonS19ProPlus(Hiveon, S19ProPlus):
pass
class HiveonS19XP(Hiveon, S19XP):
pass
class HiveonS19a(Hiveon, S19a):
pass
class HiveonS19aPro(Hiveon, S19aPro):
pass
class HiveonS19j(Hiveon, S19j):
pass
class HiveonS19jNoPIC(Hiveon, S19jNoPIC):
pass
class HiveonS19jPro(Hiveon, S19jPro):
pass
class HiveonS19L(Hiveon, S19L):
pass
class HiveonS19ProHydro(Hiveon, S19ProHydro):
pass
class HiveonS19Hydro(Hiveon, S19Hydro):
pass
class HiveonS19ProPlusHydro(Hiveon, S19ProPlusHydro):
pass
class HiveonS19KPro(Hiveon, S19KPro):
pass

View File

@@ -0,0 +1,22 @@
# ------------------------------------------------------------------------------
# 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 Hiveon
from pyasic.miners.device.models import T19
class HiveonT19(Hiveon, T19):
pass

View File

@@ -0,0 +1,35 @@
# ------------------------------------------------------------------------------
# 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 (
HiveonS19,
HiveonS19a,
HiveonS19aPro,
HiveonS19Hydro,
HiveonS19i,
HiveonS19j,
HiveonS19jNoPIC,
HiveonS19jPro,
HiveonS19KPro,
HiveonS19L,
HiveonS19Plus,
HiveonS19Pro,
HiveonS19ProHydro,
HiveonS19ProPlus,
HiveonS19ProPlusHydro,
HiveonS19XP,
)
from .T19 import HiveonT19

View File

@@ -78,17 +78,6 @@ class HiveonT9(Hiveon, T9):
### DATA GATHERING FUNCTIONS (get_{some_data}) ### ### DATA GATHERING FUNCTIONS (get_{some_data}) ###
################################################## ##################################################
async def get_mac(self):
try:
mac = (
(await self.send_ssh_command("cat /sys/class/net/eth0/address"))
.strip()
.upper()
)
return mac
except (TypeError, ValueError, asyncssh.Error, OSError, AttributeError):
pass
async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]: async def _get_hashboards(self, rpc_stats: dict = None) -> List[HashBoard]:
hashboards = [ hashboards = [
HashBoard(slot=board, expected_chips=self.expected_chips) HashBoard(slot=board, expected_chips=self.expected_chips)
@@ -133,23 +122,6 @@ class HiveonT9(Hiveon, T9):
return hashboards return hashboards
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if not rpc_stats:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats:
boards = rpc_stats.get("STATS")
try:
wattage_raw = boards[1]["chain_power"]
except (KeyError, IndexError):
pass
else:
# parse wattage position out of raw data
return round(float(wattage_raw.split(" ")[0]))
async def _get_env_temp(self, rpc_stats: dict = None) -> Optional[float]: async def _get_env_temp(self, rpc_stats: dict = None) -> Optional[float]:
env_temp_list = [] env_temp_list = []
board_map = { board_map = {

View File

@@ -15,3 +15,4 @@
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from .X9 import * from .X9 import *
from .X19 import *

View File

@@ -13,10 +13,71 @@
# See the License for the specific language governing permissions and - # See the License for the specific language governing permissions and -
# limitations under the License. - # limitations under the License. -
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from typing import Optional
from pyasic import APIError
from pyasic.miners.backends import BMMiner from pyasic.miners.backends import BMMiner
from pyasic.miners.data import DataFunction, DataLocations, DataOptions, RPCAPICommand
from pyasic.miners.device.firmware import HiveonFirmware from pyasic.miners.device.firmware import HiveonFirmware
HIVEON_DATA_LOC = DataLocations(
**{
str(DataOptions.API_VERSION): DataFunction(
"_get_api_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.FW_VERSION): DataFunction(
"_get_fw_ver",
[RPCAPICommand("rpc_version", "version")],
),
str(DataOptions.HASHRATE): DataFunction(
"_get_hashrate",
[RPCAPICommand("rpc_summary", "summary")],
),
str(DataOptions.EXPECTED_HASHRATE): DataFunction(
"_get_expected_hashrate",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.HASHBOARDS): DataFunction(
"_get_hashboards",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.WATTAGE): DataFunction(
"_get_wattage",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.FANS): DataFunction(
"_get_fans",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.UPTIME): DataFunction(
"_get_uptime",
[RPCAPICommand("rpc_stats", "stats")],
),
str(DataOptions.POOLS): DataFunction(
"_get_pools",
[RPCAPICommand("rpc_pools", "pools")],
),
}
)
class Hiveon(BMMiner, HiveonFirmware): class Hiveon(BMMiner, HiveonFirmware):
pass data_locations = HIVEON_DATA_LOC
async def _get_wattage(self, rpc_stats: dict = None) -> Optional[int]:
if not rpc_stats:
try:
rpc_stats = await self.rpc.stats()
except APIError:
pass
if rpc_stats:
boards = rpc_stats.get("STATS")
try:
wattage_raw = boards[1]["chain_power"]
except (KeyError, IndexError):
pass
else:
# parse wattage position out of raw data
return round(float(wattage_raw.split(" ")[0]))

View File

@@ -76,6 +76,8 @@ class LUXMiner(LuxOSFirmware):
_rpc_cls = LUXMinerRPCAPI _rpc_cls = LUXMinerRPCAPI
rpc: LUXMinerRPCAPI rpc: LUXMinerRPCAPI
supports_shutdown = True
data_locations = LUXMINER_DATA_LOC data_locations = LUXMINER_DATA_LOC
async def fault_light_on(self) -> bool: async def fault_light_on(self) -> bool:

View File

@@ -98,6 +98,11 @@ class VNish(VNishFirmware, BMMiner):
data_locations = VNISH_DATA_LOC data_locations = VNISH_DATA_LOC
async def send_config(self, config: MinerConfig, user_suffix: str = None) -> None:
await self.web.post_settings(
miner_settings=config.as_vnish(user_suffix=user_suffix)
)
async def restart_backend(self) -> bool: async def restart_backend(self) -> bool:
data = await self.web.restart_vnish() data = await self.web.restart_vnish()
if data: if data:

View File

@@ -529,7 +529,11 @@ class MinerProtocol(Protocol):
expected_fans=self.expected_fans, expected_fans=self.expected_fans,
hashboards=[ hashboards=[
HashBoard(slot=i, expected_chips=self.expected_chips) HashBoard(slot=i, expected_chips=self.expected_chips)
for i in range(self.expected_hashboards) for i in range(
self.expected_hashboards
if self.expected_hashboards is not None
else 0
)
], ],
) )

View File

@@ -426,6 +426,7 @@ MINER_CLASSES = {
MinerTypes.HIVEON: { MinerTypes.HIVEON: {
None: Hiveon, None: Hiveon,
"ANTMINER T9": HiveonT9, "ANTMINER T9": HiveonT9,
"ANTMINER S19JPRO": HiveonS19jPro,
}, },
MinerTypes.LUX_OS: { MinerTypes.LUX_OS: {
None: LUXMiner, None: LUXMiner,
@@ -630,6 +631,7 @@ class MinerFactory:
@staticmethod @staticmethod
def _parse_web_type(web_text: str, web_resp: httpx.Response) -> MinerTypes | None: def _parse_web_type(web_text: str, web_resp: httpx.Response) -> MinerTypes | None:
print(web_resp.headers)
if web_resp.status_code == 401 and 'realm="antMiner' in web_resp.headers.get( if web_resp.status_code == 401 and 'realm="antMiner' in web_resp.headers.get(
"www-authenticate", "" "www-authenticate", ""
): ):
@@ -862,6 +864,10 @@ class MinerFactory:
miner_model: str | None, miner_model: str | None,
miner_type: MinerTypes | None, miner_type: MinerTypes | None,
) -> AnyMiner | None: ) -> AnyMiner | None:
# special case since hiveon miners return web results copying the antminer stock FW
if "HIVEON" in str(miner_model).upper() and miner_type == MinerTypes.ANTMINER:
miner_model = str(miner_model).upper().replace(" HIVEON", "")
miner_type = MinerTypes.HIVEON
try: try:
return MINER_CLASSES[miner_type][str(miner_model).upper()](ip) return MINER_CLASSES[miner_type][str(miner_model).upper()](ip)
except LookupError: except LookupError:

View File

@@ -143,3 +143,6 @@ class VNishWebAPI(BaseWebAPI):
async def find_miner(self) -> dict: async def find_miner(self) -> dict:
return await self.send_command("find-miner", privileged=True) return await self.send_command("find-miner", privileged=True)
async def post_settings(self, miner_settings: dict):
return await self.send_command("settings", post=True, **miner_settings)

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyasic" name = "pyasic"
version = "0.64.1" version = "0.64.7"
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"