Compare commits
10 Commits
0974f5daa4
...
a7a91c9dfe
Author | SHA1 | Date | |
---|---|---|---|
|
a7a91c9dfe | ||
|
6b85fe5e70 | ||
|
0e94842c9d | ||
|
bbe51fef8d | ||
|
991755ee4e | ||
|
2aca201d3b | ||
|
53f35f04ea | ||
|
829d1cd3f7 | ||
|
dde8ba7417 | ||
|
454ee1008d |
@ -28,8 +28,10 @@ A [Home Assistant][homeassistant] integration that creates a collection of [Nice
|
||||
- Algorithm
|
||||
- Speed
|
||||
- Temperature
|
||||
- HotSpot Temperature
|
||||
- Load
|
||||
- RPM
|
||||
- Power
|
||||
- Most Recent Mining Payout
|
||||
|
||||
None of the sensors are added by default. See installation instructions for available configuration options.
|
||||
@ -46,6 +48,8 @@ Supported API permissions and associated sensors
|
||||
- Account balance sensors
|
||||
- Mining Permissions > View mining data...
|
||||
- Rig, device, and payout sensors
|
||||
- Mining Permissions > Manage Rigs
|
||||
- Device start/stop switch
|
||||
|
||||
See this [repository](https://github.com/nicehash/rest-clients-demo) for further assistance generating an API key.
|
||||
|
||||
|
@ -118,6 +118,8 @@ async def async_setup(hass: HomeAssistant, config: Config):
|
||||
|
||||
hass.data[DOMAIN]["rigs_coordinator"] = rigs_coordinator
|
||||
|
||||
await discovery.async_load_platform(hass, "switch", DOMAIN, {}, config)
|
||||
await discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config)
|
||||
|
||||
|
||||
return True
|
||||
|
@ -102,8 +102,11 @@ class BalanceSensor(Entity):
|
||||
return ICON_CURRENCY_EUR
|
||||
elif self.currency == CURRENCY_USD:
|
||||
return ICON_CURRENCY_USD
|
||||
elif self.currency == CURRENCY_BTC:
|
||||
return ICON_CURRENCY_BTC
|
||||
|
||||
return ICON_CURRENCY_USD
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Sensor unit of measurement"""
|
||||
|
@ -19,6 +19,7 @@ ICON_PICKAXE = "mdi:pickaxe"
|
||||
ICON_PULSE = "mdi:pulse"
|
||||
ICON_THERMOMETER = "mdi:thermometer"
|
||||
ICON_SPEEDOMETER = "mdi:speedometer"
|
||||
ICON_POWER = "mdi:power-plug"
|
||||
|
||||
# Platforms
|
||||
SENSOR = "sensor"
|
||||
@ -57,6 +58,23 @@ NICEHASH_ATTRIBUTION = "Data provided by NiceHash"
|
||||
CURRENCY_BTC = "BTC"
|
||||
CURRENCY_USD = "USD"
|
||||
CURRENCY_EUR = "EUR"
|
||||
SUPPORTED_CURRENCIES = [
|
||||
"ERN","HKD","GGP","RSD","SHP","USD","MYR","PYG","RON","DOP","TWD","AWG",
|
||||
"CVE","BND","RUB","NGN","XCD","JEP","ZWL","HNL","NZD","AFN","MUR","DKK",
|
||||
"CNY","JOD","CHF","COP","XAF","XAG","ZMK","GNF","ZMW","GIP","BTC","MKD",
|
||||
"WST","IDR","IQD","BHD","YER","MAD","KGS","PHP","PEN","BMD","DJF","MVR",
|
||||
"QAR","JPY","SCR","IMP","KRW","HRK","SOS","VUV","NIO","KYD","LAK","ISK",
|
||||
"BOB","IRR","NPR","EGP","BBD","CAD","XAU","CUP","SDG","PKR","UZS","CLF",
|
||||
"CUC","STD","MGA","FJD","DZD","TJS","EURKM","SZL","THB","SRD","BDT",
|
||||
"BTN","CZK","AMD","UGX","TRY","AUD","UAH","HUF","SLL","VND","RWF","LBP",
|
||||
"ANG","SAR","LVL","KHR","BYR","TTD","OMR","LTL","GTQ","ALL","MRO","MWK",
|
||||
"LSL","SBD","BGN","LRD","JMD","CRC","ETB","NAD","GYD","LKR","INR","SEK",
|
||||
"KES","KMF","VEF","ARS","HTG","BAM","BWP","GEL","KZT","AED","KWD","XDR",
|
||||
"EUR","TND","MDL","LYD","BSD","GHS","MOP","PAB","ZAR","AZN","TOP","SVC",
|
||||
"KPW","TMT","BZD","GMD","XOF","UYU","MNT","NOK","XPF","BIF","BYN","FKP",
|
||||
"GBP","MXN","SYP","PGK","MZN","PLN","MMK","SGD","AOA","ILS","CLP","TZS",
|
||||
"CDF","BRL"
|
||||
]
|
||||
# Balance type
|
||||
BALANCE_TYPE_AVAILABLE = "available"
|
||||
BALANCE_TYPE_PENDING = "pending"
|
||||
|
89
custom_components/nicehash/control_switches.py
Normal file
89
custom_components/nicehash/control_switches.py
Normal file
@ -0,0 +1,89 @@
|
||||
"""
|
||||
NiceHash Rig controls
|
||||
"""
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
|
||||
from .coordinators import MiningRigsDataUpdateCoordinator
|
||||
from .nicehash import MiningRig, MiningRigDevice, NiceHashPrivateClient
|
||||
from .const import DOMAIN, NICEHASH_ATTRIBUTION
|
||||
import asyncio
|
||||
|
||||
class DeviceSwitch(SwitchEntity):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: MiningRigsDataUpdateCoordinator,
|
||||
rig: MiningRig,
|
||||
device: MiningRigDevice,
|
||||
client: NiceHashPrivateClient,
|
||||
):
|
||||
"""Initialize the switch"""
|
||||
self.coordinator = coordinator
|
||||
self._rig_id = rig.id
|
||||
self._rig_name = rig.name
|
||||
self._device_id = device.id
|
||||
self._device_name = device.name
|
||||
self._client = client
|
||||
self._status = device.status
|
||||
|
||||
|
||||
def _get_device(self):
|
||||
try:
|
||||
mining_rigs = self.coordinator.data.get("miningRigs")
|
||||
rig = MiningRig(mining_rigs.get(self._rig_id))
|
||||
return rig.devices.get(self._device_id)
|
||||
except Exception as e:
|
||||
_LOGGER.error(f"Unable to get mining device ({self._device_id})\n{e}")
|
||||
|
||||
|
||||
class GPUSwitch(DeviceSwitch):
|
||||
|
||||
_is_on = False
|
||||
_last_response = "N/A"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""switch name"""
|
||||
return f"{self._device_name} Switch"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Unique entity id"""
|
||||
return f"{self._device_id}:switch"
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
device = self._get_device()
|
||||
if device.status == "Mining":
|
||||
self._is_on = True
|
||||
elif device:
|
||||
self._is_on = False
|
||||
else:
|
||||
self._is_on = "unavailable"
|
||||
return self._is_on
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Sensor device state attributes"""
|
||||
return {
|
||||
ATTR_ATTRIBUTION: NICEHASH_ATTRIBUTION,
|
||||
"status": self._status,
|
||||
"device": self._device_name,
|
||||
"last_response": self._last_response,
|
||||
}
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the switch on."""
|
||||
self._is_on = True
|
||||
response = asyncio.run(self._client.toggle_device(self._device_id, "START", self._rig_id))
|
||||
self._last_response = "Success!" if response["success"] else response["message"]
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the switch off."""
|
||||
self._is_on = False
|
||||
response = asyncio.run(self._client.toggle_device(self._device_id, "STOP", self._rig_id))
|
||||
self._last_response = "Success!" if response["success"] else response["message"]
|
@ -12,6 +12,7 @@ from homeassistant.helpers.update_coordinator import (
|
||||
|
||||
from .const import (
|
||||
CURRENCY_BTC,
|
||||
CURRENCY_USD,
|
||||
DOMAIN,
|
||||
)
|
||||
from .nicehash import NiceHashPrivateClient, NiceHashPublicClient
|
||||
@ -41,13 +42,25 @@ class AccountsDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
accounts = await self._client.get_accounts()
|
||||
exchange_rates = await NiceHashPublicClient().get_exchange_rates()
|
||||
rates_dict = dict()
|
||||
|
||||
# NiceHash supports BTC->USD and BTC->EUR only.
|
||||
for rate in exchange_rates:
|
||||
fromCurrency = rate.get("fromCurrency")
|
||||
# Only care about the Bitcoin exchange rates
|
||||
if fromCurrency == CURRENCY_BTC:
|
||||
toCurrency = rate.get("toCurrency")
|
||||
exchange_rate = float(rate.get("exchangeRate"))
|
||||
rates_dict[f"{fromCurrency}-{toCurrency}"] = exchange_rate
|
||||
|
||||
# For non-USD/EUR currencies, get exchange rate using USD as an intermediary. e.g. BTC->USD->CAD
|
||||
btc_to_usd_rate = rates_dict[f"BTC-USD"]
|
||||
for rate in exchange_rates:
|
||||
fromCurrency = rate.get("fromCurrency")
|
||||
if fromCurrency == CURRENCY_USD:
|
||||
toCurrency = rate.get("toCurrency")
|
||||
exchange_rate = float(rate.get("exchangeRate")) * btc_to_usd_rate
|
||||
if f"{CURRENCY_BTC}-{toCurrency}" not in rates_dict:
|
||||
rates_dict[f"{CURRENCY_BTC}-{toCurrency}"] = exchange_rate
|
||||
|
||||
return {
|
||||
"accounts": accounts,
|
||||
"exchange_rates": rates_dict,
|
||||
|
@ -17,6 +17,7 @@ from .const import (
|
||||
ICON_PULSE,
|
||||
ICON_THERMOMETER,
|
||||
ICON_SPEEDOMETER,
|
||||
ICON_POWER,
|
||||
NICEHASH_ATTRIBUTION,
|
||||
)
|
||||
from .coordinators import MiningRigsDataUpdateCoordinator
|
||||
@ -381,3 +382,48 @@ class DeviceRPMSensor(DeviceSensor):
|
||||
"rig": self._rig_name,
|
||||
}
|
||||
|
||||
class DevicePowerSensor(DeviceSensor):
|
||||
"""
|
||||
Displays power of a mining rig device
|
||||
"""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Sensor name"""
|
||||
return f"{self._device_name} Power"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Unique entity id"""
|
||||
return f"{self._device_id}:power"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Sensor state"""
|
||||
device = self._get_device()
|
||||
if device:
|
||||
self._power = device.power
|
||||
else:
|
||||
self._power = 0
|
||||
|
||||
return self._power
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Sensor icon"""
|
||||
return ICON_POWER
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Sensor unit of measurement"""
|
||||
return "W"
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Sensor device state attributes"""
|
||||
return {
|
||||
ATTR_ATTRIBUTION: NICEHASH_ATTRIBUTION,
|
||||
"power": self._power,
|
||||
"rig": self._rig_name,
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ class MiningRigDevice:
|
||||
self.load = float(data.get("load"))
|
||||
self.rpm = float(data.get("revolutionsPerMinute"))
|
||||
self.speeds = data.get("speeds")
|
||||
self.power = float(data.get("powerUsage"))
|
||||
|
||||
|
||||
class MiningRig:
|
||||
@ -156,6 +157,13 @@ class NiceHashPrivateClient:
|
||||
query = f"size={size}"
|
||||
return await self.request("GET", "/main/api/v2/mining/rigs/payouts", query)
|
||||
|
||||
async def toggle_device(self, device_id, action, rig_id):
|
||||
query = ""
|
||||
body = {"deviceId":device_id,
|
||||
"action":action,
|
||||
"rigId":rig_id}
|
||||
return await self.request("POST", "/main/api/v2/mining/rigs/status2", query, body)
|
||||
|
||||
async def request(self, method, path, query="", body=None):
|
||||
xtime = self.get_epoch_ms_from_now()
|
||||
xnonce = str(uuid.uuid4())
|
||||
|
@ -378,7 +378,7 @@ class RigSpeedSensor(RigSensor):
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Sensor unit of measurement"""
|
||||
return None
|
||||
return f"{self._unit}"
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
|
@ -20,6 +20,7 @@ from .const import (
|
||||
DEVICE_RPM,
|
||||
DEVICE_SPEED_RATE,
|
||||
DEVICE_SPEED_ALGORITHM,
|
||||
SUPPORTED_CURRENCIES,
|
||||
)
|
||||
from .nicehash import (
|
||||
MiningRig,
|
||||
@ -39,6 +40,7 @@ from .rig_sensors import (
|
||||
)
|
||||
from .device_sensors import (
|
||||
DeviceAlgorithmSensor,
|
||||
DevicePowerSensor,
|
||||
DeviceSpeedSensor,
|
||||
DeviceStatusSensor,
|
||||
DeviceLoadSensor,
|
||||
@ -121,7 +123,7 @@ def create_balance_sensors(organization_id, currency, coordinator):
|
||||
balance_type=BALANCE_TYPE_TOTAL,
|
||||
),
|
||||
]
|
||||
if currency == CURRENCY_USD or currency == CURRENCY_EUR:
|
||||
if currency in SUPPORTED_CURRENCIES:
|
||||
_LOGGER.debug(f"Creating {currency} account balance sensors")
|
||||
balance_sensors.append(
|
||||
BalanceSensor(
|
||||
@ -148,8 +150,7 @@ def create_balance_sensors(organization_id, currency, coordinator):
|
||||
)
|
||||
)
|
||||
else:
|
||||
_LOGGER.warn("Invalid currency: must be EUR or USD")
|
||||
|
||||
_LOGGER.warn("Currency is invalid.")
|
||||
return balance_sensors
|
||||
|
||||
|
||||
@ -192,5 +193,6 @@ def create_device_sensors(mining_rigs, coordinator):
|
||||
device_sensors.append(DeviceTemperatureSensor(coordinator, rig, device))
|
||||
device_sensors.append(DeviceLoadSensor(coordinator, rig, device))
|
||||
device_sensors.append(DeviceRPMSensor(coordinator, rig, device))
|
||||
device_sensors.append(DevicePowerSensor(coordinator, rig, device))
|
||||
|
||||
return device_sensors
|
||||
|
82
custom_components/nicehash/switch.py
Normal file
82
custom_components/nicehash/switch.py
Normal file
@ -0,0 +1,82 @@
|
||||
"""
|
||||
Sensor platform for NiceHash
|
||||
"""
|
||||
import logging
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.core import Config, HomeAssistant
|
||||
|
||||
from .const import (
|
||||
BALANCE_TYPE_AVAILABLE,
|
||||
BALANCE_TYPE_PENDING,
|
||||
BALANCE_TYPE_TOTAL,
|
||||
CURRENCY_BTC,
|
||||
CURRENCY_EUR,
|
||||
CURRENCY_USD,
|
||||
DOMAIN,
|
||||
DEVICE_LOAD,
|
||||
DEVICE_RPM,
|
||||
DEVICE_SPEED_RATE,
|
||||
DEVICE_SPEED_ALGORITHM,
|
||||
)
|
||||
from .nicehash import (
|
||||
MiningRig,
|
||||
MiningRigDevice,
|
||||
NiceHashPrivateClient,
|
||||
NiceHashPublicClient,
|
||||
)
|
||||
from .control_switches import (
|
||||
GPUSwitch
|
||||
)
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant, config: Config, async_add_entities, discovery_info=None
|
||||
):
|
||||
"""Setup NiceHash sensor platform"""
|
||||
_LOGGER.debug("Creating new NiceHash switch components")
|
||||
|
||||
data = hass.data[DOMAIN]
|
||||
# Configuration
|
||||
organization_id = data.get("organization_id")
|
||||
client = data.get("client")
|
||||
# Options
|
||||
currency = data.get("currency")
|
||||
balances_enabled = data.get("balances_enabled")
|
||||
payouts_enabled = data.get("payouts_enabled")
|
||||
rigs_enabled = data.get("rigs_enabled")
|
||||
devices_enabled = data.get("devices_enabled")
|
||||
|
||||
|
||||
# Mining rig and device sensors
|
||||
if rigs_enabled or devices_enabled:
|
||||
rigs_coordinator = data.get("rigs_coordinator")
|
||||
rig_data = await client.get_mining_rigs()
|
||||
mining_rigs = rig_data.get("miningRigs")
|
||||
_LOGGER.debug(f"Found {len(mining_rigs)} rigs")
|
||||
|
||||
if devices_enabled:
|
||||
_LOGGER.debug("Device sensors enabled")
|
||||
device_switches = create_device_switches(mining_rigs, rigs_coordinator,client)
|
||||
async_add_entities(device_switches, True)
|
||||
|
||||
|
||||
|
||||
def create_device_switches(mining_rigs, coordinator, client):
|
||||
device_switches = []
|
||||
for rig_data in mining_rigs:
|
||||
rig = MiningRig(rig_data)
|
||||
devices = rig.devices.values()
|
||||
_LOGGER.debug(
|
||||
f"Found {len(devices)} device switches(s) for {rig.name} ({rig.id})"
|
||||
)
|
||||
for device in devices:
|
||||
_LOGGER.debug(f"Creating {device.name} ({device.id}) switches")
|
||||
device_switches.append(GPUSwitch(coordinator, rig, device, client))
|
||||
|
||||
return device_switches
|
||||
|
Loading…
Reference in New Issue
Block a user