"""Support for Abode Home Security system.""" import logging from functools import partial from requests.exceptions import HTTPError, ConnectTimeout import abodepy import abodepy.helpers.constants as CONST from abodepy.exceptions import AbodeException import abodepy.helpers.timeline as TIMELINE 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.""" 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.""" 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.""" 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.""" 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.""" 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()