mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00: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:
parent
446f998759
commit
d13cc227cc
@ -204,11 +204,12 @@ class APIEntityStateView(HomeAssistantView):
|
|||||||
return self.json_message('No state specified', HTTP_BAD_REQUEST)
|
return self.json_message('No state specified', HTTP_BAD_REQUEST)
|
||||||
|
|
||||||
attributes = request.json.get('attributes')
|
attributes = request.json.get('attributes')
|
||||||
|
force_update = request.json.get('force_update', False)
|
||||||
|
|
||||||
is_new_state = self.hass.states.get(entity_id) is None
|
is_new_state = self.hass.states.get(entity_id) is None
|
||||||
|
|
||||||
# Write state
|
# 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
|
# Read the state back for our response
|
||||||
resp = self.json(self.hass.states.get(entity_id))
|
resp = self.json(self.hass.states.get(entity_id))
|
||||||
|
@ -456,7 +456,7 @@ class StateMachine(object):
|
|||||||
|
|
||||||
return True
|
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.
|
"""Set the state of an entity, add entity if it does not exist.
|
||||||
|
|
||||||
Attributes is an optional dict to specify attributes of this state.
|
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)
|
old_state = self._states.get(entity_id)
|
||||||
|
|
||||||
is_existing = old_state is not None
|
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
|
same_attr = is_existing and old_state.attributes == attributes
|
||||||
|
|
||||||
if same_state and same_attr:
|
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 True if unable to access real state of the entity."""
|
||||||
return False
|
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):
|
def update(self):
|
||||||
"""Retrieve latest state."""
|
"""Retrieve latest state."""
|
||||||
pass
|
pass
|
||||||
@ -190,7 +199,8 @@ class Entity(object):
|
|||||||
state, attr[ATTR_UNIT_OF_MEASUREMENT])
|
state, attr[ATTR_UNIT_OF_MEASUREMENT])
|
||||||
state = str(state)
|
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):
|
def _attr_setter(self, name, typ, attr, attrs):
|
||||||
"""Helper method to populate attributes based on properties."""
|
"""Helper method to populate attributes based on properties."""
|
||||||
|
@ -259,9 +259,9 @@ class StateMachine(ha.StateMachine):
|
|||||||
"""
|
"""
|
||||||
return remove_state(self._api, entity_id)
|
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."""
|
"""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):
|
def mirror(self):
|
||||||
"""Discard current data and mirrors the remote state machine."""
|
"""Discard current data and mirrors the remote state machine."""
|
||||||
@ -450,7 +450,7 @@ def remove_state(api, entity_id):
|
|||||||
return False
|
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.
|
"""Tell API to update state for entity_id.
|
||||||
|
|
||||||
Return True if success.
|
Return True if success.
|
||||||
@ -458,7 +458,8 @@ def set_state(api, entity_id, new_state, attributes=None):
|
|||||||
attributes = attributes or {}
|
attributes = attributes or {}
|
||||||
|
|
||||||
data = {'state': new_state,
|
data = {'state': new_state,
|
||||||
'attributes': attributes}
|
'attributes': attributes,
|
||||||
|
'force_update': force_update}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req = api(METHOD_POST,
|
req = api(METHOD_POST,
|
||||||
|
@ -136,6 +136,27 @@ class TestAPI(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(400, req.status_code)
|
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
|
# pylint: disable=invalid-name
|
||||||
def test_api_fire_event_with_no_data(self):
|
def test_api_fire_event_with_no_data(self):
|
||||||
"""Test if the API allows us to fire an event."""
|
"""Test if the API allows us to fire an event."""
|
||||||
|
@ -334,6 +334,20 @@ class TestStateMachine(unittest.TestCase):
|
|||||||
self.assertEqual(state.last_changed,
|
self.assertEqual(state.last_changed,
|
||||||
self.states.get('light.Bowl').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):
|
class TestServiceCall(unittest.TestCase):
|
||||||
"""Test ServiceCall class."""
|
"""Test ServiceCall class."""
|
||||||
|
@ -8,7 +8,7 @@ import homeassistant.core as ha
|
|||||||
import homeassistant.bootstrap as bootstrap
|
import homeassistant.bootstrap as bootstrap
|
||||||
import homeassistant.remote as remote
|
import homeassistant.remote as remote
|
||||||
import homeassistant.components.http as http
|
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
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import get_test_instance_port, get_test_home_assistant
|
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'))
|
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):
|
def test_is_state(self):
|
||||||
"""Test Python API is_state."""
|
"""Test Python API is_state."""
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user