mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
Add default code to alarm_control_panel (#112540)
This commit is contained in:
parent
38da61a5ac
commit
6b7ff2bf44
@ -21,7 +21,8 @@ from homeassistant.const import (
|
|||||||
SERVICE_ALARM_DISARM,
|
SERVICE_ALARM_DISARM,
|
||||||
SERVICE_ALARM_TRIGGER,
|
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
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.config_validation import make_entity_service_schema
|
from homeassistant.helpers.config_validation import make_entity_service_schema
|
||||||
from homeassistant.helpers.deprecation import (
|
from homeassistant.helpers.deprecation import (
|
||||||
@ -55,6 +56,8 @@ _LOGGER: Final = logging.getLogger(__name__)
|
|||||||
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||||
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
ENTITY_ID_FORMAT: Final = DOMAIN + ".{}"
|
||||||
|
|
||||||
|
CONF_DEFAULT_CODE = "default_code"
|
||||||
|
|
||||||
ALARM_SERVICE_SCHEMA: Final = make_entity_service_schema(
|
ALARM_SERVICE_SCHEMA: Final = make_entity_service_schema(
|
||||||
{vol.Optional(ATTR_CODE): cv.string}
|
{vol.Optional(ATTR_CODE): cv.string}
|
||||||
)
|
)
|
||||||
@ -74,36 +77,38 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
await component.async_setup(config)
|
await component.async_setup(config)
|
||||||
|
|
||||||
component.async_register_entity_service(
|
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(
|
component.async_register_entity_service(
|
||||||
SERVICE_ALARM_ARM_HOME,
|
SERVICE_ALARM_ARM_HOME,
|
||||||
ALARM_SERVICE_SCHEMA,
|
ALARM_SERVICE_SCHEMA,
|
||||||
"async_alarm_arm_home",
|
"async_handle_alarm_arm_home",
|
||||||
[AlarmControlPanelEntityFeature.ARM_HOME],
|
[AlarmControlPanelEntityFeature.ARM_HOME],
|
||||||
)
|
)
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
SERVICE_ALARM_ARM_AWAY,
|
SERVICE_ALARM_ARM_AWAY,
|
||||||
ALARM_SERVICE_SCHEMA,
|
ALARM_SERVICE_SCHEMA,
|
||||||
"async_alarm_arm_away",
|
"async_handle_alarm_arm_away",
|
||||||
[AlarmControlPanelEntityFeature.ARM_AWAY],
|
[AlarmControlPanelEntityFeature.ARM_AWAY],
|
||||||
)
|
)
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
SERVICE_ALARM_ARM_NIGHT,
|
SERVICE_ALARM_ARM_NIGHT,
|
||||||
ALARM_SERVICE_SCHEMA,
|
ALARM_SERVICE_SCHEMA,
|
||||||
"async_alarm_arm_night",
|
"async_handle_alarm_arm_night",
|
||||||
[AlarmControlPanelEntityFeature.ARM_NIGHT],
|
[AlarmControlPanelEntityFeature.ARM_NIGHT],
|
||||||
)
|
)
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
SERVICE_ALARM_ARM_VACATION,
|
SERVICE_ALARM_ARM_VACATION,
|
||||||
ALARM_SERVICE_SCHEMA,
|
ALARM_SERVICE_SCHEMA,
|
||||||
"async_alarm_arm_vacation",
|
"async_handle_alarm_arm_vacation",
|
||||||
[AlarmControlPanelEntityFeature.ARM_VACATION],
|
[AlarmControlPanelEntityFeature.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,
|
||||||
"async_alarm_arm_custom_bypass",
|
"async_handle_alarm_arm_custom_bypass",
|
||||||
[AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS],
|
[AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS],
|
||||||
)
|
)
|
||||||
component.async_register_entity_service(
|
component.async_register_entity_service(
|
||||||
@ -150,6 +155,21 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
_attr_supported_features: AlarmControlPanelEntityFeature = (
|
_attr_supported_features: AlarmControlPanelEntityFeature = (
|
||||||
AlarmControlPanelEntityFeature(0)
|
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
|
@cached_property
|
||||||
def code_format(self) -> CodeFormat | None:
|
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."""
|
"""Whether the code is required for arm actions."""
|
||||||
return self._attr_code_arm_required
|
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:
|
def alarm_disarm(self, code: str | None = None) -> None:
|
||||||
"""Send disarm command."""
|
"""Send disarm command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -174,6 +214,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
"""Send disarm command."""
|
"""Send disarm command."""
|
||||||
await self.hass.async_add_executor_job(self.alarm_disarm, code)
|
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:
|
def alarm_arm_home(self, code: str | None = None) -> None:
|
||||||
"""Send arm home command."""
|
"""Send arm home command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -182,6 +227,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
"""Send arm home command."""
|
"""Send arm home command."""
|
||||||
await self.hass.async_add_executor_job(self.alarm_arm_home, code)
|
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:
|
def alarm_arm_away(self, code: str | None = None) -> None:
|
||||||
"""Send arm away command."""
|
"""Send arm away command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -190,6 +240,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
"""Send arm away command."""
|
"""Send arm away command."""
|
||||||
await self.hass.async_add_executor_job(self.alarm_arm_away, code)
|
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:
|
def alarm_arm_night(self, code: str | None = None) -> None:
|
||||||
"""Send arm night command."""
|
"""Send arm night command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -198,6 +253,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
"""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)
|
||||||
|
|
||||||
|
@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:
|
def alarm_arm_vacation(self, code: str | None = None) -> None:
|
||||||
"""Send arm vacation command."""
|
"""Send arm vacation command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -214,6 +274,13 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
"""Send alarm trigger command."""
|
"""Send alarm trigger command."""
|
||||||
await self.hass.async_add_executor_job(self.alarm_trigger, code)
|
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:
|
def alarm_arm_custom_bypass(self, code: str | None = None) -> None:
|
||||||
"""Send arm custom bypass command."""
|
"""Send arm custom bypass command."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -242,6 +309,33 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
|||||||
ATTR_CODE_ARM_REQUIRED: self.code_arm_required,
|
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
|
# As we import constants of the const module here, we need to add the following
|
||||||
# functions to check for deprecated constants again
|
# functions to check for deprecated constants again
|
||||||
|
@ -53,6 +53,7 @@ class CanaryAlarm(
|
|||||||
| AlarmControlPanelEntityFeature.ARM_AWAY
|
| AlarmControlPanelEntityFeature.ARM_AWAY
|
||||||
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
||||||
)
|
)
|
||||||
|
_attr_code_arm_required = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, coordinator: CanaryDataUpdateCoordinator, location: Location
|
self, coordinator: CanaryDataUpdateCoordinator, location: Location
|
||||||
|
@ -30,7 +30,7 @@ async def async_setup_entry(
|
|||||||
"""Set up the Demo config entry."""
|
"""Set up the Demo config entry."""
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
ManualAlarm( # type:ignore[no-untyped-call]
|
DemoAlarm( # type:ignore[no-untyped-call]
|
||||||
hass,
|
hass,
|
||||||
"Security",
|
"Security",
|
||||||
"1234",
|
"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):
|
class FreeboxAlarm(FreeboxHomeEntity, AlarmControlPanelEntity):
|
||||||
"""Representation of a Freebox alarm."""
|
"""Representation of a Freebox alarm."""
|
||||||
|
|
||||||
|
_attr_code_arm_required = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, hass: HomeAssistant, router: FreeboxRouter, node: dict[str, Any]
|
self, hass: HomeAssistant, router: FreeboxRouter, node: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -47,6 +47,7 @@ class HomematicipAlarmControlPanelEntity(AlarmControlPanelEntity):
|
|||||||
AlarmControlPanelEntityFeature.ARM_HOME
|
AlarmControlPanelEntityFeature.ARM_HOME
|
||||||
| AlarmControlPanelEntityFeature.ARM_AWAY
|
| AlarmControlPanelEntityFeature.ARM_AWAY
|
||||||
)
|
)
|
||||||
|
_attr_code_arm_required = False
|
||||||
|
|
||||||
def __init__(self, hap: HomematicipHAP) -> None:
|
def __init__(self, hap: HomematicipHAP) -> None:
|
||||||
"""Initialize the alarm control panel."""
|
"""Initialize the alarm control panel."""
|
||||||
|
@ -74,6 +74,7 @@ class TotalConnectAlarm(TotalConnectLocationEntity, AlarmControlPanelEntity):
|
|||||||
| AlarmControlPanelEntityFeature.ARM_AWAY
|
| AlarmControlPanelEntityFeature.ARM_AWAY
|
||||||
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
| AlarmControlPanelEntityFeature.ARM_NIGHT
|
||||||
)
|
)
|
||||||
|
_attr_code_arm_required = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -1,8 +1,33 @@
|
|||||||
"""Fixturs for Alarm Control Panel tests."""
|
"""Fixturs for Alarm Control Panel tests."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
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
|
@pytest.fixture
|
||||||
@ -20,3 +45,157 @@ def mock_alarm_control_panel_entities() -> dict[str, MockAlarm]:
|
|||||||
unique_id="unique_no_arm_code",
|
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."""
|
"""Test for the alarm control panel const module."""
|
||||||
|
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import alarm_control_panel
|
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
|
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(
|
@pytest.mark.parametrize(
|
||||||
"module",
|
"module",
|
||||||
[alarm_control_panel, alarm_control_panel.const],
|
[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)
|
is alarm_control_panel.AlarmControlPanelEntityFeature(1)
|
||||||
)
|
)
|
||||||
assert "is using deprecated supported features values" not in caplog.text
|
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(
|
await hass.services.async_call(
|
||||||
"alarm_control_panel",
|
"alarm_control_panel",
|
||||||
"alarm_arm_home",
|
"alarm_arm_home",
|
||||||
{"entity_id": "alarm_control_panel.testdevice"},
|
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
helper.async_assert_service_values(
|
helper.async_assert_service_values(
|
||||||
@ -47,7 +47,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"alarm_control_panel",
|
"alarm_control_panel",
|
||||||
"alarm_arm_away",
|
"alarm_arm_away",
|
||||||
{"entity_id": "alarm_control_panel.testdevice"},
|
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
helper.async_assert_service_values(
|
helper.async_assert_service_values(
|
||||||
@ -60,7 +60,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"alarm_control_panel",
|
"alarm_control_panel",
|
||||||
"alarm_arm_night",
|
"alarm_arm_night",
|
||||||
{"entity_id": "alarm_control_panel.testdevice"},
|
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
helper.async_assert_service_values(
|
helper.async_assert_service_values(
|
||||||
@ -73,7 +73,7 @@ async def test_switch_change_alarm_state(hass: HomeAssistant) -> None:
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
"alarm_control_panel",
|
"alarm_control_panel",
|
||||||
"alarm_disarm",
|
"alarm_disarm",
|
||||||
{"entity_id": "alarm_control_panel.testdevice"},
|
{"entity_id": "alarm_control_panel.testdevice", "code": "1234"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
helper.async_assert_service_values(
|
helper.async_assert_service_values(
|
||||||
|
@ -315,7 +315,7 @@ async def test_with_specific_pending(
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
alarm_control_panel.DOMAIN,
|
alarm_control_panel.DOMAIN,
|
||||||
service,
|
service,
|
||||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
{ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: "1234"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ async def test_with_specific_pending(
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
alarm_control_panel.DOMAIN,
|
alarm_control_panel.DOMAIN,
|
||||||
service,
|
service,
|
||||||
{ATTR_ENTITY_ID: "alarm_control_panel.test"},
|
{ATTR_ENTITY_ID: "alarm_control_panel.test", ATTR_CODE: "1234"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1442,7 +1442,7 @@ async def test_state_changes_are_published_to_mqtt(
|
|||||||
mqtt_mock.async_publish.reset_mock()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
|
||||||
# Arm in home mode
|
# 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()
|
await hass.async_block_till_done()
|
||||||
mqtt_mock.async_publish.assert_called_once_with(
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
"alarm/state", STATE_ALARM_PENDING, 0, True
|
"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()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
|
||||||
# Arm in away mode
|
# 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()
|
await hass.async_block_till_done()
|
||||||
mqtt_mock.async_publish.assert_called_once_with(
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
"alarm/state", STATE_ALARM_PENDING, 0, True
|
"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()
|
mqtt_mock.async_publish.reset_mock()
|
||||||
|
|
||||||
# Arm in night mode
|
# 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()
|
await hass.async_block_till_done()
|
||||||
mqtt_mock.async_publish.assert_called_once_with(
|
mqtt_mock.async_publish.assert_called_once_with(
|
||||||
"alarm/state", STATE_ALARM_PENDING, 0, True
|
"alarm/state", STATE_ALARM_PENDING, 0, True
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""The tests the MQTT alarm control panel component."""
|
"""The tests the MQTT alarm control panel component."""
|
||||||
|
|
||||||
|
from contextlib import AbstractContextManager, contextmanager
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@ -37,7 +38,7 @@ from homeassistant.const import (
|
|||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||||
|
|
||||||
from .test_common import (
|
from .test_common import (
|
||||||
help_custom_config,
|
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 = {
|
DEFAULT_CONFIG_CODE = {
|
||||||
mqtt.DOMAIN: {
|
mqtt.DOMAIN: {
|
||||||
alarm_control_panel.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(
|
@pytest.mark.parametrize(
|
||||||
("hass_config", "valid"),
|
("hass_config", "valid"),
|
||||||
[
|
[
|
||||||
@ -317,13 +335,17 @@ async def test_supported_features(
|
|||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("hass_config", "service", "payload"),
|
("hass_config", "service", "payload"),
|
||||||
[
|
[
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_HOME, "ARM_HOME"),
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_AWAY, "ARM_AWAY"),
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
(DEFAULT_CONFIG_CODE_NOT_REQUIRED, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_ARM_CUSTOM_BYPASS, "ARM_CUSTOM_BYPASS"),
|
(
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_DISARM, "DISARM"),
|
DEFAULT_CONFIG_CODE_NOT_REQUIRED,
|
||||||
(DEFAULT_CONFIG, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
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(
|
async def test_publish_mqtt_no_code(
|
||||||
@ -346,28 +368,55 @@ async def test_publish_mqtt_no_code(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@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,
|
||||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
SERVICE_ALARM_ARM_HOME,
|
||||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
"ARM_HOME",
|
||||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_ARM_CUSTOM_BYPASS, "ARM_CUSTOM_BYPASS"),
|
pytest.raises(ServiceValidationError),
|
||||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_DISARM, "DISARM"),
|
),
|
||||||
(DEFAULT_CONFIG_CODE, SERVICE_ALARM_TRIGGER, "TRIGGER"),
|
(
|
||||||
|
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(
|
async def test_publish_mqtt_with_code(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
service,
|
service: str,
|
||||||
payload,
|
payload: str,
|
||||||
|
raises: AbstractContextManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test publishing of MQTT messages when code is configured."""
|
"""Test publishing of MQTT messages when code is configured."""
|
||||||
mqtt_mock = await mqtt_mock_entry()
|
mqtt_mock = await mqtt_mock_entry()
|
||||||
call_count = mqtt_mock.async_publish.call_count
|
call_count = mqtt_mock.async_publish.call_count
|
||||||
|
|
||||||
# No code provided, should not publish
|
# No code provided, should not publish
|
||||||
|
with raises:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
alarm_control_panel.DOMAIN,
|
alarm_control_panel.DOMAIN,
|
||||||
service,
|
service,
|
||||||
@ -396,32 +445,60 @@ async def test_publish_mqtt_with_code(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@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,
|
||||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
SERVICE_ALARM_ARM_HOME,
|
||||||
(DEFAULT_CONFIG_REMOTE_CODE, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
"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,
|
DEFAULT_CONFIG_REMOTE_CODE,
|
||||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
"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(
|
async def test_publish_mqtt_with_remote_code(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
service,
|
service: str,
|
||||||
payload,
|
payload: str,
|
||||||
|
raises: AbstractContextManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test publishing of MQTT messages when remode code is configured."""
|
"""Test publishing of MQTT messages when remode code is configured."""
|
||||||
mqtt_mock = await mqtt_mock_entry()
|
mqtt_mock = await mqtt_mock_entry()
|
||||||
call_count = mqtt_mock.async_publish.call_count
|
call_count = mqtt_mock.async_publish.call_count
|
||||||
|
|
||||||
# No code provided, should not publish
|
# No code provided, should not publish
|
||||||
|
with raises:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
alarm_control_panel.DOMAIN,
|
alarm_control_panel.DOMAIN,
|
||||||
service,
|
service,
|
||||||
@ -441,19 +518,50 @@ async def test_publish_mqtt_with_remote_code(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@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,
|
||||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_ARM_NIGHT, "ARM_NIGHT"),
|
SERVICE_ALARM_ARM_HOME,
|
||||||
(DEFAULT_CONFIG_REMOTE_CODE_TEXT, SERVICE_ALARM_ARM_VACATION, "ARM_VACATION"),
|
"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,
|
DEFAULT_CONFIG_REMOTE_CODE_TEXT,
|
||||||
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
SERVICE_ALARM_ARM_CUSTOM_BYPASS,
|
||||||
"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(
|
async def test_publish_mqtt_with_remote_code_text(
|
||||||
@ -461,12 +569,14 @@ async def test_publish_mqtt_with_remote_code_text(
|
|||||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||||
service: str,
|
service: str,
|
||||||
payload: str,
|
payload: str,
|
||||||
|
raises: AbstractContextManager,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test publishing of MQTT messages when remote text code is configured."""
|
"""Test publishing of MQTT messages when remote text code is configured."""
|
||||||
mqtt_mock = await mqtt_mock_entry()
|
mqtt_mock = await mqtt_mock_entry()
|
||||||
call_count = mqtt_mock.async_publish.call_count
|
call_count = mqtt_mock.async_publish.call_count
|
||||||
|
|
||||||
# No code provided, should not publish
|
# No code provided, should not publish
|
||||||
|
with raises:
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
alarm_control_panel.DOMAIN,
|
alarm_control_panel.DOMAIN,
|
||||||
service,
|
service,
|
||||||
|
@ -154,7 +154,10 @@ async def test_optimistic_states(hass: HomeAssistant, start_ha) -> None:
|
|||||||
("alarm_trigger", STATE_ALARM_TRIGGERED),
|
("alarm_trigger", STATE_ALARM_TRIGGERED),
|
||||||
]:
|
]:
|
||||||
await hass.services.async_call(
|
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()
|
await hass.async_block_till_done()
|
||||||
assert hass.states.get(TEMPLATE_NAME).state == set_state
|
assert hass.states.get(TEMPLATE_NAME).state == set_state
|
||||||
@ -286,7 +289,10 @@ async def test_actions(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test alarm actions."""
|
"""Test alarm actions."""
|
||||||
await hass.services.async_call(
|
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()
|
await hass.async_block_till_done()
|
||||||
assert len(call_service_events) == 1
|
assert len(call_service_events) == 1
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'ac_loss': False,
|
'ac_loss': False,
|
||||||
'changed_by': None,
|
'changed_by': None,
|
||||||
'code_arm_required': True,
|
'code_arm_required': False,
|
||||||
'code_format': None,
|
'code_format': None,
|
||||||
'cover_tampered': False,
|
'cover_tampered': False,
|
||||||
'friendly_name': 'test',
|
'friendly_name': 'test',
|
||||||
@ -95,7 +95,7 @@
|
|||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'ac_loss': False,
|
'ac_loss': False,
|
||||||
'changed_by': None,
|
'changed_by': None,
|
||||||
'code_arm_required': True,
|
'code_arm_required': False,
|
||||||
'code_format': None,
|
'code_format': None,
|
||||||
'cover_tampered': False,
|
'cover_tampered': False,
|
||||||
'friendly_name': 'test Partition 2',
|
'friendly_name': 'test Partition 2',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user