mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add instant arming for totalconnect (#60156)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
f75b325ab2
commit
3198211a7f
@ -21,12 +21,16 @@ from homeassistant.const import (
|
|||||||
STATE_ALARM_TRIGGERED,
|
STATE_ALARM_TRIGGERED,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers import entity_platform
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
SERVICE_ALARM_ARM_AWAY_INSTANT = "arm_away_instant"
|
||||||
|
SERVICE_ALARM_ARM_HOME_INSTANT = "arm_home_instant"
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, entry, async_add_entities) -> None:
|
async def async_setup_entry(hass, entry, async_add_entities) -> None:
|
||||||
"""Set up TotalConnect alarm panels based on a config entry."""
|
"""Set up TotalConnect alarm panels based on a config entry."""
|
||||||
@ -48,6 +52,21 @@ async def async_setup_entry(hass, entry, async_add_entities) -> None:
|
|||||||
|
|
||||||
async_add_entities(alarms, True)
|
async_add_entities(alarms, True)
|
||||||
|
|
||||||
|
# Set up services
|
||||||
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_ALARM_ARM_AWAY_INSTANT,
|
||||||
|
None,
|
||||||
|
"async_alarm_arm_away_instant",
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
SERVICE_ALARM_ARM_HOME_INSTANT,
|
||||||
|
None,
|
||||||
|
"async_alarm_arm_home_instant",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity):
|
class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity):
|
||||||
"""Represent an TotalConnect status."""
|
"""Represent an TotalConnect status."""
|
||||||
@ -201,3 +220,31 @@ class TotalConnectAlarm(CoordinatorEntity, alarm.AlarmControlPanelEntity):
|
|||||||
raise HomeAssistantError(
|
raise HomeAssistantError(
|
||||||
f"TotalConnect failed to arm night {self._name}."
|
f"TotalConnect failed to arm night {self._name}."
|
||||||
) from error
|
) from error
|
||||||
|
|
||||||
|
async def async_alarm_arm_home_instant(self, code=None):
|
||||||
|
"""Send arm home instant command."""
|
||||||
|
await self.hass.async_add_executor_job(self._arm_home_instant)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
def _arm_home_instant(self):
|
||||||
|
"""Arm home instant synchronous."""
|
||||||
|
try:
|
||||||
|
ArmingHelper(self._partition).arm_stay_instant()
|
||||||
|
except BadResultCodeError as error:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
f"TotalConnect failed to arm home instant {self._name}."
|
||||||
|
) from error
|
||||||
|
|
||||||
|
async def async_alarm_arm_away_instant(self, code=None):
|
||||||
|
"""Send arm away instant command."""
|
||||||
|
await self.hass.async_add_executor_job(self._arm_away_instant)
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
|
|
||||||
|
def _arm_away_instant(self, code=None):
|
||||||
|
"""Arm away instant synchronous."""
|
||||||
|
try:
|
||||||
|
ArmingHelper(self._partition).arm_away_instant()
|
||||||
|
except BadResultCodeError as error:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
f"TotalConnect failed to arm away instant {self._name}."
|
||||||
|
) from error
|
||||||
|
15
homeassistant/components/totalconnect/services.yaml
Normal file
15
homeassistant/components/totalconnect/services.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
arm_away_instant:
|
||||||
|
name: Arm Away Instant
|
||||||
|
description: Arm Away with zero entry delay.
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
integration: totalconnect
|
||||||
|
domain: alarm_control_panel
|
||||||
|
|
||||||
|
arm_home_instant:
|
||||||
|
name: Arm Home Instant
|
||||||
|
description: Arm Home with zero entry delay.
|
||||||
|
target:
|
||||||
|
entity:
|
||||||
|
integration: totalconnect
|
||||||
|
domain: alarm_control_panel
|
@ -5,6 +5,11 @@ from unittest.mock import patch
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.alarm_control_panel import DOMAIN as ALARM_DOMAIN
|
from homeassistant.components.alarm_control_panel import DOMAIN as ALARM_DOMAIN
|
||||||
|
from homeassistant.components.totalconnect import DOMAIN
|
||||||
|
from homeassistant.components.totalconnect.alarm_control_panel import (
|
||||||
|
SERVICE_ALARM_ARM_AWAY_INSTANT,
|
||||||
|
SERVICE_ALARM_ARM_HOME_INSTANT,
|
||||||
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_FRIENDLY_NAME,
|
ATTR_FRIENDLY_NAME,
|
||||||
@ -121,6 +126,82 @@ async def test_arm_home_failure(hass: HomeAssistant) -> None:
|
|||||||
assert mock_request.call_count == 2
|
assert mock_request.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_arm_home_instant_success(hass: HomeAssistant) -> None:
|
||||||
|
"""Test arm home instant method success."""
|
||||||
|
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_STAY]
|
||||||
|
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||||
|
await setup_platform(hass, ALARM_DOMAIN)
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_DISARMED
|
||||||
|
assert hass.states.get(ENTITY_ID_2).state == STATE_ALARM_DISARMED
|
||||||
|
assert mock_request.call_count == 1
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN, SERVICE_ALARM_ARM_HOME_INSTANT, DATA, blocking=True
|
||||||
|
)
|
||||||
|
assert mock_request.call_count == 2
|
||||||
|
|
||||||
|
async_fire_time_changed(hass, dt.utcnow() + DELAY)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert mock_request.call_count == 3
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_ARMED_HOME
|
||||||
|
|
||||||
|
|
||||||
|
async def test_arm_home_instant_failure(hass: HomeAssistant) -> None:
|
||||||
|
"""Test arm home instant method failure."""
|
||||||
|
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE]
|
||||||
|
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||||
|
await setup_platform(hass, ALARM_DOMAIN)
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_DISARMED
|
||||||
|
assert mock_request.call_count == 1
|
||||||
|
|
||||||
|
with pytest.raises(HomeAssistantError) as err:
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN, SERVICE_ALARM_ARM_HOME_INSTANT, DATA, blocking=True
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert f"{err.value}" == "TotalConnect failed to arm home instant test."
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_DISARMED
|
||||||
|
assert mock_request.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_arm_away_instant_success(hass: HomeAssistant) -> None:
|
||||||
|
"""Test arm home instant method success."""
|
||||||
|
responses = [RESPONSE_DISARMED, RESPONSE_ARM_SUCCESS, RESPONSE_ARMED_AWAY]
|
||||||
|
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||||
|
await setup_platform(hass, ALARM_DOMAIN)
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_DISARMED
|
||||||
|
assert hass.states.get(ENTITY_ID_2).state == STATE_ALARM_DISARMED
|
||||||
|
assert mock_request.call_count == 1
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN, SERVICE_ALARM_ARM_AWAY_INSTANT, DATA, blocking=True
|
||||||
|
)
|
||||||
|
assert mock_request.call_count == 2
|
||||||
|
|
||||||
|
async_fire_time_changed(hass, dt.utcnow() + DELAY)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert mock_request.call_count == 3
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_ARMED_AWAY
|
||||||
|
|
||||||
|
|
||||||
|
async def test_arm_away_instant_failure(hass: HomeAssistant) -> None:
|
||||||
|
"""Test arm home instant method failure."""
|
||||||
|
responses = [RESPONSE_DISARMED, RESPONSE_ARM_FAILURE]
|
||||||
|
with patch(TOTALCONNECT_REQUEST, side_effect=responses) as mock_request:
|
||||||
|
await setup_platform(hass, ALARM_DOMAIN)
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_DISARMED
|
||||||
|
assert mock_request.call_count == 1
|
||||||
|
|
||||||
|
with pytest.raises(HomeAssistantError) as err:
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN, SERVICE_ALARM_ARM_AWAY_INSTANT, DATA, blocking=True
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert f"{err.value}" == "TotalConnect failed to arm away instant test."
|
||||||
|
assert hass.states.get(ENTITY_ID).state == STATE_ALARM_DISARMED
|
||||||
|
assert mock_request.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
async def test_arm_home_invalid_usercode(hass: HomeAssistant) -> None:
|
async def test_arm_home_invalid_usercode(hass: HomeAssistant) -> None:
|
||||||
"""Test arm home method with invalid usercode."""
|
"""Test arm home method with invalid usercode."""
|
||||||
responses = [RESPONSE_DISARMED, RESPONSE_USER_CODE_INVALID]
|
responses = [RESPONSE_DISARMED, RESPONSE_USER_CODE_INVALID]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user