diff --git a/homeassistant/components/mqtt/abbreviations.py b/homeassistant/components/mqtt/abbreviations.py index 6bb7a92e8af..dd2f631848e 100644 --- a/homeassistant/components/mqtt/abbreviations.py +++ b/homeassistant/components/mqtt/abbreviations.py @@ -113,6 +113,7 @@ ABBREVIATIONS = { "pl_arm_away": "payload_arm_away", "pl_arm_home": "payload_arm_home", "pl_arm_nite": "payload_arm_night", + "pl_arm_vacation": "payload_arm_vacation", "pl_arm_custom_b": "payload_arm_custom_bypass", "pl_avail": "payload_available", "pl_cln_sp": "payload_clean_spot", diff --git a/homeassistant/components/mqtt/alarm_control_panel.py b/homeassistant/components/mqtt/alarm_control_panel.py index aa98a48dc10..f3e8e112f1a 100644 --- a/homeassistant/components/mqtt/alarm_control_panel.py +++ b/homeassistant/components/mqtt/alarm_control_panel.py @@ -11,6 +11,7 @@ from homeassistant.components.alarm_control_panel.const import ( SUPPORT_ALARM_ARM_CUSTOM_BYPASS, SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_ARM_VACATION, ) from homeassistant.const import ( CONF_CODE, @@ -20,6 +21,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMING, STATE_ALARM_DISARMED, STATE_ALARM_DISARMING, @@ -52,6 +54,7 @@ CONF_PAYLOAD_DISARM = "payload_disarm" CONF_PAYLOAD_ARM_HOME = "payload_arm_home" CONF_PAYLOAD_ARM_AWAY = "payload_arm_away" CONF_PAYLOAD_ARM_NIGHT = "payload_arm_night" +CONF_PAYLOAD_ARM_VACATION = "payload_arm_vacation" CONF_PAYLOAD_ARM_CUSTOM_BYPASS = "payload_arm_custom_bypass" CONF_COMMAND_TEMPLATE = "command_template" @@ -65,6 +68,7 @@ MQTT_ALARM_ATTRIBUTES_BLOCKED = frozenset( DEFAULT_COMMAND_TEMPLATE = "{{action}}" DEFAULT_ARM_NIGHT = "ARM_NIGHT" +DEFAULT_ARM_VACATION = "ARM_VACATION" DEFAULT_ARM_AWAY = "ARM_AWAY" DEFAULT_ARM_HOME = "ARM_HOME" DEFAULT_ARM_CUSTOM_BYPASS = "ARM_CUSTOM_BYPASS" @@ -83,6 +87,9 @@ PLATFORM_SCHEMA = mqtt.MQTT_BASE_PLATFORM_SCHEMA.extend( 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, + vol.Optional( + CONF_PAYLOAD_ARM_VACATION, default=DEFAULT_ARM_VACATION + ): cv.string, vol.Optional( CONF_PAYLOAD_ARM_CUSTOM_BYPASS, default=DEFAULT_ARM_CUSTOM_BYPASS ): cv.string, @@ -158,6 +165,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_PENDING, STATE_ALARM_ARMING, @@ -193,6 +201,7 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT + | SUPPORT_ALARM_ARM_VACATION | SUPPORT_ALARM_ARM_CUSTOM_BYPASS ) @@ -256,6 +265,17 @@ class MqttAlarm(MqttEntity, alarm.AlarmControlPanelEntity): action = self._config[CONF_PAYLOAD_ARM_NIGHT] self._publish(code, action) + async def async_alarm_arm_vacation(self, code=None): + """Send arm vacation command. + + This method is a coroutine. + """ + code_required = self._config[CONF_CODE_ARM_REQUIRED] + if code_required and not self._validate_code(code, "arming vacation"): + return + action = self._config[CONF_PAYLOAD_ARM_VACATION] + self._publish(code, action) + async def async_alarm_arm_custom_bypass(self, code=None): """Send arm custom bypass command. diff --git a/tests/components/alarm_control_panel/common.py b/tests/components/alarm_control_panel/common.py index fa50a1aab41..e46bac2fc1f 100644 --- a/tests/components/alarm_control_panel/common.py +++ b/tests/components/alarm_control_panel/common.py @@ -12,6 +12,7 @@ from homeassistant.const import ( SERVICE_ALARM_ARM_CUSTOM_BYPASS, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_NIGHT, + SERVICE_ALARM_ARM_VACATION, SERVICE_ALARM_DISARM, SERVICE_ALARM_TRIGGER, ) @@ -61,6 +62,19 @@ async def async_alarm_arm_night(hass, code=None, entity_id=ENTITY_MATCH_ALL): await hass.services.async_call(DOMAIN, SERVICE_ALARM_ARM_NIGHT, data, blocking=True) +async def async_alarm_arm_vacation(hass, code=None, entity_id=ENTITY_MATCH_ALL): + """Send the alarm the command for vacation mode.""" + data = {} + if code: + data[ATTR_CODE] = code + if entity_id: + data[ATTR_ENTITY_ID] = entity_id + + await hass.services.async_call( + DOMAIN, SERVICE_ALARM_ARM_VACATION, data, blocking=True + ) + + async def async_alarm_trigger(hass, code=None, entity_id=ENTITY_MATCH_ALL): """Send the alarm the command for disarm.""" data = {} diff --git a/tests/components/mqtt/test_alarm_control_panel.py b/tests/components/mqtt/test_alarm_control_panel.py index c9d06ef343e..c05aa052b5a 100644 --- a/tests/components/mqtt/test_alarm_control_panel.py +++ b/tests/components/mqtt/test_alarm_control_panel.py @@ -14,6 +14,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMING, STATE_ALARM_DISARMED, STATE_ALARM_DISARMING, @@ -124,6 +125,7 @@ async def test_update_state_via_state_topic(hass, mqtt_mock): STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_PENDING, STATE_ALARM_ARMING, @@ -176,6 +178,7 @@ async def test_arm_home_not_publishes_mqtt_with_invalid_code_when_req(hass, mqtt alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE, ) + await hass.async_block_till_done() call_count = mqtt_mock.async_publish.call_count await common.async_alarm_arm_home(hass, "abcd") @@ -227,6 +230,7 @@ async def test_arm_away_not_publishes_mqtt_with_invalid_code_when_req(hass, mqtt alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE, ) + await hass.async_block_till_done() call_count = mqtt_mock.async_publish.call_count await common.async_alarm_arm_away(hass, "abcd") @@ -278,6 +282,7 @@ async def test_arm_night_not_publishes_mqtt_with_invalid_code_when_req(hass, mqt alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE, ) + await hass.async_block_till_done() call_count = mqtt_mock.async_publish.call_count await common.async_alarm_arm_night(hass, "abcd") @@ -304,6 +309,60 @@ async def test_arm_night_publishes_mqtt_when_code_not_req(hass, mqtt_mock): ) +async def test_arm_vacation_publishes_mqtt(hass, mqtt_mock): + """Test publishing of MQTT messages while armed.""" + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG, + ) + await hass.async_block_till_done() + + await common.async_alarm_arm_vacation(hass) + mqtt_mock.async_publish.assert_called_once_with( + "alarm/command", "ARM_VACATION", 0, False + ) + + +async def test_arm_vacation_not_publishes_mqtt_with_invalid_code_when_req( + hass, mqtt_mock +): + """Test not publishing of MQTT messages with invalid code. + + When code_arm_required = True + """ + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + DEFAULT_CONFIG_CODE, + ) + await hass.async_block_till_done() + + call_count = mqtt_mock.async_publish.call_count + await common.async_alarm_arm_vacation(hass, "abcd") + assert mqtt_mock.async_publish.call_count == call_count + + +async def test_arm_vacation_publishes_mqtt_when_code_not_req(hass, mqtt_mock): + """Test publishing of MQTT messages. + + When code_arm_required = False + """ + config = copy.deepcopy(DEFAULT_CONFIG_CODE) + config[alarm_control_panel.DOMAIN]["code_arm_required"] = False + assert await async_setup_component( + hass, + alarm_control_panel.DOMAIN, + config, + ) + await hass.async_block_till_done() + + await common.async_alarm_arm_vacation(hass) + mqtt_mock.async_publish.assert_called_once_with( + "alarm/command", "ARM_VACATION", 0, False + ) + + async def test_arm_custom_bypass_publishes_mqtt(hass, mqtt_mock): """Test publishing of MQTT messages while armed.""" assert await async_setup_component( @@ -446,6 +505,7 @@ async def test_disarm_not_publishes_mqtt_with_invalid_code_when_req(hass, mqtt_m alarm_control_panel.DOMAIN, DEFAULT_CONFIG_CODE, ) + await hass.async_block_till_done() call_count = mqtt_mock.async_publish.call_count await common.async_alarm_disarm(hass, "abcd")