From 1a117d0bea557bf5b537fed68f4951178209dfb6 Mon Sep 17 00:00:00 2001 From: jnimmo Date: Fri, 18 Nov 2016 19:13:22 +1300 Subject: [PATCH] Add keypress & output control services to Envisalink component (#3932) * Add keypress & output control services to Envisalink component Add services to allow sending custom keypresses and activating programmable outputs on an alarm control panel. Implemented for the Envisalink alarm, and moving to new version of pyenvisalink to support this. Replicated the service handler mapping code from Cover component into Alarm Control Panel to allow handling alternative schemas if required by new services. * Update requirements_all.txt * Updated services.yaml * Removed requirement to enter code in HA UI Incorporated changes suggested by @sriram https://github.com/srirams/home-assistant/commit/2f8deb70cb5f3621a69b6b9 acb72f8e29123650c Including pending state for exit/entry delay Clarified services to use the code passed to them as a first priority, otherwise use the code from configuration Swapped back to using NotImplementedError for the service definitions * - Add support for alarm_keypress to manual alarm (functions like a standard alarm keypad where entering the code disarms or arms the alarm) - Add tests for alarm_keypress to manual alarm - Style corrections (too many returns, comment & whitespace issues) * Removed alarm_output_control service as unable to incorporate in the demo/test in a meaningful way * Add keypress & output control services to Envisalink component Add services to allow sending custom keypresses and activating programmable outputs on an alarm control panel. Implemented for the Envisalink alarm, and moving to new version of pyenvisalink to support this. Replicated the service handler mapping code from Cover component into Alarm Control Panel to allow handling alternative schemas if required by new services. * Update requirements_all.txt * Updated services.yaml * Removed requirement to enter code in HA UI Incorporated changes suggested by @sriram https://github.com/srirams/home-assistant/commit/2f8deb70cb5f3621a69b6b9 acb72f8e29123650c Including pending state for exit/entry delay Clarified services to use the code passed to them as a first priority, otherwise use the code from configuration Swapped back to using NotImplementedError for the service definitions * - Add support for alarm_keypress to manual alarm (functions like a standard alarm keypad where entering the code disarms or arms the alarm) - Add tests for alarm_keypress to manual alarm - Style corrections (too many returns, comment & whitespace issues) * Removed alarm_output_control service as unable to incorporate in the demo/test in a meaningful way * Moved the Alarm_Keypress service into Envisalink component out of the generic * Update envisalink.py * Update services.yaml --- .../alarm_control_panel/envisalink.py | 99 +++++++++++++++---- .../alarm_control_panel/services.yaml | 11 +++ homeassistant/components/envisalink.py | 2 +- requirements_all.txt | 2 +- 4 files changed, 91 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/envisalink.py b/homeassistant/components/alarm_control_panel/envisalink.py index fa013cd3ffe..e84320738a2 100644 --- a/homeassistant/components/alarm_control_panel/envisalink.py +++ b/homeassistant/components/alarm_control_panel/envisalink.py @@ -4,20 +4,45 @@ Support for Envisalink-based alarm control panels (Honeywell/DSC). For more details about this platform, please refer to the documentation at https://home-assistant.io/components/alarm_control_panel.envisalink/ """ +from os import path import logging +import voluptuous as vol import homeassistant.components.alarm_control_panel as alarm +import homeassistant.helpers.config_validation as cv +from homeassistant.config import load_yaml_config_file from homeassistant.components.envisalink import ( EVL_CONTROLLER, EnvisalinkDevice, PARTITION_SCHEMA, CONF_CODE, CONF_PANIC, CONF_PARTITIONNAME, SIGNAL_PARTITION_UPDATE, SIGNAL_KEYPAD_UPDATE) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED, - STATE_UNKNOWN, STATE_ALARM_TRIGGERED) + STATE_UNKNOWN, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, ATTR_ENTITY_ID) _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['envisalink'] +DEVICES = [] + +SERVICE_ALARM_KEYPRESS = 'envisalink_alarm_keypress' +ATTR_KEYPRESS = 'keypress' +ALARM_KEYPRESS_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_KEYPRESS): cv.string +}) + + +def alarm_keypress_handler(service): + """Map services to methods on Alarm.""" + entity_ids = service.data.get(ATTR_ENTITY_ID) + keypress = service.data.get(ATTR_KEYPRESS) + + _target_devices = [device for device in DEVICES + if device.entity_id in entity_ids] + + for device in _target_devices: + EnvisalinkAlarm.alarm_keypress(device, keypress) + # pylint: disable=unused-argument def setup_platform(hass, config, add_devices, discovery_info=None): @@ -35,8 +60,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): _panic_type, EVL_CONTROLLER.alarm_state['partition'][part_num], EVL_CONTROLLER) - add_devices([_device]) + DEVICES.append(_device) + add_devices(DEVICES) + + # Register Envisalink specific services + descriptions = load_yaml_config_file( + path.join(path.dirname(__file__), 'services.yaml')) + + hass.services.register(alarm.DOMAIN, SERVICE_ALARM_KEYPRESS, + alarm_keypress_handler, + descriptions.get(SERVICE_ALARM_KEYPRESS), + schema=ALARM_KEYPRESS_SCHEMA) return True @@ -66,42 +101,64 @@ class EnvisalinkAlarm(EnvisalinkDevice, alarm.AlarmControlPanel): @property def code_format(self): - """The characters if code is defined.""" - return self._code + """Regex for code format or None if no code is required.""" + if self._code: + return None + else: + return '^\\d{4,6}$' @property def state(self): """Return the state of the device.""" + state = STATE_UNKNOWN + if self._info['status']['alarm']: - return STATE_ALARM_TRIGGERED + state = STATE_ALARM_TRIGGERED elif self._info['status']['armed_away']: - return STATE_ALARM_ARMED_AWAY + state = STATE_ALARM_ARMED_AWAY elif self._info['status']['armed_stay']: - return STATE_ALARM_ARMED_HOME + state = STATE_ALARM_ARMED_HOME + elif self._info['status']['exit_delay']: + state = STATE_ALARM_PENDING + elif self._info['status']['entry_delay']: + state = STATE_ALARM_PENDING elif self._info['status']['alpha']: - return STATE_ALARM_DISARMED - else: - return STATE_UNKNOWN + state = STATE_ALARM_DISARMED + return state def alarm_disarm(self, code=None): """Send disarm command.""" - if self._code: - EVL_CONTROLLER.disarm_partition( - str(code), self._partition_number) + if code: + EVL_CONTROLLER.disarm_partition(str(code), + self._partition_number) + else: + EVL_CONTROLLER.disarm_partition(str(self._code), + self._partition_number) def alarm_arm_home(self, code=None): """Send arm home command.""" - if self._code: - EVL_CONTROLLER.arm_stay_partition( - str(code), self._partition_number) + if code: + EVL_CONTROLLER.arm_stay_partition(str(code), + self._partition_number) + else: + EVL_CONTROLLER.arm_stay_partition(str(self._code), + self._partition_number) def alarm_arm_away(self, code=None): """Send arm away command.""" - if self._code: - EVL_CONTROLLER.arm_away_partition( - str(code), self._partition_number) + if code: + EVL_CONTROLLER.arm_away_partition(str(code), + self._partition_number) + else: + EVL_CONTROLLER.arm_away_partition(str(self._code), + self._partition_number) def alarm_trigger(self, code=None): """Alarm trigger command. Will be used to trigger a panic alarm.""" - if self._code: - EVL_CONTROLLER.panic_alarm(self._panic_type) + EVL_CONTROLLER.panic_alarm(self._panic_type) + + def alarm_keypress(self, keypress=None): + """Send custom keypress.""" + if keypress: + EVL_CONTROLLER.keypresses_to_partition(self._partition_number, + keypress) diff --git a/homeassistant/components/alarm_control_panel/services.yaml b/homeassistant/components/alarm_control_panel/services.yaml index 40188e32d99..6cc3946ca66 100644 --- a/homeassistant/components/alarm_control_panel/services.yaml +++ b/homeassistant/components/alarm_control_panel/services.yaml @@ -41,3 +41,14 @@ alarm_trigger: code: description: An optional code to trigger the alarm control panel with example: 1234 + +envisalink_alarm_keypress: + description: Send custom keypresses to the alarm + + fields: + entity_id: + description: Name of the alarm control panel to trigger + example: 'alarm_control_panel.downstairs' + keypress: + description: 'String to send to the alarm panel (1-6 characters)' + example: '*71' diff --git a/homeassistant/components/envisalink.py b/homeassistant/components/envisalink.py index 21bc081224b..29ce08b2f0a 100644 --- a/homeassistant/components/envisalink.py +++ b/homeassistant/components/envisalink.py @@ -12,7 +12,7 @@ from homeassistant.const import EVENT_HOMEASSISTANT_STOP from homeassistant.helpers.entity import Entity from homeassistant.components.discovery import load_platform -REQUIREMENTS = ['pyenvisalink==1.7', 'pydispatcher==2.0.5'] +REQUIREMENTS = ['pyenvisalink==1.9', 'pydispatcher==2.0.5'] _LOGGER = logging.getLogger(__name__) DOMAIN = 'envisalink' diff --git a/requirements_all.txt b/requirements_all.txt index eea460ad5dc..ade99399154 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -363,7 +363,7 @@ pydispatcher==2.0.5 pyemby==0.1 # homeassistant.components.envisalink -pyenvisalink==1.7 +pyenvisalink==1.9 # homeassistant.components.ifttt pyfttt==0.3