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_CUSTOM_BYPASS,
SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_HOME,
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_NIGHT,
SERVICE_ALARM_ARM_VACATION,
SERVICE_ALARM_DISARM, SERVICE_ALARM_DISARM,
SERVICE_ALARM_TRIGGER, SERVICE_ALARM_TRIGGER,
) )
@ -30,6 +31,7 @@ from .const import (
SUPPORT_ALARM_ARM_CUSTOM_BYPASS, SUPPORT_ALARM_ARM_CUSTOM_BYPASS,
SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT, SUPPORT_ALARM_ARM_NIGHT,
SUPPORT_ALARM_ARM_VACATION,
SUPPORT_ALARM_TRIGGER, SUPPORT_ALARM_TRIGGER,
) )
@ -81,6 +83,12 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"async_alarm_arm_night", "async_alarm_arm_night",
[SUPPORT_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( component.async_register_entity_service(
SERVICE_ALARM_ARM_CUSTOM_BYPASS, SERVICE_ALARM_ARM_CUSTOM_BYPASS,
ALARM_SERVICE_SCHEMA, ALARM_SERVICE_SCHEMA,
@ -164,6 +172,14 @@ class AlarmControlPanelEntity(Entity):
"""Send arm night command.""" """Send arm night command."""
await self.hass.async_add_executor_job(self.alarm_arm_night, code) 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: def alarm_trigger(self, code: str | None = None) -> None:
"""Send alarm trigger command.""" """Send alarm trigger command."""
raise NotImplementedError() raise NotImplementedError()

View File

@ -7,10 +7,12 @@ SUPPORT_ALARM_ARM_AWAY: Final = 2
SUPPORT_ALARM_ARM_NIGHT: Final = 4 SUPPORT_ALARM_ARM_NIGHT: Final = 4
SUPPORT_ALARM_TRIGGER: Final = 8 SUPPORT_ALARM_TRIGGER: Final = 8
SUPPORT_ALARM_ARM_CUSTOM_BYPASS: Final = 16 SUPPORT_ALARM_ARM_CUSTOM_BYPASS: Final = 16
SUPPORT_ALARM_ARM_VACATION: Final = 32
CONDITION_TRIGGERED: Final = "is_triggered" CONDITION_TRIGGERED: Final = "is_triggered"
CONDITION_DISARMED: Final = "is_disarmed" CONDITION_DISARMED: Final = "is_disarmed"
CONDITION_ARMED_HOME: Final = "is_armed_home" CONDITION_ARMED_HOME: Final = "is_armed_home"
CONDITION_ARMED_AWAY: Final = "is_armed_away" CONDITION_ARMED_AWAY: Final = "is_armed_away"
CONDITION_ARMED_NIGHT: Final = "is_armed_night" CONDITION_ARMED_NIGHT: Final = "is_armed_night"
CONDITION_ARMED_VACATION: Final = "is_armed_vacation"
CONDITION_ARMED_CUSTOM_BYPASS: Final = "is_armed_custom_bypass" 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_AWAY,
SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_HOME,
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_NIGHT,
SERVICE_ALARM_ARM_VACATION,
SERVICE_ALARM_DISARM, SERVICE_ALARM_DISARM,
SERVICE_ALARM_TRIGGER, SERVICE_ALARM_TRIGGER,
) )
@ -30,6 +31,7 @@ from .const import (
SUPPORT_ALARM_ARM_AWAY, SUPPORT_ALARM_ARM_AWAY,
SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT, SUPPORT_ALARM_ARM_NIGHT,
SUPPORT_ALARM_ARM_VACATION,
SUPPORT_ALARM_TRIGGER, SUPPORT_ALARM_TRIGGER,
) )
@ -37,6 +39,7 @@ ACTION_TYPES: Final[set[str]] = {
"arm_away", "arm_away",
"arm_home", "arm_home",
"arm_night", "arm_night",
"arm_vacation",
"disarm", "disarm",
"trigger", "trigger",
} }
@ -77,6 +80,8 @@ async def async_get_actions(
actions.append({**base_action, CONF_TYPE: "arm_home"}) actions.append({**base_action, CONF_TYPE: "arm_home"})
if supported_features & SUPPORT_ALARM_ARM_NIGHT: if supported_features & SUPPORT_ALARM_ARM_NIGHT:
actions.append({**base_action, CONF_TYPE: "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"}) actions.append({**base_action, CONF_TYPE: "disarm"})
if supported_features & SUPPORT_ALARM_TRIGGER: if supported_features & SUPPORT_ALARM_TRIGGER:
actions.append({**base_action, CONF_TYPE: "trigger"}) actions.append({**base_action, CONF_TYPE: "trigger"})
@ -98,6 +103,8 @@ async def async_call_action_from_config(
service = SERVICE_ALARM_ARM_HOME service = SERVICE_ALARM_ARM_HOME
elif config[CONF_TYPE] == "arm_night": elif config[CONF_TYPE] == "arm_night":
service = SERVICE_ALARM_ARM_NIGHT service = SERVICE_ALARM_ARM_NIGHT
elif config[CONF_TYPE] == "arm_vacation":
service = SERVICE_ALARM_ARM_VACATION
elif config[CONF_TYPE] == "disarm": elif config[CONF_TYPE] == "disarm":
service = SERVICE_ALARM_DISARM service = SERVICE_ALARM_DISARM
elif config[CONF_TYPE] == "trigger": 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_CUSTOM_BYPASS,
SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT, SUPPORT_ALARM_ARM_NIGHT,
SUPPORT_ALARM_ARM_VACATION,
) )
from homeassistant.const import ( from homeassistant.const import (
ATTR_ENTITY_ID, ATTR_ENTITY_ID,
@ -22,6 +23,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
@ -37,6 +39,7 @@ from .const import (
CONDITION_ARMED_CUSTOM_BYPASS, CONDITION_ARMED_CUSTOM_BYPASS,
CONDITION_ARMED_HOME, CONDITION_ARMED_HOME,
CONDITION_ARMED_NIGHT, CONDITION_ARMED_NIGHT,
CONDITION_ARMED_VACATION,
CONDITION_DISARMED, CONDITION_DISARMED,
CONDITION_TRIGGERED, CONDITION_TRIGGERED,
) )
@ -47,6 +50,7 @@ CONDITION_TYPES: Final[set[str]] = {
CONDITION_ARMED_HOME, CONDITION_ARMED_HOME,
CONDITION_ARMED_AWAY, CONDITION_ARMED_AWAY,
CONDITION_ARMED_NIGHT, CONDITION_ARMED_NIGHT,
CONDITION_ARMED_VACATION,
CONDITION_ARMED_CUSTOM_BYPASS, CONDITION_ARMED_CUSTOM_BYPASS,
} }
@ -90,6 +94,8 @@ async def async_get_conditions(
conditions.append({**base_condition, CONF_TYPE: CONDITION_ARMED_AWAY}) conditions.append({**base_condition, CONF_TYPE: CONDITION_ARMED_AWAY})
if supported_features & SUPPORT_ALARM_ARM_NIGHT: if supported_features & SUPPORT_ALARM_ARM_NIGHT:
conditions.append({**base_condition, CONF_TYPE: CONDITION_ARMED_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: if supported_features & SUPPORT_ALARM_ARM_CUSTOM_BYPASS:
conditions.append( conditions.append(
{**base_condition, CONF_TYPE: CONDITION_ARMED_CUSTOM_BYPASS} {**base_condition, CONF_TYPE: CONDITION_ARMED_CUSTOM_BYPASS}
@ -114,6 +120,8 @@ def async_condition_from_config(
state = STATE_ALARM_ARMED_AWAY state = STATE_ALARM_ARMED_AWAY
elif config[CONF_TYPE] == CONDITION_ARMED_NIGHT: elif config[CONF_TYPE] == CONDITION_ARMED_NIGHT:
state = STATE_ALARM_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: elif config[CONF_TYPE] == CONDITION_ARMED_CUSTOM_BYPASS:
state = STATE_ALARM_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_AWAY,
SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT, SUPPORT_ALARM_ARM_NIGHT,
SUPPORT_ALARM_ARM_VACATION,
) )
from homeassistant.components.automation import AutomationActionType from homeassistant.components.automation import AutomationActionType
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA 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_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_ARMING, STATE_ALARM_ARMING,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
@ -39,6 +41,7 @@ TRIGGER_TYPES: Final[set[str]] = BASIC_TRIGGER_TYPES | {
"armed_home", "armed_home",
"armed_away", "armed_away",
"armed_night", "armed_night",
"armed_vacation",
} }
TRIGGER_SCHEMA: Final = DEVICE_TRIGGER_BASE_SCHEMA.extend( TRIGGER_SCHEMA: Final = DEVICE_TRIGGER_BASE_SCHEMA.extend(
@ -100,6 +103,13 @@ async def async_get_triggers(
CONF_TYPE: "armed_night", CONF_TYPE: "armed_night",
} }
) )
if supported_features & SUPPORT_ALARM_ARM_VACATION:
triggers.append(
{
**base_trigger,
CONF_TYPE: "armed_vacation",
}
)
return triggers return triggers
@ -134,6 +144,8 @@ async def async_attach_trigger(
to_state = STATE_ALARM_ARMED_AWAY to_state = STATE_ALARM_ARMED_AWAY
elif config[CONF_TYPE] == "armed_night": elif config[CONF_TYPE] == "armed_night":
to_state = STATE_ALARM_ARMED_NIGHT to_state = STATE_ALARM_ARMED_NIGHT
elif config[CONF_TYPE] == "armed_vacation":
to_state = STATE_ALARM_ARMED_VACATION
state_config = { state_config = {
state_trigger.CONF_PLATFORM: "state", state_trigger.CONF_PLATFORM: "state",

View File

@ -7,6 +7,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
STATE_OFF, STATE_OFF,
) )
@ -24,6 +25,7 @@ def async_describe_on_off_states(
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
}, },
STATE_OFF, STATE_OFF,

View File

@ -12,12 +12,14 @@ from homeassistant.const import (
SERVICE_ALARM_ARM_CUSTOM_BYPASS, SERVICE_ALARM_ARM_CUSTOM_BYPASS,
SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_HOME,
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_NIGHT,
SERVICE_ALARM_ARM_VACATION,
SERVICE_ALARM_DISARM, SERVICE_ALARM_DISARM,
SERVICE_ALARM_TRIGGER, SERVICE_ALARM_TRIGGER,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
@ -32,6 +34,7 @@ VALID_STATES: Final[set[str]] = {
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
} }
@ -71,6 +74,8 @@ async def _async_reproduce_state(
service = SERVICE_ALARM_ARM_HOME service = SERVICE_ALARM_ARM_HOME
elif state.state == STATE_ALARM_ARMED_NIGHT: elif state.state == STATE_ALARM_ARMED_NIGHT:
service = SERVICE_ALARM_ARM_NIGHT service = SERVICE_ALARM_ARM_NIGHT
elif state.state == STATE_ALARM_ARMED_VACATION:
service = SERVICE_ALARM_ARM_VACATION
elif state.state == STATE_ALARM_DISARMED: elif state.state == STATE_ALARM_DISARMED:
service = SERVICE_ALARM_DISARM service = SERVICE_ALARM_DISARM
elif state.state == STATE_ALARM_TRIGGERED: elif state.state == STATE_ALARM_TRIGGERED:

View File

@ -70,6 +70,20 @@ alarm_arm_night:
selector: selector:
text: 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: alarm_trigger:
name: Trigger name: Trigger
description: Send the alarm the command for trigger. description: Send the alarm the command for trigger.

View File

@ -5,6 +5,7 @@
"arm_away": "Arm {entity_name} away", "arm_away": "Arm {entity_name} away",
"arm_home": "Arm {entity_name} home", "arm_home": "Arm {entity_name} home",
"arm_night": "Arm {entity_name} night", "arm_night": "Arm {entity_name} night",
"arm_vacation": "Arm {entity_name} vacation",
"disarm": "Disarm {entity_name}", "disarm": "Disarm {entity_name}",
"trigger": "Trigger {entity_name}" "trigger": "Trigger {entity_name}"
}, },
@ -13,14 +14,16 @@
"is_disarmed": "{entity_name} is disarmed", "is_disarmed": "{entity_name} is disarmed",
"is_armed_home": "{entity_name} is armed home", "is_armed_home": "{entity_name} is armed home",
"is_armed_away": "{entity_name} is armed away", "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": { "trigger_type": {
"triggered": "{entity_name} triggered", "triggered": "{entity_name} triggered",
"disarmed": "{entity_name} disarmed", "disarmed": "{entity_name} disarmed",
"armed_home": "{entity_name} armed home", "armed_home": "{entity_name} armed home",
"armed_away": "{entity_name} armed away", "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": { "state": {
@ -30,6 +33,7 @@
"armed_home": "Armed home", "armed_home": "Armed home",
"armed_away": "Armed away", "armed_away": "Armed away",
"armed_night": "Armed night", "armed_night": "Armed night",
"armed_vacation": "Armed vacation",
"armed_custom_bypass": "Armed custom bypass", "armed_custom_bypass": "Armed custom bypass",
"pending": "Pending", "pending": "Pending",
"arming": "Arming", "arming": "Arming",

View File

@ -266,6 +266,7 @@ STATE_ALARM_DISARMED: Final = "disarmed"
STATE_ALARM_ARMED_HOME: Final = "armed_home" STATE_ALARM_ARMED_HOME: Final = "armed_home"
STATE_ALARM_ARMED_AWAY: Final = "armed_away" STATE_ALARM_ARMED_AWAY: Final = "armed_away"
STATE_ALARM_ARMED_NIGHT: Final = "armed_night" 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_ARMED_CUSTOM_BYPASS: Final = "armed_custom_bypass"
STATE_ALARM_PENDING: Final = "pending" STATE_ALARM_PENDING: Final = "pending"
STATE_ALARM_ARMING: Final = "arming" 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_HOME: Final = "alarm_arm_home"
SERVICE_ALARM_ARM_AWAY: Final = "alarm_arm_away" SERVICE_ALARM_ARM_AWAY: Final = "alarm_arm_away"
SERVICE_ALARM_ARM_NIGHT: Final = "alarm_arm_night" 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_ARM_CUSTOM_BYPASS: Final = "alarm_arm_custom_bypass"
SERVICE_ALARM_TRIGGER: Final = "alarm_trigger" SERVICE_ALARM_TRIGGER: Final = "alarm_trigger"

View File

@ -8,6 +8,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
STATE_UNKNOWN, 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_AWAY, ["disarm", "arm_away"]),
(True, 0, const.SUPPORT_ALARM_ARM_HOME, ["disarm", "arm_home"]), (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_NIGHT, ["disarm", "arm_night"]),
(True, 0, const.SUPPORT_ALARM_ARM_VACATION, ["disarm", "arm_vacation"]),
(True, 0, const.SUPPORT_ALARM_TRIGGER, ["disarm", "trigger"]), (True, 0, const.SUPPORT_ALARM_TRIGGER, ["disarm", "trigger"]),
], ],
) )
@ -150,13 +152,14 @@ async def test_get_action_capabilities(
"arm_away": {"extra_fields": []}, "arm_away": {"extra_fields": []},
"arm_home": {"extra_fields": []}, "arm_home": {"extra_fields": []},
"arm_night": {"extra_fields": []}, "arm_night": {"extra_fields": []},
"arm_vacation": {"extra_fields": []},
"disarm": { "disarm": {
"extra_fields": [{"name": "code", "optional": True, "type": "string"}] "extra_fields": [{"name": "code", "optional": True, "type": "string"}]
}, },
"trigger": {"extra_fields": []}, "trigger": {"extra_fields": []},
} }
actions = await async_get_device_automations(hass, "action", device_entry.id) actions = await async_get_device_automations(hass, "action", device_entry.id)
assert len(actions) == 5 assert len(actions) == 6
for action in actions: for action in actions:
capabilities = await async_get_device_automation_capabilities( capabilities = await async_get_device_automation_capabilities(
hass, "action", action hass, "action", action
@ -196,13 +199,16 @@ async def test_get_action_capabilities_arm_code(
"arm_night": { "arm_night": {
"extra_fields": [{"name": "code", "optional": True, "type": "string"}] "extra_fields": [{"name": "code", "optional": True, "type": "string"}]
}, },
"arm_vacation": {
"extra_fields": [{"name": "code", "optional": True, "type": "string"}]
},
"disarm": { "disarm": {
"extra_fields": [{"name": "code", "optional": True, "type": "string"}] "extra_fields": [{"name": "code", "optional": True, "type": "string"}]
}, },
"trigger": {"extra_fields": []}, "trigger": {"extra_fields": []},
} }
actions = await async_get_device_automations(hass, "action", device_entry.id) actions = await async_get_device_automations(hass, "action", device_entry.id)
assert len(actions) == 5 assert len(actions) == 6
for action in actions: for action in actions:
capabilities = await async_get_device_automation_capabilities( capabilities = await async_get_device_automation_capabilities(
hass, "action", action hass, "action", action
@ -256,6 +262,18 @@ async def test_action(hass, enable_custom_integrations):
"type": "arm_night", "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"}, "trigger": {"platform": "event", "event_type": "test_event_disarm"},
"action": { "action": {
@ -302,6 +320,13 @@ async def test_action(hass, enable_custom_integrations):
== STATE_ALARM_ARMED_HOME == 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") hass.bus.async_fire("test_event_arm_night")
await hass.async_block_till_done() await hass.async_block_till_done()
assert ( assert (

View File

@ -8,6 +8,7 @@ from homeassistant.const import (
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, 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_AWAY, 0, ["is_armed_away"]),
(False, const.SUPPORT_ALARM_ARM_HOME, 0, ["is_armed_home"]), (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_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"]), (False, const.SUPPORT_ALARM_ARM_CUSTOM_BYPASS, 0, ["is_armed_custom_bypass"]),
(True, 0, 0, []), (True, 0, 0, []),
(True, 0, const.SUPPORT_ALARM_ARM_AWAY, ["is_armed_away"]), (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_HOME, ["is_armed_home"]),
(True, 0, const.SUPPORT_ALARM_ARM_NIGHT, ["is_armed_night"]), (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"]), (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"}, "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": [
{ {
"condition": "device", "condition": "device",
@ -238,6 +259,7 @@ async def test_if_state(hass, calls):
hass.bus.async_fire("test_event4") hass.bus.async_fire("test_event4")
hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event5")
hass.bus.async_fire("test_event6") hass.bus.async_fire("test_event6")
hass.bus.async_fire("test_event7")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 1 assert len(calls) == 1
assert calls[0].data["some"] == "is_triggered - event - test_event1" 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_event4")
hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event5")
hass.bus.async_fire("test_event6") hass.bus.async_fire("test_event6")
hass.bus.async_fire("test_event7")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 2 assert len(calls) == 2
assert calls[1].data["some"] == "is_disarmed - event - test_event2" 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_event4")
hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event5")
hass.bus.async_fire("test_event6") hass.bus.async_fire("test_event6")
hass.bus.async_fire("test_event7")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 3 assert len(calls) == 3
assert calls[2].data["some"] == "is_armed_home - event - test_event3" 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_event4")
hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event5")
hass.bus.async_fire("test_event6") hass.bus.async_fire("test_event6")
hass.bus.async_fire("test_event7")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 4 assert len(calls) == 4
assert calls[3].data["some"] == "is_armed_away - event - test_event4" 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_event4")
hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event5")
hass.bus.async_fire("test_event6") hass.bus.async_fire("test_event6")
hass.bus.async_fire("test_event7")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 5 assert len(calls) == 5
assert calls[4].data["some"] == "is_armed_night - event - test_event5" 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.states.async_set("alarm_control_panel.entity", STATE_ALARM_ARMED_CUSTOM_BYPASS)
hass.bus.async_fire("test_event1") hass.bus.async_fire("test_event1")
hass.bus.async_fire("test_event2") 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_event4")
hass.bus.async_fire("test_event5") hass.bus.async_fire("test_event5")
hass.bus.async_fire("test_event6") hass.bus.async_fire("test_event6")
hass.bus.async_fire("test_event7")
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(calls) == 6 assert len(calls) == 7
assert calls[5].data["some"] == "is_armed_custom_bypass - event - test_event6" 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_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_PENDING, STATE_ALARM_PENDING,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
@ -54,7 +55,7 @@ def calls(hass):
(False, 0, 0, ["triggered", "disarmed", "arming"]), (False, 0, 0, ["triggered", "disarmed", "arming"]),
( (
False, False,
15, 47,
0, 0,
[ [
"triggered", "triggered",
@ -63,13 +64,14 @@ def calls(hass):
"armed_home", "armed_home",
"armed_away", "armed_away",
"armed_night", "armed_night",
"armed_vacation",
], ],
), ),
(True, 0, 0, ["triggered", "disarmed", "arming"]), (True, 0, 0, ["triggered", "disarmed", "arming"]),
( (
True, True,
0, 0,
15, 47,
[ [
"triggered", "triggered",
"disarmed", "disarmed",
@ -77,6 +79,7 @@ def calls(hass):
"armed_home", "armed_home",
"armed_away", "armed_away",
"armed_night", "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" == "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): async def test_if_fires_on_state_change_with_for(hass, calls):
"""Test for triggers firing with delay.""" """Test for triggers firing with delay."""

View File

@ -4,12 +4,14 @@ from homeassistant.const import (
SERVICE_ALARM_ARM_CUSTOM_BYPASS, SERVICE_ALARM_ARM_CUSTOM_BYPASS,
SERVICE_ALARM_ARM_HOME, SERVICE_ALARM_ARM_HOME,
SERVICE_ALARM_ARM_NIGHT, SERVICE_ALARM_ARM_NIGHT,
SERVICE_ALARM_ARM_VACATION,
SERVICE_ALARM_DISARM, SERVICE_ALARM_DISARM,
SERVICE_ALARM_TRIGGER, SERVICE_ALARM_TRIGGER,
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
@ -34,6 +36,9 @@ async def test_reproducing_states(hass, caplog):
hass.states.async_set( hass.states.async_set(
"alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_NIGHT, {} "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( hass.states.async_set(
"alarm_control_panel.entity_disarmed", STATE_ALARM_DISARMED, {} "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( arm_night_calls = async_mock_service(
hass, "alarm_control_panel", SERVICE_ALARM_ARM_NIGHT 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) disarm_calls = async_mock_service(hass, "alarm_control_panel", SERVICE_ALARM_DISARM)
trigger_calls = async_mock_service( trigger_calls = async_mock_service(
hass, "alarm_control_panel", SERVICE_ALARM_TRIGGER 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_home", STATE_ALARM_ARMED_HOME),
State("alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_NIGHT), 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_disarmed", STATE_ALARM_DISARMED),
State("alarm_control_panel.entity_triggered", STATE_ALARM_TRIGGERED), 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_custom_bypass_calls) == 0
assert len(arm_home_calls) == 0 assert len(arm_home_calls) == 0
assert len(arm_night_calls) == 0 assert len(arm_night_calls) == 0
assert len(arm_vacation_calls) == 0
assert len(disarm_calls) == 0 assert len(disarm_calls) == 0
assert len(trigger_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_custom_bypass_calls) == 0
assert len(arm_home_calls) == 0 assert len(arm_home_calls) == 0
assert len(arm_night_calls) == 0 assert len(arm_night_calls) == 0
assert len(arm_vacation_calls) == 0
assert len(disarm_calls) == 0 assert len(disarm_calls) == 0
assert len(trigger_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 "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_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), State("alarm_control_panel.entity_triggered", STATE_ALARM_DISARMED),
# Should not raise # Should not raise
State("alarm_control_panel.non_existing", "on"), 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 len(arm_night_calls) == 1
assert arm_night_calls[0].domain == "alarm_control_panel" assert arm_night_calls[0].domain == "alarm_control_panel"
assert arm_night_calls[0].data == { 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" "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 "alarm_control_panel", "test", "5678", device_id=device_entry.id
) )
hass.states.async_set( hass.states.async_set(
"alarm_control_panel.test_5678", "attributes", {"supported_features": 15} "alarm_control_panel.test_5678", "attributes", {"supported_features": 47}
) )
expected_capabilities = { expected_capabilities = {
"arm_away": {"extra_fields": []}, "arm_away": {"extra_fields": []},
"arm_home": {"extra_fields": []}, "arm_home": {"extra_fields": []},
"arm_night": {"extra_fields": []}, "arm_night": {"extra_fields": []},
"arm_vacation": {"extra_fields": []},
"disarm": { "disarm": {
"extra_fields": [{"name": "code", "optional": True, "type": "string"}] "extra_fields": [{"name": "code", "optional": True, "type": "string"}]
}, },
@ -209,7 +210,7 @@ async def test_websocket_get_action_capabilities(
actions = msg["result"] actions = msg["result"]
id = 2 id = 2
assert len(actions) == 5 assert len(actions) == 6
for action in actions: for action in actions:
await client.send_json( 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_AWAY,
SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT, SUPPORT_ALARM_ARM_NIGHT,
SUPPORT_ALARM_ARM_VACATION,
SUPPORT_ALARM_TRIGGER, SUPPORT_ALARM_TRIGGER,
) )
from homeassistant.const import ( from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_ARMED_VACATION,
STATE_ALARM_DISARMED, STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED, STATE_ALARM_TRIGGERED,
) )
@ -79,6 +81,7 @@ class MockAlarm(MockEntity, AlarmControlPanelEntity):
| SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_AWAY
| SUPPORT_ALARM_ARM_NIGHT | SUPPORT_ALARM_ARM_NIGHT
| SUPPORT_ALARM_TRIGGER | SUPPORT_ALARM_TRIGGER
| SUPPORT_ALARM_ARM_VACATION
) )
def alarm_arm_away(self, code=None): def alarm_arm_away(self, code=None):
@ -96,6 +99,11 @@ class MockAlarm(MockEntity, AlarmControlPanelEntity):
self._state = STATE_ALARM_ARMED_NIGHT self._state = STATE_ALARM_ARMED_NIGHT
self.schedule_update_ha_state() 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): def alarm_disarm(self, code=None):
"""Send disarm command.""" """Send disarm command."""
if code == "1234": if code == "1234":