From a31d524e1099a34135aa5812703993399ac344b8 Mon Sep 17 00:00:00 2001 From: Brian Berg Date: Thu, 9 Jul 2020 00:05:11 +0000 Subject: [PATCH] refactor(sensor): separate sensors --- custom_components/nicehash/account_sensors.py | 131 ++++ custom_components/nicehash/const.py | 2 + custom_components/nicehash/device_sensors.py | 307 ++++++++ custom_components/nicehash/rig_sensors.py | 280 +++++++ custom_components/nicehash/sensor.py | 9 +- custom_components/nicehash/sensors.py | 703 ------------------ 6 files changed, 724 insertions(+), 708 deletions(-) create mode 100644 custom_components/nicehash/account_sensors.py create mode 100644 custom_components/nicehash/device_sensors.py create mode 100644 custom_components/nicehash/rig_sensors.py delete mode 100644 custom_components/nicehash/sensors.py diff --git a/custom_components/nicehash/account_sensors.py b/custom_components/nicehash/account_sensors.py new file mode 100644 index 0000000..9b6fe31 --- /dev/null +++ b/custom_components/nicehash/account_sensors.py @@ -0,0 +1,131 @@ +""" +NiceHash Account Sensors +""" +import logging + +from homeassistant.const import ATTR_ATTRIBUTION +from homeassistant.helpers.entity import Entity + +from .const import ( + BALANCE_TYPE_AVAILABLE, + BALANCE_TYPE_PENDING, + BALANCE_TYPE_TOTAL, + CURRENCY_BTC, + CURRENCY_EUR, + CURRENCY_USD, + DEFAULT_NAME, + ICON_CURRENCY_BTC, + ICON_CURRENCY_EUR, + ICON_CURRENCY_USD, + NICEHASH_ATTRIBUTION, +) + +_LOGGER = logging.getLogger(__name__) + + +class NiceHashBalanceSensor(Entity): + """ + Displays [available|pending|total] balance of an account for a currency + """ + + def __init__( + self, + coordinator, + organization_id, + currency, + balance_type=BALANCE_TYPE_AVAILABLE, + ): + """Initialize the sensor""" + _LOGGER.debug(f"Account Balance Sensor: {balance_type} {currency}") + self.coordinator = coordinator + self.currency = currency + self.organization_id = organization_id + self.balance_type = balance_type + self._available = 0.00 + self._pending = 0.00 + self._total_balance = 0.00 + self._exchange_rate = 0.00 + + @property + def name(self): + """Sensor name""" + balance_type = self.balance_type[0].upper() + self.balance_type[1:] + return f"{DEFAULT_NAME} {balance_type} Account Balance {self.currency}" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self.organization_id}:{self.currency}:{self.balance_type}" + + @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""" + accounts = self.coordinator.data.get("accounts") + total = accounts.get("total") + pending = float(total.get("pending")) + available = float(total.get("available")) + total_balance = float(total.get("totalBalance")) + + if self.currency == CURRENCY_BTC: + self._pending = pending + self._available = available + self._total_balance = total_balance + else: + exchange_rates = self.coordinator.data.get("exchange_rates") + exchange_rate = exchange_rates.get(f"{CURRENCY_BTC}-{self.currency}") + self._pending = round(pending * exchange_rate, 2) + self._available = round(available * exchange_rate, 2) + self._total_balance = round(total_balance * exchange_rate, 2) + self._exchange_rate = exchange_rate + + if self.balance_type == BALANCE_TYPE_TOTAL: + return self._total_balance + elif self.balance_type == BALANCE_TYPE_PENDING: + return self._pending + + return self._available + + @property + def icon(self): + """Sensor icon""" + if self.currency == CURRENCY_EUR: + return ICON_CURRENCY_EUR + elif self.currency == CURRENCY_USD: + return ICON_CURRENCY_USD + return ICON_CURRENCY_BTC + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + return self.currency + + @property + def device_state_attributes(self): + """Sensor device state attributes""" + return { + ATTR_ATTRIBUTION: NICEHASH_ATTRIBUTION, + "total": self._total_balance, + "available": self._available, + "pending": self._pending, + "exchange_rate": self._exchange_rate, + } + + 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() diff --git a/custom_components/nicehash/const.py b/custom_components/nicehash/const.py index 5ac983c..51965cf 100644 --- a/custom_components/nicehash/const.py +++ b/custom_components/nicehash/const.py @@ -35,6 +35,7 @@ CONF_DEVICES_ENABLED = "devices" # Defaults DEFAULT_NAME = NAME +FORMAT_DATETIME = "%d-%m-%Y %H:%M" # Startup STARTUP_MESSAGE = f""" @@ -49,6 +50,7 @@ If you have any issues with this you need to open an issue here: # NiceHash NICEHASH_API_URL = "https://api2.nicehash.com" +NICEHASH_ATTRIBUTION = "Data provided by NiceHash" # Currency CURRENCY_BTC = "BTC" CURRENCY_USD = "USD" diff --git a/custom_components/nicehash/device_sensors.py b/custom_components/nicehash/device_sensors.py new file mode 100644 index 0000000..c053f55 --- /dev/null +++ b/custom_components/nicehash/device_sensors.py @@ -0,0 +1,307 @@ +""" +NiceHash Rig Device Sensors +""" +from datetime import datetime +import logging + +from homeassistant.const import ATTR_ATTRIBUTION +from homeassistant.helpers.entity import Entity + +from .const import ( + DEVICE_STATUS_UNKNOWN, + DEVICE_LOAD, + DEVICE_RPM, + DEVICE_SPEED_ALGORITHM, + DEVICE_SPEED_RATE, + ICON_PICKAXE, + ICON_PULSE, + ICON_THERMOMETER, + ICON_SPEEDOMETER, + NICEHASH_ATTRIBUTION, +) + +_LOGGER = logging.getLogger(__name__) + + +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: NICEHASH_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" + diff --git a/custom_components/nicehash/rig_sensors.py b/custom_components/nicehash/rig_sensors.py new file mode 100644 index 0000000..ce4161d --- /dev/null +++ b/custom_components/nicehash/rig_sensors.py @@ -0,0 +1,280 @@ +""" +NiceHash Rig Sensors +""" +from datetime import datetime +import logging + +from homeassistant.const import ATTR_ATTRIBUTION +from homeassistant.helpers.entity import Entity + +from .const import ( + CURRENCY_BTC, + DEVICE_STATUS_UNKNOWN, + FORMAT_DATETIME, + ICON_CURRENCY_BTC, + ICON_PULSE, + ICON_THERMOMETER, + NICEHASH_ATTRIBUTION, +) + +_LOGGER = logging.getLogger(__name__) + + +class NiceHashRigTemperatureSensor(Entity): + """ + Displays highest temperature of active mining rig devices + """ + + def __init__(self, coordinator, rig): + """Initialize the sensor""" + self.coordinator = coordinator + self._rig_id = rig["rigId"] + self._rig_name = rig["name"] + self._temps = [] + self._num_devices = 0 + _LOGGER.debug( + f"Mining Rig Temperature Sensor: {self._rig_name} ({self._rig_id})" + ) + + @property + def name(self): + """Sensor name""" + return f"{self._rig_name} Temperature" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._rig_id}:temperature" + + @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") + self._highest_temp = 0 + try: + rig_data = mining_rigs.get(self._rig_id) + devices = rig_data.get("devices") + self._temps = [] + self._num_devices = len(devices) + + if self._num_devices > 0: + for device in devices: + temp = int(device.get("temperature")) + self._temps.append(temp) + if temp > self._highest_temp: + self._highest_temp = temp + else: + self._num_devices = 0 + except Exception as e: + _LOGGER.error(f"Unable to get mining rig ({self._rig_id}) temperature\n{e}") + + return self._highest_temp + + @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" + + @property + def device_state_attributes(self): + """Sensor device state attributes""" + return { + ATTR_ATTRIBUTION: NICEHASH_ATTRIBUTION, + "highest_temperature": self._highest_temp, + "temperatures": self._temps, + "total_devices": self._num_devices, + } + + 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() + + +class NiceHashRigStatusSensor(Entity): + """ + Displays status 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._status = DEVICE_STATUS_UNKNOWN + self._status_time = None + self._num_devices = 0 + self._unit_of_measurement = "\u200b" + _LOGGER.debug(f"Mining Rig Status Sensor: {self._rig_name} ({self._rig_id})") + + @property + def name(self): + """Sensor name""" + return f"{self._rig_name} Status" + + @property + def unique_id(self): + """Unique entity id""" + return f"{self._rig_id}:status" + + @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") + status = DEVICE_STATUS_UNKNOWN + try: + rig_data = mining_rigs.get(self._rig_id) + devices = rig_data.get("devices") + status = rig_data.get("minerStatus") + status_time_ms = int(rig_data.get("statusTime")) + self._num_devices = len(devices) + self._status_time = datetime.fromtimestamp(status_time_ms / 1000.0) + except Exception as e: + _LOGGER.error(f"Unable to get mining rig ({self._rig_id}) status\n{e}") + self._status_time = None + status = DEVICE_STATUS_UNKNOWN + + self._status = status[0].upper() + status.lower()[1:] + return self._status + + @property + def icon(self): + """Sensor icon""" + return ICON_PULSE + + @property + def unit_of_measurement(self): + """Sensor unit of measurement""" + return self._unit_of_measurement + + @property + def device_state_attributes(self): + """Sensor device state attributes""" + return { + ATTR_ATTRIBUTION: NICEHASH_ATTRIBUTION, + "status": self._status, + "status_time": self._status_time.strftime(FORMAT_DATETIME), + "total_devices": self._num_devices, + } + + 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() + + +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: NICEHASH_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() diff --git a/custom_components/nicehash/sensor.py b/custom_components/nicehash/sensor.py index 855a361..875d5d1 100644 --- a/custom_components/nicehash/sensor.py +++ b/custom_components/nicehash/sensor.py @@ -22,11 +22,13 @@ from .const import ( DEVICE_SPEED_ALGORITHM, ) from .nicehash import NiceHashPrivateClient, NiceHashPublicClient -from .sensors import ( - NiceHashBalanceSensor, +from .account_sensors import NiceHashBalanceSensor +from .rig_sensors import ( NiceHashRigStatusSensor, NiceHashRigTemperatureSensor, NiceHashRigProfitabilitySensor, +) +from .device_sensors import ( NiceHashDeviceAlgorithmSensor, NiceHashDeviceSpeedSensor, NiceHashDeviceStatusSensor, @@ -53,9 +55,6 @@ async def async_setup_platform( rigs_enabled = data.get("rigs_enabled") devices_enabled = data.get("devices_enabled") - print(f"rigs: {rigs_enabled}") - print(f"devices: {devices_enabled}") - # Add account balance sensors accounts_coordinator = data.get("accounts_coordinator") balance_sensors = create_balance_sensors( diff --git a/custom_components/nicehash/sensors.py b/custom_components/nicehash/sensors.py deleted file mode 100644 index e01dafb..0000000 --- a/custom_components/nicehash/sensors.py +++ /dev/null @@ -1,703 +0,0 @@ -""" -NiceHash Sensors -""" -from datetime import datetime -import logging - -from homeassistant.const import ATTR_ATTRIBUTION -from homeassistant.helpers.entity import Entity - -from .const import ( - BALANCE_TYPE_AVAILABLE, - BALANCE_TYPE_PENDING, - BALANCE_TYPE_TOTAL, - CURRENCY_BTC, - CURRENCY_EUR, - 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_THERMOMETER, - ICON_SPEEDOMETER, -) - -ATTRIBUTION = "Data provided by NiceHash" -FORMAT_DATETIME = "%d-%m-%Y %H:%M" - -_LOGGER = logging.getLogger(__name__) - - -####################################### -# Account Sensors -####################################### - - -class NiceHashBalanceSensor(Entity): - """ - Displays [available|pending|total] balance of an account for a currency - """ - - def __init__( - self, - coordinator, - organization_id, - currency, - balance_type=BALANCE_TYPE_AVAILABLE, - ): - """Initialize the sensor""" - _LOGGER.debug(f"Account Balance Sensor: {balance_type} {currency}") - self.coordinator = coordinator - self.currency = currency - self.organization_id = organization_id - self.balance_type = balance_type - self._available = 0.00 - self._pending = 0.00 - self._total_balance = 0.00 - self._exchange_rate = 0.00 - - @property - def name(self): - """Sensor name""" - balance_type = self.balance_type[0].upper() + self.balance_type[1:] - return f"{DEFAULT_NAME} {balance_type} Account Balance {self.currency}" - - @property - def unique_id(self): - """Unique entity id""" - return f"{self.organization_id}:{self.currency}:{self.balance_type}" - - @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""" - accounts = self.coordinator.data.get("accounts") - total = accounts.get("total") - pending = float(total.get("pending")) - available = float(total.get("available")) - total_balance = float(total.get("totalBalance")) - - if self.currency == CURRENCY_BTC: - self._pending = pending - self._available = available - self._total_balance = total_balance - else: - exchange_rates = self.coordinator.data.get("exchange_rates") - exchange_rate = exchange_rates.get(f"{CURRENCY_BTC}-{self.currency}") - self._pending = round(pending * exchange_rate, 2) - self._available = round(available * exchange_rate, 2) - self._total_balance = round(total_balance * exchange_rate, 2) - self._exchange_rate = exchange_rate - - if self.balance_type == BALANCE_TYPE_TOTAL: - return self._total_balance - elif self.balance_type == BALANCE_TYPE_PENDING: - return self._pending - - return self._available - - @property - def icon(self): - """Sensor icon""" - if self.currency == CURRENCY_EUR: - return ICON_CURRENCY_EUR - elif self.currency == CURRENCY_USD: - return ICON_CURRENCY_USD - return ICON_CURRENCY_BTC - - @property - def unit_of_measurement(self): - """Sensor unit of measurement""" - return self.currency - - @property - def device_state_attributes(self): - """Sensor device state attributes""" - return { - ATTR_ATTRIBUTION: ATTRIBUTION, - "total": self._total_balance, - "available": self._available, - "pending": self._pending, - "exchange_rate": self._exchange_rate, - } - - 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 Rig Sensors -####################################### - - -class NiceHashRigTemperatureSensor(Entity): - """ - Displays highest temperature of active mining rig devices - """ - - def __init__(self, coordinator, rig): - """Initialize the sensor""" - self.coordinator = coordinator - self._rig_id = rig["rigId"] - self._rig_name = rig["name"] - self._temps = [] - self._num_devices = 0 - _LOGGER.debug( - f"Mining Rig Temperature Sensor: {self._rig_name} ({self._rig_id})" - ) - - @property - def name(self): - """Sensor name""" - return f"{self._rig_name} Temperature" - - @property - def unique_id(self): - """Unique entity id""" - return f"{self._rig_id}:temperature" - - @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") - self._highest_temp = 0 - try: - rig_data = mining_rigs.get(self._rig_id) - devices = rig_data.get("devices") - self._temps = [] - self._num_devices = len(devices) - - if self._num_devices > 0: - for device in devices: - temp = int(device.get("temperature")) - self._temps.append(temp) - if temp > self._highest_temp: - self._highest_temp = temp - else: - self._num_devices = 0 - except Exception as e: - _LOGGER.error(f"Unable to get mining rig ({self._rig_id}) temperature\n{e}") - - return self._highest_temp - - @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" - - @property - def device_state_attributes(self): - """Sensor device state attributes""" - return { - ATTR_ATTRIBUTION: ATTRIBUTION, - "highest_temperature": self._highest_temp, - "temperatures": self._temps, - "total_devices": self._num_devices, - } - - 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() - - -class NiceHashRigStatusSensor(Entity): - """ - Displays status 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._status = DEVICE_STATUS_UNKNOWN - self._status_time = None - self._num_devices = 0 - self._unit_of_measurement = "\u200b" - _LOGGER.debug(f"Mining Rig Status Sensor: {self._rig_name} ({self._rig_id})") - - @property - def name(self): - """Sensor name""" - return f"{self._rig_name} Status" - - @property - def unique_id(self): - """Unique entity id""" - return f"{self._rig_id}:status" - - @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") - status = DEVICE_STATUS_UNKNOWN - try: - rig_data = mining_rigs.get(self._rig_id) - devices = rig_data.get("devices") - status = rig_data.get("minerStatus") - status_time_ms = int(rig_data.get("statusTime")) - self._num_devices = len(devices) - self._status_time = datetime.fromtimestamp(status_time_ms / 1000.0) - except Exception as e: - _LOGGER.error(f"Unable to get mining rig ({self._rig_id}) status\n{e}") - self._status_time = None - status = DEVICE_STATUS_UNKNOWN - - self._status = status[0].upper() + status.lower()[1:] - return self._status - - @property - def icon(self): - """Sensor icon""" - return ICON_PULSE - - @property - def unit_of_measurement(self): - """Sensor unit of measurement""" - return self._unit_of_measurement - - @property - def device_state_attributes(self): - """Sensor device state attributes""" - return { - ATTR_ATTRIBUTION: ATTRIBUTION, - "status": self._status, - "status_time": self._status_time.strftime(FORMAT_DATETIME), - "total_devices": self._num_devices, - } - - 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() - - -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" -