Compare commits

...

16 Commits

Author SHA1 Message Date
Brett Rowan
d40d92c1ca version: bump version number. 2024-05-28 21:46:41 -06:00
Brett Rowan
7ea63643a9 bug: fix a bunch of spelling mistakes. 2024-05-28 21:46:13 -06:00
upstreamdata
313c324771 bug: fix into naming on vnish with expected hashrate. 2024-05-27 15:57:06 -06:00
upstreamdata
a9fd9343d8 feature: add vnish fault light. 2024-05-26 21:59:59 -06:00
upstreamdata
8f41d4d0bc bug: fix MRO with vnish. 2024-05-26 21:59:57 -06:00
Brett Rowan
6f10c91482 Merge pull request #150 from UpstreamData/snyk-fix-c1d73ce3e71f8a7a86f4d2be00564ee4 2024-05-25 09:00:39 -06:00
snyk-bot
f2d6bce165 fix: docs/requirements.txt to reduce vulnerabilities
The following vulnerabilities are fixed by pinning transitive dependencies:
- https://snyk.io/vuln/SNYK-PYTHON-JINJA2-6809379
2024-05-25 02:29:08 +00:00
Brett Rowan
61623cc44d version: bump version number. 2024-05-19 11:41:43 -06:00
Brett Rowan
a30a726324 bug: fix some issues with boser handlers. 2024-05-19 11:41:10 -06:00
Brett Rowan
0e90ad64cd Merge pull request #145 from Ytemiloluwa/master 2024-05-18 11:08:31 -06:00
ytemiloluwa
53572c6236 add new attributes 2024-05-18 16:53:06 +01:00
Brett Rowan
1d656da2a2 bug: remove StrEnum references. 2024-05-17 21:00:43 -06:00
ytemiloluwa
5d90b7e938 pools: updated metrics dataclass 2024-05-16 20:20:53 +01:00
ytemiloluwa
3f90799544 update metrics dataclass 2024-05-16 19:25:48 +01:00
Temi
1f70ec0d28 Merge branch 'UpstreamData:master' into master 2024-05-16 19:00:45 +01:00
ytemiloluwa
58c95559dd pools: dataclass metrics 2024-05-16 14:08:00 +01:00
18 changed files with 1373 additions and 37 deletions

1180
poetry.lock generated Normal file

File diff suppressed because it is too large Load Diff

59
pyasic/data/pools.py Normal file
View File

