From e2e58e6acc5bb34c2a5d30e36ff7a34a926888dd Mon Sep 17 00:00:00 2001 From: Adam Mills Date: Mon, 15 May 2017 03:34:30 -0400 Subject: [PATCH] Automation State Change For timer attribute fix (#7584) --- homeassistant/components/automation/state.py | 4 + tests/components/automation/test_state.py | 79 ++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/homeassistant/components/automation/state.py b/homeassistant/components/automation/state.py index 576e9e60186..9c12a37f9b8 100644 --- a/homeassistant/components/automation/state.py +++ b/homeassistant/components/automation/state.py @@ -79,6 +79,10 @@ def async_trigger(hass, config, action): call_action() return + # If only state attributes changed, ignore this event + if from_s.last_changed == to_s.last_changed: + return + @callback def state_for_listener(now): """Fire on state changes after a delay and calls action.""" diff --git a/tests/components/automation/test_state.py b/tests/components/automation/test_state.py index afddaa85b04..d65ffcb4d4f 100644 --- a/tests/components/automation/test_state.py +++ b/tests/components/automation/test_state.py @@ -333,6 +333,40 @@ class TestAutomationState(unittest.TestCase): self.hass.block_till_done() self.assertEqual(0, len(self.calls)) + def test_if_fires_on_entity_change_with_for_attribute_change(self): + """Test for firing on entity change with for and attribute change.""" + assert setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'state', + 'entity_id': 'test.entity', + 'to': 'world', + 'for': { + 'seconds': 5 + }, + }, + 'action': { + 'service': 'test.automation' + } + } + }) + + utcnow = dt_util.utcnow() + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = utcnow + self.hass.states.set('test.entity', 'world') + self.hass.block_till_done() + mock_utcnow.return_value += timedelta(seconds=4) + fire_time_changed(self.hass, mock_utcnow.return_value) + self.hass.states.set('test.entity', 'world', + attributes={"mock_attr": "attr_change"}) + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + mock_utcnow.return_value += timedelta(seconds=4) + fire_time_changed(self.hass, mock_utcnow.return_value) + self.hass.block_till_done() + self.assertEqual(1, len(self.calls)) + def test_if_fires_on_entity_change_with_for(self): """Test for firing on entity change with for.""" assert setup_component(self.hass, automation.DOMAIN, { @@ -393,6 +427,51 @@ class TestAutomationState(unittest.TestCase): self.hass.block_till_done() self.assertEqual(1, len(self.calls)) + def test_if_fires_on_for_condition_attribute_change(self): + """Test for firing if contition is on with attribute change.""" + point1 = dt_util.utcnow() + point2 = point1 + timedelta(seconds=4) + point3 = point1 + timedelta(seconds=8) + with patch('homeassistant.core.dt_util.utcnow') as mock_utcnow: + mock_utcnow.return_value = point1 + self.hass.states.set('test.entity', 'on') + assert setup_component(self.hass, automation.DOMAIN, { + automation.DOMAIN: { + 'trigger': { + 'platform': 'event', + 'event_type': 'test_event', + }, + 'condition': { + 'condition': 'state', + 'entity_id': 'test.entity', + 'state': 'on', + 'for': { + 'seconds': 5 + }, + }, + 'action': {'service': 'test.automation'}, + } + }) + + # not enough time has passed + self.hass.bus.fire('test_event') + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + + # Still not enough time has passed, but an attribute is changed + mock_utcnow.return_value = point2 + self.hass.states.set('test.entity', 'on', + attributes={"mock_attr": "attr_change"}) + self.hass.bus.fire('test_event') + self.hass.block_till_done() + self.assertEqual(0, len(self.calls)) + + # Enough time has now passed + mock_utcnow.return_value = point3 + self.hass.bus.fire('test_event') + self.hass.block_till_done() + self.assertEqual(1, len(self.calls)) + def test_if_fails_setup_for_without_time(self): """Test for setup failure if no time is provided.""" with assert_setup_component(0):