"""Support for Abode Home Security system.""" import logging from functools import partial from requests.exceptions import HTTPError, ConnectTimeout import voluptuous as vol from homeassistant.const import ( ATTR_ATTRIBUTION, ATTR_DATE, ATTR_TIME, ATTR_ENTITY_ID, CONF_USERNAME, CONF_PASSWORD, CONF_EXCLUDE, CONF_NAME, CONF_LIGHTS, EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START) from homeassistant.helpers import config_validation as cv from homeassistant.helpers import discovery from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) ATTRIBUTION = "Data provided by goabode.com" CONF_POLLING = 'polling' DOMAIN = 'abode' DEFAULT_CACHEDB = './abodepy_cache.pickle' NOTIFICATION_ID = 'abode_notification' NOTIFICATION_TITLE = 'Abode Security Setup' EVENT_ABODE_ALARM = 'abode_alarm' EVENT_ABODE_ALARM_END = 'abode_alarm_end' EVENT_ABODE_AUTOMATION = 'abode_automation' EVENT_ABODE_FAULT = 'abode_panel_fault' EVENT_ABODE_RESTORE = 'abode_panel_restore' SERVICE_SETTINGS = 'change_setting' SERVICE_CAPTURE_IMAGE = 'capture_image' SERVICE_TRIGGER = 'trigger_quick_action' ATTR_DEVICE_ID = 'device_id' ATTR_DEVICE_NAME = 'device_name' ATTR_DEVICE_TYPE = 'device_type' ATTR_EVENT_CODE = 'event_code' ATTR_EVENT_NAME = 'event_name' ATTR_EVENT_TYPE = 'event_type' ATTR_EVENT_UTC = 'event_utc' ATTR_SETTING = 'setting' ATTR_USER_NAME = 'user_name' ATTR_VALUE = 'value' ABODE_DEVICE_ID_LIST_SCHEMA = vol.Schema([str]) CONFIG_SCHEMA = vol.Schema({ DOMAIN: vol.Schema({ vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string, vol.Optional(CONF_NAME): cv.string, vol.Optional(CONF_POLLING, default=False): cv.boolean, vol.Optional(CONF_EXCLUDE, default=[]): ABODE_DEVICE_ID_LIST_SCHEMA, vol.Optional(CONF_LIGHTS, default=[]): ABODE_DEVICE_ID_LIST_SCHEMA }), }, extra=vol.ALLOW_EXTRA) CHANGE_SETTING_SCHEMA = vol.Schema({ vol.Required(ATTR_SETTING): cv.string, vol.Required(ATTR_VALUE): cv.string }) CAPTURE_IMAGE_SCHEMA = vol.Schema({ ATTR_ENTITY_ID: cv.entity_ids, }) TRIGGER_SCHEMA = vol.Schema({ ATTR_ENTITY_ID: cv.entity_ids, }) ABODE_PLATFORMS = [ 'alarm_control_panel', 'binary_sensor', 'lock', 'switch', 'cover', 'camera', 'light', 'sensor' ] class AbodeSystem: """Abode System class.""" def __init__(self, username, password, cache, name, polling, exclude, lights): """Initialize the system.""" import abodepy self.abode = abodepy.Abode( username, password, auto_login=True, get_devices=True, get_automations=True, cache_path=cache) self.name = name self.polling = polling self.exclude = exclude self.lights = lights self.devices = [] def is_excluded(self, device): """Check if a device is configured to be excluded.""" return device.device_id in self.exclude def is_automation_excluded(self, automation): """Check if an automation is configured to be excluded.""" return automation.automation_id in self.exclude def is_light(self, device): """Check if a switch device is configured as a light.""" import abodepy.helpers.constants as CONST return (device.generic_type == CONST.TYPE_LIGHT or (device.generic_type == CONST.TYPE_SWITCH and device.device_id in self.lights)) def setup(hass, config): """Set up Abode component.""" from abodepy.exceptions import AbodeException conf = config[DOMAIN] username = conf.get(CONF_USERNAME) password = conf.get(CONF_PASSWORD) name = conf.get(CONF_NAME) polling = conf.get(CONF_POLLING) exclude = conf.get(CONF_EXCLUDE) lights = conf.get(CONF_LIGHTS) try: cache = hass.config.path(DEFAULT_CACHEDB) hass.data[DOMAIN] = AbodeSystem( username, password, cache, name, polling, exclude, lights) except (AbodeException, ConnectTimeout, HTTPError) as ex: _LOGGER.error("Unable to connect to Abode: %s", str(ex)) hass.components.persistent_notification.create( 'Error: {}
' 'You will need to restart hass after fixing.' ''.format(ex), title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID) return False setup_hass_services(hass) setup_hass_events(hass) setup_abode_events(hass) for platform in ABODE_PLATFORMS: discovery.load_platform(hass, platform, DOMAIN, {}, config) return True def setup_hass_services(hass): """Home assistant services.""" from abodepy.exceptions import AbodeException def change_setting(call): """Change an Abode system setting.""" setting = call.data.get(ATTR_SETTING) value = call.data.get(ATTR_VALUE) try: hass.data[DOMAIN].abode.set_setting(setting, value) except AbodeException as ex: _LOGGER.warning(ex) def capture_image(call): """Capture a new image.""" entity_ids = call.data.get(ATTR_ENTITY_ID) target_devices = [device for device in hass.data[DOMAIN].devices if device.entity_id in entity_ids] for device in target_devices: device.capture() def trigger_quick_action(call): """Trigger a quick action.""" entity_ids = call.data.get(ATTR_ENTITY_ID, None) target_devices = [device for device in hass.data[DOMAIN].devices if device.entity_id in entity_ids] for device in target_devices: device.trigger() hass.services.register( DOMAIN, SERVICE_SETTINGS, change_setting, schema=CHANGE_SETTING_SCHEMA) hass.services.register( DOMAIN, SERVICE_CAPTURE_IMAGE, capture_image, schema=CAPTURE_IMAGE_SCHEMA) hass.services.register( DOMAIN, SERVICE_TRIGGER, trigger_quick_action, schema=TRIGGER_SCHEMA) def setup_hass_events(hass): """Home Assistant start and stop callbacks.""" def startup(event): """Listen for push events.""" hass.data[DOMAIN].abode.events.start() def logout(event): """Logout of Abode.""" if not hass.data[DOMAIN].polling: hass.data[DOMAIN].abode.events.stop() hass.data[DOMAIN].abode.logout() _LOGGER.info("Logged out of Abode") if not hass.data[DOMAIN].polling: hass.bus.listen_once(EVENT_HOMEASSISTANT_START, startup) hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, logout) def setup_abode_events(hass): """Event callbacks.""" import abodepy.helpers.timeline as TIMELINE def event_callback(event, event_json): """Handle an event callback from Abode.""" data = { ATTR_DEVICE_ID: event_json.get(ATTR_DEVICE_ID, ''), ATTR_DEVICE_NAME: event_json.get(ATTR_DEVICE_NAME, ''), ATTR_DEVICE_TYPE: event_json.get(ATTR_DEVICE_TYPE, ''), ATTR_EVENT_CODE: event_json.get(ATTR_EVENT_CODE, ''), ATTR_EVENT_NAME: event_json.get(ATTR_EVENT_NAME, ''), ATTR_EVENT_TYPE: event_json.get(ATTR_EVENT_TYPE, ''), ATTR_EVENT_UTC: event_json.get(ATTR_EVENT_UTC, ''), ATTR_USER_NAME: event_json.get(ATTR_USER_NAME, ''), ATTR_DATE: event_json.get(ATTR_DATE, ''), ATTR_TIME: event_json.get(ATTR_TIME, ''), } hass.bus.fire(event, data) events = [TIMELINE.ALARM_GROUP, TIMELINE.ALARM_END_GROUP, TIMELINE.PANEL_FAULT_GROUP, TIMELINE.PANEL_RESTORE_GROUP, TIMELINE.AUTOMATION_GROUP] for event in events: hass.data[DOMAIN].abode.events.add_event_callback( event, partial(event_callback, event)) class AbodeDevice(Entity): """Representation of an Abode device.""" def __init__(self, data, device): """Initialize a sensor for Abode device.""" self._data = data self._device = device async def async_added_to_hass(self): """Subscribe Abode events.""" self.hass.async_add_job( self._data.abode.events.add_device_callback, self._device.device_id, self._update_callback ) @property def should_poll(self): """Return the polling state.""" return self._data.polling def update(self): """Update automation state.""" self._device.refresh() @property def name(self): """Return the name of the sensor.""" return self._device.name @property def device_state_attributes(self): """Return the state attributes.""" return { ATTR_ATTRIBUTION: ATTRIBUTION, 'device_id': self._device.device_id, 'battery_low': self._device.battery_low, 'no_response': self._device.no_response, 'device_type': self._device.type } def _update_callback(self, device): """Update the device state.""" self.schedule_update_ha_state() class AbodeAutomation(Entity): """Representation of an Abode automation.""" def __init__(self, data, automation, event=None): """Initialize for Abode automation.""" self._data = data self._automation = automation self._event = event async def async_added_to_hass(self): """Subscribe Abode events.""" if self._event: self.hass.async_add_job( self._data.abode.events.add_event_callback, self._event, self._update_callback ) @property def should_poll(self): """Return the polling state.""" return self._data.polling def update(self): """Update automation state.""" self._automation.refresh() @property def name(self): """Return the name of the sensor.""" return self._automation.name @property def device_state_attributes(self): """Return the state attributes.""" return { ATTR_ATTRIBUTION: ATTRIBUTION, 'automation_id': self._automation.automation_id, 'type': self._automation.type, 'sub_type': self._automation.sub_type } def _update_callback(self, device): """Update the device state.""" self._automation.refresh() self.schedule_update_ha_state()