mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Add modern configuration for template alarm control panel (#144834)
* Add modern configuration for template alarm control panel * address comments and add tests for coverage --------- Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
fa3edb5c01
commit
66ecc4d69d
@ -4,7 +4,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -21,6 +21,7 @@ from homeassistant.const import (
|
|||||||
ATTR_CODE,
|
ATTR_CODE,
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
|
CONF_STATE,
|
||||||
CONF_UNIQUE_ID,
|
CONF_UNIQUE_ID,
|
||||||
CONF_VALUE_TEMPLATE,
|
CONF_VALUE_TEMPLATE,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
@ -28,7 +29,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import TemplateError
|
||||||
from homeassistant.helpers import config_validation as cv, selector
|
from homeassistant.helpers import config_validation as cv, selector, template
|
||||||
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
|
from homeassistant.helpers.device import async_device_info_to_link_from_device_id
|
||||||
from homeassistant.helpers.entity import async_generate_entity_id
|
from homeassistant.helpers.entity import async_generate_entity_id
|
||||||
from homeassistant.helpers.entity_platform import (
|
from homeassistant.helpers.entity_platform import (
|
||||||
@ -37,10 +38,15 @@ from homeassistant.helpers.entity_platform import (
|
|||||||
)
|
)
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util import slugify
|
|
||||||
|
|
||||||
from .const import DOMAIN
|
from .const import CONF_OBJECT_ID, CONF_PICTURE, DOMAIN
|
||||||
from .template_entity import TemplateEntity, rewrite_common_legacy_to_modern_conf
|
from .template_entity import (
|
||||||
|
LEGACY_FIELDS as TEMPLATE_ENTITY_LEGACY_FIELDS,
|
||||||
|
TEMPLATE_ENTITY_AVAILABILITY_SCHEMA,
|
||||||
|
TEMPLATE_ENTITY_ICON_SCHEMA,
|
||||||
|
TemplateEntity,
|
||||||
|
rewrite_common_legacy_to_modern_conf,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_VALID_STATES = [
|
_VALID_STATES = [
|
||||||
@ -51,21 +57,22 @@ _VALID_STATES = [
|
|||||||
AlarmControlPanelState.ARMED_VACATION,
|
AlarmControlPanelState.ARMED_VACATION,
|
||||||
AlarmControlPanelState.ARMING,
|
AlarmControlPanelState.ARMING,
|
||||||
AlarmControlPanelState.DISARMED,
|
AlarmControlPanelState.DISARMED,
|
||||||
|
AlarmControlPanelState.DISARMING,
|
||||||
AlarmControlPanelState.PENDING,
|
AlarmControlPanelState.PENDING,
|
||||||
AlarmControlPanelState.TRIGGERED,
|
AlarmControlPanelState.TRIGGERED,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CONF_ALARM_CONTROL_PANELS = "panels"
|
||||||
CONF_ARM_AWAY_ACTION = "arm_away"
|
CONF_ARM_AWAY_ACTION = "arm_away"
|
||||||
CONF_ARM_CUSTOM_BYPASS_ACTION = "arm_custom_bypass"
|
CONF_ARM_CUSTOM_BYPASS_ACTION = "arm_custom_bypass"
|
||||||
CONF_ARM_HOME_ACTION = "arm_home"
|
CONF_ARM_HOME_ACTION = "arm_home"
|
||||||
CONF_ARM_NIGHT_ACTION = "arm_night"
|
CONF_ARM_NIGHT_ACTION = "arm_night"
|
||||||
CONF_ARM_VACATION_ACTION = "arm_vacation"
|
CONF_ARM_VACATION_ACTION = "arm_vacation"
|
||||||
CONF_DISARM_ACTION = "disarm"
|
|
||||||
CONF_TRIGGER_ACTION = "trigger"
|
|
||||||
CONF_ALARM_CONTROL_PANELS = "panels"
|
|
||||||
CONF_CODE_ARM_REQUIRED = "code_arm_required"
|
CONF_CODE_ARM_REQUIRED = "code_arm_required"
|
||||||
CONF_CODE_FORMAT = "code_format"
|
CONF_CODE_FORMAT = "code_format"
|
||||||
|
CONF_DISARM_ACTION = "disarm"
|
||||||
|
CONF_TRIGGER_ACTION = "trigger"
|
||||||
|
|
||||||
|
|
||||||
class TemplateCodeFormat(Enum):
|
class TemplateCodeFormat(Enum):
|
||||||
@ -76,73 +83,140 @@ class TemplateCodeFormat(Enum):
|
|||||||
text = CodeFormat.TEXT
|
text = CodeFormat.TEXT
|
||||||
|
|
||||||
|
|
||||||
ALARM_CONTROL_PANEL_SCHEMA = vol.Schema(
|
LEGACY_FIELDS = TEMPLATE_ENTITY_LEGACY_FIELDS | {
|
||||||
|
CONF_VALUE_TEMPLATE: CONF_STATE,
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFAULT_NAME = "Template Alarm Control Panel"
|
||||||
|
|
||||||
|
ALARM_CONTROL_PANEL_SCHEMA = vol.All(
|
||||||
|
vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name
|
||||||
|
): cv.enum(TemplateCodeFormat),
|
||||||
|
vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_NAME): cv.template,
|
||||||
|
vol.Optional(CONF_PICTURE): cv.template,
|
||||||
|
vol.Optional(CONF_STATE): cv.template,
|
||||||
|
vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(TEMPLATE_ENTITY_AVAILABILITY_SCHEMA.schema)
|
||||||
|
.extend(TEMPLATE_ENTITY_ICON_SCHEMA.schema),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
LEGACY_ALARM_CONTROL_PANEL_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
|
||||||
vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA,
|
|
||||||
vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA,
|
|
||||||
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
|
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
|
||||||
vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum(
|
vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum(
|
||||||
TemplateCodeFormat
|
TemplateCodeFormat
|
||||||
),
|
),
|
||||||
|
vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_NAME): cv.string,
|
vol.Optional(CONF_NAME): cv.string,
|
||||||
|
vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
vol.Optional(CONF_UNIQUE_ID): cv.string,
|
||||||
|
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
PLATFORM_SCHEMA = ALARM_CONTROL_PANEL_PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = ALARM_CONTROL_PANEL_PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_ALARM_CONTROL_PANELS): cv.schema_with_slug_keys(
|
vol.Required(CONF_ALARM_CONTROL_PANELS): cv.schema_with_slug_keys(
|
||||||
ALARM_CONTROL_PANEL_SCHEMA
|
LEGACY_ALARM_CONTROL_PANEL_SCHEMA
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
ALARM_CONTROL_PANEL_CONFIG_SCHEMA = vol.Schema(
|
ALARM_CONTROL_PANEL_CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_NAME): cv.template,
|
|
||||||
vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
|
|
||||||
vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA,
|
|
||||||
vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_AWAY_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_CUSTOM_BYPASS_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_HOME_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_NIGHT_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA,
|
vol.Optional(CONF_ARM_VACATION_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA,
|
|
||||||
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
|
vol.Optional(CONF_CODE_ARM_REQUIRED, default=True): cv.boolean,
|
||||||
vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum(
|
vol.Optional(CONF_CODE_FORMAT, default=TemplateCodeFormat.number.name): cv.enum(
|
||||||
TemplateCodeFormat
|
TemplateCodeFormat
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
|
vol.Optional(CONF_DEVICE_ID): selector.DeviceSelector(),
|
||||||
|
vol.Optional(CONF_DISARM_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
|
vol.Required(CONF_NAME): cv.template,
|
||||||
|
vol.Optional(CONF_STATE): cv.template,
|
||||||
|
vol.Optional(CONF_TRIGGER_ACTION): cv.SCRIPT_SCHEMA,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _async_create_entities(
|
def rewrite_legacy_to_modern_conf(
|
||||||
hass: HomeAssistant, config: dict[str, Any]
|
hass: HomeAssistant, config: dict[str, dict]
|
||||||
) -> list[AlarmControlPanelTemplate]:
|
) -> list[dict]:
|
||||||
"""Create Template Alarm Control Panels."""
|
"""Rewrite legacy alarm control panel configuration definitions to modern ones."""
|
||||||
alarm_control_panels = []
|
alarm_control_panels = []
|
||||||
|
|
||||||
for object_id, entity_config in config[CONF_ALARM_CONTROL_PANELS].items():
|
for object_id, entity_conf in config.items():
|
||||||
entity_config = rewrite_common_legacy_to_modern_conf(hass, entity_config)
|
entity_conf = {**entity_conf, CONF_OBJECT_ID: object_id}
|
||||||
unique_id = entity_config.get(CONF_UNIQUE_ID)
|
|
||||||
|
entity_conf = rewrite_common_legacy_to_modern_conf(
|
||||||
|
hass, entity_conf, LEGACY_FIELDS
|
||||||
|
)
|
||||||
|
|
||||||
|
if CONF_NAME not in entity_conf:
|
||||||
|
entity_conf[CONF_NAME] = template.Template(object_id, hass)
|
||||||
|
|
||||||
|
alarm_control_panels.append(entity_conf)
|
||||||
|
|
||||||
|
return alarm_control_panels
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_create_template_tracking_entities(
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
definitions: list[dict],
|
||||||
|
unique_id_prefix: str | None,
|
||||||
|
) -> None:
|
||||||
|
"""Create the template alarm control panels."""
|
||||||
|
alarm_control_panels = []
|
||||||
|
|
||||||
|
for entity_conf in definitions:
|
||||||
|
unique_id = entity_conf.get(CONF_UNIQUE_ID)
|
||||||
|
|
||||||
|
if unique_id and unique_id_prefix:
|
||||||
|
unique_id = f"{unique_id_prefix}-{unique_id}"
|
||||||
|
|
||||||
alarm_control_panels.append(
|
alarm_control_panels.append(
|
||||||
AlarmControlPanelTemplate(
|
AlarmControlPanelTemplate(
|
||||||
hass,
|
hass,
|
||||||
object_id,
|
entity_conf,
|
||||||
entity_config,
|
|
||||||
unique_id,
|
unique_id,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return alarm_control_panels
|
async_add_entities(alarm_control_panels)
|
||||||
|
|
||||||
|
|
||||||
|
def rewrite_options_to_modern_conf(option_config: dict[str, dict]) -> dict[str, dict]:
|
||||||
|
"""Rewrite option configuration to modern configuration."""
|
||||||
|
option_config = {**option_config}
|
||||||
|
|
||||||
|
if CONF_VALUE_TEMPLATE in option_config:
|
||||||
|
option_config[CONF_STATE] = option_config.pop(CONF_VALUE_TEMPLATE)
|
||||||
|
|
||||||
|
return option_config
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -153,12 +227,12 @@ async def async_setup_entry(
|
|||||||
"""Initialize config entry."""
|
"""Initialize config entry."""
|
||||||
_options = dict(config_entry.options)
|
_options = dict(config_entry.options)
|
||||||
_options.pop("template_type")
|
_options.pop("template_type")
|
||||||
|
_options = rewrite_options_to_modern_conf(_options)
|
||||||
validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options)
|
validated_config = ALARM_CONTROL_PANEL_CONFIG_SCHEMA(_options)
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
[
|
||||||
AlarmControlPanelTemplate(
|
AlarmControlPanelTemplate(
|
||||||
hass,
|
hass,
|
||||||
slugify(_options[CONF_NAME]),
|
|
||||||
validated_config,
|
validated_config,
|
||||||
config_entry.entry_id,
|
config_entry.entry_id,
|
||||||
)
|
)
|
||||||
@ -172,8 +246,22 @@ async def async_setup_platform(
|
|||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
discovery_info: DiscoveryInfoType | None = None,
|
discovery_info: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Template Alarm Control Panels."""
|
"""Set up the Template cover."""
|
||||||
async_add_entities(await _async_create_entities(hass, config))
|
if discovery_info is None:
|
||||||
|
_async_create_template_tracking_entities(
|
||||||
|
async_add_entities,
|
||||||
|
hass,
|
||||||
|
rewrite_legacy_to_modern_conf(hass, config[CONF_ALARM_CONTROL_PANELS]),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
_async_create_template_tracking_entities(
|
||||||
|
async_add_entities,
|
||||||
|
hass,
|
||||||
|
discovery_info["entities"],
|
||||||
|
discovery_info["unique_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity, RestoreEntity):
|
class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity, RestoreEntity):
|
||||||
@ -184,20 +272,20 @@ class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity, Restore
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
object_id: str,
|
|
||||||
config: dict,
|
config: dict,
|
||||||
unique_id: str | None,
|
unique_id: str | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the panel."""
|
"""Initialize the panel."""
|
||||||
super().__init__(
|
super().__init__(hass, config=config, fallback_name=None, unique_id=unique_id)
|
||||||
hass, config=config, fallback_name=object_id, unique_id=unique_id
|
if (object_id := config.get(CONF_OBJECT_ID)) is not None:
|
||||||
)
|
self.entity_id = async_generate_entity_id(
|
||||||
self.entity_id = async_generate_entity_id(
|
ENTITY_ID_FORMAT, object_id, hass=hass
|
||||||
ENTITY_ID_FORMAT, object_id, hass=hass
|
)
|
||||||
)
|
|
||||||
name = self._attr_name
|
name = self._attr_name
|
||||||
assert name is not None
|
if TYPE_CHECKING:
|
||||||
self._template = config.get(CONF_VALUE_TEMPLATE)
|
assert name is not None
|
||||||
|
self._template = config.get(CONF_STATE)
|
||||||
|
|
||||||
self._attr_code_arm_required: bool = config[CONF_CODE_ARM_REQUIRED]
|
self._attr_code_arm_required: bool = config[CONF_CODE_ARM_REQUIRED]
|
||||||
self._attr_code_format = config[CONF_CODE_FORMAT].value
|
self._attr_code_format = config[CONF_CODE_FORMAT].value
|
||||||
|
|
||||||
|
@ -7,6 +7,9 @@ from typing import Any
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components.alarm_control_panel import (
|
||||||
|
DOMAIN as ALARM_CONTROL_PANEL_DOMAIN,
|
||||||
|
)
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
from homeassistant.components.blueprint import (
|
from homeassistant.components.blueprint import (
|
||||||
is_blueprint_instance_config,
|
is_blueprint_instance_config,
|
||||||
@ -45,6 +48,7 @@ from homeassistant.helpers.typing import ConfigType
|
|||||||
from homeassistant.setup import async_notify_setup_error
|
from homeassistant.setup import async_notify_setup_error
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
|
alarm_control_panel as alarm_control_panel_platform,
|
||||||
binary_sensor as binary_sensor_platform,
|
binary_sensor as binary_sensor_platform,
|
||||||
button as button_platform,
|
button as button_platform,
|
||||||
cover as cover_platform,
|
cover as cover_platform,
|
||||||
@ -114,6 +118,10 @@ CONFIG_SECTION_SCHEMA = vol.All(
|
|||||||
vol.Optional(CONF_BINARY_SENSORS): cv.schema_with_slug_keys(
|
vol.Optional(CONF_BINARY_SENSORS): cv.schema_with_slug_keys(
|
||||||
binary_sensor_platform.LEGACY_BINARY_SENSOR_SCHEMA
|
binary_sensor_platform.LEGACY_BINARY_SENSOR_SCHEMA
|
||||||
),
|
),
|
||||||
|
vol.Optional(ALARM_CONTROL_PANEL_DOMAIN): vol.All(
|
||||||
|
cv.ensure_list,
|
||||||
|
[alarm_control_panel_platform.ALARM_CONTROL_PANEL_SCHEMA],
|
||||||
|
),
|
||||||
vol.Optional(SELECT_DOMAIN): vol.All(
|
vol.Optional(SELECT_DOMAIN): vol.All(
|
||||||
cv.ensure_list, [select_platform.SELECT_SCHEMA]
|
cv.ensure_list, [select_platform.SELECT_SCHEMA]
|
||||||
),
|
),
|
||||||
@ -144,7 +152,7 @@ CONFIG_SECTION_SCHEMA = vol.All(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
ensure_domains_do_not_have_trigger_or_action(
|
ensure_domains_do_not_have_trigger_or_action(
|
||||||
BUTTON_DOMAIN, COVER_DOMAIN, FAN_DOMAIN, LOCK_DOMAIN
|
ALARM_CONTROL_PANEL_DOMAIN, BUTTON_DOMAIN, COVER_DOMAIN, FAN_DOMAIN, LOCK_DOMAIN
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
"""The tests for the Template alarm control panel platform."""
|
"""The tests for the Template alarm control panel platform."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from syrupy.assertion import SnapshotAssertion
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
@ -13,6 +15,7 @@ from homeassistant.const import (
|
|||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_SERVICE_DATA,
|
ATTR_SERVICE_DATA,
|
||||||
EVENT_CALL_SERVICE,
|
EVENT_CALL_SERVICE,
|
||||||
|
STATE_ON,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
STATE_UNKNOWN,
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
@ -20,10 +23,13 @@ from homeassistant.core import Event, HomeAssistant, State, callback
|
|||||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from .conftest import ConfigurationStyle
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, assert_setup_component, mock_restore_cache
|
from tests.common import MockConfigEntry, assert_setup_component, mock_restore_cache
|
||||||
|
|
||||||
TEMPLATE_NAME = "alarm_control_panel.test_template_panel"
|
TEST_OBJECT_ID = "test_template_panel"
|
||||||
PANEL_NAME = "alarm_control_panel.test"
|
TEST_ENTITY_ID = f"alarm_control_panel.{TEST_OBJECT_ID}"
|
||||||
|
TEST_STATE_ENTITY_ID = "alarm_control_panel.test"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -93,50 +99,295 @@ EMPTY_ACTIONS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
UNIQUE_ID_CONFIG = {
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
"unique_id": "not-so-unique-anymore",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEMPLATE_ALARM_CONFIG = {
|
TEMPLATE_ALARM_CONFIG = {
|
||||||
"value_template": "{{ states('alarm_control_panel.test') }}",
|
"value_template": "{{ states('alarm_control_panel.test') }}",
|
||||||
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
async def async_setup_legacy_format(
|
||||||
|
hass: HomeAssistant, count: int, panel_config: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Do setup of alarm control panel integration via legacy format."""
|
||||||
|
config = {"alarm_control_panel": {"platform": "template", "panels": panel_config}}
|
||||||
|
|
||||||
|
with assert_setup_component(count, ALARM_DOMAIN):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
ALARM_DOMAIN,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_modern_format(
|
||||||
|
hass: HomeAssistant, count: int, panel_config: dict[str, Any]
|
||||||
|
) -> None:
|
||||||
|
"""Do setup of alarm control panel integration via modern format."""
|
||||||
|
config = {"template": {"alarm_control_panel": panel_config}}
|
||||||
|
|
||||||
|
with assert_setup_component(count, template.DOMAIN):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
template.DOMAIN,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def setup_panel(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
count: int,
|
||||||
|
style: ConfigurationStyle,
|
||||||
|
panel_config: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Do setup of alarm control panel integration."""
|
||||||
|
if style == ConfigurationStyle.LEGACY:
|
||||||
|
await async_setup_legacy_format(hass, count, panel_config)
|
||||||
|
elif style == ConfigurationStyle.MODERN:
|
||||||
|
await async_setup_modern_format(hass, count, panel_config)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_state_panel(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
count: int,
|
||||||
|
style: ConfigurationStyle,
|
||||||
|
state_template: str,
|
||||||
|
):
|
||||||
|
"""Do setup of alarm control panel integration using a state template."""
|
||||||
|
if style == ConfigurationStyle.LEGACY:
|
||||||
|
await async_setup_legacy_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
TEST_OBJECT_ID: {
|
||||||
|
"value_template": state_template,
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
elif style == ConfigurationStyle.MODERN:
|
||||||
|
await async_setup_modern_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
"state": state_template,
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def setup_state_panel(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
count: int,
|
||||||
|
style: ConfigurationStyle,
|
||||||
|
state_template: str,
|
||||||
|
):
|
||||||
|
"""Do setup of alarm control panel integration using a state template."""
|
||||||
|
await async_setup_state_panel(hass, count, style, state_template)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def setup_base_panel(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
count: int,
|
||||||
|
style: ConfigurationStyle,
|
||||||
|
state_template: str | None,
|
||||||
|
panel_config: str,
|
||||||
|
):
|
||||||
|
"""Do setup of alarm control panel integration using a state template."""
|
||||||
|
if style == ConfigurationStyle.LEGACY:
|
||||||
|
extra = {"value_template": state_template} if state_template else {}
|
||||||
|
await async_setup_legacy_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{TEST_OBJECT_ID: {**extra, **panel_config}},
|
||||||
|
)
|
||||||
|
elif style == ConfigurationStyle.MODERN:
|
||||||
|
extra = {"state": state_template} if state_template else {}
|
||||||
|
await async_setup_modern_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
**extra,
|
||||||
|
**panel_config,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def setup_single_attribute_state_panel(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
count: int,
|
||||||
|
style: ConfigurationStyle,
|
||||||
|
state_template: str,
|
||||||
|
attribute: str,
|
||||||
|
attribute_template: str,
|
||||||
|
) -> None:
|
||||||
|
"""Do setup of alarm control panel integration testing a single attribute."""
|
||||||
|
extra = {attribute: attribute_template} if attribute and attribute_template else {}
|
||||||
|
if style == ConfigurationStyle.LEGACY:
|
||||||
|
await async_setup_legacy_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
TEST_OBJECT_ID: {
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
"value_template": state_template,
|
||||||
|
**extra,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
elif style == ConfigurationStyle.MODERN:
|
||||||
|
await async_setup_modern_format(
|
||||||
|
hass,
|
||||||
|
count,
|
||||||
|
{
|
||||||
|
"name": TEST_OBJECT_ID,
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
"state": state_template,
|
||||||
|
**extra,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
("count", "state_template"), [(1, "{{ states('alarm_control_panel.test') }}")]
|
||||||
[
|
|
||||||
{
|
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {"test_template_panel": TEMPLATE_ALARM_CONFIG},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.parametrize(
|
||||||
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("setup_state_panel")
|
||||||
async def test_template_state_text(hass: HomeAssistant) -> None:
|
async def test_template_state_text(hass: HomeAssistant) -> None:
|
||||||
"""Test the state text of a template."""
|
"""Test the state text of a template."""
|
||||||
|
|
||||||
for set_state in (
|
for set_state in (
|
||||||
AlarmControlPanelState.ARMED_HOME,
|
|
||||||
AlarmControlPanelState.ARMED_AWAY,
|
AlarmControlPanelState.ARMED_AWAY,
|
||||||
|
AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
|
||||||
|
AlarmControlPanelState.ARMED_HOME,
|
||||||
AlarmControlPanelState.ARMED_NIGHT,
|
AlarmControlPanelState.ARMED_NIGHT,
|
||||||
AlarmControlPanelState.ARMED_VACATION,
|
AlarmControlPanelState.ARMED_VACATION,
|
||||||
AlarmControlPanelState.ARMED_CUSTOM_BYPASS,
|
|
||||||
AlarmControlPanelState.ARMING,
|
AlarmControlPanelState.ARMING,
|
||||||
AlarmControlPanelState.DISARMED,
|
AlarmControlPanelState.DISARMED,
|
||||||
|
AlarmControlPanelState.DISARMING,
|
||||||
AlarmControlPanelState.PENDING,
|
AlarmControlPanelState.PENDING,
|
||||||
AlarmControlPanelState.TRIGGERED,
|
AlarmControlPanelState.TRIGGERED,
|
||||||
):
|
):
|
||||||
hass.states.async_set(PANEL_NAME, set_state)
|
hass.states.async_set(TEST_STATE_ENTITY_ID, set_state)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(TEMPLATE_NAME)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
assert state.state == set_state
|
assert state.state == set_state
|
||||||
|
|
||||||
hass.states.async_set(PANEL_NAME, "invalid_state")
|
hass.states.async_set(TEST_STATE_ENTITY_ID, "invalid_state")
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(TEMPLATE_NAME)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
assert state.state == "unknown"
|
assert state.state == "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [1])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("state_template", "expected"),
|
||||||
|
[
|
||||||
|
("{{ 'disarmed' }}", AlarmControlPanelState.DISARMED),
|
||||||
|
("{{ 'armed_home' }}", AlarmControlPanelState.ARMED_HOME),
|
||||||
|
("{{ 'armed_away' }}", AlarmControlPanelState.ARMED_AWAY),
|
||||||
|
("{{ 'armed_night' }}", AlarmControlPanelState.ARMED_NIGHT),
|
||||||
|
("{{ 'armed_vacation' }}", AlarmControlPanelState.ARMED_VACATION),
|
||||||
|
("{{ 'armed_custom_bypass' }}", AlarmControlPanelState.ARMED_CUSTOM_BYPASS),
|
||||||
|
("{{ 'pending' }}", AlarmControlPanelState.PENDING),
|
||||||
|
("{{ 'arming' }}", AlarmControlPanelState.ARMING),
|
||||||
|
("{{ 'disarming' }}", AlarmControlPanelState.DISARMING),
|
||||||
|
("{{ 'triggered' }}", AlarmControlPanelState.TRIGGERED),
|
||||||
|
("{{ x - 1 }}", STATE_UNKNOWN),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("setup_state_panel")
|
||||||
|
async def test_state_template_states(hass: HomeAssistant, expected: str) -> None:
|
||||||
|
"""Test the state template."""
|
||||||
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
|
assert state.state == expected
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("count", "state_template", "attribute_template"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
"{{ 'disarmed' }}",
|
||||||
|
"{% if states.switch.test_state.state %}mdi:check{% endif %}",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("style", "attribute"),
|
||||||
|
[
|
||||||
|
(ConfigurationStyle.MODERN, "icon"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("setup_single_attribute_state_panel")
|
||||||
|
async def test_icon_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test icon template."""
|
||||||
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
|
assert state.attributes.get("icon") in ("", None)
|
||||||
|
|
||||||
|
hass.states.async_set("switch.test_state", STATE_ON)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
|
assert state.attributes["icon"] == "mdi:check"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("count", "state_template", "attribute_template"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
1,
|
||||||
|
"{{ 'disarmed' }}",
|
||||||
|
"{% if states.switch.test_state.state %}local/panel.png{% endif %}",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("style", "attribute"),
|
||||||
|
[
|
||||||
|
(ConfigurationStyle.MODERN, "picture"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("setup_single_attribute_state_panel")
|
||||||
|
async def test_picture_template(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test icon template."""
|
||||||
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
|
assert state.attributes.get("entity_picture") in ("", None)
|
||||||
|
|
||||||
|
hass.states.async_set("switch.test_state", STATE_ON)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
|
assert state.attributes["entity_picture"] == "local/panel.png"
|
||||||
|
|
||||||
|
|
||||||
async def test_setup_config_entry(
|
async def test_setup_config_entry(
|
||||||
hass: HomeAssistant, snapshot: SnapshotAssertion
|
hass: HomeAssistant, snapshot: SnapshotAssertion
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -172,29 +423,18 @@ async def test_setup_config_entry(
|
|||||||
assert state.state == AlarmControlPanelState.DISARMED
|
assert state.state == AlarmControlPanelState.DISARMED
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
@pytest.mark.parametrize(("count", "state_template"), [(1, None)])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
[
|
|
||||||
{
|
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {"test_template_panel": OPTIMISTIC_TEMPLATE_ALARM_CONFIG},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {"test_template_panel": EMPTY_ACTIONS},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.parametrize(
|
||||||
|
"panel_config", [OPTIMISTIC_TEMPLATE_ALARM_CONFIG, EMPTY_ACTIONS]
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("setup_base_panel")
|
||||||
async def test_optimistic_states(hass: HomeAssistant) -> None:
|
async def test_optimistic_states(hass: HomeAssistant) -> None:
|
||||||
"""Test the optimistic state."""
|
"""Test the optimistic state."""
|
||||||
|
|
||||||
state = hass.states.get(TEMPLATE_NAME)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert state.state == "unknown"
|
assert state.state == "unknown"
|
||||||
|
|
||||||
@ -210,31 +450,45 @@ async def test_optimistic_states(hass: HomeAssistant) -> None:
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
ALARM_DOMAIN,
|
ALARM_DOMAIN,
|
||||||
service,
|
service,
|
||||||
{"entity_id": TEMPLATE_NAME, "code": "1234"},
|
{"entity_id": TEST_ENTITY_ID, "code": "1234"},
|
||||||
blocking=True,
|
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(TEST_ENTITY_ID).state == set_state
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("count", [0])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("panel_config", "state_template", "msg"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
"{% if blah %}",
|
||||||
|
"invalid template",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"code_format": "bad_format", **OPTIMISTIC_TEMPLATE_ALARM_CONFIG},
|
||||||
|
"disarmed",
|
||||||
|
"value must be one of ['no_code', 'number', 'text']",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.usefixtures("setup_base_panel")
|
||||||
|
async def test_template_syntax_error(
|
||||||
|
hass: HomeAssistant, msg, caplog_setup_text
|
||||||
|
) -> None:
|
||||||
|
"""Test templating syntax error."""
|
||||||
|
assert len(hass.states.async_all("alarm_control_panel")) == 0
|
||||||
|
assert (msg) in caplog_setup_text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(0, "alarm_control_panel")])
|
@pytest.mark.parametrize(("count", "domain"), [(0, "alarm_control_panel")])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("config", "msg"),
|
("config", "msg"),
|
||||||
[
|
[
|
||||||
(
|
|
||||||
{
|
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "{% if blah %}",
|
|
||||||
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"invalid template",
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"alarm_control_panel": {
|
"alarm_control_panel": {
|
||||||
@ -264,25 +518,10 @@ async def test_optimistic_states(hass: HomeAssistant) -> None:
|
|||||||
},
|
},
|
||||||
"required key 'panels' not provided",
|
"required key 'panels' not provided",
|
||||||
),
|
),
|
||||||
(
|
|
||||||
{
|
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "disarmed",
|
|
||||||
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
|
||||||
"code_format": "bad_format",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"value must be one of ['no_code', 'number', 'text']",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.usefixtures("start_ha")
|
||||||
async def test_template_syntax_error(
|
async def test_legacy_template_syntax_error(
|
||||||
hass: HomeAssistant, msg, caplog_setup_text
|
hass: HomeAssistant, msg, caplog_setup_text
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test templating syntax error."""
|
"""Test templating syntax error."""
|
||||||
@ -290,43 +529,30 @@ async def test_template_syntax_error(
|
|||||||
assert (msg) in caplog_setup_text
|
assert (msg) in caplog_setup_text
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
("count", "state_template", "attribute", "attribute_template"),
|
||||||
|
[(1, "disarmed", "name", '{{ "Template Alarm Panel" }}')],
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("style", "test_entity_id"),
|
||||||
[
|
[
|
||||||
{
|
(ConfigurationStyle.LEGACY, TEST_ENTITY_ID),
|
||||||
"alarm_control_panel": {
|
(ConfigurationStyle.MODERN, "alarm_control_panel.template_alarm_panel"),
|
||||||
"platform": "template",
|
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"name": '{{ "Template Alarm Panel" }}',
|
|
||||||
"value_template": "disarmed",
|
|
||||||
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.usefixtures("setup_single_attribute_state_panel")
|
||||||
async def test_name(hass: HomeAssistant) -> None:
|
async def test_name(hass: HomeAssistant, test_entity_id: str) -> None:
|
||||||
"""Test the accessibility of the name attribute."""
|
"""Test the accessibility of the name attribute."""
|
||||||
state = hass.states.get(TEMPLATE_NAME)
|
state = hass.states.get(test_entity_id)
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.attributes.get("friendly_name") == "Template Alarm Panel"
|
assert state.attributes.get("friendly_name") == "Template Alarm Panel"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
("count", "state_template"), [(1, "{{ states('alarm_control_panel.test') }}")]
|
||||||
[
|
)
|
||||||
{
|
@pytest.mark.parametrize(
|
||||||
"alarm_control_panel": {
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
"platform": "template",
|
|
||||||
"panels": {"test_template_panel": TEMPLATE_ALARM_CONFIG},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"service",
|
"service",
|
||||||
@ -340,7 +566,7 @@ async def test_name(hass: HomeAssistant) -> None:
|
|||||||
"alarm_trigger",
|
"alarm_trigger",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.usefixtures("setup_state_panel")
|
||||||
async def test_actions(
|
async def test_actions(
|
||||||
hass: HomeAssistant, service, call_service_events: list[Event]
|
hass: HomeAssistant, service, call_service_events: list[Event]
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -348,128 +574,147 @@ async def test_actions(
|
|||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
ALARM_DOMAIN,
|
ALARM_DOMAIN,
|
||||||
service,
|
service,
|
||||||
{"entity_id": TEMPLATE_NAME, "code": "1234"},
|
{"entity_id": TEST_ENTITY_ID, "code": "1234"},
|
||||||
blocking=True,
|
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
|
||||||
assert call_service_events[0].data["service"] == service
|
assert call_service_events[0].data["service"] == service
|
||||||
assert call_service_events[0].data["service_data"]["code"] == TEMPLATE_NAME
|
assert call_service_events[0].data["service_data"]["code"] == TEST_ENTITY_ID
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
@pytest.mark.parametrize("count", [1])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
("panel_config", "style"),
|
||||||
[
|
[
|
||||||
{
|
(
|
||||||
"alarm_control_panel": {
|
{
|
||||||
"platform": "template",
|
"test_template_alarm_control_panel_01": {
|
||||||
"panels": {
|
"value_template": "{{ true }}",
|
||||||
"test_template_alarm_control_panel_01": {
|
**UNIQUE_ID_CONFIG,
|
||||||
"unique_id": "not-so-unique-anymore",
|
},
|
||||||
"value_template": "{{ true }}",
|
"test_template_alarm_control_panel_02": {
|
||||||
},
|
"value_template": "{{ false }}",
|
||||||
"test_template_alarm_control_panel_02": {
|
**UNIQUE_ID_CONFIG,
|
||||||
"unique_id": "not-so-unique-anymore",
|
|
||||||
"value_template": "{{ false }}",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
ConfigurationStyle.LEGACY,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "test_template_alarm_control_panel_01",
|
||||||
|
"state": "{{ true }}",
|
||||||
|
**UNIQUE_ID_CONFIG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test_template_alarm_control_panel_02",
|
||||||
|
"state": "{{ false }}",
|
||||||
|
**UNIQUE_ID_CONFIG,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
ConfigurationStyle.MODERN,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.usefixtures("setup_panel")
|
||||||
async def test_unique_id(hass: HomeAssistant) -> None:
|
async def test_unique_id(hass: HomeAssistant) -> None:
|
||||||
"""Test unique_id option only creates one alarm control panel per id."""
|
"""Test unique_id option only creates one alarm control panel per id."""
|
||||||
assert len(hass.states.async_all()) == 1
|
assert len(hass.states.async_all()) == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
async def test_nested_unique_id(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test a template unique_id propagates to alarm_control_panel unique_ids."""
|
||||||
|
with assert_setup_component(1, template.DOMAIN):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
template.DOMAIN,
|
||||||
|
{
|
||||||
|
"template": {
|
||||||
|
"unique_id": "x",
|
||||||
|
"alarm_control_panel": [
|
||||||
|
{
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
"name": "test_a",
|
||||||
|
"unique_id": "a",
|
||||||
|
"state": "{{ true }}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
**OPTIMISTIC_TEMPLATE_ALARM_CONFIG,
|
||||||
|
"name": "test_b",
|
||||||
|
"unique_id": "b",
|
||||||
|
"state": "{{ true }}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_start()
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(hass.states.async_all("alarm_control_panel")) == 2
|
||||||
|
|
||||||
|
entry = entity_registry.async_get("alarm_control_panel.test_a")
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "x-a"
|
||||||
|
|
||||||
|
entry = entity_registry.async_get("alarm_control_panel.test_b")
|
||||||
|
assert entry
|
||||||
|
assert entry.unique_id == "x-b"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("count", "state_template"), [(1, "disarmed")])
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("config", "code_format", "code_arm_required"),
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("panel_config", "code_format", "code_arm_required"),
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
{
|
{},
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "disarmed",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"number",
|
"number",
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{"code_format": "text"},
|
||||||
"alarm_control_panel": {
|
|
||||||
"platform": "template",
|
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "disarmed",
|
|
||||||
"code_format": "text",
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"text",
|
"text",
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"alarm_control_panel": {
|
"code_format": "no_code",
|
||||||
"platform": "template",
|
"code_arm_required": False,
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "disarmed",
|
|
||||||
"code_format": "no_code",
|
|
||||||
"code_arm_required": False,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
"alarm_control_panel": {
|
"code_format": "text",
|
||||||
"platform": "template",
|
"code_arm_required": False,
|
||||||
"panels": {
|
|
||||||
"test_template_panel": {
|
|
||||||
"value_template": "disarmed",
|
|
||||||
"code_format": "text",
|
|
||||||
"code_arm_required": False,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"text",
|
"text",
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.usefixtures("start_ha")
|
@pytest.mark.usefixtures("setup_base_panel")
|
||||||
async def test_code_config(hass: HomeAssistant, code_format, code_arm_required) -> None:
|
async def test_code_config(hass: HomeAssistant, code_format, code_arm_required) -> None:
|
||||||
"""Test configuration options related to alarm code."""
|
"""Test configuration options related to alarm code."""
|
||||||
state = hass.states.get(TEMPLATE_NAME)
|
state = hass.states.get(TEST_ENTITY_ID)
|
||||||
assert state.attributes.get("code_format") == code_format
|
assert state.attributes.get("code_format") == code_format
|
||||||
assert state.attributes.get("code_arm_required") == code_arm_required
|
assert state.attributes.get("code_arm_required") == code_arm_required
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(("count", "domain"), [(1, "alarm_control_panel")])
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"config",
|
("count", "state_template"), [(1, "{{ states('alarm_control_panel.test') }}")]
|
||||||
[
|
)
|
||||||
{
|
@pytest.mark.parametrize(
|
||||||
"alarm_control_panel": {
|
"style", [ConfigurationStyle.LEGACY, ConfigurationStyle.MODERN]
|
||||||
"platform": "template",
|
|
||||||
"panels": {"test_template_panel": TEMPLATE_ALARM_CONFIG},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("restored_state", "initial_state"),
|
("restored_state", "initial_state"),
|
||||||
@ -508,11 +753,11 @@ async def test_code_config(hass: HomeAssistant, code_format, code_arm_required)
|
|||||||
)
|
)
|
||||||
async def test_restore_state(
|
async def test_restore_state(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
count,
|
count: int,
|
||||||
domain,
|
state_template: str,
|
||||||
config,
|
style: ConfigurationStyle,
|
||||||
restored_state,
|
restored_state: str,
|
||||||
initial_state,
|
initial_state: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test restoring template alarm control panel."""
|
"""Test restoring template alarm control panel."""
|
||||||
|
|
||||||
@ -522,17 +767,7 @@ async def test_restore_state(
|
|||||||
{},
|
{},
|
||||||
)
|
)
|
||||||
mock_restore_cache(hass, (fake_state,))
|
mock_restore_cache(hass, (fake_state,))
|
||||||
with assert_setup_component(count, domain):
|
await async_setup_state_panel(hass, count, style, state_template)
|
||||||
assert await async_setup_component(
|
|
||||||
hass,
|
|
||||||
domain,
|
|
||||||
config,
|
|
||||||
)
|
|
||||||
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
await hass.async_start()
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("alarm_control_panel.test_template_panel")
|
state = hass.states.get("alarm_control_panel.test_template_panel")
|
||||||
assert state.state == initial_state
|
assert state.state == initial_state
|
||||||
|
Loading…
x
Reference in New Issue
Block a user