Use a cache object to reduce the frequency of calls to APCUPSd

This commit is contained in:
Flyte 2016-02-11 07:33:53 +00:00
parent bb8981b611
commit 91fb2764cc
3 changed files with 59 additions and 51 deletions

View File

@ -5,6 +5,9 @@ Sets up and provides access to the status output of APCUPSd via its Network
Information Server (NIS). Information Server (NIS).
""" """
import logging import logging
from datetime import timedelta
from homeassistant.util import Throttle
DOMAIN = "apcupsd" DOMAIN = "apcupsd"
@ -21,31 +24,59 @@ KEY_STATUS = "STATUS"
VALUE_ONLINE = "ONLINE" VALUE_ONLINE = "ONLINE"
GET_STATUS = None MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60)
DATA = None
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def setup(hass, config): def setup(hass, config):
""" Use config values to set up a function enabling status retrieval. """ """ Use config values to set up a function enabling status retrieval. """
global GET_STATUS global DATA
from apcaccess import status
host = config[DOMAIN].get(CONF_HOST, DEFAULT_HOST) host = config[DOMAIN].get(CONF_HOST, DEFAULT_HOST)
port = config[DOMAIN].get(CONF_PORT, DEFAULT_PORT) port = config[DOMAIN].get(CONF_PORT, DEFAULT_PORT)
def get_status(): DATA = APCUPSdData(host, port)
""" Get the status from APCUPSd and parse it into a dict. """
return status.parse(status.get(host=host, port=port))
GET_STATUS = get_status
# It doesn't really matter why we're not able to get the status, just that # It doesn't really matter why we're not able to get the status, just that
# we can't. # we can't.
# pylint: disable=broad-except # pylint: disable=broad-except
try: try:
GET_STATUS() DATA.update(no_throttle=True)
except Exception: except Exception:
_LOGGER.exception("Failure while testing APCUPSd status retrieval.") _LOGGER.exception("Failure while testing APCUPSd status retrieval.")
return False return False
return True return True
class APCUPSdData(object):
"""
Stores the data retrieved from APCUPSd for each entity to use, acts as the
single point responsible for fetching updates from the server.
"""
def __init__(self, host, port):
from apcaccess import status
self._host = host
self._port = port
self._status = None
self._get = status.get
self._parse = status.parse
@property
def status(self):
""" Get latest update if throttle allows. Return status. """
self.update()
return self._status
def _get_status(self):
""" Get the status from APCUPSd and parse it into a dict. """
return self._parse(self._get(host=self._host, port=self._port))
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self, **kwargs):
"""
Fetch the latest status from APCUPSd and store it in self._status.
"""
self._status = self._get_status()

View File

@ -3,7 +3,6 @@ homeassistant.components.binary_sensor.apcupsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides a binary sensor to track online status of a UPS. Provides a binary sensor to track online status of a UPS.
""" """
from homeassistant.core import JobPriority
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components import apcupsd from homeassistant.components import apcupsd
@ -15,17 +14,16 @@ DEFAULT_NAME = "UPS Online Status"
def setup_platform(hass, config, add_entities, discovery_info=None): def setup_platform(hass, config, add_entities, discovery_info=None):
""" Instantiate an OnlineStatus binary sensor entity and add it to HA. """ """ Instantiate an OnlineStatus binary sensor entity and add it to HA. """
add_entities((OnlineStatus(hass, config),)) add_entities((OnlineStatus(config, apcupsd.DATA),))
class OnlineStatus(BinarySensorDevice): class OnlineStatus(BinarySensorDevice):
""" Binary sensor to represent UPS online status. """ """ Binary sensor to represent UPS online status. """
def __init__(self, hass, config): def __init__(self, config, data):
self._config = config self._config = config
self._data = data
self._state = None self._state = None
# Get initial state self.update()
hass.pool.add_job(
JobPriority.EVENT_STATE, (self.update_ha_state, True))
@property @property
def name(self): def name(self):
@ -39,8 +37,7 @@ class OnlineStatus(BinarySensorDevice):
def update(self): def update(self):
""" """
Get the latest status report from APCUPSd and establish whether the Get the status report from APCUPSd (or cache) and set this entity's
UPS is online. state.
""" """
status = apcupsd.GET_STATUS() self._state = self._data.status[apcupsd.KEY_STATUS]
self._state = status[apcupsd.KEY_STATUS]

View File

@ -5,7 +5,6 @@ Provides a sensor to track various status aspects of a UPS.
""" """
import logging import logging
from homeassistant.core import JobPriority
from homeassistant.const import TEMP_CELCIUS from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.components import apcupsd from homeassistant.components import apcupsd
@ -32,28 +31,17 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
_LOGGER.error( _LOGGER.error(
"You must include a '%s' when configuring an APCUPSd sensor.", "You must include a '%s' when configuring an APCUPSd sensor.",
apcupsd.CONF_TYPE) apcupsd.CONF_TYPE)
return return False
typ = typ.upper() typ = typ.upper()
# Get a status reading from APCUPSd and check whether the user provided if typ not in apcupsd.DATA.status:
# 'type' is present in the output. If we're not able to check, then assume
# the user knows what they're doing.
# pylint: disable=broad-except
status = None
try:
status = apcupsd.GET_STATUS()
if typ not in status:
_LOGGER.error( _LOGGER.error(
"Specified '%s' of '%s' does not appear in the APCUPSd status " "Specified '%s' of '%s' does not appear in the APCUPSd status "
"output.", apcupsd.CONF_TYPE, typ) "output.", apcupsd.CONF_TYPE, typ)
return return False
except Exception as exc:
_LOGGER.warning(
"Unable to fetch initial value from ACPUPSd to check that '%s' is "
"a supported '%s': %s", typ, apcupsd.CONF_TYPE, exc)
unit = SPECIFIC_UNITS.get(typ)
add_entities(( add_entities((
Sensor(hass, config, unit=unit, initial_status=status), Sensor(config, apcupsd.DATA, unit=SPECIFIC_UNITS.get(typ)),
)) ))
@ -72,16 +60,12 @@ def infer_unit(value):
class Sensor(Entity): class Sensor(Entity):
""" Generic sensor entity for APCUPSd status values. """ """ Generic sensor entity for APCUPSd status values. """
def __init__(self, hass, config, unit=None, initial_status=None): def __init__(self, config, data, unit=None):
self._config = config self._config = config
self._unit = unit self._unit = unit
self._state = None self._data = data
self._inferred_unit = None self._inferred_unit = None
if initial_status is None: self.update()
hass.pool.add_job(
JobPriority.EVENT_STATE, (self.update_ha_state, True))
else:
self._update_from_status(initial_status)
@property @property
def name(self): def name(self):
@ -99,9 +83,5 @@ class Sensor(Entity):
def update(self): def update(self):
""" Get the latest status and use it to update our sensor state. """ """ Get the latest status and use it to update our sensor state. """
self._update_from_status(apcupsd.GET_STATUS())
def _update_from_status(self, status):
""" Set state and infer unit from status. """
key = self._config[apcupsd.CONF_TYPE].upper() key = self._config[apcupsd.CONF_TYPE].upper()
self._state, self._inferred_unit = infer_unit(status[key]) self._state, self._inferred_unit = infer_unit(self._data.status[key])