diff --git a/homeassistant/components/template/alarm_control_panel.py b/homeassistant/components/template/alarm_control_panel.py index 36cdcf3a55e..e802ea40ebb 100644 --- a/homeassistant/components/template/alarm_control_panel.py +++ b/homeassistant/components/template/alarm_control_panel.py @@ -19,8 +19,6 @@ from homeassistant.const import ( CONF_NAME, CONF_UNIQUE_ID, CONF_VALUE_TEMPLATE, - EVENT_HOMEASSISTANT_START, - MATCH_ALL, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, @@ -33,9 +31,10 @@ from homeassistant.core import callback from homeassistant.exceptions import TemplateError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import async_generate_entity_id -from homeassistant.helpers.event import async_track_state_change_event from homeassistant.helpers.script import Script +from .template_entity import TemplateEntity + _LOGGER = logging.getLogger(__name__) _VALID_STATES = [ STATE_ALARM_ARMED_AWAY, @@ -90,18 +89,6 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= code_arm_required = device_config[CONF_CODE_ARM_REQUIRED] unique_id = device_config.get(CONF_UNIQUE_ID) - template_entity_ids = set() - - if state_template is not None: - temp_ids = state_template.extract_entities() - if str(temp_ids) != MATCH_ALL: - template_entity_ids |= set(temp_ids) - else: - _LOGGER.warning("No value template - will use optimistic state") - - if not template_entity_ids: - template_entity_ids = MATCH_ALL - alarm_control_panels.append( AlarmControlPanelTemplate( hass, @@ -113,7 +100,6 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= arm_home_action, arm_night_action, code_arm_required, - template_entity_ids, unique_id, ) ) @@ -121,7 +107,7 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= async_add_entities(alarm_control_panels) -class AlarmControlPanelTemplate(AlarmControlPanelEntity): +class AlarmControlPanelTemplate(TemplateEntity, AlarmControlPanelEntity): """Representation of a templated Alarm Control Panel.""" def __init__( @@ -135,11 +121,10 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity): arm_home_action, arm_night_action, code_arm_required, - template_entity_ids, unique_id, ): """Initialize the panel.""" - self.hass = hass + super().__init__() self.entity_id = async_generate_entity_id( ENTITY_ID_FORMAT, device_id, hass=hass ) @@ -161,12 +146,8 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity): self._arm_night_script = Script(hass, arm_night_action, name, domain) self._state = None - self._entities = template_entity_ids self._unique_id = unique_id - if self._template is not None: - self._template.hass = self.hass - @property def name(self): """Return the display name of this alarm control panel.""" @@ -177,11 +158,6 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity): """Return the unique id of this alarm control panel.""" return self._unique_id - @property - def should_poll(self): - """Return the polling state.""" - return False - @property def state(self): """Return the state of the device.""" @@ -212,28 +188,32 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity): """Whether the code is required for arm actions.""" return self._code_arm_required + @callback + def _update_state(self, state): + if isinstance(state, TemplateError): + self._state = None + return + + # Validate state + if state in _VALID_STATES: + self._state = state + _LOGGER.debug("Valid state - %s", state) + return + + _LOGGER.error( + "Received invalid alarm panel state: %s. Expected: %s", + state, + ", ".join(_VALID_STATES), + ) + self._state = None + async def async_added_to_hass(self): """Register callbacks.""" - - @callback - def template_alarm_state_listener(event): - """Handle target device state changes.""" - self.async_schedule_update_ha_state(True) - - @callback - def template_alarm_control_panel_startup(event): - """Update template on startup.""" - if self._template is not None and self._entities != MATCH_ALL: - # Track state change only for valid templates - async_track_state_change_event( - self.hass, self._entities, template_alarm_state_listener - ) - - self.async_schedule_update_ha_state(True) - - self.hass.bus.async_listen_once( - EVENT_HOMEASSISTANT_START, template_alarm_control_panel_startup - ) + if self._template: + self.add_template_attribute( + "_state", self._template, None, self._update_state + ) + await super().async_added_to_hass() async def _async_alarm_arm(self, state, script=None, code=None): """Arm the panel to specified state with supplied script.""" @@ -274,25 +254,3 @@ class AlarmControlPanelTemplate(AlarmControlPanelEntity): await self._async_alarm_arm( STATE_ALARM_DISARMED, script=self._disarm_script, code=code ) - - async def async_update(self): - """Update the state from the template.""" - if self._template is None: - return - - try: - state = self._template.async_render().lower() - except TemplateError as ex: - _LOGGER.error(ex) - self._state = None - - if state in _VALID_STATES: - self._state = state - _LOGGER.debug("Valid state - %s", state) - else: - _LOGGER.error( - "Received invalid alarm panel state: %s. Expected: %s", - state, - ", ".join(_VALID_STATES), - ) - self._state = None diff --git a/tests/components/template/test_alarm_control_panel.py b/tests/components/template/test_alarm_control_panel.py index e27359fd56e..c6453ba31ac 100644 --- a/tests/components/template/test_alarm_control_panel.py +++ b/tests/components/template/test_alarm_control_panel.py @@ -148,6 +148,7 @@ async def test_optimistic_states(hass): await common.async_alarm_arm_away( hass, entity_id="alarm_control_panel.test_template_panel" ) + await hass.async_block_till_done() state = hass.states.get("alarm_control_panel.test_template_panel") await hass.async_block_till_done() assert state.state == STATE_ALARM_ARMED_AWAY @@ -201,6 +202,7 @@ async def test_no_action_scripts(hass): await common.async_alarm_arm_away( hass, entity_id="alarm_control_panel.test_template_panel" ) + await hass.async_block_till_done() state = hass.states.get("alarm_control_panel.test_template_panel") await hass.async_block_till_done() assert state.state == STATE_ALARM_ARMED_AWAY @@ -208,6 +210,7 @@ async def test_no_action_scripts(hass): await common.async_alarm_arm_home( hass, entity_id="alarm_control_panel.test_template_panel" ) + await hass.async_block_till_done() state = hass.states.get("alarm_control_panel.test_template_panel") await hass.async_block_till_done() assert state.state == STATE_ALARM_ARMED_AWAY @@ -215,6 +218,7 @@ async def test_no_action_scripts(hass): await common.async_alarm_arm_night( hass, entity_id="alarm_control_panel.test_template_panel" ) + await hass.async_block_till_done() state = hass.states.get("alarm_control_panel.test_template_panel") await hass.async_block_till_done() assert state.state == STATE_ALARM_ARMED_AWAY @@ -222,6 +226,7 @@ async def test_no_action_scripts(hass): await common.async_alarm_disarm( hass, entity_id="alarm_control_panel.test_template_panel" ) + await hass.async_block_till_done() state = hass.states.get("alarm_control_panel.test_template_panel") await hass.async_block_till_done() assert state.state == STATE_ALARM_ARMED_AWAY