feat(sensor): add recent payout sensor

- fetches payouts every hour
- displays most recently created in BTC
This commit is contained in:
Brian Berg 2020-07-11 18:40:42 +00:00
parent 5c99904287
commit eefd191e51
6 changed files with 209 additions and 11 deletions

View File

@ -20,6 +20,7 @@ from .const import (
CONF_ORGANIZATION_ID, CONF_ORGANIZATION_ID,
CONF_RIGS_ENABLED, CONF_RIGS_ENABLED,
CONF_DEVICES_ENABLED, CONF_DEVICES_ENABLED,
CONF_PAYOUTS_ENABLED,
CURRENCY_USD, CURRENCY_USD,
DOMAIN, DOMAIN,
STARTUP_MESSAGE, STARTUP_MESSAGE,
@ -27,6 +28,7 @@ from .const import (
from .nicehash import NiceHashPrivateClient from .nicehash import NiceHashPrivateClient
from .data_coordinators import ( from .data_coordinators import (
AccountsDataUpdateCoordinator, AccountsDataUpdateCoordinator,
MiningPayoutsDataUpdateCoordinator,
MiningRigsDataUpdateCoordinator, MiningRigsDataUpdateCoordinator,
) )
@ -42,6 +44,7 @@ CONFIG_SCHEMA = vol.Schema(
vol.Required(CONF_CURRENCY, default=CURRENCY_USD): cv.string, vol.Required(CONF_CURRENCY, default=CURRENCY_USD): cv.string,
vol.Required(CONF_RIGS_ENABLED, default=False): cv.boolean, vol.Required(CONF_RIGS_ENABLED, default=False): cv.boolean,
vol.Required(CONF_DEVICES_ENABLED, default=False): cv.boolean, vol.Required(CONF_DEVICES_ENABLED, default=False): cv.boolean,
vol.Required(CONF_PAYOUTS_ENABLED, default=False): cv.boolean,
} }
) )
}, },
@ -64,22 +67,39 @@ async def async_setup(hass: HomeAssistant, config: Config):
currency = nicehash_config.get(CONF_CURRENCY).upper() currency = nicehash_config.get(CONF_CURRENCY).upper()
rigs_enabled = nicehash_config.get(CONF_RIGS_ENABLED) rigs_enabled = nicehash_config.get(CONF_RIGS_ENABLED)
devices_enabled = nicehash_config.get(CONF_DEVICES_ENABLED) devices_enabled = nicehash_config.get(CONF_DEVICES_ENABLED)
payouts_enabled = nicehash_config.get(CONF_PAYOUTS_ENABLED)
client = NiceHashPrivateClient(organization_id, api_key, api_secret) client = NiceHashPrivateClient(organization_id, api_key, api_secret)
accounts_coordinator = AccountsDataUpdateCoordinator(hass, client) hass.data[DOMAIN]["organization_id"] = organization_id
hass.data[DOMAIN]["client"] = client
hass.data[DOMAIN]["currency"] = currency
hass.data[DOMAIN]["rigs_enabled"] = rigs_enabled
hass.data[DOMAIN]["devices_enabled"] = devices_enabled
hass.data[DOMAIN]["payouts_enabled"] = payouts_enabled
# Accounts
accounts_coordinator = AccountsDataUpdateCoordinator(hass, client)
await accounts_coordinator.async_refresh() await accounts_coordinator.async_refresh()
if not accounts_coordinator.last_update_success: if not accounts_coordinator.last_update_success:
_LOGGER.error("Unable to get NiceHash accounts") _LOGGER.error("Unable to get NiceHash accounts")
raise PlatformNotReady raise PlatformNotReady
hass.data[DOMAIN]["organization_id"] = organization_id
hass.data[DOMAIN]["client"] = client
hass.data[DOMAIN]["currency"] = currency
hass.data[DOMAIN]["accounts_coordinator"] = accounts_coordinator hass.data[DOMAIN]["accounts_coordinator"] = accounts_coordinator
# Payouts
if payouts_enabled:
payouts_coordinator = MiningPayoutsDataUpdateCoordinator(hass, client)
await payouts_coordinator.async_refresh()
if not payouts_coordinator.last_update_success:
_LOGGER.error("Unable to get NiceHash mining payouts")
raise PlatformNotReady
hass.data[DOMAIN]["payouts_coordinator"] = payouts_coordinator
# Rigs
if rigs_enabled or devices_enabled: if rigs_enabled or devices_enabled:
rigs_coordinator = MiningRigsDataUpdateCoordinator(hass, client) rigs_coordinator = MiningRigsDataUpdateCoordinator(hass, client)
await rigs_coordinator.async_refresh() await rigs_coordinator.async_refresh()
@ -88,8 +108,6 @@ async def async_setup(hass: HomeAssistant, config: Config):
_LOGGER.error("Unable to get NiceHash mining rigs") _LOGGER.error("Unable to get NiceHash mining rigs")
raise PlatformNotReady raise PlatformNotReady
hass.data[DOMAIN]["rigs_enabled"] = rigs_enabled
hass.data[DOMAIN]["devices_enabled"] = devices_enabled
hass.data[DOMAIN]["rigs_coordinator"] = rigs_coordinator hass.data[DOMAIN]["rigs_coordinator"] = rigs_coordinator
await discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config) await discovery.async_load_platform(hass, "sensor", DOMAIN, {}, config)

