diff --git a/.coveragerc b/.coveragerc index df4f46b7884ba543194042338727c171c3edbcde..894d76473fbb803a6fbbcd4ac77ec4aab9c75aef 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,9 @@ omit = homeassistant/helpers/signal.py # omit pieces of code that rely on external devices being present + homeassistant/components/abode.py + homeassistant/components/*/abode.py + homeassistant/components/alarmdecoder.py homeassistant/components/*/alarmdecoder.py diff --git a/homeassistant/components/abode.py b/homeassistant/components/abode.py new file mode 100644 index 0000000000000000000000000000000000000000..1eeb62991e18e5938d24617fa1eea260a0b44016 --- /dev/null +++ b/homeassistant/components/abode.py @@ -0,0 +1,75 @@ +""" +This component provides basic support for Abode Home Security system. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/abode/ +""" +import logging + +import voluptuous as vol +from requests.exceptions import HTTPError, ConnectTimeout +from homeassistant.helpers import discovery +from homeassistant.helpers import config_validation as cv +from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_NAME + +REQUIREMENTS = ['abodepy==0.5.1'] + +_LOGGER = logging.getLogger(__name__) + +CONF_ATTRIBUTION = "Data provided by goabode.com" + +DOMAIN = 'abode' +DEFAULT_NAME = 'Abode' +DATA_ABODE = 'data_abode' +DEFAULT_ENTITY_NAMESPACE = 'abode' + +NOTIFICATION_ID = 'abode_notification' +NOTIFICATION_TITLE = 'Abode Security Setup' + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, + vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + }), +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up Abode component.""" + conf = config[DOMAIN] + username = conf.get(CONF_USERNAME) + password = conf.get(CONF_PASSWORD) + + try: + data = AbodeData(username, password) + hass.data[DATA_ABODE] = data + + for component in ['binary_sensor', 'alarm_control_panel']: + discovery.load_platform(hass, component, DOMAIN, {}, config) + + except (ConnectTimeout, HTTPError) as ex: + _LOGGER.error("Unable to connect to Abode: %s", str(ex)) + hass.components.persistent_notification.create( + 'Error: {}<br />' + 'You will need to restart hass after fixing.' + ''.format(ex), + title=NOTIFICATION_TITLE, + notification_id=NOTIFICATION_ID) + return False + + return True + + +class AbodeData: + """Shared Abode data.""" + + def __init__(self, username, password): + """Initialize Abode oject.""" + import abodepy + + self.abode = abodepy.Abode(username, password) + self.devices = self.abode.get_devices() + + _LOGGER.debug("Abode Security set up with %s devices", + len(self.devices)) diff --git a/homeassistant/components/alarm_control_panel/abode.py b/homeassistant/components/alarm_control_panel/abode.py new file mode 100644 index 0000000000000000000000000000000000000000..0d4891cc27726bdb3a456e1b9f662a9221d57187 --- /dev/null +++ b/homeassistant/components/alarm_control_panel/abode.py @@ -0,0 +1,81 @@ +""" +This component provides HA alarm_control_panel support for Abode System. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/alarm_control_panel.abode/ +""" +import logging + +from homeassistant.components.abode import (DATA_ABODE, DEFAULT_NAME) +from homeassistant.const import (STATE_UNKNOWN) +import homeassistant.components.alarm_control_panel as alarm + +DEPENDENCIES = ['abode'] + +_LOGGER = logging.getLogger(__name__) + +ALARM_STATE_HOME = 'home' +ALARM_STATE_STANDBY = 'standby' +ALARM_STATE_AWAY = 'away' +ICON = 'mdi:security' + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up a sensor for an Abode device.""" + data = hass.data.get(DATA_ABODE) + + add_devices([AbodeAlarm(hass, data, data.abode.get_alarm())]) + + +class AbodeAlarm(alarm.AlarmControlPanel): + """An alarm_control_panel implementation for Abode.""" + + def __init__(self, hass, data, device): + """Initialize the alarm control panel.""" + super(AbodeAlarm, self).__init__() + self._device = device + self._name = "{0}".format(DEFAULT_NAME) + + @property + def should_poll(self): + """Return the polling state.""" + return True + + @property + def name(self): + """Return the name of the sensor.""" + return self._name + + @property + def icon(self): + """Return icon.""" + return ICON + + @property + def state(self): + """Return the state of the device.""" + status = self._device.mode + + if status in [ALARM_STATE_STANDBY, ALARM_STATE_HOME, ALARM_STATE_AWAY]: + return status + + return STATE_UNKNOWN + + def alarm_disarm(self, code=None): + """Send disarm command.""" + self._device.set_mode(mode=ALARM_STATE_STANDBY) + self.schedule_update_ha_state() + + def alarm_arm_home(self, code=None): + """Send arm home command.""" + self._device.set_mode(mode=ALARM_STATE_HOME) + self.schedule_update_ha_state() + + def alarm_arm_away(self, code=None): + """Send arm away command.""" + self._device.set_mode(mode=ALARM_STATE_AWAY) + self.schedule_update_ha_state() + + def update(self): + """Update the device state.""" + self._device.refresh() diff --git a/homeassistant/components/binary_sensor/abode.py b/homeassistant/components/binary_sensor/abode.py new file mode 100644 index 0000000000000000000000000000000000000000..9abff53026d1be65e4831235a6236f1c53d51d79 --- /dev/null +++ b/homeassistant/components/binary_sensor/abode.py @@ -0,0 +1,81 @@ +""" +This component provides HA binary_sensor support for Abode Security System. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.abode/ +""" +import logging + +from homeassistant.components.abode import (CONF_ATTRIBUTION, DATA_ABODE) +from homeassistant.const import (ATTR_ATTRIBUTION) +from homeassistant.components.binary_sensor import (BinarySensorDevice) + +DEPENDENCIES = ['abode'] + +_LOGGER = logging.getLogger(__name__) + +# Sensor types: Name, device_class +SENSOR_TYPES = { + 'Door Contact': 'opening', + 'Motion Camera': 'motion', +} + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up a sensor for an Abode device.""" + data = hass.data.get(DATA_ABODE) + + sensors = [] + for sensor in data.devices: + _LOGGER.debug('Sensor type %s', sensor.type) + if sensor.type in ['Door Contact', 'Motion Camera']: + sensors.append(AbodeBinarySensor(hass, data, sensor)) + + _LOGGER.debug('Adding %d sensors', len(sensors)) + add_devices(sensors) + + +class AbodeBinarySensor(BinarySensorDevice): + """A binary sensor implementation for Abode device.""" + + def __init__(self, hass, data, device): + """Initialize a sensor for Abode device.""" + super(AbodeBinarySensor, self).__init__() + self._device = device + + @property + def should_poll(self): + """Return the polling state.""" + return True + + @property + def name(self): + """Return the name of the sensor.""" + return "{0} {1}".format(self._device.type, self._device.name) + + @property + def is_on(self): + """Return True if the binary sensor is on.""" + if self._device.type == 'Door Contact': + return self._device.status != 'Closed' + elif self._device.type == 'Motion Camera': + return self._device.get_value('motion_event') == '1' + + @property + def device_class(self): + """Return the class of the binary sensor.""" + return SENSOR_TYPES.get(self._device.type) + + @property + def device_state_attributes(self): + """Return the state attributes.""" + attrs = {} + attrs[ATTR_ATTRIBUTION] = CONF_ATTRIBUTION + attrs['device_id'] = self._device.device_id + attrs['battery_low'] = self._device.battery_low + + return attrs + + def update(self): + """Update the device state.""" + self._device.refresh() diff --git a/requirements_all.txt b/requirements_all.txt index d89e93bef7e0c53c7010fff7ca825716719332ff..a16e99748ab337fab9df06266245232f7c5380dc 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -38,6 +38,9 @@ SoCo==0.12 # homeassistant.components.notify.twitter TwitterAPI==2.4.6 +# homeassistant.components.abode +abodepy==0.5.1 + # homeassistant.components.device_tracker.automatic aioautomatic==0.6.0