diff --git a/homeassistant/components/abode.py b/homeassistant/components/abode.py index f3283eff748..63c2fac48d1 100644 --- a/homeassistant/components/abode.py +++ b/homeassistant/components/abode.py @@ -6,57 +6,138 @@ https://home-assistant.io/components/abode/ """ import asyncio import logging +from functools import partial +from os import path 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.helpers.entity import Entity -from homeassistant.const import (ATTR_ATTRIBUTION, - CONF_USERNAME, CONF_PASSWORD, - CONF_NAME, EVENT_HOMEASSISTANT_STOP, +from homeassistant.config import load_yaml_config_file +from homeassistant.const import (ATTR_ATTRIBUTION, ATTR_DATE, ATTR_TIME, + ATTR_ENTITY_ID, CONF_USERNAME, CONF_PASSWORD, + CONF_EXCLUDE, CONF_NAME, + EVENT_HOMEASSISTANT_STOP, EVENT_HOMEASSISTANT_START) -REQUIREMENTS = ['abodepy==0.9.0'] +REQUIREMENTS = ['abodepy==0.11.5'] _LOGGER = logging.getLogger(__name__) CONF_ATTRIBUTION = "Data provided by goabode.com" +CONF_LIGHTS = "lights" +CONF_POLLING = "polling" DOMAIN = 'abode' -DEFAULT_NAME = 'Abode' -DATA_ABODE = 'abode' 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, default=DEFAULT_NAME): 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' + 'alarm_control_panel', 'binary_sensor', 'lock', 'switch', 'cover', + 'camera', 'light' ] +class AbodeSystem(object): + """Abode System class.""" + + def __init__(self, username, password, name, polling, exclude, lights): + """Initialize the system.""" + import abodepy + self.abode = abodepy.Abode(username, password, + auto_login=True, + get_devices=True, + get_automations=True) + 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.""" - import abodepy + 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: - hass.data[DATA_ABODE] = abode = abodepy.Abode( - username, password, auto_login=True, get_devices=True) - - except (ConnectTimeout, HTTPError) as ex: + hass.data[DOMAIN] = AbodeSystem( + username, password, 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.' @@ -65,46 +146,144 @@ def setup(hass, config): 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() + + descriptions = load_yaml_config_file( + path.join(path.dirname(__file__), 'services.yaml'))[DOMAIN] + + hass.services.register( + DOMAIN, SERVICE_SETTINGS, change_setting, + descriptions.get(SERVICE_SETTINGS), + schema=CHANGE_SETTING_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_CAPTURE_IMAGE, capture_image, + descriptions.get(SERVICE_CAPTURE_IMAGE), + schema=CAPTURE_IMAGE_SCHEMA) + + hass.services.register( + DOMAIN, SERVICE_TRIGGER, trigger_quick_action, + descriptions.get(SERVICE_TRIGGER), + 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.""" - abode.stop_listener() - abode.logout() + 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 startup(event): - """Listen for push events.""" - abode.start_listener() - hass.bus.listen_once(EVENT_HOMEASSISTANT_START, startup) +def setup_abode_events(hass): + """Event callbacks.""" + import abodepy.helpers.timeline as TIMELINE - return True + 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, controller, device): + def __init__(self, data, device): """Initialize a sensor for Abode device.""" - self._controller = controller + self._data = data self._device = device @asyncio.coroutine def async_added_to_hass(self): """Subscribe Abode events.""" self.hass.async_add_job( - self._controller.register, self._device, - self._update_callback + self._data.abode.events.add_device_callback, + self._device.device_id, self._update_callback ) @property def should_poll(self): """Return the polling state.""" - return False + return self._data.polling + + def update(self): + """Update automation state.""" + self._device.refresh() @property def name(self): @@ -124,3 +303,51 @@ class AbodeDevice(Entity): 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 + + @asyncio.coroutine + 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: CONF_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() diff --git a/homeassistant/components/alarm_control_panel/abode.py b/homeassistant/components/alarm_control_panel/abode.py index 7a615ffc7bf..aa4e86a2318 100644 --- a/homeassistant/components/alarm_control_panel/abode.py +++ b/homeassistant/components/alarm_control_panel/abode.py @@ -7,7 +7,7 @@ https://home-assistant.io/components/alarm_control_panel.abode/ import logging from homeassistant.components.abode import ( - AbodeDevice, DATA_ABODE, DEFAULT_NAME, CONF_ATTRIBUTION) + AbodeDevice, DOMAIN as ABODE_DOMAIN, CONF_ATTRIBUTION) from homeassistant.components.alarm_control_panel import (AlarmControlPanel) from homeassistant.const import (ATTR_ATTRIBUTION, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED) @@ -22,18 +22,22 @@ ICON = 'mdi:security' def setup_platform(hass, config, add_devices, discovery_info=None): """Set up a sensor for an Abode device.""" - abode = hass.data[DATA_ABODE] + data = hass.data[ABODE_DOMAIN] - add_devices([AbodeAlarm(abode, abode.get_alarm())]) + alarm_devices = [AbodeAlarm(data, data.abode.get_alarm(), data.name)] + + data.devices.extend(alarm_devices) + + add_devices(alarm_devices) class AbodeAlarm(AbodeDevice, AlarmControlPanel): """An alarm_control_panel implementation for Abode.""" - def __init__(self, controller, device): + def __init__(self, data, device, name): """Initialize the alarm control panel.""" - AbodeDevice.__init__(self, controller, device) - self._name = "{0}".format(DEFAULT_NAME) + super().__init__(data, device) + self._name = name @property def icon(self): @@ -65,6 +69,11 @@ class AbodeAlarm(AbodeDevice, AlarmControlPanel): """Send arm away command.""" self._device.set_away() + @property + def name(self): + """Return the name of the alarm.""" + return self._name or super().name + @property def device_state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/binary_sensor/abode.py b/homeassistant/components/binary_sensor/abode.py index d3b0d662a94..8ad40158958 100644 --- a/homeassistant/components/binary_sensor/abode.py +++ b/homeassistant/components/binary_sensor/abode.py @@ -6,7 +6,8 @@ https://home-assistant.io/components/binary_sensor.abode/ """ import logging -from homeassistant.components.abode import AbodeDevice, DATA_ABODE +from homeassistant.components.abode import (AbodeDevice, AbodeAutomation, + DOMAIN as ABODE_DOMAIN) from homeassistant.components.binary_sensor import BinarySensorDevice @@ -17,39 +18,38 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up a sensor for an Abode device.""" - abode = hass.data[DATA_ABODE] - - device_types = map_abode_device_class().keys() - - sensors = [] - for sensor in abode.get_devices(type_filter=device_types): - sensors.append(AbodeBinarySensor(abode, sensor)) - - add_devices(sensors) - - -def map_abode_device_class(): - """Map Abode device types to Home Assistant binary sensor class.""" import abodepy.helpers.constants as CONST + import abodepy.helpers.timeline as TIMELINE - return { - CONST.DEVICE_GLASS_BREAK: 'connectivity', - CONST.DEVICE_KEYPAD: 'connectivity', - CONST.DEVICE_DOOR_CONTACT: 'opening', - CONST.DEVICE_STATUS_DISPLAY: 'connectivity', - CONST.DEVICE_MOTION_CAMERA: 'connectivity', - CONST.DEVICE_WATER_SENSOR: 'moisture' - } + data = hass.data[ABODE_DOMAIN] + + device_types = [CONST.TYPE_CONNECTIVITY, CONST.TYPE_MOISTURE, + CONST.TYPE_MOTION, CONST.TYPE_OCCUPANCY, + CONST.TYPE_OPENING] + + devices = [] + for device in data.abode.get_devices(generic_type=device_types): + if data.is_excluded(device): + continue + + devices.append(AbodeBinarySensor(data, device)) + + for automation in data.abode.get_automations( + generic_type=CONST.TYPE_QUICK_ACTION): + if data.is_automation_excluded(automation): + continue + + devices.append(AbodeQuickActionBinarySensor( + data, automation, TIMELINE.AUTOMATION_EDIT_GROUP)) + + data.devices.extend(devices) + + add_devices(devices) class AbodeBinarySensor(AbodeDevice, BinarySensorDevice): """A binary sensor implementation for Abode device.""" - def __init__(self, controller, device): - """Initialize a sensor for Abode device.""" - AbodeDevice.__init__(self, controller, device) - self._device_class = map_abode_device_class().get(self._device.type) - @property def is_on(self): """Return True if the binary sensor is on.""" @@ -58,4 +58,17 @@ class AbodeBinarySensor(AbodeDevice, BinarySensorDevice): @property def device_class(self): """Return the class of the binary sensor.""" - return self._device_class + return self._device.generic_type + + +class AbodeQuickActionBinarySensor(AbodeAutomation, BinarySensorDevice): + """A binary sensor implementation for Abode quick action automations.""" + + def trigger(self): + """Trigger a quick automation.""" + self._automation.trigger() + + @property + def is_on(self): + """Return True if the binary sensor is on.""" + return self._automation.is_active diff --git a/homeassistant/components/camera/abode.py b/homeassistant/components/camera/abode.py new file mode 100644 index 00000000000..3c0c0a54e0e --- /dev/null +++ b/homeassistant/components/camera/abode.py @@ -0,0 +1,101 @@ +""" +This component provides HA camera support for Abode Security System. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/camera.abode/ +""" +import asyncio +import logging + +from datetime import timedelta +import requests + +from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN +from homeassistant.components.camera import Camera +from homeassistant.util import Throttle + + +DEPENDENCIES = ['abode'] + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discoveryy_info=None): + """Set up Abode camera devices.""" + import abodepy.helpers.constants as CONST + import abodepy.helpers.timeline as TIMELINE + + data = hass.data[ABODE_DOMAIN] + + devices = [] + for device in data.abode.get_devices(generic_type=CONST.TYPE_CAMERA): + if data.is_excluded(device): + continue + + devices.append(AbodeCamera(data, device, TIMELINE.CAPTURE_IMAGE)) + + data.devices.extend(devices) + + add_devices(devices) + + +class AbodeCamera(AbodeDevice, Camera): + """Representation of an Abode camera.""" + + def __init__(self, data, device, event): + """Initialize the Abode device.""" + AbodeDevice.__init__(self, data, device) + Camera.__init__(self) + self._event = event + self._response = None + + @asyncio.coroutine + def async_added_to_hass(self): + """Subscribe Abode events.""" + yield from super().async_added_to_hass() + + self.hass.async_add_job( + self._data.abode.events.add_timeline_callback, + self._event, self._capture_callback + ) + + def capture(self): + """Request a new image capture.""" + return self._device.capture() + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def refresh_image(self): + """Find a new image on the timeline.""" + if self._device.refresh_image(): + self.get_image() + + def get_image(self): + """Attempt to download the most recent capture.""" + if self._device.image_url: + try: + self._response = requests.get( + self._device.image_url, stream=True) + + self._response.raise_for_status() + except requests.HTTPError as err: + _LOGGER.warning("Failed to get camera image: %s", err) + self._response = None + else: + self._response = None + + def camera_image(self): + """Get a camera image.""" + self.refresh_image() + + if self._response: + return self._response.content + + return None + + def _capture_callback(self, capture): + """Update the image with the device then refresh device.""" + self._device.update_image_location(capture) + self.get_image() + self.schedule_update_ha_state() diff --git a/homeassistant/components/cover/abode.py b/homeassistant/components/cover/abode.py index b09c9e5e007..6eb0369aa3f 100644 --- a/homeassistant/components/cover/abode.py +++ b/homeassistant/components/cover/abode.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/cover.abode/ """ import logging -from homeassistant.components.abode import AbodeDevice, DATA_ABODE +from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN from homeassistant.components.cover import CoverDevice @@ -19,31 +19,32 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up Abode cover devices.""" import abodepy.helpers.constants as CONST - abode = hass.data[DATA_ABODE] + data = hass.data[ABODE_DOMAIN] - sensors = [] - for sensor in abode.get_devices(type_filter=(CONST.DEVICE_SECURE_BARRIER)): - sensors.append(AbodeCover(abode, sensor)) + devices = [] + for device in data.abode.get_devices(generic_type=CONST.TYPE_COVER): + if data.is_excluded(device): + continue - add_devices(sensors) + devices.append(AbodeCover(data, device)) + + data.devices.extend(devices) + + add_devices(devices) class AbodeCover(AbodeDevice, CoverDevice): """Representation of an Abode cover.""" - def __init__(self, controller, device): - """Initialize the Abode device.""" - AbodeDevice.__init__(self, controller, device) - @property def is_closed(self): """Return true if cover is closed, else False.""" - return self._device.is_open is False + return not self._device.is_open - def close_cover(self): + def close_cover(self, **kwargs): """Issue close command to cover.""" self._device.close_cover() - def open_cover(self): + def open_cover(self, **kwargs): """Issue open command to cover.""" self._device.open_cover() diff --git a/homeassistant/components/light/abode.py b/homeassistant/components/light/abode.py new file mode 100644 index 00000000000..d3e79b38647 --- /dev/null +++ b/homeassistant/components/light/abode.py @@ -0,0 +1,84 @@ +""" +This component provides HA light support for Abode Security System. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/light.abode/ +""" +import logging + +from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN +from homeassistant.components.light import ( + ATTR_BRIGHTNESS, ATTR_RGB_COLOR, + SUPPORT_BRIGHTNESS, SUPPORT_RGB_COLOR, Light) + + +DEPENDENCIES = ['abode'] + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up Abode light devices.""" + import abodepy.helpers.constants as CONST + + data = hass.data[ABODE_DOMAIN] + + device_types = [CONST.TYPE_LIGHT, CONST.TYPE_SWITCH] + + devices = [] + + # Get all regular lights that are not excluded or switches marked as lights + for device in data.abode.get_devices(generic_type=device_types): + if data.is_excluded(device) or not data.is_light(device): + continue + + devices.append(AbodeLight(data, device)) + + data.devices.extend(devices) + + add_devices(devices) + + +class AbodeLight(AbodeDevice, Light): + """Representation of an Abode light.""" + + def turn_on(self, **kwargs): + """Turn on the light.""" + if (ATTR_RGB_COLOR in kwargs and + self._device.is_dimmable and self._device.has_color): + self._device.set_color(kwargs[ATTR_RGB_COLOR]) + elif ATTR_BRIGHTNESS in kwargs and self._device.is_dimmable: + self._device.set_level(kwargs[ATTR_BRIGHTNESS]) + else: + self._device.switch_on() + + def turn_off(self, **kwargs): + """Turn off the light.""" + self._device.switch_off() + + @property + def is_on(self): + """Return true if device is on.""" + return self._device.is_on + + @property + def brightness(self): + """Return the brightness of the light.""" + if self._device.is_dimmable and self._device.has_brightness: + return self._device.brightness + + @property + def rgb_color(self): + """Return the color of the light.""" + if self._device.is_dimmable and self._device.has_color: + return self._device.color + + @property + def supported_features(self): + """Flag supported features.""" + if self._device.is_dimmable and self._device.has_color: + return SUPPORT_BRIGHTNESS | SUPPORT_RGB_COLOR + elif self._device.is_dimmable: + return SUPPORT_BRIGHTNESS + + return 0 diff --git a/homeassistant/components/lock/abode.py b/homeassistant/components/lock/abode.py index aad720e0d7d..2d342326636 100644 --- a/homeassistant/components/lock/abode.py +++ b/homeassistant/components/lock/abode.py @@ -6,7 +6,7 @@ https://home-assistant.io/components/lock.abode/ """ import logging -from homeassistant.components.abode import AbodeDevice, DATA_ABODE +from homeassistant.components.abode import AbodeDevice, DOMAIN as ABODE_DOMAIN from homeassistant.components.lock import LockDevice @@ -19,22 +19,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up Abode lock devices.""" import abodepy.helpers.constants as CONST - abode = hass.data[DATA_ABODE] + data = hass.data[ABODE_DOMAIN] - sensors = [] - for sensor in abode.get_devices(type_filter=(CONST.DEVICE_DOOR_LOCK)): - sensors.append(AbodeLock(abode, sensor)) + devices = [] + for device in data.abode.get_devices(generic_type=CONST.TYPE_LOCK): + if data.is_excluded(device): + continue - add_devices(sensors) + devices.append(AbodeLock(data, device)) + + data.devices.extend(devices) + + add_devices(devices) class AbodeLock(AbodeDevice, LockDevice): """Representation of an Abode lock.""" - def __init__(self, controller, device): - """Initialize the Abode device.""" - AbodeDevice.__init__(self, controller, device) - def lock(self, **kwargs): """Lock the device.""" self._device.lock() diff --git a/homeassistant/components/services.yaml b/homeassistant/components/services.yaml index 5428155acc4..545a883be8f 100644 --- a/homeassistant/components/services.yaml +++ b/homeassistant/components/services.yaml @@ -571,3 +571,32 @@ counter: entity_id: description: Entity id of the counter to reset. example: 'counter.count0' + +abode: + change_setting: + description: Change an Abode system setting. + + fields: + setting: + description: Setting to change. + example: 'beeper_mute' + + value: + description: Value of the setting. + example: '1' + + capture_image: + description: Request a new image capture from a camera device. + + fields: + entity_id: + description: Entity id of the camera to request an image. + example: 'camera.downstairs_motion_camera' + + trigger_quick_action: + description: Trigger an Abode quick action. + + fields: + entity_id: + description: Entity id of the quick action to trigger. + example: 'binary_sensor.home_quick_action' diff --git a/homeassistant/components/switch/abode.py b/homeassistant/components/switch/abode.py index bed0b9c0b60..63fe6b9f7b8 100644 --- a/homeassistant/components/switch/abode.py +++ b/homeassistant/components/switch/abode.py @@ -6,7 +6,8 @@ https://home-assistant.io/components/switch.abode/ """ import logging -from homeassistant.components.abode import AbodeDevice, DATA_ABODE +from homeassistant.components.abode import (AbodeDevice, AbodeAutomation, + DOMAIN as ABODE_DOMAIN) from homeassistant.components.switch import SwitchDevice @@ -18,27 +19,36 @@ _LOGGER = logging.getLogger(__name__) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up Abode switch devices.""" import abodepy.helpers.constants as CONST + import abodepy.helpers.timeline as TIMELINE - abode = hass.data[DATA_ABODE] + data = hass.data[ABODE_DOMAIN] - device_types = [ - CONST.DEVICE_POWER_SWITCH_SENSOR, - CONST.DEVICE_POWER_SWITCH_METER] + devices = [] - sensors = [] - for sensor in abode.get_devices(type_filter=device_types): - sensors.append(AbodeSwitch(abode, sensor)) + # Get all regular switches that are not excluded or marked as lights + for device in data.abode.get_devices(generic_type=CONST.TYPE_SWITCH): + if data.is_excluded(device) or not data.is_light(device): + continue - add_devices(sensors) + devices.append(AbodeSwitch(data, device)) + + # Get all Abode automations that can be enabled/disabled + for automation in data.abode.get_automations( + generic_type=CONST.TYPE_AUTOMATION): + if data.is_automation_excluded(automation): + continue + + devices.append(AbodeAutomationSwitch( + data, automation, TIMELINE.AUTOMATION_EDIT_GROUP)) + + data.devices.extend(devices) + + add_devices(devices) class AbodeSwitch(AbodeDevice, SwitchDevice): """Representation of an Abode switch.""" - def __init__(self, controller, device): - """Initialize the Abode device.""" - AbodeDevice.__init__(self, controller, device) - def turn_on(self, **kwargs): """Turn on the device.""" self._device.switch_on() @@ -51,3 +61,20 @@ class AbodeSwitch(AbodeDevice, SwitchDevice): def is_on(self): """Return true if device is on.""" return self._device.is_on + + +class AbodeAutomationSwitch(AbodeAutomation, SwitchDevice): + """A switch implementation for Abode automations.""" + + def turn_on(self, **kwargs): + """Turn on the device.""" + self._automation.set_active(True) + + def turn_off(self, **kwargs): + """Turn off the device.""" + self._automation.set_active(False) + + @property + def is_on(self): + """Return True if the binary sensor is on.""" + return self._automation.is_active diff --git a/requirements_all.txt b/requirements_all.txt index 32c941a9384..7e88616f673 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -42,7 +42,7 @@ SoCo==0.12 TwitterAPI==2.4.6 # homeassistant.components.abode -abodepy==0.9.0 +abodepy==0.11.5 # homeassistant.components.device_tracker.automatic aioautomatic==0.6.3