View File

@ -31,7 +31,7 @@ CONF_ORGANIZATION_ID = "organization_id"
CONF_CURRENCY = "currency" CONF_CURRENCY = "currency"
CONF_RIGS_ENABLED = "rigs" CONF_RIGS_ENABLED = "rigs"
CONF_DEVICES_ENABLED = "devices" CONF_DEVICES_ENABLED = "devices"
CONF_PAYOUTS_ENABLED = "payouts"
# Defaults # Defaults
DEFAULT_NAME = NAME DEFAULT_NAME = NAME
@ -73,3 +73,5 @@ DEVICE_SPEED_RATE = "device-speed-rate"
DEVICE_SPEED_ALGORITHM = "device-speed-algorithm" DEVICE_SPEED_ALGORITHM = "device-speed-algorithm"
DEVICE_LOAD = "device-load" DEVICE_LOAD = "device-load"
DEVICE_RPM = "device-rpm" DEVICE_RPM = "device-rpm"
# Payout types
PAYOUT_USER = "USER"

View File

@ -18,6 +18,7 @@ from .nicehash import NiceHashPrivateClient, NiceHashPublicClient
SCAN_INTERVAL_RIGS = timedelta(minutes=1) SCAN_INTERVAL_RIGS = timedelta(minutes=1)
SCAN_INTERVAL_ACCOUNTS = timedelta(minutes=60) SCAN_INTERVAL_ACCOUNTS = timedelta(minutes=60)
SCAN_INTERVAL_PAYOUTS = timedelta(minutes=60)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -80,3 +81,26 @@ class MiningRigsDataUpdateCoordinator(DataUpdateCoordinator):
return data return data
except Exception as e: except Exception as e:
raise UpdateFailed(e) raise UpdateFailed(e)
class MiningPayoutsDataUpdateCoordinator(DataUpdateCoordinator):
"""Manages fetching mining rig payout data from NiceHash API"""
def __init__(self, hass: HomeAssistant, client: NiceHashPrivateClient):
"""Initialize"""
self.name = f"{DOMAIN}_mining_payouts_coordinator"
self._client = client
super().__init__(
hass, _LOGGER, name=self.name, update_interval=SCAN_INTERVAL_PAYOUTS
)
async def _async_update_data(self):
"""Update mining payouts data"""
try:
data = await self._client.get_rig_payouts(42) # 6 (per day) * 7 days
payouts = data.get("list")
payouts.sort(key=lambda payout: payout.get("created"))
return payouts
except Exception as e:
raise UpdateFailed(e)

View File

@ -18,7 +18,7 @@ from .const import NICEHASH_API_URL
class MiningRigDevice: class MiningRigDevice:
def __init__(self, data): def __init__(self, data: dict):
self.device_id = data.get("id") self.device_id = data.get("id")
self.name = data.get("name") self.name = data.get("name")
self.status = data.get("status").get("description") self.status = data.get("status").get("description")
@ -29,7 +29,7 @@ class MiningRigDevice:
class MiningRig: class MiningRig:
def __init__(self, data): def __init__(self, data: dict):
self.rig_id = data.get("rig_id") self.rig_id = data.get("rig_id")
self.name = data.get("name") self.name = data.get("name")
self.status = data.get("minerStatus") self.status = data.get("minerStatus")
@ -46,6 +46,24 @@ class MiningRig:
self.temperatures.append(device.temperature) self.temperatures.append(device.temperature)
class Payout:
def __init__(self, data: dict):
self.id = data.get("id")
self.currency = "Unknown"
self.created = data.get("created")
self.amount = data.get("amount")
self.fee = data.get("feeAmount")
self.account_type = "Unknown"
# Currency
currency = data.get("currency")
if currency:
self.currency = currency.get("enumName")
# Account Type
account_type = data.get("accountType")
if account_type:
self.account_type = account_type.get("enumName")
class NiceHashPublicClient: class NiceHashPublicClient:
async def get_exchange_rates(self): async def get_exchange_rates(self):
exchange_data = await self.request("GET", "/main/api/v2/exchangeRate/list") exchange_data = await self.request("GET", "/main/api/v2/exchangeRate/list")
@ -89,6 +107,10 @@ class NiceHashPrivateClient:
async def get_mining_rig(self, rig_id): async def get_mining_rig(self, rig_id):
return await self.request("GET", f"/main/api/v2/mining/rig2/{rig_id}") return await self.request("GET", f"/main/api/v2/mining/rig2/{rig_id}")
async def get_rig_payouts(self, size=84):
query = f"size={size}"
return await self.request("GET", "/main/api/v2/mining/rigs/payouts", query)
async def request(self, method, path, query="", body=None): async def request(self, method, path, query="", body=None):
xtime = self.get_epoch_ms_from_now() xtime = self.get_epoch_ms_from_now()
xnonce = str(uuid.uuid4()) xnonce = str(uuid.uuid4())

View File

@ -0,0 +1,118 @@
"""
NiceHash Rig Payout Sensors
"""
from datetime import datetime
import logging
from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.helpers.entity import Entity
from .const import (
CURRENCY_BTC,
FORMAT_DATETIME,
ICON_CURRENCY_BTC,
ICON_PULSE,
ICON_THERMOMETER,
NICEHASH_ATTRIBUTION,
PAYOUT_USER,
)
from .data_coordinators import (
MiningPayoutsDataUpdateCoordinator,
MiningRigsDataUpdateCoordinator,
)
from .nicehash import MiningRig, Payout
_LOGGER = logging.getLogger(__name__)
class RecentMiningPayoutSensor(Entity):
"""
Displays most recent payout of a mining rig
"""
def __init__(
self, coordinator: MiningPayoutsDataUpdateCoordinator, organization_id: str
):
"""Initialize the sensor"""
self.coordinator = coordinator
self.organization_id = organization_id
self._id = None
self._created = None
self._currency = None
self._amount = 0.00
self._fee = 0.00
@property
def name(self):
"""Sensor name"""
return f"Recent Mining Payout"
@property
def unique_id(self):
"""Unique entity id"""
return f"{self.organization_id}:payouts:recent"
@property
def should_poll(self):
"""No need to poll, Coordinator notifies entity of updates"""
return False
@property
def available(self):
"""Whether sensor is available"""
return self.coordinator.last_update_success
@property
def state(self):
"""Sensor state"""
try:
for raw_payout in self.coordinator.data:
payout = Payout(raw_payout)
if payout.account_type == PAYOUT_USER:
self._id = payout.id
self._amount = payout.amount
self._currency = payout.currency
self._created = datetime.fromtimestamp(payout.created / 1000.0)
self._fee = payout.fee
except Exception as e:
_LOGGER.error(f"Unable to get most recent \n{e}")
self._id = None
self._created = None
self._currency = None
self._amount = 0.00
self._fee = 0.00
return self._amount
@property
def icon(self):
"""Sensor icon"""
return ICON_CURRENCY_BTC
@property
def unit_of_measurement(self):
"""Sensor unit of measurement"""
return CURRENCY_BTC
@property
def device_state_attributes(self):
"""Sensor device state attributes"""
created = None
if self._created:
created = self._created.strftime(FORMAT_DATETIME)
return {
ATTR_ATTRIBUTION: NICEHASH_ATTRIBUTION,
"amount": self._amount,
"created": created,
"fee": self._fee,
}
async def async_added_to_hass(self):
"""Connect to dispatcher listening for entity data notifications"""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)
async def async_update(self):
"""Update entity"""
await self.coordinator.async_request_refresh()

