Add code_arm_required to manual alarm with MQTT (#22641)

* add code_arm_requited to manual-mqtt alarm

* Add fix for alarm front end more-info-alarm_control_panel

* Fix code mistake
This commit is contained in:
Kevin Cooper 2019-06-17 22:49:10 +01:00 committed by Paulus Schoutsen
parent cb5426c1fa
commit f722a6c08d
4 changed files with 110 additions and 7 deletions

View file

@ -19,6 +19,7 @@ SCAN_INTERVAL = timedelta(seconds=30)
ATTR_CHANGED_BY = 'changed_by' ATTR_CHANGED_BY = 'changed_by'
FORMAT_TEXT = 'text' FORMAT_TEXT = 'text'
FORMAT_NUMBER = 'number' FORMAT_NUMBER = 'number'
ATTR_CODE_ARM_REQUIRED = 'code_arm_required'
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
@ -87,6 +88,11 @@ class AlarmControlPanel(Entity):
"""Last change triggered by.""" """Last change triggered by."""
return None return None
@property
def code_arm_required(self):
"""Whether the code is required for arm actions."""
return True
def alarm_disarm(self, code=None): def alarm_disarm(self, code=None):
"""Send disarm command.""" """Send disarm command."""
raise NotImplementedError() raise NotImplementedError()
@ -159,6 +165,7 @@ class AlarmControlPanel(Entity):
"""Return the state attributes.""" """Return the state attributes."""
state_attr = { state_attr = {
ATTR_CODE_FORMAT: self.code_format, 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 return state_attr

View file

@ -24,6 +24,7 @@ from homeassistant.helpers.event import track_point_in_time
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_CODE_TEMPLATE = 'code_template' CONF_CODE_TEMPLATE = 'code_template'
CONF_CODE_ARM_REQUIRED = 'code_arm_required'
CONF_PAYLOAD_DISARM = 'payload_disarm' CONF_PAYLOAD_DISARM = 'payload_disarm'
CONF_PAYLOAD_ARM_HOME = 'payload_arm_home' 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), _state_schema(STATE_ALARM_TRIGGERED),
vol.Required(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic, vol.Required(mqtt.CONF_COMMAND_TOPIC): mqtt.valid_publish_topic,
vol.Required(mqtt.CONF_STATE_TOPIC): mqtt.valid_subscribe_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_AWAY, default=DEFAULT_ARM_AWAY): cv.string,
vol.Optional(CONF_PAYLOAD_ARM_HOME, default=DEFAULT_ARM_HOME): 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, 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_STATE_TOPIC),
config.get(mqtt.CONF_COMMAND_TOPIC), config.get(mqtt.CONF_COMMAND_TOPIC),
config.get(mqtt.CONF_QOS), config.get(mqtt.CONF_QOS),
config.get(CONF_CODE_ARM_REQUIRED),
config.get(CONF_PAYLOAD_DISARM), config.get(CONF_PAYLOAD_DISARM),
config.get(CONF_PAYLOAD_ARM_HOME), config.get(CONF_PAYLOAD_ARM_HOME),
config.get(CONF_PAYLOAD_ARM_AWAY), 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, def __init__(self, hass, name, code, code_template, disarm_after_trigger,
state_topic, command_topic, qos, payload_disarm, state_topic, command_topic, qos, code_arm_required,
payload_arm_home, payload_arm_away, payload_arm_night, payload_disarm, payload_arm_home, payload_arm_away,
config): payload_arm_night, config):
"""Init the manual MQTT alarm panel.""" """Init the manual MQTT alarm panel."""
self._state = STATE_ALARM_DISARMED self._state = STATE_ALARM_DISARMED
self._hass = hass self._hass = hass
@ -175,6 +178,7 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
self._state_topic = state_topic self._state_topic = state_topic
self._command_topic = command_topic self._command_topic = command_topic
self._qos = qos self._qos = qos
self._code_arm_required = code_arm_required
self._payload_disarm = payload_disarm self._payload_disarm = payload_disarm
self._payload_arm_home = payload_arm_home self._payload_arm_home = payload_arm_home
self._payload_arm_away = payload_arm_away self._payload_arm_away = payload_arm_away
@ -237,6 +241,11 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
return alarm.FORMAT_NUMBER return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT 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): def alarm_disarm(self, code=None):
"""Send disarm command.""" """Send disarm command."""
if not self._validate_code(code, STATE_ALARM_DISARMED): if not self._validate_code(code, STATE_ALARM_DISARMED):
@ -248,21 +257,24 @@ class ManualMQTTAlarm(alarm.AlarmControlPanel):
def alarm_arm_home(self, code=None): def alarm_arm_home(self, code=None):
"""Send arm home command.""" """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 return
self._update_state(STATE_ALARM_ARMED_HOME) self._update_state(STATE_ALARM_ARMED_HOME)
def alarm_arm_away(self, code=None): def alarm_arm_away(self, code=None):
"""Send arm away command.""" """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 return
self._update_state(STATE_ALARM_ARMED_AWAY) self._update_state(STATE_ALARM_ARMED_AWAY)
def alarm_arm_night(self, code=None): def alarm_arm_night(self, code=None):
"""Send arm night command.""" """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 return
self._update_state(STATE_ALARM_ARMED_NIGHT) self._update_state(STATE_ALARM_ARMED_NIGHT)

View file

@ -192,6 +192,12 @@ class MqttAlarm(MqttAttributes, MqttAvailability, MqttDiscoveryUpdate,
return alarm.FORMAT_NUMBER return alarm.FORMAT_NUMBER
return alarm.FORMAT_TEXT 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): async def async_alarm_disarm(self, code=None):
"""Send disarm command. """Send disarm command.

View file

@ -77,6 +77,32 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase):
assert STATE_ALARM_ARMED_HOME == \ assert STATE_ALARM_ARMED_HOME == \
self.hass.states.get(entity_id).state 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): def test_arm_home_with_pending(self):
"""Test arm home method.""" """Test arm home method."""
assert setup_component( assert setup_component(
@ -164,6 +190,32 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase):
assert STATE_ALARM_ARMED_AWAY == \ assert STATE_ALARM_ARMED_AWAY == \
self.hass.states.get(entity_id).state 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): def test_arm_home_with_template_code(self):
"""Attempt to arm with a template-based code.""" """Attempt to arm with a template-based code."""
assert setup_component( assert setup_component(
@ -279,6 +331,32 @@ class TestAlarmControlPanelManualMqtt(unittest.TestCase):
assert STATE_ALARM_ARMED_NIGHT == \ assert STATE_ALARM_ARMED_NIGHT == \
self.hass.states.get(entity_id).state 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): def test_arm_night_with_pending(self):
"""Test arm night method.""" """Test arm night method."""
assert setup_component( assert setup_component(