Add improved scene support to the alarm_control_panel integration (#28269)
* Add improved scene support to the alarm_control_panel integration * Add service description for alarm_arm_custom_bypass
This commit is contained in:
parent
1fb377e61e
commit
12f1a8f551
3 changed files with 242 additions and 0 deletions
|
@ -0,0 +1,84 @@
|
||||||
|
"""Reproduce an Alarm control panel state."""
|
||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
from typing import Iterable, Optional
|
||||||
|
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
SERVICE_ALARM_ARM_AWAY,
|
||||||
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
|
SERVICE_ALARM_ARM_HOME,
|
||||||
|
SERVICE_ALARM_ARM_NIGHT,
|
||||||
|
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_DISARMED,
|
||||||
|
STATE_ALARM_TRIGGERED,
|
||||||
|
)
|
||||||
|
from homeassistant.core import Context, State
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
|
|
||||||
|
from . import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
VALID_STATES = {
|
||||||
|
STATE_ALARM_ARMED_AWAY,
|
||||||
|
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
|
STATE_ALARM_ARMED_HOME,
|
||||||
|
STATE_ALARM_ARMED_NIGHT,
|
||||||
|
STATE_ALARM_DISARMED,
|
||||||
|
STATE_ALARM_TRIGGERED,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_reproduce_state(
|
||||||
|
hass: HomeAssistantType, state: State, context: Optional[Context] = None
|
||||||
|
) -> None:
|
||||||
|
"""Reproduce a single state."""
|
||||||
|
cur_state = hass.states.get(state.entity_id)
|
||||||
|
|
||||||
|
if cur_state is None:
|
||||||
|
_LOGGER.warning("Unable to find entity %s", state.entity_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
if state.state not in VALID_STATES:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Invalid state specified for %s: %s", state.entity_id, state.state
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Return if we are already at the right state.
|
||||||
|
if cur_state.state == state.state:
|
||||||
|
return
|
||||||
|
|
||||||
|
service_data = {ATTR_ENTITY_ID: state.entity_id}
|
||||||
|
|
||||||
|
if state.state == STATE_ALARM_ARMED_AWAY:
|
||||||
|
service = SERVICE_ALARM_ARM_AWAY
|
||||||
|
elif state.state == STATE_ALARM_ARMED_CUSTOM_BYPASS:
|
||||||
|
service = SERVICE_ALARM_ARM_CUSTOM_BYPASS
|
||||||
|
elif state.state == STATE_ALARM_ARMED_HOME:
|
||||||
|
service = SERVICE_ALARM_ARM_HOME
|
||||||
|
elif state.state == STATE_ALARM_ARMED_NIGHT:
|
||||||
|
service = SERVICE_ALARM_ARM_NIGHT
|
||||||
|
elif state.state == STATE_ALARM_DISARMED:
|
||||||
|
service = SERVICE_ALARM_DISARM
|
||||||
|
elif state.state == STATE_ALARM_TRIGGERED:
|
||||||
|
service = SERVICE_ALARM_TRIGGER
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN, service, service_data, context=context, blocking=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_reproduce_states(
|
||||||
|
hass: HomeAssistantType, states: Iterable[State], context: Optional[Context] = None
|
||||||
|
) -> None:
|
||||||
|
"""Reproduce Alarm control panel states."""
|
||||||
|
await asyncio.gather(
|
||||||
|
*(_async_reproduce_state(hass, state, context) for state in states)
|
||||||
|
)
|
|
@ -10,6 +10,16 @@ alarm_disarm:
|
||||||
description: An optional code to disarm the alarm control panel with.
|
description: An optional code to disarm the alarm control panel with.
|
||||||
example: 1234
|
example: 1234
|
||||||
|
|
||||||
|
alarm_arm_custom_bypass:
|
||||||
|
description: Send arm custom bypass command.
|
||||||
|
fields:
|
||||||
|
entity_id:
|
||||||
|
description: Name of alarm control panel to arm custom bypass.
|
||||||
|
example: 'alarm_control_panel.downstairs'
|
||||||
|
code:
|
||||||
|
description: An optional code to arm custom bypass the alarm control panel with.
|
||||||
|
example: 1234
|
||||||
|
|
||||||
alarm_arm_home:
|
alarm_arm_home:
|
||||||
description: Send the alarm the command for arm home.
|
description: Send the alarm the command for arm home.
|
||||||
fields:
|
fields:
|
||||||
|
|
148
tests/components/alarm_control_panel/test_reproduce_state.py
Normal file
148
tests/components/alarm_control_panel/test_reproduce_state.py
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
"""Test reproduce state for Alarm control panel."""
|
||||||
|
from homeassistant.const import (
|
||||||
|
SERVICE_ALARM_ARM_AWAY,
|
||||||
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
|
SERVICE_ALARM_ARM_HOME,
|
||||||
|
SERVICE_ALARM_ARM_NIGHT,
|
||||||
|
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_DISARMED,
|
||||||
|
STATE_ALARM_TRIGGERED,
|
||||||
|
)
|
||||||
|
from homeassistant.core import State
|
||||||
|
|
||||||
|
from tests.common import async_mock_service
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reproducing_states(hass, caplog):
|
||||||
|
"""Test reproducing Alarm control panel states."""
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.entity_armed_away", STATE_ALARM_ARMED_AWAY, {}
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.entity_armed_custom_bypass",
|
||||||
|
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.entity_armed_home", STATE_ALARM_ARMED_HOME, {}
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.entity_armed_night", STATE_ALARM_ARMED_NIGHT, {}
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.entity_disarmed", STATE_ALARM_DISARMED, {}
|
||||||
|
)
|
||||||
|
hass.states.async_set(
|
||||||
|
"alarm_control_panel.entity_triggered", STATE_ALARM_TRIGGERED, {}
|
||||||
|
)
|
||||||
|
|
||||||
|
arm_away_calls = async_mock_service(
|
||||||
|
hass, "alarm_control_panel", SERVICE_ALARM_ARM_AWAY
|
||||||
|
)
|
||||||
|
arm_custom_bypass_calls = async_mock_service(
|
||||||
|
hass, "alarm_control_panel", SERVICE_ALARM_ARM_CUSTOM_BYPASS
|
||||||
|
)
|
||||||
|
arm_home_calls = async_mock_service(
|
||||||
|
hass, "alarm_control_panel", SERVICE_ALARM_ARM_HOME
|
||||||
|
)
|
||||||
|
arm_night_calls = async_mock_service(
|
||||||
|
hass, "alarm_control_panel", SERVICE_ALARM_ARM_NIGHT
|
||||||
|
)
|
||||||
|
disarm_calls = async_mock_service(hass, "alarm_control_panel", SERVICE_ALARM_DISARM)
|
||||||
|
trigger_calls = async_mock_service(
|
||||||
|
hass, "alarm_control_panel", SERVICE_ALARM_TRIGGER
|
||||||
|
)
|
||||||
|
|
||||||
|
# These calls should do nothing as entities already in desired state
|
||||||
|
await hass.helpers.state.async_reproduce_state(
|
||||||
|
[
|
||||||
|
State("alarm_control_panel.entity_armed_away", STATE_ALARM_ARMED_AWAY),
|
||||||
|
State(
|
||||||
|
"alarm_control_panel.entity_armed_custom_bypass",
|
||||||
|
STATE_ALARM_ARMED_CUSTOM_BYPASS,
|
||||||
|
),
|
||||||
|
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_disarmed", STATE_ALARM_DISARMED),
|
||||||
|
State("alarm_control_panel.entity_triggered", STATE_ALARM_TRIGGERED),
|
||||||
|
],
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(arm_away_calls) == 0
|
||||||
|
assert len(arm_custom_bypass_calls) == 0
|
||||||
|
assert len(arm_home_calls) == 0
|
||||||
|
assert len(arm_night_calls) == 0
|
||||||
|
assert len(disarm_calls) == 0
|
||||||
|
assert len(trigger_calls) == 0
|
||||||
|
|
||||||
|
# Test invalid state is handled
|
||||||
|
await hass.helpers.state.async_reproduce_state(
|
||||||
|
[State("alarm_control_panel.entity_triggered", "not_supported")], blocking=True
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "not_supported" in caplog.text
|
||||||
|
assert len(arm_away_calls) == 0
|
||||||
|
assert len(arm_custom_bypass_calls) == 0
|
||||||
|
assert len(arm_home_calls) == 0
|
||||||
|
assert len(arm_night_calls) == 0
|
||||||
|
assert len(disarm_calls) == 0
|
||||||
|
assert len(trigger_calls) == 0
|
||||||
|
|
||||||
|
# Make sure correct services are called
|
||||||
|
await hass.helpers.state.async_reproduce_state(
|
||||||
|
[
|
||||||
|
State("alarm_control_panel.entity_armed_away", STATE_ALARM_TRIGGERED),
|
||||||
|
State(
|
||||||
|
"alarm_control_panel.entity_armed_custom_bypass", STATE_ALARM_ARMED_AWAY
|
||||||
|
),
|
||||||
|
State(
|
||||||
|
"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_triggered", STATE_ALARM_DISARMED),
|
||||||
|
# Should not raise
|
||||||
|
State("alarm_control_panel.non_existing", "on"),
|
||||||
|
],
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(arm_away_calls) == 1
|
||||||
|
assert arm_away_calls[0].domain == "alarm_control_panel"
|
||||||
|
assert arm_away_calls[0].data == {
|
||||||
|
"entity_id": "alarm_control_panel.entity_armed_custom_bypass"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(arm_custom_bypass_calls) == 1
|
||||||
|
assert arm_custom_bypass_calls[0].domain == "alarm_control_panel"
|
||||||
|
assert arm_custom_bypass_calls[0].data == {
|
||||||
|
"entity_id": "alarm_control_panel.entity_armed_home"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(arm_home_calls) == 1
|
||||||
|
assert arm_home_calls[0].domain == "alarm_control_panel"
|
||||||
|
assert arm_home_calls[0].data == {
|
||||||
|
"entity_id": "alarm_control_panel.entity_armed_night"
|
||||||
|
}
|
||||||
|
|
||||||
|
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_disarmed"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert len(disarm_calls) == 1
|
||||||
|
assert disarm_calls[0].domain == "alarm_control_panel"
|
||||||
|
assert disarm_calls[0].data == {"entity_id": "alarm_control_panel.entity_triggered"}
|
||||||
|
|
||||||
|
assert len(trigger_calls) == 1
|
||||||
|
assert trigger_calls[0].domain == "alarm_control_panel"
|
||||||
|
assert trigger_calls[0].data == {
|
||||||
|
"entity_id": "alarm_control_panel.entity_armed_away"
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue