From d5f9f56f98eb622a22aa800ebb3fecdf627b8e05 Mon Sep 17 00:00:00 2001 From: Brian Berg Date: Sun, 5 Jul 2020 18:02:46 +0000 Subject: [PATCH] feat(sensor): add device sensors - status, algorithm, speed, temp, load, rpm --- custom_components/nicehash/const.py | 14 +- custom_components/nicehash/sensor.py | 45 ++- custom_components/nicehash/sensors.py | 381 +++++++++++++++++++++++++- 3 files changed, 435 insertions(+), 5 deletions(-) diff --git a/custom_components/nicehash/const.py b/custom_components/nicehash/const.py index 7d0232a..8c4ab76 100644 --- a/custom_components/nicehash/const.py +++ b/custom_components/nicehash/const.py @@ -10,12 +10,14 @@ VERSION = "0.1.0" ISSUE_URL = "https://github.com/brianberg/ha-nicehash/issues" # Icons -ICON = "mdi:pickaxe" ICON_CURRENCY_BTC = "mdi:currency-btc" ICON_CURRENCY_EUR = "mdi:currency-eur" ICON_CURRENCY_USD = "mdi:currency-usd" +ICON_MEMORY = "mdi:memory" +ICON_PICKAXE = "mdi:pickaxe" ICON_PULSE = "mdi:pulse" -ICON_TEMPERATURE = "mdi:thermometer" +ICON_THERMOMETER = "mdi:thermometer" +ICON_SPEEDOMETER = "mdi:speedometer" # Platforms SENSOR = "sensor" @@ -46,12 +48,15 @@ If you have any issues with this you need to open an issue here: # NiceHash NICEHASH_API_URL = "https://api2.nicehash.com" +# Currency CURRENCY_BTC = "BTC" CURRENCY_USD = "USD" CURRENCY_EUR = "EUR" +# Balance type BALANCE_TYPE_AVAILABLE = "available" BALANCE_TYPE_PENDING = "pending" BALANCE_TYPE_TOTAL = "total" +# Device status DEVICE_STATUS_UNKNOWN = "UNKNOWN" DEVICE_STATUS_DISABLED = "DISABLED" DEVICE_STATUS_INACTIVE = "INACTIVE" @@ -60,3 +65,8 @@ DEVICE_STATUS_BENCHMARKING = "BENCHMARKING" DEVICE_STATUS_ERROR = "ERROR" DEVICE_STATUS_PENDING = "PENDING" DEVICE_STATUS_OFFLINE = "OFFLINE" +# Device stat +DEVICE_SPEED_RATE = "device-speed-rate" +DEVICE_SPEED_ALGORITHM = "device-speed-algorithm" +DEVICE_LOAD = "device-load" +DEVICE_RPM = "device-rpm" diff --git a/custom_components/nicehash/sensor.py b/custom_components/nicehash/sensor.py index 0bbaa26..f80d44e 100644 --- a/custom_components/nicehash/sensor.py +++ b/custom_components/nicehash/sensor.py @@ -17,12 +17,23 @@ from .const import ( CURRENCY_EUR, CURRENCY_USD, DOMAIN, + DEVICE_LOAD, + DEVICE_RPM, + DEVICE_SPEED_RATE, + DEVICE_SPEED_ALGORITHM, ) from .nicehash import NiceHashPrivateClient, NiceHashPublicClient from .sensors import ( NiceHashBalanceSensor, NiceHashRigStatusSensor, NiceHashRigTemperatureSensor, + NiceHashRigProfitabilitySensor, + NiceHashDeviceAlgorithmSensor, + NiceHashDeviceSpeedSensor, + NiceHashDeviceStatusSensor, + NiceHashDeviceLoadSensor, + NiceHashDeviceRPMSensor, + NiceHashDeviceTemperatureSensor, ) _LOGGER = logging.getLogger(__name__) @@ -94,7 +105,8 @@ async def async_setup_platform( # Add mining rig sensors rig_data = await client.get_mining_rigs() - mining_rigs = rig_data["miningRigs"] + mining_rigs = rig_data.get("miningRigs") + # Add status sensors async_add_entities( [NiceHashRigStatusSensor(rigs_coordinator, rig) for rig in mining_rigs], True, @@ -104,3 +116,34 @@ async def async_setup_platform( [NiceHashRigTemperatureSensor(rigs_coordinator, rig) for rig in mining_rigs], True, ) + # Add profitability sensors + async_add_entities( + [NiceHashRigProfitabilitySensor(rigs_coordinator, rig) for rig in mining_rigs], + True, + ) + # Add device sensors + device_sensors = [] + for rig in mining_rigs: + devices = rig.get("devices") + for i in range(len(devices)): + device = devices[i] + device_sensors.append( + NiceHashDeviceAlgorithmSensor(rigs_coordinator, rig, device) + ) + device_sensors.append( + NiceHashDeviceSpeedSensor(rigs_coordinator, rig, device) + ) + device_sensors.append( + NiceHashDeviceStatusSensor(rigs_coordinator, rig, device) + ) + device_sensors.append( + NiceHashDeviceTemperatureSensor(rigs_coordinator, rig, device) + ) + device_sensors.append( + NiceHashDeviceLoadSensor(rigs_coordinator, rig, device) + ) + device_sensors.append( + NiceHashDeviceRPMSensor(rigs_coordinator, rig, device) + ) + + async_add_entities(device_sensors, True) diff --git a/custom_components/nicehash/sensors.py b/custom_components/nicehash/sensors.py index 1d73a79..e01dafb 100644 --- a/custom_components/nicehash/sensors.py +++ b/custom_components/nicehash/sensors.py @@ -16,11 +16,18 @@ from .const import ( CURRENCY_USD, DEFAULT_NAME, DEVICE_STATUS_UNKNOWN, + DEVICE_LOAD, + DEVICE_RPM, + DEVICE_SPEED_ALGORITHM, + DEVICE_SPEED_RATE, ICON_CURRENCY_BTC, ICON_CURRENCY_EUR, ICON_CURRENCY_USD, + ICON_MEMORY, + ICON_PICKAXE, ICON_PULSE, - ICON_TEMPERATURE, + ICON_THERMOMETER, + ICON_SPEEDOMETER, ) ATTRIBUTION = "Data provided by NiceHash" @@ -210,7 +217,7 @@ class NiceHashRigTemperatureSensor(Entity): @property def icon(self): """Sensor icon""" - return ICON_TEMPERATURE + return ICON_THERMOMETER @property def unit_of_measurement(self): @@ -324,3 +331,373 @@ class NiceHashRigStatusSensor(Entity): async def async_update(self): """Update entity""" await self.coordinator.async_request_refresh() + + +class NiceHashRigProfitabilitySensor(Entity): + """ + Displays profitability of a mining rig + """ + + def __init__(self, coordinator, rig): + """Initialize the sensor""" + self.coordinator = coordinator + self._rig_id = rig["rigId"] + self._rig_name = rig["name"] + self._profitability = 0 + self._unpaid_amount = 0 + _LOGGER.debug( + f"Mining Rig Profitability Sensor: {self._rig_name} ({self._rig_id})" + ) + + @property + def name(self): + """Sensor name""" + return f"{self._rig_name} Profitability" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._rig_id}:profitability" + + @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""" + mining_rigs = self.coordinator.data.get("miningRigs") + try: + rig_data = mining_rigs.get(self._rig_id) + self._profitability = rig_data.get("profitability") + self._unpaid_amount = rig_data.get("unpaidAmount") + except Exception as e: + _LOGGER.error(f"Unable to get mining rig ({self._rig_id}) status\n{e}") + self._profitability = 0 + self._unpaid_amount = 0 + + return self._profitability + + @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""" + return { + ATTR_ATTRIBUTION: ATTRIBUTION, + "profitability": self._profitability, + "unpaid_amount": self._unpaid_amount, + } + + 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() + + +####################################### +# Mining Device Sensors +####################################### + + +class NiceHashDeviceSensor(Entity): + """ + Mining rig device sensor + """ + + def __init__(self, coordinator, rig, device): + """Initialize the sensor""" + self.coordinator = coordinator + self._rig_id = rig.get("rigId") + self._rig_name = rig.get("name") + self._device_name = device.get("name") + self._device_id = device.get("id") + self._status = DEVICE_STATUS_UNKNOWN + self._load = 0 + self._rpm = 0 + self._algorithm = None + self._speed = 0 + self._speed_title = "Unknown" + self._speed_unit = "MH" + self._temperature = 0 + + @property + def name(self): + """Sensor name""" + return f"{self._device_name}" + + @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 icon(self): + """Sensor icon""" + return ICON_PICKAXE + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + return None + + @property + def device_state_attributes(self): + """Sensor device state attributes""" + return { + ATTR_ATTRIBUTION: ATTRIBUTION, + "status": self._status, + "algorithm": self._algorithm, + "speed": self._speed, + "speed_unit": f"{self._speed_unit}/s", + "temperature": self._temperature, + "load": f"{self._load}%", + "rpm": self._rpm, + } + + 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() + mining_rigs = self.coordinator.data.get("miningRigs") + try: + rig_data = mining_rigs.get(self._rig_id) + devices = rig_data.get("devices") + for device in devices: + if device.get("id") == self._device_id: + algorithms = device.get("speeds") + if len(algorithms) > 0: + status = device.get("status") + self._status = status.get("description") + algorithm = algorithms[0] + self._load = float(device.get("load")) + self._rpm = float(device.get("revolutionsPerMinute")) + self._algorithm = algorithm.get("title") + self._speed = float(algorithm.get("speed")) + self._speed_title = algorithm.get("title") + self._speed_unit = algorithm.get("displaySuffix") + self._temperature = int(device.get("temperature")) + else: + self._status = DEVICE_STATUS_UNKNOWN + self._load = 0 + self._rpm = 0 + self._speed = 0 + self._speed_title = "Unknown" + self._speed_unit = "MH" + self._algorithm = None + except Exception as e: + _LOGGER.error(f"Unable to get mining device ({self._device_id}) speed\n{e}") + self._status = DEVICE_STATUS_UNKNOWN + self._load = 0 + self._rpm = 0 + self._speed = 0 + self._speed_title = "Unknown" + self._speed_unit = "MH" + self._algorithm = None + + +class NiceHashDeviceStatusSensor(NiceHashDeviceSensor): + """ + Displays status of a mining rig device + """ + + @property + def name(self): + """Sensor name""" + return f"{self._device_name} Status" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._device_id}:status" + + @property + def state(self): + """Sensor state""" + return self._status + + @property + def icon(self): + """Sensor icon""" + return ICON_PULSE + + +class NiceHashDeviceSpeedSensor(NiceHashDeviceSensor): + """ + Displays speed of a mining rig device + """ + + @property + def name(self): + """Sensor name""" + return f"{self._device_name} Speed" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._device_id}:speed" + + @property + def state(self): + """Sensor state""" + return self._speed + + @property + def icon(self): + """Sensor icon""" + return ICON_SPEEDOMETER + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + return f"{self._speed_unit}/s" + + +class NiceHashDeviceAlgorithmSensor(NiceHashDeviceSensor): + """ + Displays algorithm of a mining rig device + """ + + @property + def name(self): + """Sensor name""" + return f"{self._device_name} Algorithm" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._device_id}:algorithm" + + @property + def state(self): + """Sensor state""" + return self._algorithm + + @property + def icon(self): + """Sensor icon""" + return ICON_PICKAXE + + +class NiceHashDeviceTemperatureSensor(NiceHashDeviceSensor): + """ + Displays temperature of a mining rig device + """ + + @property + def name(self): + """Sensor name""" + return f"{self._device_name} Temperature" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._device_id}:temperature" + + @property + def state(self): + """Sensor state""" + return self._temperature + + @property + def icon(self): + """Sensor icon""" + return ICON_THERMOMETER + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + # Not Celsius because then HA might convert to Fahrenheit + return "C" + + +class NiceHashDeviceLoadSensor(NiceHashDeviceSensor): + """ + Displays load of a mining rig device + """ + + @property + def name(self): + """Sensor name""" + return f"{self._device_name} Load" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._device_id}:load" + + @property + def state(self): + """Sensor state""" + return self._load + + @property + def icon(self): + """Sensor icon""" + return ICON_SPEEDOMETER + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + return "%" + + +class NiceHashDeviceRPMSensor(NiceHashDeviceSensor): + """ + Displays RPM of a mining rig device + """ + + @property + def name(self): + """Sensor name""" + return f"{self._device_name} RPM" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._device_id}:rpm" + + @property + def state(self): + """Sensor state""" + return self._rpm + + @property + def icon(self): + """Sensor icon""" + return ICON_SPEEDOMETER + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + return "RPM" +