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:
parent
446f998759
commit
d13cc227cc
7 changed files with 72 additions and 9 deletions
|
@ -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))
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue