1
mirror of https://github.com/home-assistant/core synced 2024-08-02 23:40:32 +02:00

Push State (#2365)

* Add ability to push state changes

* Add tests for push state changes

* Fix style issues

* Use better name to force an update
This commit is contained in:
Philip Lundrigan 2016-06-26 01:33:23 -06:00 committed by Paulus Schoutsen
parent 446f998759
commit d13cc227cc
7 changed files with 72 additions and 9 deletions

View File

@ -204,11 +204,12 @@ class APIEntityStateView(HomeAssistantView):
return self.json_message('No state specified', HTTP_BAD_REQUEST)
attributes = request.json.get('attributes')
force_update = request.json.get('force_update', False)
is_new_state = self.hass.states.get(entity_id) is None
# Write state
self.hass.states.set(entity_id, new_state, attributes)
self.hass.states.set(entity_id, new_state, attributes, force_update)
# Read the state back for our response
resp = self.json(self.hass.states.get(entity_id))

View File

@ -456,7 +456,7 @@ class StateMachine(object):
return True
def set(self, entity_id, new_state, attributes=None):
def set(self, entity_id, new_state, attributes=None, force_update=False):
"""Set the state of an entity, add entity if it does not exist.
Attributes is an optional dict to specify attributes of this state.
@ -472,7 +472,8 @@ class StateMachine(object):
old_state = self._states.get(entity_id)
is_existing = old_state is not None
same_state = is_existing and old_state.state == new_state
same_state = (is_existing and old_state.state == new_state and
not force_update)
same_attr = is_existing and old_state.attributes == attributes
if same_state and same_attr:

View File

@ -125,6 +125,15 @@ class Entity(object):
"""Return True if unable to access real state of the entity."""
return False
@property
def force_update(self):
"""Return True if state updates should be forced.
If True, a state change will be triggered anytime the state property is
updated, not just when the value changes.
"""
return False
def update(self):
"""Retrieve latest state."""
pass
@ -190,7 +199,8 @@ class Entity(object):
state, attr[ATTR_UNIT_OF_MEASUREMENT])
state = str(state)
return self.hass.states.set(self.entity_id, state, attr)
return self.hass.states.set(
self.entity_id, state, attr, self.force_update)
def _attr_setter(self, name, typ, attr, attrs):
"""Helper method to populate attributes based on properties."""

View File

@ -259,9 +259,9 @@ class StateMachine(ha.StateMachine):
"""
return remove_state(self._api, entity_id)
def set(self, entity_id, new_state, attributes=None):
def set(self, entity_id, new_state, attributes=None, force_update=False):
"""Call set_state on remote API."""
set_state(self._api, entity_id, new_state, attributes)
set_state(self._api, entity_id, new_state, attributes, force_update)
def mirror(self):
"""Discard current data and mirrors the remote state machine."""
@ -450,7 +450,7 @@ def remove_state(api, entity_id):
return False
def set_state(api, entity_id, new_state, attributes=None):
def set_state(api, entity_id, new_state, attributes=None, force_update=False):
"""Tell API to update state for entity_id.
Return True if success.
@ -458,7 +458,8 @@ def set_state(api, entity_id, new_state, attributes=None):
attributes = attributes or {}
data = {'state': new_state,
'attributes': attributes}
'attributes': attributes,
'force_update': force_update}
try:
req = api(METHOD_POST,

View File

@ -136,6 +136,27 @@ class TestAPI(unittest.TestCase):
self.assertEqual(400, req.status_code)
# pylint: disable=invalid-name
def test_api_state_change_push(self):
"""Test if we can push a change the state of an entity."""
hass.states.set("test.test", "not_to_be_set")
events = []
hass.bus.listen(const.EVENT_STATE_CHANGED, events.append)
requests.post(_url(const.URL_API_STATES_ENTITY.format("test.test")),
data=json.dumps({"state": "not_to_be_set"}),
headers=HA_HEADERS)
hass.bus._pool.block_till_done()
self.assertEqual(0, len(events))
requests.post(_url(const.URL_API_STATES_ENTITY.format("test.test")),
data=json.dumps({"state": "not_to_be_set",
"force_update": True}),
headers=HA_HEADERS)
hass.bus._pool.block_till_done()
self.assertEqual(1, len(events))
# pylint: disable=invalid-name
def test_api_fire_event_with_no_data(self):
"""Test if the API allows us to fire an event."""

View File

@ -334,6 +334,20 @@ class TestStateMachine(unittest.TestCase):
self.assertEqual(state.last_changed,
self.states.get('light.Bowl').last_changed)
def test_force_update(self):
"""Test force update option."""
self.pool.add_worker()
events = []
self.bus.listen(EVENT_STATE_CHANGED, events.append)
self.states.set('light.bowl', 'on')
self.bus._pool.block_till_done()
self.assertEqual(0, len(events))
self.states.set('light.bowl', 'on', None, True)
self.bus._pool.block_till_done()
self.assertEqual(1, len(events))
class TestServiceCall(unittest.TestCase):
"""Test ServiceCall class."""

View File

@ -8,7 +8,7 @@ import homeassistant.core as ha
import homeassistant.bootstrap as bootstrap
import homeassistant.remote as remote
import homeassistant.components.http as http
from homeassistant.const import HTTP_HEADER_HA_AUTH
from homeassistant.const import HTTP_HEADER_HA_AUTH, EVENT_STATE_CHANGED
import homeassistant.util.dt as dt_util
from tests.common import get_test_instance_port, get_test_home_assistant
@ -155,6 +155,21 @@ class TestRemoteMethods(unittest.TestCase):
self.assertFalse(remote.set_state(broken_api, 'test.test', 'set_test'))
def test_set_state_with_push(self):
"""TestPython API set_state with push option."""
events = []
hass.bus.listen(EVENT_STATE_CHANGED, events.append)
remote.set_state(master_api, 'test.test', 'set_test_2')
remote.set_state(master_api, 'test.test', 'set_test_2')
hass.bus._pool.block_till_done()
self.assertEqual(1, len(events))
remote.set_state(
master_api, 'test.test', 'set_test_2', force_update=True)
hass.bus._pool.block_till_done()
self.assertEqual(2, len(events))
def test_is_state(self):
"""Test Python API is_state."""
self.assertTrue(