@@ -0,0 +1,59 @@
from dataclasses import dataclass, field
@dataclass
class PoolMetrics:
"""A dataclass to standardize pool metrics returned from miners.
Attributes:
accepted: Number of accepted shares.
rejected: Number of rejected shares.
get_failures: Number of failures in obtaining work from the pool.
remote_failures: Number of failures communicating with the pool server.
active: Indicates if the miner is connected to the stratum server.
Alive : Indicates if a pool is alive.
url: URL of the pool.
index: Index of the pool.
user: Username for the pool.
pool_rejected_percent: Percentage of rejected shares by the pool.
pool_stale_percent: Percentage of stale shares by the pool.
"""
accepted: int = None
rejected: int = None
get_failures: int = None
remote_failures: int = None
active: bool = None
alive: bool = None
url: str = None
index: int = None
user: str = None
pool_rejected_percent: float = field(init=False)
pool_stale_percent: float = field(init=False)
@property
def pool_rejected_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of rejected shares"""
return self._calculate_percentage(self.rejected, self.accepted + self.rejected)
@pool_rejected_percent.setter
def pool_rejected_percent(self, val):
pass
@property
def pool_stale_percent(self) -> float: # noqa - Skip PyCharm inspection
"""Calculate and return the percentage of stale shares."""
return self._calculate_percentage(
self.get_failures, self.accepted + self.rejected
)
@pool_stale_percent.setter
def pool_stale_percent(self, val):
pass
@staticmethod
def _calculate_percentage(value: int, total: int) -> float:
"""Calculate the percentage."""
if total == 0:
return 0
return (value / total) * 100

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 VNish
from pyasic.miners.device.models import S21
class VNishS21(VNish, S21):
pass

View 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 .S21 import VNishS21

View File

@@ -18,3 +18,4 @@ from .X3 import *
from .X7 import *
from .X17 import *
from .X19 import *
from .X21 import *

View File

@@ -275,7 +275,7 @@ class AntminerModern(BMMiner):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -251,7 +251,7 @@ class AvalonMiner(CGMiner):
parsed_stats = self.parse_stats(unparsed_stats)
return AlgoHashRate.SHA256(
parsed_stats["GHSmm"], HashUnit.SHA256.GH
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except (IndexError, KeyError, ValueError, TypeError):
pass

View File

@@ -224,6 +224,6 @@ class BFGMiner(StockFirmware):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -240,7 +240,7 @@ class BMMiner(StockFirmware):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -865,7 +865,9 @@ class BOSer(BraiinsOSFirmware):
) -> Optional[int]:
if grpc_active_performance_mode is None:
try:
grpc_active_performance_mode = self.web.get_active_performance_mode()
grpc_active_performance_mode = (
await self.web.get_active_performance_mode()
)
except APIError:
pass

View File

@@ -575,7 +575,7 @@ class BTMiner(StockFirmware):
if expected_hashrate:
return AlgoHashRate.SHA256(
expected_hashrate, HashUnit.SHA256.GH
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -279,7 +279,7 @@ class LUXMiner(LuxOSFirmware):
rate_unit = "GH"
return AlgoHashRate.SHA256(
expected_rate, HashUnit.SHA256.from_str(rate_unit)
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -282,7 +282,7 @@ class MaraMiner(MaraFirmware):
try:
return AlgoHashRate.SHA256(
web_brief["hashrate_ideal"], HashUnit.SHA256.GH
).int(self.algo.unit.default)
).into(self.algo.unit.default)
except LookupError:
pass

View File

@@ -84,7 +84,7 @@ VNISH_DATA_LOC = DataLocations(
)
class VNish(BMMiner, VNishFirmware):
class VNish(VNishFirmware, BMMiner):
"""Handler for VNish miners"""
_web_cls = VNishWebAPI
@@ -147,6 +147,22 @@ class VNish(BMMiner, VNishFirmware):
except KeyError:
pass
async def fault_light_off(self) -> bool:
result = await self.web.find_miner()
if result is not None:
if result.get("on") is False:
return True
else:
await self.web.find_miner()
async def fault_light_on(self) -> bool:
result = await self.web.find_miner()
if result is not None:
if result.get("on") is True:
return True
else:
await self.web.find_miner()
async def _get_hostname(self, web_summary: dict = None) -> str:
if web_summary is None:
web_info = await self.web.info()

View File

@@ -384,6 +384,7 @@ MINER_CLASSES = {
"ANTMINER S19A": VNishS19a,
"ANTMINER S19A PRO": VNishS19aPro,
"ANTMINER T19": VNishT19,
"ANTMINER S21": VNishS21,
},
MinerTypes.EPIC: {
None: ePIC,
@@ -530,7 +531,6 @@ class MinerFactory:
miner_type=miner_type,
miner_model=miner_model,
)
return miner
async def _get_miner_type(self, ip: str) -> MinerTypes | None:
@@ -673,7 +673,7 @@ class MinerFactory:
return MinerTypes.BRAIINS_OS
if "BTMINER" in upper_data or "BITMICRO" in upper_data:
return MinerTypes.WHATSMINER
if "VNISH" in upper_data:
if "VNISH" in upper_data or "DEVICE PATH" in upper_data:
return MinerTypes.VNISH
if "HIVEON" in upper_data:
return MinerTypes.HIVEON

View File

@@ -78,13 +78,16 @@ class BOSerWebAPI(BaseWebAPI):
for command in commands:
try:
tasks[command] = asyncio.create_task(getattr(self, command)())
except (APIError, AttributeError):
result["command"] = {}
except AttributeError:
pass
await asyncio.gather(*list(tasks.values()))
await asyncio.gather(*[t for t in tasks.values()], return_exceptions=True)
for cmd in tasks:
result[cmd] = tasks[cmd].result()
try:
result[cmd] = await tasks[cmd]
except (GRPCError, APIError):
pass
return result
@@ -149,42 +152,55 @@ class BOSerWebAPI(BaseWebAPI):
)
async def start(self) -> dict:
return await self.send_command("start", message=StartRequest())
return await self.send_command("start", message=StartRequest(), privileged=True)
async def stop(self) -> dict:
return await self.send_command("stop", message=StopRequest())
return await self.send_command("stop", message=StopRequest(), privileged=True)
async def pause_mining(self) -> dict:
return await self.send_command("pause_mining", message=PauseMiningRequest())
return await self.send_command(
"pause_mining", message=PauseMiningRequest(), privileged=True
)
async def resume_mining(self) -> dict:
return await self.send_command("resume_mining", message=ResumeMiningRequest())
return await self.send_command(
"resume_mining", message=ResumeMiningRequest(), privileged=True
)
async def restart(self) -> dict:
return await self.send_command("restart", message=RestartRequest())
return await self.send_command(
"restart", message=RestartRequest(), privileged=True
)
async def reboot(self) -> dict:
return await self.send_command("reboot", message=RebootRequest())
return await self.send_command(
"reboot", message=RebootRequest(), privileged=True
)
async def set_locate_device_status(self, enable: bool) -> dict:
return await self.send_command(
"set_locate_device_status",
message=SetLocateDeviceStatusRequest(enable=enable),
privileged=True,
)
async def get_locate_device_status(self) -> dict:
return await self.send_command(
"get_locate_device_status", message=GetLocateDeviceStatusRequest()
"get_locate_device_status",
message=GetLocateDeviceStatusRequest(),
privileged=True,
)
async def set_password(self, password: str = None) -> dict:
return await self.send_command(
"set_password", message=SetPasswordRequest(password=password)
"set_password",
message=SetPasswordRequest(password=password),
privileged=True,
)
async def get_cooling_state(self) -> dict:
return await self.send_command(
"get_cooling_state", message=GetCoolingStateRequest()
"get_cooling_state", message=GetCoolingStateRequest(), privileged=True
)
async def set_immersion_mode(
@@ -197,16 +213,17 @@ class BOSerWebAPI(BaseWebAPI):
message=SetImmersionModeRequest(
enable_immersion_mode=enable, save_action=save_action
),
privileged=True,
)
async def get_tuner_state(self) -> dict:
return await self.send_command(
"get_tuner_state", message=GetTunerStateRequest()
"get_tuner_state", message=GetTunerStateRequest(), privileged=True
)
async def list_target_profiles(self) -> dict:
return await self.send_command(
"list_target_profiles", message=ListTargetProfilesRequest()
"list_target_profiles", message=ListTargetProfilesRequest(), privileged=True
)
async def set_default_power_target(
@@ -215,6 +232,7 @@ class BOSerWebAPI(BaseWebAPI):
return await self.send_command(
"set_default_power_target",
message=SetDefaultPowerTargetRequest(save_action=save_action),
privileged=True,
)
async def set_power_target(
@@ -227,6 +245,7 @@ class BOSerWebAPI(BaseWebAPI):
message=SetPowerTargetRequest(
power_target=Power(watt=power_target), save_action=save_action
),
privileged=True,
)
async def increment_power_target(
@@ -240,6 +259,7 @@ class BOSerWebAPI(BaseWebAPI):
power_target_increment=Power(watt=power_target_increment),
save_action=save_action,
),
privileged=True,
)
async def decrement_power_target(
@@ -253,6 +273,7 @@ class BOSerWebAPI(BaseWebAPI):
power_target_decrement=Power(watt=power_target_decrement),
save_action=save_action,
),
privileged=True,
)
async def set_default_hashrate_target(
@@ -261,6 +282,7 @@ class BOSerWebAPI(BaseWebAPI):
return await self.send_command(
"set_default_hashrate_target",
message=SetDefaultHashrateTargetRequest(save_action=save_action),
privileged=True,
)
async def set_hashrate_target(
@@ -274,6 +296,7 @@ class BOSerWebAPI(BaseWebAPI):
hashrate_target=TeraHashrate(terahash_per_second=hashrate_target),
save_action=save_action,
),
privileged=True,
)
async def increment_hashrate_target(
@@ -289,6 +312,7 @@ class BOSerWebAPI(BaseWebAPI):
),
save_action=save_action,
),
privileged=True,
)
async def decrement_hashrate_target(
@@ -304,6 +328,7 @@ class BOSerWebAPI(BaseWebAPI):
),
save_action=save_action,
),
privileged=True,
)
async def set_dps(
@@ -327,6 +352,7 @@ class BOSerWebAPI(BaseWebAPI):
)
),
),
privileged=True,
)
async def set_performance_mode(
@@ -356,6 +382,7 @@ class BOSerWebAPI(BaseWebAPI):
)
),
),
privileged=True,
)
if hashrate_target is not None:
return await self.send_command(
@@ -372,16 +399,19 @@ class BOSerWebAPI(BaseWebAPI):
)
),
),
privileged=True,
)
async def get_active_performance_mode(self) -> dict:
return await self.send_command(
"get_active_performance_mode", message=GetPerformanceModeRequest()
"get_active_performance_mode",
message=GetPerformanceModeRequest(),
privileged=True,
)
async def get_pool_groups(self) -> dict:
return await self.send_command(
"get_pool_groups", message=GetPoolGroupsRequest()
"get_pool_groups", message=GetPoolGroupsRequest(), privileged=True
)
async def create_pool_group(self) -> dict:
@@ -395,40 +425,44 @@ class BOSerWebAPI(BaseWebAPI):
async def get_miner_configuration(self) -> dict:
return await self.send_command(
"get_miner_configuration", message=GetMinerConfigurationRequest()
"get_miner_configuration",
message=GetMinerConfigurationRequest(),
privileged=True,
)
async def get_constraints(self) -> dict:
return await self.send_command(
"get_constraints", message=GetConstraintsRequest()
"get_constraints", message=GetConstraintsRequest(), privileged=True
)
async def get_license_state(self) -> dict:
return await self.send_command(
"get_license_state", message=GetLicenseStateRequest()
"get_license_state", message=GetLicenseStateRequest(), privileged=True
)
async def get_miner_status(self) -> dict:
return await self.send_command(
"get_miner_status", message=GetMinerStatusRequest()
"get_miner_status", message=GetMinerStatusRequest(), privileged=True
)
async def get_miner_details(self) -> dict:
return await self.send_command(
"get_miner_details", message=GetMinerDetailsRequest()
"get_miner_details", message=GetMinerDetailsRequest(), privileged=True
)
async def get_miner_stats(self) -> dict:
return await self.send_command(
"get_miner_stats", message=GetMinerStatsRequest()
"get_miner_stats", message=GetMinerStatsRequest(), privileged=True
)
async def get_hashboards(self) -> dict:
return await self.send_command("get_hashboards", message=GetHashboardsRequest())
return await self.send_command(
"get_hashboards", message=GetHashboardsRequest(), privileged=True
)
async def get_support_archive(self) -> dict:
return await self.send_command(
"get_support_archive", message=GetSupportArchiveRequest()
"get_support_archive", message=GetSupportArchiveRequest(), privileged=True
)
async def enable_hashboards(
@@ -441,6 +475,7 @@ class BOSerWebAPI(BaseWebAPI):
message=EnableHashboardsRequest(
hashboard_ids=hashboard_ids, save_action=save_action
),
privileged=True,
)
async def disable_hashboards(
@@ -453,4 +488,5 @@ class BOSerWebAPI(BaseWebAPI):
message=DisableHashboardsRequest(
hashboard_ids=hashboard_ids, save_action=save_action
),
privileged=True,
)

View File

@@ -140,3 +140,6 @@ class VNishWebAPI(BaseWebAPI):
async def autotune_presets(self) -> dict:
return await self.send_command("autotune/presets")
async def find_miner(self) -> dict:
return await self.send_command("find-miner", privileged=True)

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyasic"
version = "0.57.3"
version = "0.57.5"
description = "A simplified and standardized interface for Bitcoin ASICs."
authors = ["UpstreamData <brett@upstreamdata.ca>"]
repository = "https://github.com/UpstreamData/pyasic"