Skip to content

Commit

Permalink
Rework refresh logic, make it work reliably
Browse files Browse the repository at this point in the history
  • Loading branch information
Breina committed Feb 13, 2024
1 parent 8f438e2 commit d125cf3
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 54 deletions.
15 changes: 8 additions & 7 deletions custom_components/idrac_power/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
rest_client = hass.data[DOMAIN][entry.entry_id][DATA_IDRAC_REST_CLIENT]

# TODO figure out how to properly do async stuff in Python lol
info = await hass.async_add_executor_job(target=rest_client.get_device_info)
firmware_version = await hass.async_add_executor_job(target=rest_client.get_firmware_version)
info = await hass.async_add_executor_job(rest_client.get_device_info)
firmware_version = await hass.async_add_executor_job(rest_client.get_firmware_version)

model = info[JSON_MODEL]
name = model
Expand Down Expand Up @@ -57,19 +57,20 @@ def __init__(self, hass, rest: IdracRest, device_info, unique_id, name):
key='status',
name=name,
icon='mdi:power',
device_class=BinarySensorDeviceClass.POWER,
device_class=BinarySensorDeviceClass.RUNNING,
)

self._attr_device_info = device_info
self._attr_unique_id = unique_id
self._attr_has_entity_name = True

async def async_update(self) -> None:
"""Get the latest data from the iDrac."""

self._attr_is_on = await self.hass.async_add_executor_job(self.rest.get_status)
self.rest.register_callback_status(self.update_value)

@property
def name(self):
"""Name of the entity."""
return "Server Status"

def update_value(self, status: bool):
self._attr_is_on = status
self.async_schedule_update_ha_state()
2 changes: 1 addition & 1 deletion custom_components/idrac_power/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def __init__(self, hass, rest: IdracRest, device_info, unique_id, name):
self._attr_has_entity_name = True

async def async_press(self) -> None:
_LOGGER.warn("Refreshing sensors")
_LOGGER.info("Refreshing sensors manually")
await self.hass.async_add_executor_job(self.rest.update_thermals)
await self.hass.async_add_executor_job(self.rest.update_status)
await self.hass.async_add_executor_job(self.rest.update_power_usage)
Expand Down
68 changes: 37 additions & 31 deletions custom_components/idrac_power/idrac_rest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from typing import Callable

import requests
import urllib3
Expand Down Expand Up @@ -35,17 +36,20 @@ def handle_error(result):
raise CannotConnect(result.text)


thermals_values = None
status_values = None
power_values = None


class IdracRest:
def __init__(self, host, username, password, interval):
self.host = host
self.auth = (username, password)
self.interval = interval

self.callback_thermals: list[Callable[[dict], None]] = []
self.callback_status: list[Callable[[bool], None]] = []
self.callback_power_usage: list[Callable[[int], None]] = []

self.thermal_values: dict = {}
self.status: bool = False
self.power_usage: int = 0

def get_device_info(self):
result = self.get_path(drac_chassis_path)
handle_error(result)
Expand Down Expand Up @@ -85,50 +89,52 @@ def power_on(self):

return result

def update_thermals(self):
global thermals_values
def register_callback_thermals(self, callback: Callable[[dict], None]) -> None:
self.callback_thermals.append(callback)

def register_callback_status(self, callback: Callable[[bool], None]) -> None:
self.callback_status.append(callback)

def register_callback_power_usage(self, callback: Callable[[int], None]) -> None:
self.callback_power_usage.append(callback)

def update_thermals(self) -> dict:
req = self.get_path(drac_thermals)
handle_error(req)
thermals_values = req.json()
return thermals_values
new_thermals = req.json()

def get_thermals(self):
global thermals_values
return thermals_values
if new_thermals != self.thermal_values:
self.thermal_values = new_thermals
for callback in self.callback_thermals:
callback(self.thermal_values)
return self.thermal_values

def update_status(self):
global status_values
result = self.get_path(drac_chassis_path)
handle_error(result)
status_values = result.json()
try:
return status_values[JSON_STATUS][JSON_STATUS_STATE] == 'Enabled'
new_status = status_values[JSON_STATUS][JSON_STATUS_STATE] == 'Enabled'
except:
return False
new_status = False