View File

@ -23,6 +23,7 @@ from .const import (
) )
from .nicehash import NiceHashPrivateClient, NiceHashPublicClient from .nicehash import NiceHashPrivateClient, NiceHashPublicClient
from .account_sensors import BalanceSensor from .account_sensors import BalanceSensor
from .payout_sensors import RecentMiningPayoutSensor
from .rig_sensors import ( from .rig_sensors import (
RigStatusSensor, RigStatusSensor,
RigTemperatureSensor, RigTemperatureSensor,
@ -52,6 +53,7 @@ async def async_setup_platform(
client = data.get("client") client = data.get("client")
# Options # Options
currency = data.get("currency") currency = data.get("currency")
payouts_enabled = data.get("payouts_enabled")
rigs_enabled = data.get("rigs_enabled") rigs_enabled = data.get("rigs_enabled")
devices_enabled = data.get("devices_enabled") devices_enabled = data.get("devices_enabled")
@ -62,17 +64,22 @@ async def async_setup_platform(
) )
async_add_entities(balance_sensors, True) async_add_entities(balance_sensors, True)
# Payout sensors
if payouts_enabled:
payouts_coordinator = data.get("payouts_coordinator")
payout_sensors = create_payout_sensors(organization_id, payouts_coordinator)
async_add_entities(payout_sensors)
# Mining rig and device sensors
if rigs_enabled or devices_enabled: if rigs_enabled or devices_enabled:
rigs_coordinator = data.get("rigs_coordinator") rigs_coordinator = data.get("rigs_coordinator")
rig_data = await client.get_mining_rigs() rig_data = await client.get_mining_rigs()
mining_rigs = rig_data.get("miningRigs") mining_rigs = rig_data.get("miningRigs")
# Add mining rig sensors if enabled
if rigs_enabled: if rigs_enabled:
rig_sensors = create_rig_sensors(mining_rigs, rigs_coordinator) rig_sensors = create_rig_sensors(mining_rigs, rigs_coordinator)
async_add_entities(rig_sensors, True) async_add_entities(rig_sensors, True)
# Add device sensors if enabled
if devices_enabled: if devices_enabled:
device_sensors = create_device_sensors(mining_rigs, rigs_coordinator) device_sensors = create_device_sensors(mining_rigs, rigs_coordinator)
async_add_entities(device_sensors, True) async_add_entities(device_sensors, True)
@ -130,6 +137,13 @@ def create_balance_sensors(organization_id, currency, coordinator):
return balance_sensors return balance_sensors
def create_payout_sensors(organization_id, coordinator):
payout_sensors = []
payout_sensors.append(RecentMiningPayoutSensor(coordinator, organization_id))
return payout_sensors
def create_rig_sensors(mining_rigs, coordinator): def create_rig_sensors(mining_rigs, coordinator):
rig_sensors = [] rig_sensors = []
for rig in mining_rigs: for rig in mining_rigs: