diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index 36a68eda174..47f92bfe641 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -19,6 +19,7 @@ SCAN_INTERVAL = timedelta(seconds=30) ATTR_CHANGED_BY = 'changed_by' FORMAT_TEXT = 'text' FORMAT_NUMBER = 'number' +ATTR_CODE_ARM_REQUIRED = 'code_arm_required' ENTITY_ID_FORMAT = DOMAIN + '.{}' @@ -87,6 +88,11 @@ class AlarmControlPanel(Entity): """Last change triggered by.""" return None + @property + def code_arm_required(self): + """Whether the code is required for arm actions.""" + return True + def alarm_disarm(self, code=None): """Send disarm command.""" raise NotImplementedError() @@ -159,6 +165,7 @@ class AlarmControlPanel(Entity): """Return the state attributes.""" state_attr = { ATTR_CODE_FORMAT: self.code_format, - ATTR_CHANGED_BY: self.changed_by + ATTR_CHANGED_BY: self.changed_by, + ATTR_CODE_ARM_REQUIRED: self.code_arm_required } return state_attr diff --git a/homeassistant/components/manual_mqtt/alarm_control_panel.py b/homeassistant/components/manual_mqtt/alarm_control_panel.py index d952dd68ebb..c051ce47173 100644 --- a/homeassistant/components/manual_mqtt/alarm_control_panel.py +++ b/homeassistant/components/manual_mqtt/alarm_control_panel.py @@ -24,6 +24,7 @@ from homeassistant.helpers.event import track_point_in_time _LOGGER = logging.getLogger(__name__) CONF_CODE_TEMPLATE = 'code_template' +CONF_CODE_ARM_REQUIRED = 'code_arm_required' CONF_PAYLOAD_DISARM = 'payload_disarm' CONF_PAYLOAD_ARM_HOME = 'payload_arm_home' @@ -108,6 +109,7 @@ PLATFORM_SCHEMA = vol.Schema(vol.All(mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend({ _state_schema(STATE_ALARM_TRIGGERED), vol.Required(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Required(mqtt.CONF_STATE_TOPIC): mqtt.valid_subscribe_topic, + vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean, vol.Optional(CONF_PAYLOAD_ARM_AWAY, default=DEFAULT_ARM_AWAY): cv.string, vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): cv.string, vol.Optional(CONF_PAYLOAD_ARM_NIGHT, default=DEFAULT_ARM_NIGHT): cv.string, @@ -126,6 +128,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None): config.get(mqtt.CONF_STATE_TOPIC), config.get(mqtt.CONF_COMMAND_TOPIC), config.get(mqtt.CONF_QOS), + config.get(CONF_CODE_ARM_REQUIRED), config.get(CONF_PAYLOAD_DISARM), config.get(CONF_PAYLOAD_ARM_HOME), config.get(CONF_PAYLOAD_ARM_AWAY), @@ -146,9 +149,9 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel): """ def __init__(self, hass, name, code, code_template, disarm_after_trigger, - state_topic, command_topic, qos, payload_disarm, - payload_arm_home, payload_arm_away, payload_arm_night, - config): + state_topic, command_topic, qos, code_arm_required, + payload_disarm, payload_arm_home, payload_arm_away, + payload_arm_night, config): """Init the manual MQTT alarm panel.""" self._state = STATE_ALARM_DISARMED self._hass = hass @@ -175,6 +178,7 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel): self._state_topic = state_topic self._command_topic = command_topic self._qos = qos + self._code_arm_required = code_arm_required self._payload_disarm = payload_disarm self._payload_arm_home = payload_arm_home self._payload_arm_away = payload_arm_away @@ -237,6 +241,11 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel): return alarm.FORMAT_NUMBER return alarm.FORMAT_TEXT + @property + def code_arm_required(self): + """Whether the code is required for arm actions.""" + return self._code_arm_required + def alarm_disarm(self, code=None): """Send disarm command.""" if not self._validate_code(code, STATE_ALARM_DISARMED): @@ -248,21 +257,24 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel): def alarm_arm_home(self, code=None): """Send arm home command.""" - if not self._validate_code(code, STATE_ALARM_ARMED_HOME): + if self._code_arm_required and not \ + self._validate_code(code, STATE_ALARM_ARMED_HOME): return self._update_state(STATE_ALARM_ARMED_HOME) def alarm_arm_away(self, code=None): """Send arm away command.""" - if not self._validate_code(code, STATE_ALARM_ARMED_AWAY): + if self._code_arm_required and not \ + self._validate_code(code, STATE_ALARM_ARMED_AWAY): return self._update_state(STATE_ALARM_ARMED_AWAY) def alarm_arm_night(self, code=None): """Send arm night command.""" - if not self._validate_code(code, STATE_ALARM_ARMED_NIGHT): + if self._code_arm_required and not \ + self._validate_code(code, STATE_ALARM_ARMED_NIGHT): return self._update_state(STATE_ALARM_ARMED_NIGHT) diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index da3e2faf224..9827c7c4df9 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -192,6 +192,12 @@ class MqttAlarm(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate, return alarm.FORMAT_NUMBER return alarm.FORMAT_TEXT + @property + def code_arm_required(self): + """Whether the code is required for arm actions.""" + code_required = self._config.get(CONF_CODE_ARM_REQUIRED) + return code_required + async def async_alarm_disarm(self, code=None): """Send disarm command. diff --git a/tests/components/manual_mqtt/test_alarm_control_panel.py b/tests/components/manual_mqtt/test_alarm_control_panel.py index f5558331bce..a2004140141 100644 --- a/tests/components/manual_mqtt/test_alarm_control_panel.py +++ b/tests/components/manual_mqtt/test_alarm_control_panel.py @@ -77,6 +77,32 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): assert STATE_ALARM_ARMED_HOME == \ self.hass.states.get(entity_id).state + def test_arm_home_no_pending_when_code_not_req(self): + """Test arm home method.""" + assert setup_component( + self.hass, alarm_control_panel.DOMAIN, + {'alarm_control_panel': { + 'platform': 'manual_mqtt', + 'name': 'test', + 'code': CODE, + 'code_arm_required': False, + 'pending_time': 0, + 'disarm_after_trigger': False, + 'command_topic': 'alarm/command', + 'state_topic': 'alarm/state', + }}) + + entity_id = 'alarm_control_panel.test' + + assert STATE_ALARM_DISARMED == \ + self.hass.states.get(entity_id).state + + common.alarm_arm_home(self.hass, 0) + self.hass.block_till_done() + + assert STATE_ALARM_ARMED_HOME == \ + self.hass.states.get(entity_id).state + def test_arm_home_with_pending(self): """Test arm home method.""" assert setup_component( @@ -164,6 +190,32 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): assert STATE_ALARM_ARMED_AWAY == \ self.hass.states.get(entity_id).state + def test_arm_away_no_pending_when_code_not_req(self): + """Test arm home method.""" + assert setup_component( + self.hass, alarm_control_panel.DOMAIN, + {'alarm_control_panel': { + 'platform': 'manual_mqtt', + 'name': 'test', + 'code_arm_required': False, + 'code': CODE, + 'pending_time': 0, + 'disarm_after_trigger': False, + 'command_topic': 'alarm/command', + 'state_topic': 'alarm/state', + }}) + + entity_id = 'alarm_control_panel.test' + + assert STATE_ALARM_DISARMED == \ + self.hass.states.get(entity_id).state + + common.alarm_arm_away(self.hass, 0, entity_id) + self.hass.block_till_done() + + assert STATE_ALARM_ARMED_AWAY == \ + self.hass.states.get(entity_id).state + def test_arm_home_with_template_code(self): """Attempt to arm with a template-based code.""" assert setup_component( @@ -279,6 +331,32 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase): assert STATE_ALARM_ARMED_NIGHT == \ self.hass.states.get(entity_id).state + def test_arm_night_no_pending_when_code_not_req(self): + """Test arm night method.""" + assert setup_component( + self.hass, alarm_control_panel.DOMAIN, + {'alarm_control_panel': { + 'platform': 'manual_mqtt', + 'name': 'test', + 'code_arm_required': False, + 'code': CODE, + 'pending_time': 0, + 'disarm_after_trigger': False, + 'command_topic': 'alarm/command', + 'state_topic': 'alarm/state', + }}) + + entity_id = 'alarm_control_panel.test' + + assert STATE_ALARM_DISARMED == \ + self.hass.states.get(entity_id).state + + common.alarm_arm_night(self.hass, 0, entity_id) + self.hass.block_till_done() + + assert STATE_ALARM_ARMED_NIGHT == \ + self.hass.states.get(entity_id).state + def test_arm_night_with_pending(self): """Test arm night method.""" assert setup_component(