def get_status(self):
global status_values
try:
return status_values[JSON_STATUS][JSON_STATUS_STATE] == 'Enabled'
except:
return False
if new_status != self.status:
self.status = new_status
for callback in self.callback_status:
callback(self.status)

def update_power_usage(self):
global power_values
result = self.get_path(drac_powercontrol_path)
handle_error(result)
power_values = result.json()
try:
return power_values[JSON_POWER_CONSUMED_WATTS]
except:
return 0

def get_power_usage(self):
global power_values
try:
return power_values[JSON_POWER_CONSUMED_WATTS]
new_power_usage = power_values[JSON_POWER_CONSUMED_WATTS]
if new_power_usage != self.power_usage:
self.power_usage = new_power_usage
for callback in self.callback_power_usage:
callback(self.power_usage)
except:
return 0
pass


class CannotConnect(HomeAssistantError):
Expand Down
31 changes: 16 additions & 15 deletions custom_components/idrac_power/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from __future__ import annotations

import asyncio
import functools
import logging

from homeassistant.components.sensor import SensorEntity, SensorEntityDescription, SensorStateClass, SensorDeviceClass
Expand Down Expand Up @@ -55,19 +56,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
async_add_entities([
IdracTempSensor(hass, rest_client, device_info, f"{model}_temp_{i}", temp["Name"], i)])

async def refresh_sensors_task(hass):
async def refresh_sensors_task():
while True:
_LOGGER.debug("Refreshing sensors")
await hass.async_add_executor_job(rest_client.update_thermals)
await hass.async_add_executor_job(rest_client.update_status)
await hass.async_add_executor_job(rest_client.update_power_usage)
await asyncio.sleep(rest_client.interval)

def start_sensors_task(event):
_LOGGER.info("Starting sensors task")
hass.async_create_task(refresh_sensors_task(hass))

hass.bus.async_listen_once('homeassistant_started', start_sensors_task)
hass.async_create_background_task(refresh_sensors_task(), "Update iDRAC task")


class IdracCurrentPowerSensor(SensorEntity):
Expand All @@ -92,15 +89,17 @@ def __init__(self, hass, rest: IdracRest, device_info, unique_id, name):

self._attr_native_value = None

async def async_update(self) -> None:
"""Get the latest data from the iDrac."""
self._attr_native_value = self.rest.get_power_usage()
self.rest.register_callback_power_usage(self.update_value)

@property
def name(self):
"""Name of the entity."""
return "Power Usage"

def update_value(self, new_value: int):
self._attr_native_value = new_value
self.async_schedule_update_ha_state()


class IdracFanSensor(SensorEntity):
id = 0
Expand All @@ -125,15 +124,16 @@ def __init__(self, hass, rest: IdracRest, device_info, unique_id, name, id):
self._attr_native_value = None
self.id = id

self.rest.register_callback_thermals(self.update_value)

@property
def name(self):
"""Name of the entity."""
return self.custom_name

async def async_update(self) -> None:
"""Get the latest data from the iDrac."""
thermal = self.rest.get_thermals()
def update_value(self, thermal: dict):
self._attr_native_value = thermal['Fans'][self.id]['Reading']
self.async_schedule_update_ha_state()


class IdracTempSensor(SensorEntity):
Expand All @@ -159,12 +159,13 @@ def __init__(self, hass, rest: IdracRest, device_info, unique_id, name, id):
self._attr_native_value = None
self.id = id

self.rest.register_callback_thermals(self.update_value)

@property
def name(self):
"""Name of the entity."""
return self.custom_name

async def async_update(self) -> None:
"""Get the latest data from the iDrac."""
thermal = self.rest.get_thermals()
def update_value(self, thermal: dict):
self._attr_native_value = thermal['Temperatures'][self.id]['ReadingCelsius']
self.async_schedule_update_ha_state()

0 comments on commit d125cf3

Please sign in to comment.