From f3870a8a48535132ac8b3a434a7ee1215a08bdb0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 2 Mar 2017 08:50:41 +0100 Subject: [PATCH] Template binary_sensor change flow / add restore (#6343) * Template binary_sensor change flow / add restore * fix lint --- .../components/binary_sensor/template.py | 30 +++++-- .../components/binary_sensor/test_template.py | 82 +++++++++++++++---- 2 files changed, 92 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/binary_sensor/template.py b/homeassistant/components/binary_sensor/template.py index 35666e0ea55..fbdfa2eb4de 100644 --- a/homeassistant/components/binary_sensor/template.py +++ b/homeassistant/components/binary_sensor/template.py @@ -15,12 +15,14 @@ from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA) from homeassistant.const import ( ATTR_FRIENDLY_NAME, ATTR_ENTITY_ID, CONF_VALUE_TEMPLATE, - CONF_SENSOR_CLASS, CONF_SENSORS, CONF_DEVICE_CLASS) + CONF_SENSOR_CLASS, CONF_SENSORS, CONF_DEVICE_CLASS, + EVENT_HOMEASSISTANT_START) from homeassistant.exceptions import TemplateError -from homeassistant.helpers.entity import async_generate_entity_id -from homeassistant.helpers.event import async_track_state_change import homeassistant.helpers.config_validation as cv from homeassistant.helpers.deprecation import get_deprecated +from homeassistant.helpers.entity import async_generate_entity_id +from homeassistant.helpers.event import async_track_state_change +from homeassistant.helpers.restore_state import async_get_last_state _LOGGER = logging.getLogger(__name__) @@ -83,14 +85,30 @@ class BinarySensorTemplate(BinarySensorDevice): self._device_class = device_class self._template = value_template self._state = None + self._entities = entity_ids + + @asyncio.coroutine + def async_added_to_hass(self): + """Register callbacks.""" + state = yield from async_get_last_state(self.hass, self.entity_id) + if state: + self._state = state.state @callback def template_bsensor_state_listener(entity, old_state, new_state): """Called when the target device changes state.""" - hass.async_add_job(self.async_update_ha_state, True) + self.hass.async_add_job(self.async_update_ha_state(True)) - async_track_state_change( - hass, entity_ids, template_bsensor_state_listener) + @callback + def template_bsensor_startup(event): + """Update template on startup.""" + async_track_state_change( + self.hass, self._entities, template_bsensor_state_listener) + + self.hass.async_add_job(self.async_update_ha_state(True)) + + self.hass.bus.async_listen_once( + EVENT_HOMEASSISTANT_START, template_bsensor_startup) @property def name(self): diff --git a/tests/components/binary_sensor/test_template.py b/tests/components/binary_sensor/test_template.py index cb526851710..77818c339e2 100644 --- a/tests/components/binary_sensor/test_template.py +++ b/tests/components/binary_sensor/test_template.py @@ -1,15 +1,19 @@ """The tests for the Template Binary sensor platform.""" +import asyncio import unittest from unittest import mock -from homeassistant.const import EVENT_STATE_CHANGED, MATCH_ALL +from homeassistant.core import CoreState, State +from homeassistant.const import MATCH_ALL import homeassistant.bootstrap as bootstrap from homeassistant.components.binary_sensor import template from homeassistant.exceptions import TemplateError from homeassistant.helpers import template as template_hlpr from homeassistant.util.async import run_callback_threadsafe +from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE -from tests.common import get_test_home_assistant, assert_setup_component +from tests.common import ( + get_test_home_assistant, assert_setup_component, mock_component) class TestBinarySensorTemplate(unittest.TestCase): @@ -26,8 +30,7 @@ class TestBinarySensorTemplate(unittest.TestCase): """Stop everything that was started.""" self.hass.stop() - @mock.patch.object(template, 'BinarySensorTemplate') - def test_setup(self, mock_template): + def test_setup(self): """"Test the setup.""" config = { 'binary_sensor': { @@ -117,18 +120,34 @@ class TestBinarySensorTemplate(unittest.TestCase): def test_event(self): """"Test the event.""" - vs = run_callback_threadsafe( - self.hass.loop, template.BinarySensorTemplate, - self.hass, 'parent', 'Parent', 'motion', - template_hlpr.Template('{{ 1 > 1 }}', self.hass), MATCH_ALL - ).result() - vs.update_ha_state() + config = { + 'binary_sensor': { + 'platform': 'template', + 'sensors': { + 'test': { + 'friendly_name': 'virtual thingy', + 'value_template': + "{{ states.sensor.test_state.state == 'on' }}", + 'device_class': 'motion', + }, + }, + }, + } + with assert_setup_component(1): + assert bootstrap.setup_component( + self.hass, 'binary_sensor', config) + + self.hass.start() self.hass.block_till_done() - with mock.patch.object(vs, 'async_update') as mock_update: - self.hass.bus.fire(EVENT_STATE_CHANGED) - self.hass.block_till_done() - assert mock_update.call_count == 1 + state = self.hass.states.get('binary_sensor.test') + assert state.state == 'off' + + self.hass.states.set('sensor.test_state', 'on') + self.hass.block_till_done() + + state = self.hass.states.get('binary_sensor.test') + assert state.state == 'on' @mock.patch('homeassistant.helpers.template.Template.render') def test_update_template_error(self, mock_render): @@ -143,3 +162,38 @@ class TestBinarySensorTemplate(unittest.TestCase): mock_render.side_effect = TemplateError( "UndefinedError: 'None' has no attribute") vs.update() + + +@asyncio.coroutine +def test_restore_state(hass): + """Ensure states are restored on startup.""" + hass.data[DATA_RESTORE_CACHE] = { + 'binary_sensor.test': State('binary_sensor.test', 'on'), + } + + hass.state = CoreState.starting + mock_component(hass, 'recorder') + + config = { + 'binary_sensor': { + 'platform': 'template', + 'sensors': { + 'test': { + 'friendly_name': 'virtual thingy', + 'value_template': + "{{ states.sensor.test_state.state == 'on' }}", + 'device_class': 'motion', + }, + }, + }, + } + yield from bootstrap.async_setup_component(hass, 'binary_sensor', config) + + state = hass.states.get('binary_sensor.test') + assert state.state == 'on' + + yield from hass.async_start() + yield from hass.async_block_till_done() + + state = hass.states.get('binary_sensor.test') + assert state.state == 'off'