From d1beb92c5dad24ae31366b7a2755c5b4206b70e2 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 4 Mar 2020 14:54:28 -0800 Subject: [PATCH] Add abode support for CUE automations (#32296) * Add support for CUE automations * Update requirements * Minor update to string name --- homeassistant/components/abode/__init__.py | 37 ++++++++---------- .../components/abode/alarm_control_panel.py | 9 +++-- .../components/abode/binary_sensor.py | 34 +--------------- homeassistant/components/abode/camera.py | 7 +--- homeassistant/components/abode/config_flow.py | 11 ++---- homeassistant/components/abode/const.py | 4 ++ homeassistant/components/abode/cover.py | 4 -- homeassistant/components/abode/light.py | 3 -- homeassistant/components/abode/lock.py | 4 -- homeassistant/components/abode/manifest.json | 2 +- homeassistant/components/abode/sensor.py | 4 -- homeassistant/components/abode/services.yaml | 6 +-- homeassistant/components/abode/switch.py | 39 +++++++++++++------ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 15 files changed, 66 insertions(+), 102 deletions(-) diff --git a/homeassistant/components/abode/__init__.py b/homeassistant/components/abode/__init__.py index 4ce9a4faca0..666c8481bfb 100644 --- a/homeassistant/components/abode/__init__.py +++ b/homeassistant/components/abode/__init__.py @@ -2,7 +2,6 @@ from asyncio import gather from copy import deepcopy from functools import partial -import logging from abodepy import Abode from abodepy.exceptions import AbodeException @@ -24,15 +23,13 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.helpers.entity import Entity -from .const import ATTRIBUTION, DEFAULT_CACHEDB, DOMAIN - -_LOGGER = logging.getLogger(__name__) +from .const import ATTRIBUTION, DEFAULT_CACHEDB, DOMAIN, LOGGER CONF_POLLING = "polling" SERVICE_SETTINGS = "change_setting" SERVICE_CAPTURE_IMAGE = "capture_image" -SERVICE_TRIGGER = "trigger_quick_action" +SERVICE_TRIGGER_AUTOMATION = "trigger_automation" ATTR_DEVICE_ID = "device_id" ATTR_DEVICE_NAME = "device_name" @@ -47,8 +44,6 @@ ATTR_APP_TYPE = "app_type" ATTR_EVENT_BY = "event_by" ATTR_VALUE = "value" -ABODE_DEVICE_ID_LIST_SCHEMA = vol.Schema([str]) - CONFIG_SCHEMA = vol.Schema( { DOMAIN: vol.Schema( @@ -68,7 +63,7 @@ CHANGE_SETTING_SCHEMA = vol.Schema( CAPTURE_IMAGE_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids}) -TRIGGER_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids}) +AUTOMATION_SCHEMA = vol.Schema({ATTR_ENTITY_ID: cv.entity_ids}) ABODE_PLATFORMS = [ "alarm_control_panel", @@ -87,7 +82,6 @@ class AbodeSystem: def __init__(self, abode, polling): """Initialize the system.""" - self.abode = abode self.polling = polling self.entity_ids = set() @@ -124,7 +118,7 @@ async def async_setup_entry(hass, config_entry): hass.data[DOMAIN] = AbodeSystem(abode, polling) except (AbodeException, ConnectTimeout, HTTPError) as ex: - _LOGGER.error("Unable to connect to Abode: %s", str(ex)) + LOGGER.error("Unable to connect to Abode: %s", str(ex)) return False for platform in ABODE_PLATFORMS: @@ -143,7 +137,7 @@ async def async_unload_entry(hass, config_entry): """Unload a config entry.""" hass.services.async_remove(DOMAIN, SERVICE_SETTINGS) hass.services.async_remove(DOMAIN, SERVICE_CAPTURE_IMAGE) - hass.services.async_remove(DOMAIN, SERVICE_TRIGGER) + hass.services.async_remove(DOMAIN, SERVICE_TRIGGER_AUTOMATION) tasks = [] @@ -174,7 +168,7 @@ def setup_hass_services(hass): try: hass.data[DOMAIN].abode.set_setting(setting, value) except AbodeException as ex: - _LOGGER.warning(ex) + LOGGER.warning(ex) def capture_image(call): """Capture a new image.""" @@ -190,8 +184,8 @@ def setup_hass_services(hass): signal = f"abode_camera_capture_{entity_id}" dispatcher_send(hass, signal) - def trigger_quick_action(call): - """Trigger a quick action.""" + def trigger_automation(call): + """Trigger an Abode automation.""" entity_ids = call.data.get(ATTR_ENTITY_ID, None) target_entities = [ @@ -201,7 +195,7 @@ def setup_hass_services(hass): ] for entity_id in target_entities: - signal = f"abode_trigger_quick_action_{entity_id}" + signal = f"abode_trigger_automation_{entity_id}" dispatcher_send(hass, signal) hass.services.register( @@ -213,7 +207,7 @@ def setup_hass_services(hass): ) hass.services.register( - DOMAIN, SERVICE_TRIGGER, trigger_quick_action, schema=TRIGGER_SCHEMA + DOMAIN, SERVICE_TRIGGER_AUTOMATION, trigger_automation, schema=AUTOMATION_SCHEMA ) @@ -226,7 +220,7 @@ async def setup_hass_events(hass): hass.data[DOMAIN].abode.events.stop() hass.data[DOMAIN].abode.logout() - _LOGGER.info("Logged out of Abode") + LOGGER.info("Logged out of Abode") if not hass.data[DOMAIN].polling: await hass.async_add_executor_job(hass.data[DOMAIN].abode.events.start) @@ -384,11 +378,14 @@ class AbodeAutomation(Entity): """Return the state attributes.""" return { ATTR_ATTRIBUTION: ATTRIBUTION, - "automation_id": self._automation.automation_id, - "type": self._automation.type, - "sub_type": self._automation.sub_type, + "type": "CUE automation", } + @property + def unique_id(self): + """Return a unique ID to use for this automation.""" + return self._automation.automation_id + def _update_callback(self, device): """Update the automation state.""" self._automation.refresh() diff --git a/homeassistant/components/abode/alarm_control_panel.py b/homeassistant/components/abode/alarm_control_panel.py index b9a0a8ce192..40040d90d0d 100644 --- a/homeassistant/components/abode/alarm_control_panel.py +++ b/homeassistant/components/abode/alarm_control_panel.py @@ -1,6 +1,4 @@ """Support for Abode Security System alarm control panels.""" -import logging - import homeassistant.components.alarm_control_panel as alarm from homeassistant.components.alarm_control_panel.const import ( SUPPORT_ALARM_ARM_AWAY, @@ -16,8 +14,6 @@ from homeassistant.const import ( from . import AbodeDevice from .const import ATTRIBUTION, DOMAIN -_LOGGER = logging.getLogger(__name__) - ICON = "mdi:security" @@ -50,6 +46,11 @@ class AbodeAlarm(AbodeDevice, alarm.AlarmControlPanel): state = None return state + @property + def code_arm_required(self): + """Whether the code is required for arm actions.""" + return False + @property def supported_features(self) -> int: """Return the list of supported features.""" diff --git a/homeassistant/components/abode/binary_sensor.py b/homeassistant/components/abode/binary_sensor.py index 7d4474437e9..c4cdadf9bd9 100644 --- a/homeassistant/components/abode/binary_sensor.py +++ b/homeassistant/components/abode/binary_sensor.py @@ -1,17 +1,11 @@ """Support for Abode Security System binary sensors.""" -import logging - import abodepy.helpers.constants as CONST -import abodepy.helpers.timeline as TIMELINE from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.helpers.dispatcher import async_dispatcher_connect -from . import AbodeAutomation, AbodeDevice +from . import AbodeDevice from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Abode binary sensor devices.""" @@ -30,13 +24,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device in data.abode.get_devices(generic_type=device_types): entities.append(AbodeBinarySensor(data, device)) - for automation in data.abode.get_automations(generic_type=CONST.TYPE_QUICK_ACTION): - entities.append( - AbodeQuickActionBinarySensor( - data, automation, TIMELINE.AUTOMATION_EDIT_GROUP - ) - ) - async_add_entities(entities) @@ -52,22 +39,3 @@ class AbodeBinarySensor(AbodeDevice, BinarySensorDevice): def device_class(self): """Return the class of the binary sensor.""" return self._device.generic_type - - -class AbodeQuickActionBinarySensor(AbodeAutomation, BinarySensorDevice): - """A binary sensor implementation for Abode quick action automations.""" - - async def async_added_to_hass(self): - """Subscribe Abode events.""" - await super().async_added_to_hass() - signal = f"abode_trigger_quick_action_{self.entity_id}" - async_dispatcher_connect(self.hass, signal, self.trigger) - - 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/abode/camera.py b/homeassistant/components/abode/camera.py index edf29c4a198..bee73644890 100644 --- a/homeassistant/components/abode/camera.py +++ b/homeassistant/components/abode/camera.py @@ -1,6 +1,5 @@ """Support for Abode Security System cameras.""" from datetime import timedelta -import logging import abodepy.helpers.constants as CONST import abodepy.helpers.timeline as TIMELINE @@ -11,12 +10,10 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.util import Throttle from . import AbodeDevice -from .const import DOMAIN +from .const import DOMAIN, LOGGER MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=90) -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Abode camera devices.""" @@ -71,7 +68,7 @@ class AbodeCamera(AbodeDevice, Camera): self._response.raise_for_status() except requests.HTTPError as err: - _LOGGER.warning("Failed to get camera image: %s", err) + LOGGER.warning("Failed to get camera image: %s", err) self._response = None else: self._response = None diff --git a/homeassistant/components/abode/config_flow.py b/homeassistant/components/abode/config_flow.py index 89b389798f6..5c2c5e7b843 100644 --- a/homeassistant/components/abode/config_flow.py +++ b/homeassistant/components/abode/config_flow.py @@ -1,6 +1,4 @@ """Config flow for the Abode Security System component.""" -import logging - from abodepy import Abode from abodepy.exceptions import AbodeException from requests.exceptions import ConnectTimeout, HTTPError @@ -10,12 +8,10 @@ from homeassistant import config_entries from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import callback -from .const import DEFAULT_CACHEDB, DOMAIN # pylint: disable=unused-import +from .const import DEFAULT_CACHEDB, DOMAIN, LOGGER # pylint: disable=unused-import CONF_POLLING = "polling" -_LOGGER = logging.getLogger(__name__) - class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for Abode.""" @@ -32,7 +28,6 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" - if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") @@ -50,7 +45,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): ) except (AbodeException, ConnectTimeout, HTTPError) as ex: - _LOGGER.error("Unable to connect to Abode: %s", str(ex)) + LOGGER.error("Unable to connect to Abode: %s", str(ex)) if ex.errcode == 400: return self._show_form({"base": "invalid_credentials"}) return self._show_form({"base": "connection_error"}) @@ -76,7 +71,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): async def async_step_import(self, import_config): """Import a config entry from configuration.yaml.""" if self._async_current_entries(): - _LOGGER.warning("Only one configuration of abode is allowed.") + LOGGER.warning("Only one configuration of abode is allowed.") return self.async_abort(reason="single_instance_allowed") return await self.async_step_user(import_config) diff --git a/homeassistant/components/abode/const.py b/homeassistant/components/abode/const.py index 092843ba212..b509984876b 100644 --- a/homeassistant/components/abode/const.py +++ b/homeassistant/components/abode/const.py @@ -1,4 +1,8 @@ """Constants for the Abode Security System component.""" +import logging + +LOGGER = logging.getLogger(__package__) + DOMAIN = "abode" ATTRIBUTION = "Data provided by goabode.com" diff --git a/homeassistant/components/abode/cover.py b/homeassistant/components/abode/cover.py index ec4f54a985c..6e38c11cfcc 100644 --- a/homeassistant/components/abode/cover.py +++ b/homeassistant/components/abode/cover.py @@ -1,6 +1,4 @@ """Support for Abode Security System covers.""" -import logging - import abodepy.helpers.constants as CONST from homeassistant.components.cover import CoverDevice @@ -8,8 +6,6 @@ from homeassistant.components.cover import CoverDevice from . import AbodeDevice from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Abode cover devices.""" diff --git a/homeassistant/components/abode/light.py b/homeassistant/components/abode/light.py index ad2df23ef9c..7602768a6e3 100644 --- a/homeassistant/components/abode/light.py +++ b/homeassistant/components/abode/light.py @@ -1,5 +1,4 @@ """Support for Abode Security System lights.""" -import logging from math import ceil import abodepy.helpers.constants as CONST @@ -21,8 +20,6 @@ from homeassistant.util.color import ( from . import AbodeDevice from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Abode light devices.""" diff --git a/homeassistant/components/abode/lock.py b/homeassistant/components/abode/lock.py index b05a3e7f297..33431433ef9 100644 --- a/homeassistant/components/abode/lock.py +++ b/homeassistant/components/abode/lock.py @@ -1,6 +1,4 @@ """Support for the Abode Security System locks.""" -import logging - import abodepy.helpers.constants as CONST from homeassistant.components.lock import LockDevice @@ -8,8 +6,6 @@ from homeassistant.components.lock import LockDevice from . import AbodeDevice from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Abode lock devices.""" diff --git a/homeassistant/components/abode/manifest.json b/homeassistant/components/abode/manifest.json index 383320141e5..eabd4a7f74f 100644 --- a/homeassistant/components/abode/manifest.json +++ b/homeassistant/components/abode/manifest.json @@ -3,7 +3,7 @@ "name": "Abode", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/abode", - "requirements": ["abodepy==0.17.0"], + "requirements": ["abodepy==0.18.1"], "dependencies": [], "codeowners": ["@shred86"] } diff --git a/homeassistant/components/abode/sensor.py b/homeassistant/components/abode/sensor.py index afa5e372222..6ecc5c871cd 100644 --- a/homeassistant/components/abode/sensor.py +++ b/homeassistant/components/abode/sensor.py @@ -1,6 +1,4 @@ """Support for Abode Security System sensors.""" -import logging - import abodepy.helpers.constants as CONST from homeassistant.const import ( @@ -12,8 +10,6 @@ from homeassistant.const import ( from . import AbodeDevice from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - # Sensor types: Name, icon SENSOR_TYPES = { CONST.TEMP_STATUS_KEY: ["Temperature", DEVICE_CLASS_TEMPERATURE], diff --git a/homeassistant/components/abode/services.yaml b/homeassistant/components/abode/services.yaml index ad0bb076d90..5818cdc0048 100644 --- a/homeassistant/components/abode/services.yaml +++ b/homeassistant/components/abode/services.yaml @@ -7,7 +7,7 @@ change_setting: fields: setting: {description: Setting to change., example: beeper_mute} value: {description: Value of the setting., example: '1'} -trigger_quick_action: - description: Trigger an Abode quick action. +trigger_automation: + description: Trigger an Abode automation. fields: - entity_id: {description: Entity id of the quick action to trigger., example: binary_sensor.home_quick_action} + entity_id: {description: Entity id of the automation to trigger., example: switch.my_automation} \ No newline at end of file diff --git a/homeassistant/components/abode/switch.py b/homeassistant/components/abode/switch.py index d6773e10ca1..e29deb72f82 100644 --- a/homeassistant/components/abode/switch.py +++ b/homeassistant/components/abode/switch.py @@ -1,18 +1,17 @@ """Support for Abode Security System switches.""" -import logging - import abodepy.helpers.constants as CONST import abodepy.helpers.timeline as TIMELINE from homeassistant.components.switch import SwitchDevice +from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import AbodeAutomation, AbodeDevice from .const import DOMAIN -_LOGGER = logging.getLogger(__name__) - DEVICE_TYPES = [CONST.TYPE_SWITCH, CONST.TYPE_VALVE] +ICON = "mdi:robot" + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up Abode switch devices.""" @@ -24,7 +23,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for device in data.abode.get_devices(generic_type=device_type): entities.append(AbodeSwitch(data, device)) - for automation in data.abode.get_automations(generic_type=CONST.TYPE_AUTOMATION): + for automation in data.abode.get_automations(): entities.append( AbodeAutomationSwitch(data, automation, TIMELINE.AUTOMATION_EDIT_GROUP) ) @@ -52,15 +51,33 @@ class AbodeSwitch(AbodeDevice, SwitchDevice): class AbodeAutomationSwitch(AbodeAutomation, SwitchDevice): """A switch implementation for Abode automations.""" + async def async_added_to_hass(self): + """Subscribe Abode events.""" + await super().async_added_to_hass() + + signal = f"abode_trigger_automation_{self.entity_id}" + async_dispatcher_connect(self.hass, signal, self.trigger) + def turn_on(self, **kwargs): - """Turn on the device.""" - self._automation.set_active(True) + """Enable the automation.""" + if self._automation.enable(True): + self.schedule_update_ha_state() def turn_off(self, **kwargs): - """Turn off the device.""" - self._automation.set_active(False) + """Disable the automation.""" + if self._automation.enable(False): + self.schedule_update_ha_state() + + def trigger(self): + """Trigger the automation.""" + self._automation.trigger() @property def is_on(self): - """Return True if the binary sensor is on.""" - return self._automation.is_active + """Return True if the automation is enabled.""" + return self._automation.is_enabled + + @property + def icon(self): + """Return the robot icon to match Home Assistant automations.""" + return ICON diff --git a/requirements_all.txt b/requirements_all.txt index 0a107eb34b2..3adff8a4743 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -106,7 +106,7 @@ WazeRouteCalculator==0.12 YesssSMS==0.4.1 # homeassistant.components.abode -abodepy==0.17.0 +abodepy==0.18.1 # homeassistant.components.mcp23017 adafruit-blinka==3.9.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1b954093028..c7f59369b26 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -26,7 +26,7 @@ RtmAPI==0.7.2 YesssSMS==0.4.1 # homeassistant.components.abode -abodepy==0.17.0 +abodepy==0.18.1 # homeassistant.components.androidtv adb-shell==0.1.1