Add apcupsd component.

This commit is contained in:
Flyte 2016-02-10 16:32:18 +00:00
parent b350f22a77
commit bb8981b611
5 changed files with 210 additions and 0 deletions

View file

@ -11,6 +11,9 @@ omit =
homeassistant/components/arduino.py
homeassistant/components/*/arduino.py
homeassistant/components/apcupsd.py
homeassistant/components/*/apcupsd.py
homeassistant/components/bloomsky.py
homeassistant/components/*/bloomsky.py

View file

@ -0,0 +1,51 @@
"""
homeassistant.components.apcupsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets up and provides access to the status output of APCUPSd via its Network
Information Server (NIS).
"""
import logging
DOMAIN = "apcupsd"
REQUIREMENTS = ("apcaccess==0.0.4",)
CONF_HOST = "host"
CONF_PORT = "port"
CONF_TYPE = "type"
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 3551
KEY_STATUS = "STATUS"
VALUE_ONLINE = "ONLINE"
GET_STATUS = None
_LOGGER = logging.getLogger(__name__)
def setup(hass, config):
""" Use config values to set up a function enabling status retrieval. """
global GET_STATUS
from apcaccess import status
host = config[DOMAIN].get(CONF_HOST, DEFAULT_HOST)
port = config[DOMAIN].get(CONF_PORT, DEFAULT_PORT)
def get_status():
""" 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
# we can't.
# pylint: disable=broad-except
try:
GET_STATUS()
except Exception:
_LOGGER.exception("Failure while testing APCUPSd status retrieval.")
return False
return True

View file

@ -0,0 +1,46 @@
"""
homeassistant.components.binary_sensor.apcupsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 import apcupsd
DEPENDENCIES = [apcupsd.DOMAIN]
DEFAULT_NAME = "UPS Online Status"
def setup_platform(hass, config, add_entities, discovery_info=None):
""" Instantiate an OnlineStatus binary sensor entity and add it to HA. """
add_entities((OnlineStatus(hass, config),))
class OnlineStatus(BinarySensorDevice):
""" Binary sensor to represent UPS online status. """
def __init__(self, hass, config):
self._config = config
self._state = None
# Get initial state
hass.pool.add_job(
JobPriority.EVENT_STATE, (self.update_ha_state, True))
@property
def name(self):
""" The name of the UPS online status sensor. """
return self._config.get("name", DEFAULT_NAME)
@property
def is_on(self):
""" True if the UPS is online, else False. """
return self._state == apcupsd.VALUE_ONLINE
def update(self):
"""
Get the latest status report from APCUPSd and establish whether the
UPS is online.
"""
status = apcupsd.GET_STATUS()
self._state = status[apcupsd.KEY_STATUS]

View file

@ -0,0 +1,107 @@
"""
homeassistant.components.sensor.apcupsd
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Provides a sensor to track various status aspects of a UPS.
"""
import logging
from homeassistant.core import JobPriority
from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
from homeassistant.components import apcupsd
DEPENDENCIES = [apcupsd.DOMAIN]
DEFAULT_NAME = "UPS Status"
SPECIFIC_UNITS = {
"ITEMP": TEMP_CELCIUS
}
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""
Ensure that the 'type' config value has been set and use a specific unit
of measurement if required.
"""
typ = config.get(apcupsd.CONF_TYPE)
if typ is None:
_LOGGER.error(
"You must include a '%s' when configuring an APCUPSd sensor.",
apcupsd.CONF_TYPE)
return
typ = typ.upper()
# Get a status reading from APCUPSd and check whether the user provided
# '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(
"Specified '%s' of '%s' does not appear in the APCUPSd status "
"output.", apcupsd.CONF_TYPE, typ)
return
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((
Sensor(hass, config, unit=unit, initial_status=status),
))
def infer_unit(value):
"""
If the value ends with any of the units from ALL_UNITS, split the unit
off the end of the value and return the value, unit tuple pair. Else return
the original value and None as the unit.
"""
from apcaccess.status import ALL_UNITS
for unit in ALL_UNITS:
if value.endswith(unit):
return value[:-len(unit)], unit
return value, None
class Sensor(Entity):
""" Generic sensor entity for APCUPSd status values. """
def __init__(self, hass, config, unit=None, initial_status=None):
self._config = config
self._unit = unit
self._state = None
self._inferred_unit = None
if initial_status is None:
hass.pool.add_job(
JobPriority.EVENT_STATE, (self.update_ha_state, True))
else:
self._update_from_status(initial_status)
@property
def name(self):
return self._config.get("name", DEFAULT_NAME)
@property
def state(self):
return self._state
@property
def unit_of_measurement(self):
if self._unit is None:
return self._inferred_unit
return self._unit
def update(self):
""" 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()
self._state, self._inferred_unit = infer_unit(status[key])

View file

@ -21,6 +21,9 @@ SoCo==0.11.1
# homeassistant.components.notify.twitter
TwitterAPI==2.3.6
# homeassistant.components.apcupsd
apcaccess==0.0.4
# homeassistant.components.sun
astral==0.9