From 55f8668cd8299a3328e9354f6714d57d8ec8aeba Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Sun, 26 Feb 2017 22:09:20 +0200 Subject: [PATCH 1/4] Restore for automation entities --- .../components/automation/__init__.py | 13 +++- tests/common.py | 15 ++-- tests/components/automation/test_init.py | 70 +++++++++++++++---- 3 files changed, 73 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/automation/__init__.py b/homeassistant/components/automation/__init__.py index 0e734d7214d0a..a5fc52c448ea8 100644 --- a/homeassistant/components/automation/__init__.py +++ b/homeassistant/components/automation/__init__.py @@ -21,6 +21,7 @@ from homeassistant.helpers import extract_domain_configs, script, condition from homeassistant.helpers.entity import ToggleEntity from homeassistant.helpers.entity_component import EntityComponent +from homeassistant.helpers.restore_state import async_get_last_state from homeassistant.loader import get_platform from homeassistant.util.dt import utcnow import homeassistant.helpers.config_validation as cv @@ -265,9 +266,15 @@ def is_on(self) -> bool: @asyncio.coroutine def async_added_to_hass(self) -> None: - """Startup if initial_state.""" - if self._initial_state: - yield from self.async_enable() + """Startup with initial state or previous state.""" + state = yield from async_get_last_state(self.hass, self.entity_id) + if state is None: + if self._initial_state: + yield from self.async_enable() + else: + self._last_triggered = state.attributes.get('last_triggered') + if state.state == STATE_ON: + yield from self.async_enable() @asyncio.coroutine def async_turn_on(self, **kwargs) -> None: diff --git a/tests/common.py b/tests/common.py index a1635e3387c61..6e159db03e350 100644 --- a/tests/common.py +++ b/tests/common.py @@ -131,6 +131,7 @@ def mock_async_start(): @ha.callback def clear_instance(event): + """Clear global instance.""" global INST_COUNT INST_COUNT -= 1 @@ -152,20 +153,18 @@ def get_test_instance_port(): def mock_service(hass, domain, service): - """Setup a fake service. - - Return a list that logs all calls to fake service. - """ + """Setup a fake service & return a list that logs calls to this service.""" calls = [] - # pylint: disable=redefined-outer-name @ha.callback - def mock_service(call): + def mock_service_log(call): # pylint: disable=unnecessary-lambda """"Mocked service call.""" calls.append(call) - # pylint: disable=unnecessary-lambda - hass.services.register(domain, service, mock_service) + if hass.loop.__dict__.get("_thread_ident", 0) == threading.get_ident(): + hass.services.async_register(domain, service, mock_service_log) + else: + hass.services.register(domain, service, mock_service_log) return calls diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index fa7658f340759..340bcf76d8d06 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -1,17 +1,19 @@ """The tests for the automation component.""" -import unittest +import asyncio from datetime import timedelta +import unittest from unittest.mock import patch -from homeassistant.core import callback -from homeassistant.bootstrap import setup_component +from homeassistant.core import State +from homeassistant.bootstrap import setup_component, async_setup_component import homeassistant.components.automation as automation -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, STATE_OFF from homeassistant.exceptions import HomeAssistantError import homeassistant.util.dt as dt_util -from tests.common import get_test_home_assistant, assert_setup_component, \ - fire_time_changed, mock_component +from tests.common import ( + assert_setup_component, get_test_home_assistant, fire_time_changed, + mock_component, mock_service, mock_restore_cache) # pylint: disable=invalid-name @@ -22,14 +24,7 @@ def setUp(self): """Setup things to be run when tests are started.""" self.hass = get_test_home_assistant() mock_component(self.hass, 'group') - self.calls = [] - - @callback - def record_call(service): - """Helper to record calls.""" - self.calls.append(service) - - self.hass.services.register('test', 'automation', record_call) + self.calls = mock_service(self.hass, 'test', 'automation') def tearDown(self): """Stop everything that was started.""" @@ -572,3 +567,50 @@ def test_reload_config_handles_load_fails(self): self.hass.bus.fire('test_event') self.hass.block_till_done() assert len(self.calls) == 2 + + +@asyncio.coroutine +def test_automation_restore_state(hass): + """Ensure states are restored on startup.""" + time = dt_util.utcnow() + + mock_restore_cache(hass, ( + State('automation.hello', STATE_ON), + State('automation.bye', STATE_OFF, {'last_triggered': time}), + )) + + config = {automation.DOMAIN: [{ + 'alias': 'hello', + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event_hello', + }, + 'action': {'service': 'test.automation'} + }, { + 'alias': 'bye', + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event_bye', + }, + 'action': {'service': 'test.automation'} + }]} + + assert (yield from async_setup_component(hass, automation.DOMAIN, config)) + + state = hass.states.get('automation.hello') + assert state + assert state.state == STATE_ON + + calls = mock_service(hass, 'test', 'automation') + + state = hass.states.get('automation.bye') + assert state + assert state.state == STATE_OFF + assert state.attributes.get('last_triggered') == time + hass.bus.fire('test_event_bye') + yield from hass.async_block_till_done() + assert len(calls) == 0 + + hass.bus.fire('test_event_hello') + yield from hass.async_block_till_done() + assert len(calls) == 1 From ff947978abbe2be5853fb4c75e094cb2738355bc Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Thu, 2 Mar 2017 20:41:26 +0200 Subject: [PATCH 2/4] coroutine --- tests/common.py | 2 +- tests/components/automation/test_init.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/common.py b/tests/common.py index 6e159db03e350..380072eabc891 100644 --- a/tests/common.py +++ b/tests/common.py @@ -156,7 +156,7 @@ def mock_service(hass, domain, service): """Setup a fake service & return a list that logs calls to this service.""" calls = [] - @ha.callback + @asyncio.coroutine def mock_service_log(call): # pylint: disable=unnecessary-lambda """"Mocked service call.""" calls.append(call) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 340bcf76d8d06..41aa8311c7eb8 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -601,12 +601,13 @@ def test_automation_restore_state(hass): assert state assert state.state == STATE_ON - calls = mock_service(hass, 'test', 'automation') - state = hass.states.get('automation.bye') assert state assert state.state == STATE_OFF assert state.attributes.get('last_triggered') == time + + calls = mock_service(hass, 'test', 'automation') + hass.bus.fire('test_event_bye') yield from hass.async_block_till_done() assert len(calls) == 0 From 1034effe60c7df045c499413def69e6b3670e63c Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Fri, 3 Mar 2017 06:45:20 +0200 Subject: [PATCH 3/4] no clue what i'm doing now --- tests/components/automation/test_init.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 41aa8311c7eb8..5174c1dfa1d51 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -614,4 +614,8 @@ def test_automation_restore_state(hass): hass.bus.fire('test_event_hello') yield from hass.async_block_till_done() + + import time + time.sleep(1) + assert len(calls) == 1 From 06ca47e11296e793ae6bba822c3bc8ebf128aecc Mon Sep 17 00:00:00 2001 From: Johann Kellerman Date: Fri, 3 Mar 2017 07:43:08 +0200 Subject: [PATCH 4/4] Still passes nicely in py 3.4 --- tests/components/automation/test_init.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index 5174c1dfa1d51..9dc080890116b 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -608,14 +608,15 @@ def test_automation_restore_state(hass): calls = mock_service(hass, 'test', 'automation') - hass.bus.fire('test_event_bye') + assert automation.is_on(hass, 'automation.bye') is False + + hass.bus.async_fire('test_event_bye') yield from hass.async_block_till_done() assert len(calls) == 0 - hass.bus.fire('test_event_hello') - yield from hass.async_block_till_done() + assert automation.is_on(hass, 'automation.hello') - import time - time.sleep(1) + hass.bus.async_fire('test_event_hello') + yield from hass.async_block_till_done() assert len(calls) == 1