Vacation Mode on Alarm Panels (#45980)

Co-authored-by: Nathan Tilley <nathan@tilley.xyz>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Franck Nijhof <frenck@frenck.nl>
Co-authored-by: Khole Jones <ktech6@outlook.com>
Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
posixx 2021-07-01 17:26:32 +02:00 committed by GitHub
parent 57fbb1c3d9
commit 312531988a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 207 additions and 11 deletions

View File

@ -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()

View File

@ -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"

View File

@ -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":

View File

@ -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

View File

@ -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",

View File

@ -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,

View File

@ -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:

View File

@ -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.

View File

@ -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",

View File

@ -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"

View File

@ -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 (

View File

@ -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"

View File

@ -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."""

View File

@ -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"
}

View File

@ -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(
{

View File

@ -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":