Have template platforms never leave the event loop
parent
3e24a35c1e
commit
4198c42736
|
@ -4,6 +4,7 @@ Support for exposing a templated binary sensor.
|
|||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/binary_sensor.template/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
@ -81,9 +82,10 @@ class BinarySensorTemplate(BinarySensorDevice):
|
|||
|
||||
self.update()
|
||||
|
||||
@asyncio.coroutine
|
||||
def template_bsensor_state_listener(entity, old_state, new_state):
|
||||
"""Called when the target device changes state."""
|
||||
self.update_ha_state(True)
|
||||
yield from self.async_update_ha_state(True)
|
||||
|
||||
track_state_change(hass, entity_ids, template_bsensor_state_listener)
|
||||
|
||||
|
@ -107,10 +109,11 @@ class BinarySensorTemplate(BinarySensorDevice):
|
|||
"""No polling needed."""
|
||||
return False
|
||||
|
||||
def update(self):
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Get the latest data and update the state."""
|
||||
try:
|
||||
self._state = self._template.render().lower() == 'true'
|
||||
self._state = self._template.async_render().lower() == 'true'
|
||||
except TemplateError as ex:
|
||||
if ex.args and ex.args[0].startswith(
|
||||
"UndefinedError: 'None' has no attribute"):
|
||||
|
|
|
@ -4,6 +4,7 @@ Allows the creation of a sensor that breaks out state_attributes.
|
|||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.template/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
@ -78,9 +79,10 @@ class SensorTemplate(Entity):
|
|||
|
||||
self.update()
|
||||
|
||||
@asyncio.coroutine
|
||||
def template_sensor_state_listener(entity, old_state, new_state):
|
||||
"""Called when the target device changes state."""
|
||||
self.update_ha_state(True)
|
||||
yield from self.async_update_ha_state(True)
|
||||
|
||||
track_state_change(hass, entity_ids, template_sensor_state_listener)
|
||||
|
||||
|
@ -104,10 +106,11 @@ class SensorTemplate(Entity):
|
|||
"""No polling needed."""
|
||||
return False
|
||||
|
||||
def update(self):
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Get the latest data and update the states."""
|
||||
try:
|
||||
self._state = self._template.render()
|
||||
self._state = self._template.async_render()
|
||||
except TemplateError as ex:
|
||||
if ex.args and ex.args[0].startswith(
|
||||
"UndefinedError: 'None' has no attribute"):
|
||||
|
|
|
@ -4,6 +4,7 @@ Support for switches which integrates with other components.
|
|||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.template/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
@ -87,9 +88,10 @@ class SwitchTemplate(SwitchDevice):
|
|||
|
||||
self.update()
|
||||
|
||||
@asyncio.coroutine
|
||||
def template_switch_state_listener(entity, old_state, new_state):
|
||||
"""Called when the target device changes state."""
|
||||
self.update_ha_state(True)
|
||||
yield from self.async_update_ha_state(True)
|
||||
|
||||
track_state_change(hass, entity_ids, template_switch_state_listener)
|
||||
|
||||
|
@ -121,10 +123,11 @@ class SwitchTemplate(SwitchDevice):
|
|||
"""Fire the off action."""
|
||||
self._off_script.run()
|
||||
|
||||
def update(self):
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Update the state from the template."""
|
||||
try:
|
||||
state = self._template.render().lower()
|
||||
state = self._template.async_render().lower()
|
||||
|
||||
if state in _VALID_STATES:
|
||||
self._state = state in ('true', STATE_ON)
|
||||
|
|
|
@ -49,6 +49,11 @@ class Entity(object):
|
|||
# SAFE TO OVERWRITE
|
||||
# The properties and methods here are safe to overwrite when inheriting
|
||||
# this class. These may be used to customize the behavior of the entity.
|
||||
entity_id = None # type: str
|
||||
|
||||
# Owning hass instance. Will be set by EntityComponent
|
||||
hass = None # type: Optional[HomeAssistant]
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Return True if entity has to be polled for state.
|
||||
|
@ -128,18 +133,22 @@ class Entity(object):
|
|||
return False
|
||||
|
||||
def update(self):
|
||||
"""Retrieve latest state."""
|
||||
pass
|
||||
"""Retrieve latest state.
|
||||
|
||||
entity_id = None # type: str
|
||||
When not implemented, will forward call to async version if available.
|
||||
"""
|
||||
async_update = getattr(self, 'async_update', None)
|
||||
|
||||
if async_update is None:
|
||||
return
|
||||
|
||||
run_coroutine_threadsafe(async_update(), self.hass.loop).result()
|
||||
|
||||
# DO NOT OVERWRITE
|
||||
# These properties and methods are either managed by Home Assistant or they
|
||||
# are used to perform a very specific function. Overwriting these may
|
||||
# produce undesirable effects in the entity's operation.
|
||||
|
||||
hass = None # type: Optional[HomeAssistant]
|
||||
|
||||
def update_ha_state(self, force_refresh=False):
|
||||
"""Update Home Assistant with current state of entity.
|
||||
|
||||
|
@ -172,7 +181,7 @@ class Entity(object):
|
|||
if force_refresh:
|
||||
if hasattr(self, 'async_update'):
|
||||
# pylint: disable=no-member
|
||||
self.async_update()
|
||||
yield from self.async_update()
|
||||
else:
|
||||
# PS: Run this in our own thread pool once we have
|
||||
# future support?
|
||||
|
|
|
@ -119,7 +119,7 @@ class TestBinarySensorTemplate(unittest.TestCase):
|
|||
vs.update_ha_state()
|
||||
self.hass.block_till_done()
|
||||
|
||||
with mock.patch.object(vs, 'update') as mock_update:
|
||||
with mock.patch.object(vs, 'async_update') as mock_update:
|
||||
self.hass.bus.fire(EVENT_STATE_CHANGED)
|
||||
self.hass.block_till_done()
|
||||
assert mock_update.call_count == 1
|
||||
|
|
|
@ -53,7 +53,12 @@ def test_async_update_support(event_loop):
|
|||
assert len(sync_update) == 1
|
||||
assert len(async_update) == 0
|
||||
|
||||
ent.async_update = lambda: async_update.append(1)
|
||||
@asyncio.coroutine
|
||||
def async_update_func():
|
||||
"""Async update."""
|
||||
async_update.append(1)
|
||||
|
||||
ent.async_update = async_update_func
|
||||
|
||||
event_loop.run_until_complete(test())
|
||||
|
||||
|
@ -95,3 +100,19 @@ class TestHelpersEntity(object):
|
|||
assert entity.generate_entity_id(
|
||||
fmt, 'overwrite hidden true',
|
||||
hass=self.hass) == 'test.overwrite_hidden_true_2'
|
||||
|
||||
def test_update_calls_async_update_if_available(self):
|
||||
"""Test async update getting called."""
|
||||
async_update = []
|
||||
|
||||
class AsyncEntity(entity.Entity):
|
||||
hass = self.hass
|
||||
entity_id = 'sensor.test'
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
async_update.append([1])
|
||||
|
||||
ent = AsyncEntity()
|
||||
ent.update()
|
||||
assert len(async_update) == 1
|
||||
|
|
Loading…
Reference in New Issue