Add default code to alarm_control_panel (#112540)
This commit is contained in:
parent
38da61a5ac
commit
6b7ff2bf44
14 changed files with 680 additions and 74 deletions
|
@ -21,7 +21,8 @@ from homeassistant.const import (
|
|||
SERVICE_ALARM_DISARM,
|
||||
SERVICE_ALARM_TRIGGER,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.config_validation import make_entity_service_schema
|
||||
from homeassistant.helpers.deprecation import (
|
||||
|
@ -55,6 +56,8 @@ _LOGGER: Final = logging.getLogger(__name__)
|
|||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||
|
||||
CONF_DEFAULT_CODE = "default_code"
|
||||
|
||||
ALARM_SERVICE_SCHEMA: Final = make_entity_service_schema(
|
||||
{vol.Optional(ATTR_CODE): cv.string}
|
||||
)
|
||||
|
@ -74,36 +77,38 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
await component.async_setup(config)
|
||||
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ALARM_DISARM, ALARM_SERVICE_SCHEMA, "async_alarm_disarm"
|
||||
SERVICE_ALARM_DISARM,
|
||||
ALARM_SERVICE_SCHEMA,
|
||||
"async_handle_alarm_disarm",
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
ALARM_SERVICE_SCHEMA,
|
||||
"async_alarm_arm_home",
|
||||
"async_handle_alarm_arm_home",
|
||||
[AlarmControlPanelEntityFeature.ARM_HOME],
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
ALARM_SERVICE_SCHEMA,
|
||||
"async_alarm_arm_away",
|
||||
"async_handle_alarm_arm_away",
|
||||
[AlarmControlPanelEntityFeature.ARM_AWAY],
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ALARM_ARM_NIGHT,
|
||||
ALARM_SERVICE_SCHEMA,
|
||||
"async_alarm_arm_night",
|
||||
"async_handle_alarm_arm_night",
|
||||
[AlarmControlPanelEntityFeature.ARM_NIGHT],
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ALARM_ARM_VACATION,
|
||||
ALARM_SERVICE_SCHEMA,
|
||||
"async_alarm_arm_vacation",
|
||||
"async_handle_alarm_arm_vacation",
|
||||
[AlarmControlPanelEntityFeature.ARM_VACATION],
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||
ALARM_SERVICE_SCHEMA,
|
||||
"async_alarm_arm_custom_bypass",
|
||||
"async_handle_alarm_arm_custom_bypass",
|
||||
[AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS],
|
||||
)
|
||||
component.async_register_entity_service(
|
||||
|
@ -150,6 +155,21 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
_attr_supported_features: AlarmControlPanelEntityFeature = (
|
||||
AlarmControlPanelEntityFeature(0)
|
||||
)
|
||||
_alarm_control_panel_option_default_code: str | None = None
|
||||
|
||||
@final
|
||||
@callback
|
||||
def code_or_default_code(self, code: str | None) -> str | None:
|
||||
"""Return code to use for a service call.
|
||||
|
||||
If the passed in code is not None, it will be returned. Otherwise return the
|
||||
default code, if set, or None if not set, is returned.
|
||||
"""
|
||||
if code:
|
||||
# Return code provided by user
|
||||
return code
|
||||
# Fallback to default code or None if not set
|
||||
return self._alarm_control_panel_option_default_code
|
||||
|
||||
@cached_property
|
||||
def code_format(self) -> CodeFormat | None:
|
||||
|
@ -166,6 +186,26 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
"""Whether the code is required for arm actions."""
|
||||
return self._attr_code_arm_required
|
||||
|
||||
@final
|
||||
@callback
|
||||
def check_code_arm_required(self, code: str | None) -> str | None:
|
||||
"""Check if arm code is required, raise if no code is given."""
|
||||
if not (_code := self.code_or_default_code(code)) and self.code_arm_required:
|
||||
raise ServiceValidationError(
|
||||
f"Arming requires a code but none was given for {self.entity_id}",
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="code_arm_required",
|
||||
translation_placeholders={
|
||||
"entity_id": self.entity_id,
|
||||
},
|
||||
)
|
||||
return _code
|
||||
|
||||
@final
|
||||
async def async_handle_alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Add default code and disarm."""
|
||||
await self.async_alarm_disarm(self.code_or_default_code(code))
|
||||
|
||||
def alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Send disarm command."""
|
||||
raise NotImplementedError
|
||||
|
@ -174,6 +214,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
"""Send disarm command."""
|
||||
await self.hass.async_add_executor_job(self.alarm_disarm, code)
|
||||
|
||||
@final
|
||||
async def async_handle_alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Add default code and arm home."""
|
||||
await self.async_alarm_arm_home(self.check_code_arm_required(code))
|
||||
|
||||
def alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Send arm home command."""
|
||||
raise NotImplementedError
|
||||
|
@ -182,6 +227,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
"""Send arm home command."""
|
||||
await self.hass.async_add_executor_job(self.alarm_arm_home, code)
|
||||
|
||||
@final
|
||||
async def async_handle_alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Add default code and arm away."""
|
||||
await self.async_alarm_arm_away(self.check_code_arm_required(code))
|
||||
|
||||
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Send arm away command."""
|
||||
raise NotImplementedError
|
||||
|
@ -190,6 +240,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
"""Send arm away command."""
|
||||
await self.hass.async_add_executor_job(self.alarm_arm_away, code)
|
||||
|
||||
@final
|
||||
async def async_handle_alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Add default code and arm night."""
|
||||
await self.async_alarm_arm_night(self.check_code_arm_required(code))
|
||||
|
||||
def alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Send arm night command."""
|
||||
raise NotImplementedError
|
||||
|
@ -198,6 +253,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
"""Send arm night command."""
|
||||
await self.hass.async_add_executor_job(self.alarm_arm_night, code)
|
||||
|
||||
@final
|
||||
async def async_handle_alarm_arm_vacation(self, code: str | None = None) -> None:
|
||||
"""Add default code and arm vacation."""
|
||||
await self.async_alarm_arm_vacation(self.check_code_arm_required(code))
|
||||
|
||||
def alarm_arm_vacation(self, code: str | None = None) -> None:
|
||||
"""Send arm vacation command."""
|
||||
raise NotImplementedError
|
||||
|
@ -214,6 +274,13 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
"""Send alarm trigger command."""
|
||||
await self.hass.async_add_executor_job(self.alarm_trigger, code)
|
||||
|
||||
@final
|
||||
async def async_handle_alarm_arm_custom_bypass(
|
||||
self, code: str | None = None
|
||||
) -> None:
|
||||
"""Add default code and arm custom bypass."""
|
||||
await self.async_alarm_arm_custom_bypass(self.check_code_arm_required(code))
|
||||
|
||||
def alarm_arm_custom_bypass(self, code: str | None = None) -> None:
|
||||
"""Send arm custom bypass command."""
|
||||
raise NotImplementedError
|
||||
|
@ -242,6 +309,33 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
|
||||
}
|
||||
|
||||
async def async_internal_added_to_hass(self) -> None:
|
||||
"""Call when the alarm control panel entity is added to hass."""
|
||||
await super().async_internal_added_to_hass()
|
||||
if not self.registry_entry:
|
||||
return
|
||||
self._async_read_entity_options()
|
||||
|
||||
@callback
|
||||
def async_registry_entry_updated(self) -> None:
|
||||
"""Run when the entity registry entry has been updated."""
|
||||
self._async_read_entity_options()
|
||||
|
||||
@callback
|
||||
def _async_read_entity_options(self) -> None:
|
||||
"""Read entity options from entity registry.
|
||||
|
||||
Called when the entity registry entry has been updated and before the
|
||||
alarm control panel is added to the state machine.
|
||||
"""
|
||||
assert self.registry_entry
|
||||
if (alarm_options := self.registry_entry.options.get(DOMAIN)) and (
|
||||
default_code := alarm_options.get(CONF_DEFAULT_CODE)
|
||||
):
|
||||
self._alarm_control_panel_option_default_code = default_code
|
||||
return
|
||||
self._alarm_control_panel_option_default_code = None
|
||||
|
||||
|
||||
# As we import constants of the const module here, we need to add the following
|
||||
# functions to check for deprecated constants again
|
||||
|
|
|
@ -53,6 +53,7 @@ class CanaryAlarm(
|
|||
| AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
||||
)
|
||||
_attr_code_arm_required = False
|
||||
|
||||
def __init__(
|
||||
self, coordinator: CanaryDataUpdateCoordinator, location: Location
|
||||
|
|
|
@ -30,7 +30,7 @@ async def async_setup_entry(
|
|||
"""Set up the Demo config entry."""
|
||||
async_add_entities(
|
||||
[
|
||||
ManualAlarm( # type:ignore[no-untyped-call]
|
||||
DemoAlarm( # type:ignore[no-untyped-call]
|
||||
hass,
|
||||
"Security",
|
||||
"1234",
|
||||
|
@ -74,3 +74,9 @@ async def async_setup_entry(
|
|||
)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class DemoAlarm(ManualAlarm):
|
||||
"""Demo Alarm Control Panel."""
|
||||
|
||||
_attr_unique_id = "demo_alarm_control_panel"
|
||||
|
|
|
@ -52,6 +52,8 @@ async def async_setup_entry(
|
|||
class FreeboxAlarm(FreeboxHomeEntity, AlarmControlPanelEntity):
|
||||
"""Representation of a Freebox alarm."""
|
||||
|
||||
_attr_code_arm_required = False
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, router: FreeboxRouter, node: dict[str, Any]
|
||||
) -> None:
|
||||
|
|
|
@ -47,6 +47,7 @@ class HomematicipAlarmControlPanelEntity(AlarmControlPanelEntity):
|
|||
AlarmControlPanelEntityFeature.ARM_HOME
|
||||
| AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
)
|
||||
_attr_code_arm_required = False
|
||||
|
||||
def __init__(self, hap: HomematicipHAP) -> None:
|
||||
"""Initialize the alarm control panel."""
|
||||
|
|
|
@ -74,6 +74,7 @@ class TotalConnectAlarm(TotalConnectLocationEntity, AlarmControlPanelEntity):
|
|||
| AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
||||
)
|
||||
_attr_code_arm_required = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
|
@ -1,8 +1,33 @@
|
|||
"""Fixturs for Alarm Control Panel tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.components.alarm_control_panel.common import MockAlarm
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||
AlarmControlPanelEntity,
|
||||
AlarmControlPanelEntityFeature,
|
||||
)
|
||||
from homeassistant.components.alarm_control_panel.const import CodeFormat
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .common import MockAlarm
|
||||
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockModule,
|
||||
MockPlatform,
|
||||
mock_config_flow,
|
||||
mock_integration,
|
||||
mock_platform,
|
||||
)
|
||||
|
||||
TEST_DOMAIN = "test"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -20,3 +45,157 @@ def mock_alarm_control_panel_entities() -> dict[str, MockAlarm]:
|
|||
unique_id="unique_no_arm_code",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class MockAlarmControlPanel(AlarmControlPanelEntity):
|
||||
"""Mocked alarm control entity."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
supported_features: AlarmControlPanelEntityFeature = AlarmControlPanelEntityFeature(
|
||||
0
|
||||
),
|
||||
code_format: CodeFormat | None = None,
|
||||
code_arm_required: bool = True,
|
||||
) -> None:
|
||||
"""Initialize the alarm control."""
|
||||
self.calls_disarm = MagicMock()
|
||||
self.calls_arm_home = MagicMock()
|
||||
self.calls_arm_away = MagicMock()
|
||||
self.calls_arm_night = MagicMock()
|
||||
self.calls_arm_vacation = MagicMock()
|
||||
self.calls_trigger = MagicMock()
|
||||
self.calls_arm_custom = MagicMock()
|
||||
self._attr_code_format = code_format
|
||||
self._attr_supported_features = supported_features
|
||||
self._attr_code_arm_required = code_arm_required
|
||||
self._attr_has_entity_name = True
|
||||
self._attr_name = "test_alarm_control_panel"
|
||||
self._attr_unique_id = "very_unique_alarm_control_panel_id"
|
||||
super().__init__()
|
||||
|
||||
def alarm_disarm(self, code: str | None = None) -> None:
|
||||
"""Mock alarm disarm calls."""
|
||||
self.calls_disarm(code)
|
||||
|
||||
def alarm_arm_home(self, code: str | None = None) -> None:
|
||||
"""Mock arm home calls."""
|
||||
self.calls_arm_home(code)
|
||||
|
||||
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||
"""Mock arm away calls."""
|
||||
self.calls_arm_away(code)
|
||||
|
||||
def alarm_arm_night(self, code: str | None = None) -> None:
|
||||
"""Mock arm night calls."""
|
||||
self.calls_arm_night(code)
|
||||
|
||||
def alarm_arm_vacation(self, code: str | None = None) -> None:
|
||||
"""Mock arm vacation calls."""
|
||||
self.calls_arm_vacation(code)
|
||||
|
||||
def alarm_trigger(self, code: str | None = None) -> None:
|
||||
"""Mock trigger calls."""
|
||||
self.calls_trigger(code)
|
||||
|
||||
def alarm_arm_custom_bypass(self, code: str | None = None) -> None:
|
||||
"""Mock arm custom bypass calls."""
|
||||
self.calls_arm_custom(code)
|
||||
|
||||
|
||||
class MockFlow(ConfigFlow):
|
||||
"""Test flow."""
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]:
|
||||
"""Mock config flow."""
|
||||
mock_platform(hass, f"{TEST_DOMAIN}.config_flow")
|
||||
|
||||
with mock_config_flow(TEST_DOMAIN, MockFlow):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def code_format() -> CodeFormat | None:
|
||||
"""Return the code format for the test alarm control panel entity."""
|
||||
return CodeFormat.NUMBER
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def code_arm_required() -> bool:
|
||||
"""Return if code required for arming."""
|
||||
return True
|
||||
|
||||
|
||||
@pytest.fixture(name="supported_features")
|
||||
async def lock_supported_features() -> AlarmControlPanelEntityFeature:
|
||||
"""Return the supported features for the test alarm control panel entity."""
|
||||
return (
|
||||
AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
| AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS
|
||||
| AlarmControlPanelEntityFeature.ARM_HOME
|
||||
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
||||
| AlarmControlPanelEntityFeature.ARM_VACATION
|
||||
| AlarmControlPanelEntityFeature.TRIGGER
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_alarm_control_panel_entity")
|
||||
async def setup_lock_platform_test_entity(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
code_format: CodeFormat | None,
|
||||
supported_features: AlarmControlPanelEntityFeature,
|
||||
code_arm_required: bool,
|
||||
) -> MagicMock:
|
||||
"""Set up alarm control panel entity using an entity platform."""
|
||||
|
||||
async def async_setup_entry_init(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> bool:
|
||||
"""Set up test config entry."""
|
||||
await hass.config_entries.async_forward_entry_setup(
|
||||
config_entry, ALARM_CONTROL_PANEL_DOMAIN
|
||||
)
|
||||
return True
|
||||
|
||||
MockPlatform(hass, f"{TEST_DOMAIN}.config_flow")
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
TEST_DOMAIN,
|
||||
async_setup_entry=async_setup_entry_init,
|
||||
),
|
||||
)
|
||||
|
||||
# Unnamed sensor without device class -> no name
|
||||
entity = MockAlarmControlPanel(
|
||||
supported_features=supported_features,
|
||||
code_format=code_format,
|
||||
code_arm_required=code_arm_required,
|
||||
)
|
||||
|
||||
async def async_setup_entry_platform(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up test alarm control panel platform via config entry."""
|
||||
async_add_entities([entity])
|
||||
|
||||
mock_platform(
|
||||
hass,
|
||||
f"{TEST_DOMAIN}.{ALARM_CONTROL_PANEL_DOMAIN}",
|
||||
MockPlatform(async_setup_entry=async_setup_entry_platform),
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=TEST_DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity.entity_id)
|
||||
assert state is not None
|
||||
|
||||
return entity
|
||||
|
|
|
@ -1,14 +1,52 @@
|
|||
"""Test for the alarm control panel const module."""
|
||||
|
||||
from types import ModuleType
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import alarm_control_panel
|
||||
from homeassistant.components.alarm_control_panel.const import (
|
||||
AlarmControlPanelEntityFeature,
|
||||
CodeFormat,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_CODE,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
SERVICE_ALARM_ARM_NIGHT,
|
||||
SERVICE_ALARM_ARM_VACATION,
|
||||
SERVICE_ALARM_DISARM,
|
||||
SERVICE_ALARM_TRIGGER,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.typing import UNDEFINED, UndefinedType
|
||||
|
||||
from .conftest import MockAlarmControlPanel
|
||||
|
||||
from tests.common import help_test_all, import_and_test_deprecated_constant_enum
|
||||
|
||||
|
||||
async def help_test_async_alarm_control_panel_service(
|
||||
hass: HomeAssistant,
|
||||
entity_id: str,
|
||||
service: str,
|
||||
code: str | None | UndefinedType = UNDEFINED,
|
||||
) -> None:
|
||||
"""Help to lock a test lock."""
|
||||
data: dict[str, Any] = {"entity_id": entity_id}
|
||||
if code is not UNDEFINED:
|
||||
data[ATTR_CODE] = code
|
||||
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN, service, data, blocking=True
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"module",
|
||||
[alarm_control_panel, alarm_control_panel.const],
|
||||
|
@ -77,3 +115,171 @@ def test_deprecated_supported_features_ints(caplog: pytest.LogCaptureFixture) ->
|
|||
is alarm_control_panel.AlarmControlPanelEntityFeature(1)
|
||||
)
|
||||
assert "is using deprecated supported features values" not in caplog.text
|
||||
|
||||
|
||||
async def test_set_mock_alarm_control_panel_options(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_alarm_control_panel_entity: MockAlarmControlPanel,
|
||||
) -> None:
|
||||
"""Test mock attributes and default code stored in the registry."""
|
||||
entity_registry.async_update_entity_options(
|
||||
"alarm_control_panel.test_alarm_control_panel",
|
||||
"alarm_control_panel",
|
||||
{alarm_control_panel.CONF_DEFAULT_CODE: "1234"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
mock_alarm_control_panel_entity._alarm_control_panel_option_default_code
|
||||
== "1234"
|
||||
)
|
||||
state = hass.states.get(mock_alarm_control_panel_entity.entity_id)
|
||||
assert state is not None
|
||||
assert state.attributes["code_format"] == CodeFormat.NUMBER
|
||||
assert (
|
||||
state.attributes["supported_features"]
|
||||
== AlarmControlPanelEntityFeature.ARM_AWAY
|
||||
| AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS
|
||||
| AlarmControlPanelEntityFeature.ARM_HOME
|
||||
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
||||
| AlarmControlPanelEntityFeature.ARM_VACATION
|
||||
| AlarmControlPanelEntityFeature.TRIGGER
|
||||
)
|
||||
|
||||
|
||||
async def test_default_code_option_update(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_alarm_control_panel_entity: MockAlarmControlPanel,
|
||||
) -> None:
|
||||
"""Test default code stored in the registry is updated."""
|
||||
|
||||
assert (
|
||||
mock_alarm_control_panel_entity._alarm_control_panel_option_default_code is None
|
||||
)
|
||||
|
||||
entity_registry.async_update_entity_options(
|
||||
"alarm_control_panel.test_alarm_control_panel",
|
||||
"alarm_control_panel",
|
||||
{alarm_control_panel.CONF_DEFAULT_CODE: "4321"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
mock_alarm_control_panel_entity._alarm_control_panel_option_default_code
|
||||
== "4321"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("code_format", "supported_features"),
|
||||
[(CodeFormat.TEXT, AlarmControlPanelEntityFeature.ARM_AWAY)],
|
||||
)
|
||||
async def test_alarm_control_panel_arm_with_code(
|
||||
hass: HomeAssistant, mock_alarm_control_panel_entity: MockAlarmControlPanel
|
||||
) -> None:
|
||||
"""Test alarm control panel entity with open service."""
|
||||
state = hass.states.get(mock_alarm_control_panel_entity.entity_id)
|
||||
assert state.attributes["code_format"] == CodeFormat.TEXT
|
||||
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_AWAY
|
||||
)
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass,
|
||||
mock_alarm_control_panel_entity.entity_id,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
code="",
|
||||
)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass,
|
||||
mock_alarm_control_panel_entity.entity_id,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
code="1234",
|
||||
)
|
||||
assert mock_alarm_control_panel_entity.calls_arm_away.call_count == 1
|
||||
mock_alarm_control_panel_entity.calls_arm_away.assert_called_with("1234")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("code_format", "code_arm_required"),
|
||||
[(CodeFormat.NUMBER, False)],
|
||||
)
|
||||
async def test_alarm_control_panel_with_no_code(
|
||||
hass: HomeAssistant, mock_alarm_control_panel_entity: MockAlarmControlPanel
|
||||
) -> None:
|
||||
"""Test alarm control panel entity without code."""
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_AWAY
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_away.assert_called_with(None)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_CUSTOM_BYPASS
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_custom.assert_called_with(None)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_HOME
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_home.assert_called_with(None)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_NIGHT
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_night.assert_called_with(None)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_VACATION
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_vacation.assert_called_with(None)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_DISARM
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_disarm.assert_called_with(None)
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_TRIGGER
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_trigger.assert_called_with(None)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("code_format", "code_arm_required"),
|
||||
[(CodeFormat.NUMBER, True)],
|
||||
)
|
||||
async def test_alarm_control_panel_with_default_code(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_alarm_control_panel_entity: MockAlarmControlPanel,
|
||||
) -> None:
|
||||
"""Test alarm control panel entity without code."""
|
||||
entity_registry.async_update_entity_options(
|
||||
"alarm_control_panel.test_alarm_control_panel",
|
||||
"alarm_control_panel",
|
||||
{alarm_control_panel.CONF_DEFAULT_CODE: "1234"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_AWAY
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_away.assert_called_with("1234")
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_CUSTOM_BYPASS
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_custom.assert_called_with("1234")
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_HOME
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_home.assert_called_with("1234")
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_NIGHT
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_night.assert_called_with("1234")
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_ARM_VACATION
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_arm_vacation.assert_called_with("1234")
|
||||
await help_test_async_alarm_control_panel_service(
|
||||
hass, mock_alarm_control_panel_entity.entity_id, SERVICE_ALARM_DISARM
|
||||
)
|
||||
mock_alarm_control_panel_entity.calls_disarm.assert_called_with("1234")
|
||||
|
|
|
@ -34,7 +34,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||
await hass.services.async_call(
|
||||
"alarm_control_panel",
|
||||
"alarm_arm_home",
|
||||
{"entity_id": "alarm_control_panel.testdevice"},
|
||||
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
helper.async_assert_service_values(
|
||||
|
@ -47,7 +47,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||
await hass.services.async_call(
|
||||
"alarm_control_panel",
|
||||
"alarm_arm_away",
|
||||
{"entity_id": "alarm_control_panel.testdevice"},
|
||||
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
helper.async_assert_service_values(
|
||||
|
@ -60,7 +60,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||
await hass.services.async_call(
|
||||
"alarm_control_panel",
|
||||
"alarm_arm_night",
|
||||
{"entity_id": "alarm_control_panel.testdevice"},
|
||||
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
helper.async_assert_service_values(
|
||||
|
@ -73,7 +73,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||
await hass.services.async_call(
|
||||
"alarm_control_panel",
|
||||
"alarm_disarm",
|
||||
{"entity_id": "alarm_control_panel.testdevice"},
|
||||
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
helper.async_assert_service_values(
|
||||
|
|
|
@ -315,7 +315,7 @@ async def test_with_specific_pending(
|
|||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
|
|
@ -380,7 +380,7 @@ async def test_with_specific_pending(
|
|||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
@ -1442,7 +1442,7 @@ async def test_state_changes_are_published_to_mqtt(
|
|||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# Arm in home mode
|
||||
await common.async_alarm_arm_home(hass)
|
||||
await common.async_alarm_arm_home(hass, "1234")
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.assert_called_once_with(
|
||||
"alarm/state", STATE_ALARM_PENDING, 0, True
|
||||
|
@ -1462,7 +1462,7 @@ async def test_state_changes_are_published_to_mqtt(
|
|||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# Arm in away mode
|
||||
await common.async_alarm_arm_away(hass)
|
||||
await common.async_alarm_arm_away(hass, "1234")
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.assert_called_once_with(
|
||||
"alarm/state", STATE_ALARM_PENDING, 0, True
|
||||
|
@ -1482,7 +1482,7 @@ async def test_state_changes_are_published_to_mqtt(
|
|||
mqtt_mock.async_publish.reset_mock()
|
||||
|
||||
# Arm in night mode
|
||||
await common.async_alarm_arm_night(hass)
|
||||
await common.async_alarm_arm_night(hass, "1234")
|
||||
await hass.async_block_till_done()
|
||||
mqtt_mock.async_publish.assert_called_once_with(
|
||||
"alarm/state", STATE_ALARM_PENDING, 0, True
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""The tests the MQTT alarm control panel component."""
|
||||
|
||||
from contextlib import AbstractContextManager, contextmanager
|
||||
import copy
|
||||
import json
|
||||
from typing import Any
|
||||
|
@ -37,7 +38,7 @@ from homeassistant.const import (
|
|||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
|
||||
from .test_common import (
|
||||
help_custom_config,
|
||||
|
@ -97,6 +98,17 @@ DEFAULT_CONFIG = {
|
|||
}
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG_CODE_NOT_REQUIRED = {
|
||||
mqtt.DOMAIN: {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
"name": "test",
|
||||
"state_topic": "alarm/state",
|
||||
"command_topic": "alarm/command",
|
||||
"code_arm_required": False,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_CONFIG_CODE = {
|
||||
mqtt.DOMAIN: {
|
||||
alarm_control_panel.DOMAIN: {
|
||||
|
@ -134,6 +146,12 @@ DEFAULT_CONFIG_REMOTE_CODE_TEXT = {
|
|||
}
|
||||
|
||||
|
||||
@contextmanager
|
||||
def does_not_raise():
|
||||
"""Do not raise error."""
|
||||
yield
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("hass_config", "valid"),
|
||||
[
|
||||
|
@ -317,13 +335,17 @@ async def test_supported_features(
|
|||
@pytest.mark.parametrize(
|
||||
("hass_config", "service", "payload"),
|
||||
[
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_CUSTOM_BYPASS, "ARM_CUSTOM_BYPASS"),
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_DISARM, "DISARM"),
|
||||
(DEFAULT_CONFIG, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
||||
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
||||
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
||||
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
||||
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
||||
(
|
||||
DEFAULT_CONFIG_CODE_NOT_REQUIRED,
|
||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||
"ARM_CUSTOM_BYPASS",
|
||||
),
|
||||
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_DISARM, "DISARM"),
|
||||
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
||||
],
|
||||
)
|
||||
async def test_publish_mqtt_no_code(
|
||||
|
@ -346,34 +368,61 @@ async def test_publish_mqtt_no_code(
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("hass_config", "service", "payload"),
|
||||
("hass_config", "service", "payload", "raises"),
|
||||
[
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_CUSTOM_BYPASS, "ARM_CUSTOM_BYPASS"),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_DISARM, "DISARM"),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
||||
(
|
||||
DEFAULT_CONFIG_CODE,
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
"ARM_HOME",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_CODE,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
"ARM_AWAY",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_CODE,
|
||||
SERVICE_ALARM_ARM_NIGHT,
|
||||
"ARM_NIGHT",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_CODE,
|
||||
SERVICE_ALARM_ARM_VACATION,
|
||||
"ARM_VACATION",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_CODE,
|
||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||
"ARM_CUSTOM_BYPASS",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_DISARM, "DISARM", does_not_raise()),
|
||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_TRIGGER, "TRIGGER", does_not_raise()),
|
||||
],
|
||||
)
|
||||
async def test_publish_mqtt_with_code(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
service,
|
||||
payload,
|
||||
service: str,
|
||||
payload: str,
|
||||
raises: AbstractContextManager,
|
||||
) -> None:
|
||||
"""Test publishing of MQTT messages when code is configured."""
|
||||
mqtt_mock = await mqtt_mock_entry()
|
||||
call_count = mqtt_mock.async_publish.call_count
|
||||
|
||||
# No code provided, should not publish
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
blocking=True,
|
||||
)
|
||||
with raises:
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mqtt_mock.async_publish.call_count == call_count
|
||||
|
||||
# Wrong code provided, should not publish
|
||||
|
@ -396,38 +445,66 @@ async def test_publish_mqtt_with_code(
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("hass_config", "service", "payload"),
|
||||
("hass_config", "service", "payload", "raises"),
|
||||
[
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE,
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
"ARM_HOME",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
"ARM_AWAY",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE,
|
||||
SERVICE_ALARM_ARM_NIGHT,
|
||||
"ARM_NIGHT",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE,
|
||||
SERVICE_ALARM_ARM_VACATION,
|
||||
"ARM_VACATION",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE,
|
||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||
"ARM_CUSTOM_BYPASS",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_DISARM, "DISARM", does_not_raise()),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE,
|
||||
SERVICE_ALARM_TRIGGER,
|
||||
"TRIGGER",
|
||||
does_not_raise(),
|
||||
),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_DISARM, "DISARM"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
||||
],
|
||||
)
|
||||
async def test_publish_mqtt_with_remote_code(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
service,
|
||||
payload,
|
||||
service: str,
|
||||
payload: str,
|
||||
raises: AbstractContextManager,
|
||||
) -> None:
|
||||
"""Test publishing of MQTT messages when remode code is configured."""
|
||||
mqtt_mock = await mqtt_mock_entry()
|
||||
call_count = mqtt_mock.async_publish.call_count
|
||||
|
||||
# No code provided, should not publish
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
blocking=True,
|
||||
)
|
||||
with raises:
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mqtt_mock.async_publish.call_count == call_count
|
||||
|
||||
# Any code numbered provided, should publish
|
||||
|
@ -441,19 +518,50 @@ async def test_publish_mqtt_with_remote_code(
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("hass_config", "service", "payload"),
|
||||
("hass_config", "service", "payload", "raises"),
|
||||
[
|
||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_ARM_HOME,
|
||||
"ARM_HOME",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_ARM_AWAY,
|
||||
"ARM_AWAY",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_ARM_NIGHT,
|
||||
"ARM_NIGHT",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_ARM_VACATION,
|
||||
"ARM_VACATION",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||
"ARM_CUSTOM_BYPASS",
|
||||
pytest.raises(ServiceValidationError),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_DISARM,
|
||||
"DISARM",
|
||||
does_not_raise(),
|
||||
),
|
||||
(
|
||||
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||
SERVICE_ALARM_TRIGGER,
|
||||
"TRIGGER",
|
||||
does_not_raise(),
|
||||
),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_DISARM, "DISARM"),
|
||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
||||
],
|
||||
)
|
||||
async def test_publish_mqtt_with_remote_code_text(
|
||||
|
@ -461,18 +569,20 @@ async def test_publish_mqtt_with_remote_code_text(
|
|||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
service: str,
|
||||
payload: str,
|
||||
raises: AbstractContextManager,
|
||||
) -> None:
|
||||
"""Test publishing of MQTT messages when remote text code is configured."""
|
||||
mqtt_mock = await mqtt_mock_entry()
|
||||
call_count = mqtt_mock.async_publish.call_count
|
||||
|
||||
# No code provided, should not publish
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
blocking=True,
|
||||
)
|
||||
with raises:
|
||||
await hass.services.async_call(
|
||||
alarm_control_panel.DOMAIN,
|
||||
service,
|
||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mqtt_mock.async_publish.call_count == call_count
|
||||
|
||||
# Any code numbered provided, should publish
|
||||
|
|
|
@ -154,7 +154,10 @@ async def test_optimistic_states(hass: HomeAssistant, start_ha) -> None:
|
|||
("alarm_trigger", STATE_ALARM_TRIGGERED),
|
||||
]:
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, service, {"entity_id": TEMPLATE_NAME}, blocking=True
|
||||
ALARM_DOMAIN,
|
||||
service,
|
||||
{"entity_id": TEMPLATE_NAME, "code": "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(TEMPLATE_NAME).state == set_state
|
||||
|
@ -286,7 +289,10 @@ async def test_actions(
|
|||
) -> None:
|
||||
"""Test alarm actions."""
|
||||
await hass.services.async_call(
|
||||
ALARM_DOMAIN, service, {"entity_id": TEMPLATE_NAME}, blocking=True
|
||||
ALARM_DOMAIN,
|
||||
service,
|
||||
{"entity_id": TEMPLATE_NAME, "code": "1234"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert len(call_service_events) == 1
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
'attributes': ReadOnlyDict({
|
||||
'ac_loss': False,
|
||||
'changed_by': None,
|
||||
'code_arm_required': True,
|
||||
'code_arm_required': False,
|
||||
'code_format': None,
|
||||
'cover_tampered': False,
|
||||
'friendly_name': 'test',
|
||||
|
@ -95,7 +95,7 @@
|
|||
'attributes': ReadOnlyDict({
|
||||
'ac_loss': False,
|
||||
'changed_by': None,
|
||||
'code_arm_required': True,
|
||||
'code_arm_required': False,
|
||||
'code_format': None,
|
||||
'cover_tampered': False,
|
||||
'friendly_name': 'test Partition 2',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue