From 312531988a185608c79ed6b2cb1e27e13211f14f Mon Sep 17 00:00:00 2001 From: posixx <2280400+posixx@users.noreply.github.com> Date: Thu, 1 Jul 2021 17:26:32 +0200 Subject: [PATCH] Vacation Mode on Alarm Panels (#45980) Co-authored-by: Nathan Tilley Co-authored-by: Martin Hjelmare Co-authored-by: Franck Nijhof Co-authored-by: Khole Jones Co-authored-by: Erik Montnemery --- .../alarm_control_panel/__init__.py | 16 +++++++ .../components/alarm_control_panel/const.py | 2 + .../alarm_control_panel/device_action.py | 7 +++ .../alarm_control_panel/device_condition.py | 8 ++++ .../alarm_control_panel/device_trigger.py | 12 ++++++ .../components/alarm_control_panel/group.py | 2 + .../alarm_control_panel/reproduce_state.py | 5 +++ .../alarm_control_panel/services.yaml | 14 ++++++ .../alarm_control_panel/strings.json | 8 +++- homeassistant/const.py | 2 + .../alarm_control_panel/test_device_action.py | 29 ++++++++++++- .../test_device_condition.py | 43 ++++++++++++++++++- .../test_device_trigger.py | 35 ++++++++++++++- .../test_reproduce_state.py | 22 +++++++++- .../components/device_automation/test_init.py | 5 ++- .../test/alarm_control_panel.py | 8 ++++ 16 files changed, 207 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/alarm_control_panel/__init__.py b/homeassistant/components/alarm_control_panel/__init__.py index c8da648fec6..1032150649e 100644 --- a/homeassistant/components/alarm_control_panel/__init__.py +++ b/homeassistant/components/alarm_control_panel/__init__.py @@ -15,6 +15,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, ) @@ -30,6 +31,7 @@ from .const import ( SUPPORT_ALARM_ARM_CUSTOM_BYPASS, SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_ARM_VACATION, SUPPORT_ALARM_TRIGGER, ) @@ -81,6 +83,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: "async_alarm_arm_night", [SUPPORT_ALARM_ARM_NIGHT], ) + component.async_register_entity_service( + SERVICE_ALARM_ARM_VACATION, + ALARM_SERVICE_SCHEMA, + "async_alarm_arm_vacation", + [SUPPORT_ALARM_ARM_VACATION], + ) component.async_register_entity_service( SERVICE_ALARM_ARM_CUSTOM_BYPASS, ALARM_SERVICE_SCHEMA, @@ -164,6 +172,14 @@ class AlarmControlPanelEntity(Entity): """Send arm night command.""" await self.hass.async_add_executor_job(self.alarm_arm_night, code) + def alarm_arm_vacation(self, code: str | None = None) -> None: + """Send arm vacation command.""" + raise NotImplementedError() + + async def async_alarm_arm_vacation(self, code: str | None = None) -> None: + """Send arm vacation command.""" + await self.hass.async_add_executor_job(self.alarm_arm_vacation, code) + def alarm_trigger(self, code: str | None = None) -> None: """Send alarm trigger command.""" raise NotImplementedError() diff --git a/homeassistant/components/alarm_control_panel/const.py b/homeassistant/components/alarm_control_panel/const.py index 36e3b6a13eb..f3688a27958 100644 --- a/homeassistant/components/alarm_control_panel/const.py +++ b/homeassistant/components/alarm_control_panel/const.py @@ -7,10 +7,12 @@ SUPPORT_ALARM_ARM_AWAY: Final = 2 SUPPORT_ALARM_ARM_NIGHT: Final = 4 SUPPORT_ALARM_TRIGGER: Final = 8 SUPPORT_ALARM_ARM_CUSTOM_BYPASS: Final = 16 +SUPPORT_ALARM_ARM_VACATION: Final = 32 CONDITION_TRIGGERED: Final = "is_triggered" CONDITION_DISARMED: Final = "is_disarmed" CONDITION_ARMED_HOME: Final = "is_armed_home" CONDITION_ARMED_AWAY: Final = "is_armed_away" CONDITION_ARMED_NIGHT: Final = "is_armed_night" +CONDITION_ARMED_VACATION: Final = "is_armed_vacation" CONDITION_ARMED_CUSTOM_BYPASS: Final = "is_armed_custom_bypass" diff --git a/homeassistant/components/alarm_control_panel/device_action.py b/homeassistant/components/alarm_control_panel/device_action.py index d92f9615c9a..c37bddafcd3 100644 --- a/homeassistant/components/alarm_control_panel/device_action.py +++ b/homeassistant/components/alarm_control_panel/device_action.py @@ -16,6 +16,7 @@ from homeassistant.const import ( SERVICE_ALARM_ARM_AWAY, SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_NIGHT, + SERVICE_ALARM_ARM_VACATION, SERVICE_ALARM_DISARM, SERVICE_ALARM_TRIGGER, ) @@ -30,6 +31,7 @@ from .const import ( SUPPORT_ALARM_ARM_AWAY, SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_ARM_VACATION, SUPPORT_ALARM_TRIGGER, ) @@ -37,6 +39,7 @@ ACTION_TYPES: Final[set[str]] = { "arm_away", "arm_home", "arm_night", + "arm_vacation", "disarm", "trigger", } @@ -77,6 +80,8 @@ async def async_get_actions( actions.append({**base_action, CONF_TYPE: "arm_home"}) if supported_features & SUPPORT_ALARM_ARM_NIGHT: actions.append({**base_action, CONF_TYPE: "arm_night"}) + if supported_features & SUPPORT_ALARM_ARM_VACATION: + actions.append({**base_action, CONF_TYPE: "arm_vacation"}) actions.append({**base_action, CONF_TYPE: "disarm"}) if supported_features & SUPPORT_ALARM_TRIGGER: actions.append({**base_action, CONF_TYPE: "trigger"}) @@ -98,6 +103,8 @@ async def async_call_action_from_config( service = SERVICE_ALARM_ARM_HOME elif config[CONF_TYPE] == "arm_night": service = SERVICE_ALARM_ARM_NIGHT + elif config[CONF_TYPE] == "arm_vacation": + service = SERVICE_ALARM_ARM_VACATION elif config[CONF_TYPE] == "disarm": service = SERVICE_ALARM_DISARM elif config[CONF_TYPE] == "trigger": diff --git a/homeassistant/components/alarm_control_panel/device_condition.py b/homeassistant/components/alarm_control_panel/device_condition.py index 3cbaa019ad0..9367ef8f811 100644 --- a/homeassistant/components/alarm_control_panel/device_condition.py +++ b/homeassistant/components/alarm_control_panel/device_condition.py @@ -10,6 +10,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 ( ATTR_ENTITY_ID, @@ -22,6 +23,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, ) @@ -37,6 +39,7 @@ from .const import ( CONDITION_ARMED_CUSTOM_BYPASS, CONDITION_ARMED_HOME, CONDITION_ARMED_NIGHT, + CONDITION_ARMED_VACATION, CONDITION_DISARMED, CONDITION_TRIGGERED, ) @@ -47,6 +50,7 @@ CONDITION_TYPES: Final[set[str]] = { CONDITION_ARMED_HOME, CONDITION_ARMED_AWAY, CONDITION_ARMED_NIGHT, + CONDITION_ARMED_VACATION, CONDITION_ARMED_CUSTOM_BYPASS, } @@ -90,6 +94,8 @@ async def async_get_conditions( conditions.append({**base_condition, CONF_TYPE: CONDITION_ARMED_AWAY}) if supported_features & SUPPORT_ALARM_ARM_NIGHT: conditions.append({**base_condition, CONF_TYPE: CONDITION_ARMED_NIGHT}) + if supported_features & SUPPORT_ALARM_ARM_VACATION: + conditions.append({**base_condition, CONF_TYPE: CONDITION_ARMED_VACATION}) if supported_features & SUPPORT_ALARM_ARM_CUSTOM_BYPASS: conditions.append( {**base_condition, CONF_TYPE: CONDITION_ARMED_CUSTOM_BYPASS} @@ -114,6 +120,8 @@ def async_condition_from_config( state = STATE_ALARM_ARMED_AWAY elif config[CONF_TYPE] == CONDITION_ARMED_NIGHT: state = STATE_ALARM_ARMED_NIGHT + elif config[CONF_TYPE] == CONDITION_ARMED_VACATION: + state = STATE_ALARM_ARMED_VACATION elif config[CONF_TYPE] == CONDITION_ARMED_CUSTOM_BYPASS: state = STATE_ALARM_ARMED_CUSTOM_BYPASS diff --git a/homeassistant/components/alarm_control_panel/device_trigger.py b/homeassistant/components/alarm_control_panel/device_trigger.py index f89e03e7326..9ab6e466b6c 100644 --- a/homeassistant/components/alarm_control_panel/device_trigger.py +++ b/homeassistant/components/alarm_control_panel/device_trigger.py @@ -9,6 +9,7 @@ from homeassistant.components.alarm_control_panel.const import ( SUPPORT_ALARM_ARM_AWAY, SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_ARM_VACATION, ) from homeassistant.components.automation import AutomationActionType from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA @@ -23,6 +24,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_ARMING, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, @@ -39,6 +41,7 @@ TRIGGER_TYPES: Final[set[str]] = BASIC_TRIGGER_TYPES | { "armed_home", "armed_away", "armed_night", + "armed_vacation", } TRIGGER_SCHEMA: Final = DEVICE_TRIGGER_BASE_SCHEMA.extend( @@ -100,6 +103,13 @@ async def async_get_triggers( CONF_TYPE: "armed_night", } ) + if supported_features & SUPPORT_ALARM_ARM_VACATION: + triggers.append( + { + **base_trigger, + CONF_TYPE: "armed_vacation", + } + ) return triggers @@ -134,6 +144,8 @@ async def async_attach_trigger( to_state = STATE_ALARM_ARMED_AWAY elif config[CONF_TYPE] == "armed_night": to_state = STATE_ALARM_ARMED_NIGHT + elif config[CONF_TYPE] == "armed_vacation": + to_state = STATE_ALARM_ARMED_VACATION state_config = { state_trigger.CONF_PLATFORM: "state", diff --git a/homeassistant/components/alarm_control_panel/group.py b/homeassistant/components/alarm_control_panel/group.py index 4bfb1486814..dabe49069d5 100644 --- a/homeassistant/components/alarm_control_panel/group.py +++ b/homeassistant/components/alarm_control_panel/group.py @@ -7,6 +7,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_TRIGGERED, STATE_OFF, ) @@ -24,6 +25,7 @@ def async_describe_on_off_states( STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_TRIGGERED, }, STATE_OFF, diff --git a/homeassistant/components/alarm_control_panel/reproduce_state.py b/homeassistant/components/alarm_control_panel/reproduce_state.py index 019ba35c013..3fcc540d04b 100644 --- a/homeassistant/components/alarm_control_panel/reproduce_state.py +++ b/homeassistant/components/alarm_control_panel/reproduce_state.py @@ -12,12 +12,14 @@ 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, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, ) @@ -32,6 +34,7 @@ VALID_STATES: Final[set[str]] = { STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, } @@ -71,6 +74,8 @@ async def _async_reproduce_state( service = SERVICE_ALARM_ARM_HOME elif state.state == STATE_ALARM_ARMED_NIGHT: service = SERVICE_ALARM_ARM_NIGHT + elif state.state == STATE_ALARM_ARMED_VACATION: + service = SERVICE_ALARM_ARM_VACATION elif state.state == STATE_ALARM_DISARMED: service = SERVICE_ALARM_DISARM elif state.state == STATE_ALARM_TRIGGERED: diff --git a/homeassistant/components/alarm_control_panel/services.yaml b/homeassistant/components/alarm_control_panel/services.yaml index 8c148a6a1e0..0bf3952c4ed 100644 --- a/homeassistant/components/alarm_control_panel/services.yaml +++ b/homeassistant/components/alarm_control_panel/services.yaml @@ -70,6 +70,20 @@ alarm_arm_night: selector: text: +alarm_arm_vacation: + name: Arm vacation + description: Send the alarm the command for arm vacation. + target: + entity: + domain: alarm_control_panel + fields: + code: + name: Code + description: An optional code to arm vacation the alarm control panel with. + example: "1234" + selector: + text: + alarm_trigger: name: Trigger description: Send the alarm the command for trigger. diff --git a/homeassistant/components/alarm_control_panel/strings.json b/homeassistant/components/alarm_control_panel/strings.json index de89d28082b..5126f49d92b 100644 --- a/homeassistant/components/alarm_control_panel/strings.json +++ b/homeassistant/components/alarm_control_panel/strings.json @@ -5,6 +5,7 @@ "arm_away": "Arm {entity_name} away", "arm_home": "Arm {entity_name} home", "arm_night": "Arm {entity_name} night", + "arm_vacation": "Arm {entity_name} vacation", "disarm": "Disarm {entity_name}", "trigger": "Trigger {entity_name}" }, @@ -13,14 +14,16 @@ "is_disarmed": "{entity_name} is disarmed", "is_armed_home": "{entity_name} is armed home", "is_armed_away": "{entity_name} is armed away", - "is_armed_night": "{entity_name} is armed night" + "is_armed_night": "{entity_name} is armed night", + "is_armed_vacation": "{entity_name} is armed vacation" }, "trigger_type": { "triggered": "{entity_name} triggered", "disarmed": "{entity_name} disarmed", "armed_home": "{entity_name} armed home", "armed_away": "{entity_name} armed away", - "armed_night": "{entity_name} armed night" + "armed_night": "{entity_name} armed night", + "armed_vacation": "{entity_name} armed vacation" } }, "state": { @@ -30,6 +33,7 @@ "armed_home": "Armed home", "armed_away": "Armed away", "armed_night": "Armed night", + "armed_vacation": "Armed vacation", "armed_custom_bypass": "Armed custom bypass", "pending": "Pending", "arming": "Arming", diff --git a/homeassistant/const.py b/homeassistant/const.py index 62f0e92738e..35b946fc6ab 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -266,6 +266,7 @@ STATE_ALARM_DISARMED: Final = "disarmed" STATE_ALARM_ARMED_HOME: Final = "armed_home" STATE_ALARM_ARMED_AWAY: Final = "armed_away" STATE_ALARM_ARMED_NIGHT: Final = "armed_night" +STATE_ALARM_ARMED_VACATION: Final = "armed_vacation" STATE_ALARM_ARMED_CUSTOM_BYPASS: Final = "armed_custom_bypass" STATE_ALARM_PENDING: Final = "pending" STATE_ALARM_ARMING: Final = "arming" @@ -580,6 +581,7 @@ SERVICE_ALARM_DISARM: Final = "alarm_disarm" SERVICE_ALARM_ARM_HOME: Final = "alarm_arm_home" SERVICE_ALARM_ARM_AWAY: Final = "alarm_arm_away" SERVICE_ALARM_ARM_NIGHT: Final = "alarm_arm_night" +SERVICE_ALARM_ARM_VACATION: Final = "alarm_arm_vacation" SERVICE_ALARM_ARM_CUSTOM_BYPASS: Final = "alarm_arm_custom_bypass" SERVICE_ALARM_TRIGGER: Final = "alarm_trigger" diff --git a/tests/components/alarm_control_panel/test_device_action.py b/tests/components/alarm_control_panel/test_device_action.py index 1f66d901603..1fdb908d2e6 100644 --- a/tests/components/alarm_control_panel/test_device_action.py +++ b/tests/components/alarm_control_panel/test_device_action.py @@ -8,6 +8,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_UNKNOWN, @@ -50,6 +51,7 @@ def entity_reg(hass): (True, 0, const.SUPPORT_ALARM_ARM_AWAY, ["disarm", "arm_away"]), (True, 0, const.SUPPORT_ALARM_ARM_HOME, ["disarm", "arm_home"]), (True, 0, const.SUPPORT_ALARM_ARM_NIGHT, ["disarm", "arm_night"]), + (True, 0, const.SUPPORT_ALARM_ARM_VACATION, ["disarm", "arm_vacation"]), (True, 0, const.SUPPORT_ALARM_TRIGGER, ["disarm", "trigger"]), ], ) @@ -150,13 +152,14 @@ async def test_get_action_capabilities( "arm_away": {"extra_fields": []}, "arm_home": {"extra_fields": []}, "arm_night": {"extra_fields": []}, + "arm_vacation": {"extra_fields": []}, "disarm": { "extra_fields": [{"name": "code", "optional": True, "type": "string"}] }, "trigger": {"extra_fields": []}, } actions = await async_get_device_automations(hass, "action", device_entry.id) - assert len(actions) == 5 + assert len(actions) == 6 for action in actions: capabilities = await async_get_device_automation_capabilities( hass, "action", action @@ -196,13 +199,16 @@ async def test_get_action_capabilities_arm_code( "arm_night": { "extra_fields": [{"name": "code", "optional": True, "type": "string"}] }, + "arm_vacation": { + "extra_fields": [{"name": "code", "optional": True, "type": "string"}] + }, "disarm": { "extra_fields": [{"name": "code", "optional": True, "type": "string"}] }, "trigger": {"extra_fields": []}, } actions = await async_get_device_automations(hass, "action", device_entry.id) - assert len(actions) == 5 + assert len(actions) == 6 for action in actions: capabilities = await async_get_device_automation_capabilities( hass, "action", action @@ -256,6 +262,18 @@ async def test_action(hass, enable_custom_integrations): "type": "arm_night", }, }, + { + "trigger": { + "platform": "event", + "event_type": "test_event_arm_vacation", + }, + "action": { + "domain": DOMAIN, + "device_id": "abcdefgh", + "entity_id": "alarm_control_panel.alarm_no_arm_code", + "type": "arm_vacation", + }, + }, { "trigger": {"platform": "event", "event_type": "test_event_disarm"}, "action": { @@ -302,6 +320,13 @@ async def test_action(hass, enable_custom_integrations): == STATE_ALARM_ARMED_HOME ) + hass.bus.async_fire("test_event_arm_vacation") + await hass.async_block_till_done() + assert ( + hass.states.get("alarm_control_panel.alarm_no_arm_code").state + == STATE_ALARM_ARMED_VACATION + ) + hass.bus.async_fire("test_event_arm_night") await hass.async_block_till_done() assert ( diff --git a/tests/components/alarm_control_panel/test_device_condition.py b/tests/components/alarm_control_panel/test_device_condition.py index b1e2c171cea..d0644562850 100644 --- a/tests/components/alarm_control_panel/test_device_condition.py +++ b/tests/components/alarm_control_panel/test_device_condition.py @@ -8,6 +8,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, ) @@ -50,11 +51,13 @@ def calls(hass): (False, const.SUPPORT_ALARM_ARM_AWAY, 0, ["is_armed_away"]), (False, const.SUPPORT_ALARM_ARM_HOME, 0, ["is_armed_home"]), (False, const.SUPPORT_ALARM_ARM_NIGHT, 0, ["is_armed_night"]), + (False, const.SUPPORT_ALARM_ARM_VACATION, 0, ["is_armed_vacation"]), (False, const.SUPPORT_ALARM_ARM_CUSTOM_BYPASS, 0, ["is_armed_custom_bypass"]), (True, 0, 0, []), (True, 0, const.SUPPORT_ALARM_ARM_AWAY, ["is_armed_away"]), (True, 0, const.SUPPORT_ALARM_ARM_HOME, ["is_armed_home"]), (True, 0, const.SUPPORT_ALARM_ARM_NIGHT, ["is_armed_night"]), + (True, 0, const.SUPPORT_ALARM_ARM_VACATION, ["is_armed_vacation"]), (True, 0, const.SUPPORT_ALARM_ARM_CUSTOM_BYPASS, ["is_armed_custom_bypass"]), ], ) @@ -212,6 +215,24 @@ async def test_if_state(hass, calls): }, { "trigger": {"platform": "event", "event_type": "test_event6"}, + "condition": [ + { + "condition": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": "alarm_control_panel.entity", + "type": "is_armed_vacation", + } + ], + "action": { + "service": "test.automation", + "data_template": { + "some": "is_armed_vacation - {{ trigger.platform }} - {{ trigger.event.event_type }}" + }, + }, + }, + { + "trigger": {"platform": "event", "event_type": "test_event7"}, "condition": [ { "condition": "device", @@ -238,6 +259,7 @@ async def test_if_state(hass, calls): hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].data["some"] == "is_triggered - event - test_event1" @@ -249,6 +271,7 @@ async def test_if_state(hass, calls): hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") await hass.async_block_till_done() assert len(calls) == 2 assert calls[1].data["some"] == "is_disarmed - event - test_event2" @@ -260,6 +283,7 @@ async def test_if_state(hass, calls): hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") await hass.async_block_till_done() assert len(calls) == 3 assert calls[2].data["some"] == "is_armed_home - event - test_event3" @@ -271,6 +295,7 @@ async def test_if_state(hass, calls): hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") await hass.async_block_till_done() assert len(calls) == 4 assert calls[3].data["some"] == "is_armed_away - event - test_event4" @@ -282,10 +307,23 @@ async def test_if_state(hass, calls): hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") await hass.async_block_till_done() assert len(calls) == 5 assert calls[4].data["some"] == "is_armed_night - event - test_event5" + hass.states.async_set("alarm_control_panel.entity", STATE_ALARM_ARMED_VACATION) + hass.bus.async_fire("test_event1") + hass.bus.async_fire("test_event2") + hass.bus.async_fire("test_event3") + hass.bus.async_fire("test_event4") + hass.bus.async_fire("test_event5") + hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") + await hass.async_block_till_done() + assert len(calls) == 6 + assert calls[5].data["some"] == "is_armed_vacation - event - test_event6" + hass.states.async_set("alarm_control_panel.entity", STATE_ALARM_ARMED_CUSTOM_BYPASS) hass.bus.async_fire("test_event1") hass.bus.async_fire("test_event2") @@ -293,6 +331,7 @@ async def test_if_state(hass, calls): hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event6") + hass.bus.async_fire("test_event7") await hass.async_block_till_done() - assert len(calls) == 6 - assert calls[5].data["some"] == "is_armed_custom_bypass - event - test_event6" + assert len(calls) == 7 + assert calls[6].data["some"] == "is_armed_custom_bypass - event - test_event7" diff --git a/tests/components/alarm_control_panel/test_device_trigger.py b/tests/components/alarm_control_panel/test_device_trigger.py index 8859915b911..9edda7e98e2 100644 --- a/tests/components/alarm_control_panel/test_device_trigger.py +++ b/tests/components/alarm_control_panel/test_device_trigger.py @@ -9,6 +9,7 @@ from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_PENDING, STATE_ALARM_TRIGGERED, @@ -54,7 +55,7 @@ def calls(hass): (False, 0, 0, ["triggered", "disarmed", "arming"]), ( False, - 15, + 47, 0, [ "triggered", @@ -63,13 +64,14 @@ def calls(hass): "armed_home", "armed_away", "armed_night", + "armed_vacation", ], ), (True, 0, 0, ["triggered", "disarmed", "arming"]), ( True, 0, - 15, + 47, [ "triggered", "disarmed", @@ -77,6 +79,7 @@ def calls(hass): "armed_home", "armed_away", "armed_night", + "armed_vacation", ], ), ], @@ -256,6 +259,25 @@ async def test_if_fires_on_state_change(hass, calls): }, }, }, + { + "trigger": { + "platform": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": "alarm_control_panel.entity", + "type": "armed_vacation", + }, + "action": { + "service": "test.automation", + "data_template": { + "some": ( + "armed_vacation - {{ trigger.platform}} - " + "{{ trigger.entity_id}} - {{ trigger.from_state.state}} - " + "{{ trigger.to_state.state}} - {{ trigger.for }}" + ) + }, + }, + }, ] }, ) @@ -305,6 +327,15 @@ async def test_if_fires_on_state_change(hass, calls): == "armed_night - device - alarm_control_panel.entity - armed_away - armed_night - None" ) + # Fake that the entity is armed vacation. + hass.states.async_set("alarm_control_panel.entity", STATE_ALARM_ARMED_VACATION) + await hass.async_block_till_done() + assert len(calls) == 6 + assert ( + calls[5].data["some"] + == "armed_vacation - device - alarm_control_panel.entity - armed_night - armed_vacation - None" + ) + async def test_if_fires_on_state_change_with_for(hass, calls): """Test for triggers firing with delay.""" diff --git a/tests/components/alarm_control_panel/test_reproduce_state.py b/tests/components/alarm_control_panel/test_reproduce_state.py index 686b281bff8..0f87e2206ac 100644 --- a/tests/components/alarm_control_panel/test_reproduce_state.py +++ b/tests/components/alarm_control_panel/test_reproduce_state.py @@ -4,12 +4,14 @@ 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, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, ) @@ -34,6 +36,9 @@ async def test_reproducing_states(hass, caplog): hass.states.async_set( "alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_NIGHT, {} ) + hass.states.async_set( + "alarm_control_panel.entity_armed_vacation", STATE_ALARM_ARMED_VACATION, {} + ) hass.states.async_set( "alarm_control_panel.entity_disarmed", STATE_ALARM_DISARMED, {} ) @@ -53,6 +58,9 @@ async def test_reproducing_states(hass, caplog): arm_night_calls = async_mock_service( hass, "alarm_control_panel", SERVICE_ALARM_ARM_NIGHT ) + arm_vacation_calls = async_mock_service( + hass, "alarm_control_panel", SERVICE_ALARM_ARM_VACATION + ) disarm_calls = async_mock_service(hass, "alarm_control_panel", SERVICE_ALARM_DISARM) trigger_calls = async_mock_service( hass, "alarm_control_panel", SERVICE_ALARM_TRIGGER @@ -68,6 +76,9 @@ async def test_reproducing_states(hass, caplog): ), State("alarm_control_panel.entity_armed_home", STATE_ALARM_ARMED_HOME), State("alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_NIGHT), + State( + "alarm_control_panel.entity_armed_vacation", STATE_ALARM_ARMED_VACATION + ), State("alarm_control_panel.entity_disarmed", STATE_ALARM_DISARMED), State("alarm_control_panel.entity_triggered", STATE_ALARM_TRIGGERED), ] @@ -77,6 +88,7 @@ async def test_reproducing_states(hass, caplog): assert len(arm_custom_bypass_calls) == 0 assert len(arm_home_calls) == 0 assert len(arm_night_calls) == 0 + assert len(arm_vacation_calls) == 0 assert len(disarm_calls) == 0 assert len(trigger_calls) == 0 @@ -90,6 +102,7 @@ async def test_reproducing_states(hass, caplog): assert len(arm_custom_bypass_calls) == 0 assert len(arm_home_calls) == 0 assert len(arm_night_calls) == 0 + assert len(arm_vacation_calls) == 0 assert len(disarm_calls) == 0 assert len(trigger_calls) == 0 @@ -104,7 +117,8 @@ async def test_reproducing_states(hass, caplog): "alarm_control_panel.entity_armed_home", STATE_ALARM_ARMED_CUSTOM_BYPASS ), State("alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_HOME), - State("alarm_control_panel.entity_disarmed", STATE_ALARM_ARMED_NIGHT), + State("alarm_control_panel.entity_armed_vacation", STATE_ALARM_ARMED_NIGHT), + State("alarm_control_panel.entity_disarmed", STATE_ALARM_ARMED_VACATION), State("alarm_control_panel.entity_triggered", STATE_ALARM_DISARMED), # Should not raise State("alarm_control_panel.non_existing", "on"), @@ -132,6 +146,12 @@ async def test_reproducing_states(hass, caplog): assert len(arm_night_calls) == 1 assert arm_night_calls[0].domain == "alarm_control_panel" assert arm_night_calls[0].data == { + "entity_id": "alarm_control_panel.entity_armed_vacation" + } + + assert len(arm_vacation_calls) == 1 + assert arm_vacation_calls[0].domain == "alarm_control_panel" + assert arm_vacation_calls[0].data == { "entity_id": "alarm_control_panel.entity_disarmed" } diff --git a/tests/components/device_automation/test_init.py b/tests/components/device_automation/test_init.py index 7c16d067eff..160e6354b8b 100644 --- a/tests/components/device_automation/test_init.py +++ b/tests/components/device_automation/test_init.py @@ -185,12 +185,13 @@ async def test_websocket_get_action_capabilities( "alarm_control_panel", "test", "5678", device_id=device_entry.id ) hass.states.async_set( - "alarm_control_panel.test_5678", "attributes", {"supported_features": 15} + "alarm_control_panel.test_5678", "attributes", {"supported_features": 47} ) expected_capabilities = { "arm_away": {"extra_fields": []}, "arm_home": {"extra_fields": []}, "arm_night": {"extra_fields": []}, + "arm_vacation": {"extra_fields": []}, "disarm": { "extra_fields": [{"name": "code", "optional": True, "type": "string"}] }, @@ -209,7 +210,7 @@ async def test_websocket_get_action_capabilities( actions = msg["result"] id = 2 - assert len(actions) == 5 + assert len(actions) == 6 for action in actions: await client.send_json( { diff --git a/tests/testing_config/custom_components/test/alarm_control_panel.py b/tests/testing_config/custom_components/test/alarm_control_panel.py index 864c99ec5df..f38bf48fc94 100644 --- a/tests/testing_config/custom_components/test/alarm_control_panel.py +++ b/tests/testing_config/custom_components/test/alarm_control_panel.py @@ -8,12 +8,14 @@ from homeassistant.components.alarm_control_panel.const import ( SUPPORT_ALARM_ARM_AWAY, SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_NIGHT, + SUPPORT_ALARM_ARM_VACATION, SUPPORT_ALARM_TRIGGER, ) from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, + STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, ) @@ -79,6 +81,7 @@ class MockAlarm(MockEntity, AlarmControlPanelEntity): | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT | SUPPORT_ALARM_TRIGGER + | SUPPORT_ALARM_ARM_VACATION ) def alarm_arm_away(self, code=None): @@ -96,6 +99,11 @@ class MockAlarm(MockEntity, AlarmControlPanelEntity): self._state = STATE_ALARM_ARMED_NIGHT self.schedule_update_ha_state() + def alarm_arm_vacation(self, code=None): + """Send arm night command.""" + self._state = STATE_ALARM_ARMED_VACATION + self.schedule_update_ha_state() + def alarm_disarm(self, code=None): """Send disarm command.""" if code == "1234":