Fix async probs (#9924)

* Update entity.py

* Update entity_component.py

* Update entity_component.py

* Update __init__.py

* Update entity_component.py

* Update entity_component.py

* Update entity.py

* cleanup entity

* Update entity_component.py

* Update entity_component.py

* Fix names & comments / fix tests

* Revert deadlock protection

* Add tests for entity

* Add test fix name

* Update other code

* Fix lint

* Remove restore state from template entities

* Lint
This commit is contained in:
Pascal Vizeli 2017-10-19 10:56:25 +02:00 committed by GitHub
parent 6cce934f72
commit c1b197419d
24 changed files with 356 additions and 362 deletions

View file

@ -124,20 +124,13 @@ def async_setup(hass, config):
method = "async_{}".format(SERVICE_TO_METHOD[service.service])
update_tasks = []
for alarm in target_alarms:
yield from getattr(alarm, method)(code)
update_tasks = []
for alarm in target_alarms:
if not alarm.should_poll:
continue
update_coro = hass.async_add_job(
alarm.async_update_ha_state(True))
if hasattr(alarm, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(alarm.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -15,13 +15,12 @@ from homeassistant.components.binary_sensor import (
DEVICE_CLASSES_SCHEMA)
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_ENTITY_ID, CONF_VALUE_TEMPLATE,
CONF_SENSORS, CONF_DEVICE_CLASS, EVENT_HOMEASSISTANT_START, STATE_ON)
CONF_SENSORS, CONF_DEVICE_CLASS, EVENT_HOMEASSISTANT_START)
from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import (
async_track_state_change, async_track_same_state)
from homeassistant.helpers.restore_state import async_get_last_state
_LOGGER = logging.getLogger(__name__)
@ -94,10 +93,6 @@ class BinarySensorTemplate(BinarySensorDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if state:
self._state = state.state == STATE_ON
@callback
def template_bsensor_state_listener(entity, old_state, new_state):
"""Handle the target device state changes."""

View file

@ -126,23 +126,16 @@ def async_setup(hass, config):
"""Handle calls to the camera services."""
target_cameras = component.async_extract_from_service(service)
update_tasks = []
for camera in target_cameras:
if service.service == SERVICE_EN_MOTION:
yield from camera.async_enable_motion_detection()
elif service.service == SERVICE_DISEN_MOTION:
yield from camera.async_disable_motion_detection()
update_tasks = []
for camera in target_cameras:
if not camera.should_poll:
continue
update_coro = hass.async_add_job(
camera.async_update_ha_state(True))
if hasattr(camera, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(camera.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -236,24 +236,6 @@ def async_setup(hass, config):
load_yaml_config_file,
os.path.join(os.path.dirname(__file__), 'services.yaml'))
@asyncio.coroutine
def _async_update_climate(target_climate):
"""Update climate entity after service stuff."""
update_tasks = []
for climate in target_climate:
if not climate.should_poll:
continue
update_coro = hass.async_add_job(
climate.async_update_ha_state(True))
if hasattr(climate, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
@asyncio.coroutine
def async_away_mode_set_service(service):
"""Set away mode on target climate devices."""
@ -261,13 +243,19 @@ def async_setup(hass, config):
away_mode = service.data.get(ATTR_AWAY_MODE)
update_tasks = []
for climate in target_climate:
if away_mode:
yield from climate.async_turn_away_mode_on()
else:
yield from climate.async_turn_away_mode_off()
yield from _async_update_climate(target_climate)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_AWAY_MODE, async_away_mode_set_service,
@ -281,10 +269,16 @@ def async_setup(hass, config):
hold_mode = service.data.get(ATTR_HOLD_MODE)
update_tasks = []
for climate in target_climate:
yield from climate.async_set_hold_mode(hold_mode)
yield from _async_update_climate(target_climate)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_HOLD_MODE, async_hold_mode_set_service,
@ -298,13 +292,19 @@ def async_setup(hass, config):
aux_heat = service.data.get(ATTR_AUX_HEAT)
update_tasks = []
for climate in target_climate:
if aux_heat:
yield from climate.async_turn_aux_heat_on()
else:
yield from climate.async_turn_aux_heat_off()
yield from _async_update_climate(target_climate)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_AUX_HEAT, async_aux_heat_set_service,
@ -316,6 +316,7 @@ def async_setup(hass, config):
"""Set temperature on the target climate devices."""
target_climate = component.async_extract_from_service(service)
update_tasks = []
for climate in target_climate:
kwargs = {}
for value, temp in service.data.items():
@ -330,7 +331,12 @@ def async_setup(hass, config):
yield from climate.async_set_temperature(**kwargs)
yield from _async_update_climate(target_climate)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_TEMPERATURE, async_temperature_set_service,
@ -344,10 +350,15 @@ def async_setup(hass, config):
humidity = service.data.get(ATTR_HUMIDITY)
update_tasks = []
for climate in target_climate:
yield from climate.async_set_humidity(humidity)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
yield from _async_update_climate(target_climate)
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_HUMIDITY, async_humidity_set_service,
@ -361,10 +372,15 @@ def async_setup(hass, config):
fan = service.data.get(ATTR_FAN_MODE)
update_tasks = []
for climate in target_climate:
yield from climate.async_set_fan_mode(fan)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
yield from _async_update_climate(target_climate)
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_FAN_MODE, async_fan_mode_set_service,
@ -378,10 +394,15 @@ def async_setup(hass, config):
operation_mode = service.data.get(ATTR_OPERATION_MODE)
update_tasks = []
for climate in target_climate:
yield from climate.async_set_operation_mode(operation_mode)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
yield from _async_update_climate(target_climate)
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_OPERATION_MODE, async_operation_set_service,
@ -395,10 +416,15 @@ def async_setup(hass, config):
swing_mode = service.data.get(ATTR_SWING_MODE)
update_tasks = []
for climate in target_climate:
yield from climate.async_set_swing_mode(swing_mode)
if not climate.should_poll:
continue
update_tasks.append(climate.async_update_ha_state(True))
yield from _async_update_climate(target_climate)
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)
hass.services.async_register(
DOMAIN, SERVICE_SET_SWING_MODE, async_swing_set_service,

View file

@ -169,21 +169,12 @@ def async_setup(hass, config):
params.pop(ATTR_ENTITY_ID, None)
# call method
update_tasks = []
for cover in covers:
yield from getattr(cover, method['method'])(**params)
update_tasks = []
for cover in covers:
if not cover.should_poll:
continue
update_coro = hass.async_add_job(
cover.async_update_ha_state(True))
if hasattr(cover, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(cover.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -24,7 +24,6 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.restore_state import async_get_last_state
from homeassistant.helpers.script import Script
_LOGGER = logging.getLogger(__name__)
@ -134,7 +133,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
_LOGGER.error("No covers added")
return False
async_add_devices(covers, True)
async_add_devices(covers)
return True
@ -190,10 +189,6 @@ class CoverTemplate(CoverDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if state:
self._position = 100 if state.state == STATE_OPEN else 0
@callback
def template_cover_state_listener(entity, old_state, new_state):
"""Handle target device state changes."""

View file

@ -215,20 +215,12 @@ def async_setup(hass, config: dict):
target_fans = component.async_extract_from_service(service)
params.pop(ATTR_ENTITY_ID, None)
update_tasks = []
for fan in target_fans:
yield from getattr(fan, method['method'])(**params)
update_tasks = []
for fan in target_fans:
if not fan.should_poll:
continue
update_coro = hass.async_add_job(fan.async_update_ha_state(True))
if hasattr(fan, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(fan.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -274,6 +274,7 @@ def async_setup(hass, config):
preprocess_turn_on_alternatives(params)
update_tasks = []
for light in target_lights:
if service.service == SERVICE_TURN_ON:
yield from light.async_turn_on(**params)
@ -282,18 +283,9 @@ def async_setup(hass, config):
else:
yield from light.async_toggle(**params)
update_tasks = []
for light in target_lights:
if not light.should_poll:
continue
update_coro = hass.async_add_job(
light.async_update_ha_state(True))
if hasattr(light, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(light.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -20,7 +20,6 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.restore_state import async_get_last_state
from homeassistant.helpers.script import Script
_LOGGER = logging.getLogger(__name__)
@ -87,7 +86,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
_LOGGER.error("No lights added")
return False
async_add_devices(lights, True)
async_add_devices(lights)
return True
@ -150,10 +149,6 @@ class LightTemplate(Light):
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if state:
self._state = state.state == STATE_ON
@callback
def template_light_state_listener(entity, old_state, new_state):
"""Handle target device state changes."""
@ -207,6 +202,7 @@ class LightTemplate(Light):
@asyncio.coroutine
def async_update(self):
"""Update the state from the template."""
print("ASYNC UPDATE")
if self._template is not None:
try:
state = self._template.async_render().lower()

View file

@ -90,24 +90,16 @@ def async_setup(hass, config):
code = service.data.get(ATTR_CODE)
update_tasks = []
for entity in target_locks:
if service.service == SERVICE_LOCK:
yield from entity.async_lock(code=code)
else:
yield from entity.async_unlock(code=code)
update_tasks = []
for entity in target_locks:
if not entity.should_poll:
continue
update_coro = hass.async_add_job(
entity.async_update_ha_state(True))
if hasattr(entity, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(entity.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -406,16 +406,9 @@ def async_setup(hass, config):
update_tasks = []
for player in target_players:
yield from getattr(player, method['method'])(**params)
for player in target_players:
if not player.should_poll:
continue
update_coro = player.async_update_ha_state(True)
if hasattr(player, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(player.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -148,6 +148,7 @@ def async_setup(hass, config):
num_repeats = service.data.get(ATTR_NUM_REPEATS)
delay_secs = service.data.get(ATTR_DELAY_SECS)
update_tasks = []
for remote in target_remotes:
if service.service == SERVICE_TURN_ON:
yield from remote.async_turn_on(activity=activity_id)
@ -160,17 +161,9 @@ def async_setup(hass, config):
else:
yield from remote.async_turn_off(activity=activity_id)
update_tasks = []
for remote in target_remotes:
if not remote.should_poll:
continue
update_coro = hass.async_add_job(
remote.async_update_ha_state(True))
if hasattr(remote, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(remote.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -19,7 +19,6 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.restore_state import async_get_last_state
_LOGGER = logging.getLogger(__name__)
@ -93,10 +92,6 @@ class SensorTemplate(Entity):
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if state:
self._state = state.state
@callback
def template_sensor_state_listener(entity, old_state, new_state):
"""Handle device state changes."""

View file

@ -107,6 +107,7 @@ def async_setup(hass, config):
"""Handle calls to the switch services."""
target_switches = component.async_extract_from_service(service)
update_tasks = []
for switch in target_switches:
if service.service == SERVICE_TURN_ON:
yield from switch.async_turn_on()
@ -115,17 +116,9 @@ def async_setup(hass, config):
else:
yield from switch.async_turn_off()
update_tasks = []
for switch in target_switches:
if not switch.should_poll:
continue
update_coro = hass.async_add_job(
switch.async_update_ha_state(True))
if hasattr(switch, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(switch.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -19,7 +19,6 @@ from homeassistant.exceptions import TemplateError
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import async_generate_entity_id
from homeassistant.helpers.event import async_track_state_change
from homeassistant.helpers.restore_state import async_get_last_state
from homeassistant.helpers.script import Script
_LOGGER = logging.getLogger(__name__)
@ -71,7 +70,7 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
_LOGGER.error("No switches added")
return False
async_add_devices(switches, True)
async_add_devices(switches)
return True
@ -96,10 +95,6 @@ class SwitchTemplate(SwitchDevice):
@asyncio.coroutine
def async_added_to_hass(self):
"""Register callbacks."""
state = yield from async_get_last_state(self.hass, self.entity_id)
if state:
self._state = state.state == STATE_ON
@callback
def template_switch_state_listener(entity, old_state, new_state):
"""Handle target device state changes."""

View file

@ -200,13 +200,7 @@ def async_setup(hass, config):
yield from getattr(vacuum, method['method'])(**params)
if not vacuum.should_poll:
continue
update_coro = hass.async_add_job(
vacuum.async_update_ha_state(True))
if hasattr(vacuum, 'async_update'):
update_tasks.append(update_coro)
else:
yield from update_coro
update_tasks.append(vacuum.async_update_ha_state(True))
if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

View file

@ -71,8 +71,11 @@ class Entity(object):
# If we reported if this entity was slow
_slow_reported = False
# protect for multiple updates
_update_warn = None
# Protect for multiple updates
_update_staged = False
# Process updates pararell
parallel_updates = None
@property
def should_poll(self) -> bool:
@ -197,11 +200,15 @@ class Entity(object):
# update entity data
if force_refresh:
if self._update_warn:
# Update is already in progress.
if self._update_staged:
return
self._update_staged = True
self._update_warn = self.hass.loop.call_later(
# Process update sequential
if self.parallel_updates:
yield from self.parallel_updates.acquire()
update_warn = self.hass.loop.call_later(
SLOW_UPDATE_WARNING, _LOGGER.warning,
"Update of %s is taking over %s seconds", self.entity_id,
SLOW_UPDATE_WARNING
@ -217,8 +224,10 @@ class Entity(object):
_LOGGER.exception("Update for %s fails", self.entity_id)
return
finally:
self._update_warn.cancel()
self._update_warn = None
self._update_staged = False
update_warn.cancel()
if self.parallel_updates:
self.parallel_updates.release()
start = timer()

View file

@ -44,7 +44,7 @@ class EntityComponent(object):
self.config = None
self._platforms = {
'core': EntityPlatform(self, domain, self.scan_interval, None),
'core': EntityPlatform(self, domain, self.scan_interval, 0, None),
}
self.async_add_entities = self._platforms['core'].async_add_entities
self.add_entities = self._platforms['core'].add_entities
@ -128,16 +128,22 @@ class EntityComponent(object):
return
# Config > Platform > Component
scan_interval = (platform_config.get(CONF_SCAN_INTERVAL) or
getattr(platform, 'SCAN_INTERVAL', None) or
self.scan_interval)
scan_interval = (
platform_config.get(CONF_SCAN_INTERVAL) or
getattr(platform, 'SCAN_INTERVAL', None) or self.scan_interval)
parallel_updates = getattr(
platform, 'PARALLEL_UPDATES',
int(not hasattr(platform, 'async_setup_platform')))
entity_namespace = platform_config.get(CONF_ENTITY_NAMESPACE)
key = (platform_type, scan_interval, entity_namespace)
if key not in self._platforms:
self._platforms[key] = EntityPlatform(
self, platform_type, scan_interval, entity_namespace)
entity_platform = self._platforms[key] = EntityPlatform(
self, platform_type, scan_interval, parallel_updates,
entity_namespace)
else:
entity_platform = self._platforms[key]
self.logger.info("Setting up %s.%s", self.domain, platform_type)
@ -204,13 +210,6 @@ class EntityComponent(object):
entity.hass = self.hass
# update/init entity data
if update_before_add:
if hasattr(entity, 'async_update'):
yield from entity.async_update()
else:
yield from self.hass.async_add_job(entity.update)
if getattr(entity, 'entity_id', None) is None:
object_id = entity.name or DEVICE_DEFAULT_NAME
@ -235,7 +234,7 @@ class EntityComponent(object):
if hasattr(entity, 'async_added_to_hass'):
yield from entity.async_added_to_hass()
yield from entity.async_update_ha_state()
yield from entity.async_update_ha_state(update_before_add)
return True
@ -316,17 +315,23 @@ class EntityComponent(object):
class EntityPlatform(object):
"""Keep track of entities for a single platform and stay in loop."""
def __init__(self, component, platform, scan_interval, entity_namespace):
def __init__(self, component, platform, scan_interval, parallel_updates,
entity_namespace):
"""Initialize the entity platform."""
self.component = component
self.platform = platform
self.scan_interval = scan_interval
self.parallel_updates = None
self.entity_namespace = entity_namespace
self.platform_entities = []
self._tasks = []
self._async_unsub_polling = None
self._process_updates = asyncio.Lock(loop=component.hass.loop)
if parallel_updates:
self.parallel_updates = asyncio.Semaphore(
parallel_updates, loop=component.hass.loop)
@asyncio.coroutine
def async_block_entities_done(self):
"""Wait until all entities add to hass."""
@ -377,6 +382,7 @@ class EntityPlatform(object):
@asyncio.coroutine
def async_process_entity(new_entity):
"""Add entities to StateMachine."""
new_entity.parallel_updates = self.parallel_updates
ret = yield from self.component.async_add_entity(
new_entity, self, update_before_add=update_before_add
)
@ -432,26 +438,10 @@ class EntityPlatform(object):
with (yield from self._process_updates):
tasks = []
to_update = []
for entity in self.platform_entities:
if not entity.should_poll:
continue
update_coro = entity.async_update_ha_state(True)
if hasattr(entity, 'async_update'):
tasks.append(
self.component.hass.async_add_job(update_coro))
else:
to_update.append(update_coro)
for update_coro in to_update:
try:
yield from update_coro
except Exception: # pylint: disable=broad-except
self.component.logger.exception(
"Error while update entity from %s in %s",
self.platform, self.component.domain)
tasks.append(entity.async_update_ha_state(True))
if tasks:
yield from asyncio.wait(tasks, loop=self.component.hass.loop)

View file

@ -4,7 +4,6 @@ from datetime import timedelta
import unittest
from unittest import mock
from homeassistant.core import CoreState, State
from homeassistant.const import MATCH_ALL
from homeassistant import setup
from homeassistant.components.binary_sensor import template
@ -12,11 +11,9 @@ from homeassistant.exceptions import TemplateError
from homeassistant.helpers import template as template_hlpr
from homeassistant.util.async import run_callback_threadsafe
import homeassistant.util.dt as dt_util
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
from tests.common import (
get_test_home_assistant, assert_setup_component, mock_component,
async_fire_time_changed)
get_test_home_assistant, assert_setup_component, async_fire_time_changed)
class TestBinarySensorTemplate(unittest.TestCase):
@ -169,41 +166,6 @@ class TestBinarySensorTemplate(unittest.TestCase):
run_callback_threadsafe(self.hass.loop, vs.async_check_state).result()
@asyncio.coroutine
def test_restore_state(hass):
"""Ensure states are restored on startup."""
hass.data[DATA_RESTORE_CACHE] = {
'binary_sensor.test': State('binary_sensor.test', 'on'),
}
hass.state = CoreState.starting
mock_component(hass, 'recorder')
config = {
'binary_sensor': {
'platform': 'template',
'sensors': {
'test': {
'friendly_name': 'virtual thingy',
'value_template':
"{{ states.sensor.test_state.state == 'on' }}",
'device_class': 'motion',
},
},
},
}
yield from setup.async_setup_component(hass, 'binary_sensor', config)
state = hass.states.get('binary_sensor.test')
assert state.state == 'on'
yield from hass.async_start()
yield from hass.async_block_till_done()
state = hass.states.get('binary_sensor.test')
assert state.state == 'off'
@asyncio.coroutine
def test_template_delay_on(hass):
"""Test binary sensor template delay on."""

View file

@ -1,16 +1,14 @@
"""The tests for the Template light platform."""
import logging
import asyncio
from homeassistant.core import callback, State, CoreState
from homeassistant.core import callback
from homeassistant import setup
import homeassistant.components as core
from homeassistant.components.light import ATTR_BRIGHTNESS
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
from tests.common import (
get_test_home_assistant, assert_setup_component, mock_component)
get_test_home_assistant, assert_setup_component)
_LOGGER = logging.getLogger(__name__)
@ -627,49 +625,3 @@ class TestTemplateLight:
assert state is not None
assert state.attributes.get('friendly_name') == 'Template light'
@asyncio.coroutine
def test_restore_state(hass):
"""Ensure states are restored on startup."""
hass.data[DATA_RESTORE_CACHE] = {
'light.test_template_light':
State('light.test_template_light', 'on'),
}
hass.state = CoreState.starting
mock_component(hass, 'recorder')
yield from setup.async_setup_component(hass, 'light', {
'light': {
'platform': 'template',
'lights': {
'test_template_light': {
'value_template':
"{{states.light.test_state.state}}",
'turn_on': {
'service': 'test.automation',
},
'turn_off': {
'service': 'light.turn_off',
'entity_id': 'light.test_state'
},
'set_level': {
'service': 'test.automation',
'data_template': {
'entity_id': 'light.test_state',
'brightness': '{{brightness}}'
}
}
}
}
}
})
state = hass.states.get('light.test_template_light')
assert state.state == 'on'
yield from hass.async_start()
yield from hass.async_block_till_done()
state = hass.states.get('light.test_template_light')
assert state.state == 'off'

View file

@ -1,12 +1,7 @@
"""The test for the Template sensor platform."""
import asyncio
from homeassistant.setup import setup_component
from homeassistant.core import CoreState, State
from homeassistant.setup import setup_component, async_setup_component
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
from tests.common import (
get_test_home_assistant, assert_setup_component, mock_component)
from tests.common import get_test_home_assistant, assert_setup_component
class TestTemplateSensor:
@ -188,36 +183,3 @@ class TestTemplateSensor:
self.hass.block_till_done()
assert self.hass.states.all() == []
@asyncio.coroutine
def test_restore_state(hass):
"""Ensure states are restored on startup."""
hass.data[DATA_RESTORE_CACHE] = {
'sensor.test_template_sensor':
State('sensor.test_template_sensor', 'It Test.'),
}
hass.state = CoreState.starting
mock_component(hass, 'recorder')
yield from async_setup_component(hass, 'sensor', {
'sensor': {
'platform': 'template',
'sensors': {
'test_template_sensor': {
'value_template':
"It {{ states.sensor.test_state.state }}."
}
}
}
})
state = hass.states.get('sensor.test_template_sensor')
assert state.state == 'It Test.'
yield from hass.async_start()
yield from hass.async_block_till_done()
state = hass.states.get('sensor.test_template_sensor')
assert state.state == 'It .'

View file

@ -1,14 +1,11 @@
"""The tests for the Template switch platform."""
import asyncio
from homeassistant.core import callback, State, CoreState
from homeassistant.core import callback
from homeassistant import setup
import homeassistant.components as core
from homeassistant.const import STATE_ON, STATE_OFF
from homeassistant.helpers.restore_state import DATA_RESTORE_CACHE
from tests.common import (
get_test_home_assistant, assert_setup_component, mock_component)
get_test_home_assistant, assert_setup_component)
class TestTemplateSwitch:
@ -410,44 +407,3 @@ class TestTemplateSwitch:
self.hass.block_till_done()
assert len(self.calls) == 1
@asyncio.coroutine
def test_restore_state(hass):
"""Ensure states are restored on startup."""
hass.data[DATA_RESTORE_CACHE] = {
'switch.test_template_switch':
State('switch.test_template_switch', 'on'),
}
hass.state = CoreState.starting
mock_component(hass, 'recorder')
yield from setup.async_setup_component(hass, 'switch', {
'switch': {
'platform': 'template',
'switches': {
'test_template_switch': {
'value_template':
"{{ states.switch.test_state.state }}",
'turn_on': {
'service': 'switch.turn_on',
'entity_id': 'switch.test_state'
},
'turn_off': {
'service': 'switch.turn_off',
'entity_id': 'switch.test_state'
},
}
}
}
})
state = hass.states.get('switch.test_template_switch')
assert state.state == 'on'
yield from hass.async_start()
yield from hass.async_block_till_done()
state = hass.states.get('switch.test_template_switch')
assert state.state == 'unavailable'

View file

@ -213,3 +213,162 @@ def test_async_schedule_update_ha_state(hass):
yield from hass.async_block_till_done()
assert update_call is True
@asyncio.coroutine
def test_async_pararell_updates_with_zero(hass):
"""Test pararell updates with 0 (disabled)."""
updates = []
test_lock = asyncio.Event(loop=hass.loop)
class AsyncEntity(entity.Entity):
def __init__(self, entity_id, count):
"""Initialize Async test entity."""
self.entity_id = entity_id
self.hass = hass
self._count = count
@asyncio.coroutine
def async_update(self):
"""Test update."""
updates.append(self._count)
yield from test_lock.wait()
ent_1 = AsyncEntity("sensor.test_1", 1)
ent_2 = AsyncEntity("sensor.test_2", 2)
ent_1.async_schedule_update_ha_state(True)
ent_2.async_schedule_update_ha_state(True)
while True:
if len(updates) == 2:
break
yield from asyncio.sleep(0, loop=hass.loop)
assert len(updates) == 2
assert updates == [1, 2]
test_lock.set()
@asyncio.coroutine
def test_async_pararell_updates_with_one(hass):
"""Test pararell updates with 1 (sequential)."""
updates = []
test_lock = asyncio.Lock(loop=hass.loop)
test_semephore = asyncio.Semaphore(1, loop=hass.loop)
yield from test_lock.acquire()
class AsyncEntity(entity.Entity):
def __init__(self, entity_id, count):
"""Initialize Async test entity."""
self.entity_id = entity_id
self.hass = hass
self._count = count
self.parallel_updates = test_semephore
@asyncio.coroutine
def async_update(self):
"""Test update."""
updates.append(self._count)
yield from test_lock.acquire()
ent_1 = AsyncEntity("sensor.test_1", 1)
ent_2 = AsyncEntity("sensor.test_2", 2)
ent_3 = AsyncEntity("sensor.test_3", 3)
ent_1.async_schedule_update_ha_state(True)
ent_2.async_schedule_update_ha_state(True)
ent_3.async_schedule_update_ha_state(True)
while True:
if len(updates) == 1:
break
yield from asyncio.sleep(0, loop=hass.loop)
assert len(updates) == 1
assert updates == [1]
test_lock.release()
while True:
if len(updates) == 2:
break
yield from asyncio.sleep(0, loop=hass.loop)
assert len(updates) == 2
assert updates == [1, 2]
test_lock.release()
while True:
if len(updates) == 3:
break
yield from asyncio.sleep(0, loop=hass.loop)
assert len(updates) == 3
assert updates == [1, 2, 3]
test_lock.release()
@asyncio.coroutine
def test_async_pararell_updates_with_two(hass):
"""Test pararell updates with 2 (pararell)."""
updates = []
test_lock = asyncio.Lock(loop=hass.loop)
test_semephore = asyncio.Semaphore(2, loop=hass.loop)
yield from test_lock.acquire()
class AsyncEntity(entity.Entity):
def __init__(self, entity_id, count):
"""Initialize Async test entity."""
self.entity_id = entity_id
self.hass = hass
self._count = count
self.parallel_updates = test_semephore
@asyncio.coroutine
def async_update(self):
"""Test update."""
updates.append(self._count)
yield from test_lock.acquire()
ent_1 = AsyncEntity("sensor.test_1", 1)
ent_2 = AsyncEntity("sensor.test_2", 2)
ent_3 = AsyncEntity("sensor.test_3", 3)
ent_4 = AsyncEntity("sensor.test_4", 4)
ent_1.async_schedule_update_ha_state(True)
ent_2.async_schedule_update_ha_state(True)
ent_3.async_schedule_update_ha_state(True)
ent_4.async_schedule_update_ha_state(True)
while True:
if len(updates) == 2:
break
yield from asyncio.sleep(0, loop=hass.loop)
assert len(updates) == 2
assert updates == [1, 2]
test_lock.release()
yield from asyncio.sleep(0, loop=hass.loop)
test_lock.release()
while True:
if len(updates) == 4:
break
yield from asyncio.sleep(0, loop=hass.loop)
assert len(updates) == 4
assert updates == [1, 2, 3, 4]
test_lock.release()
yield from asyncio.sleep(0, loop=hass.loop)
test_lock.release()

View file

@ -578,3 +578,79 @@ def test_platform_not_ready(hass):
yield from hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 3
assert 'test_domain.mod1' in hass.config.components
@asyncio.coroutine
def test_pararell_updates_async_platform(hass):
"""Warn we log when platform setup takes a long time."""
platform = MockPlatform()
@asyncio.coroutine
def mock_update(*args, **kwargs):
pass
platform.async_setup_platform = mock_update
loader.set_component('test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, hass)
component._platforms = {}
yield from component.async_setup({
DOMAIN: {
'platform': 'platform',
}
})
handle = list(component._platforms.values())[-1]
assert handle.parallel_updates is None
@asyncio.coroutine
def test_pararell_updates_async_platform_with_constant(hass):
"""Warn we log when platform setup takes a long time."""
platform = MockPlatform()
@asyncio.coroutine
def mock_update(*args, **kwargs):
pass
platform.async_setup_platform = mock_update
platform.PARALLEL_UPDATES = 1
loader.set_component('test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, hass)
component._platforms = {}
yield from component.async_setup({
DOMAIN: {
'platform': 'platform',
}
})
handle = list(component._platforms.values())[-1]
assert handle.parallel_updates is not None
@asyncio.coroutine
def test_pararell_updates_sync_platform(hass):
"""Warn we log when platform setup takes a long time."""
platform = MockPlatform()
loader.set_component('test_domain.platform', platform)
component = EntityComponent(_LOGGER, DOMAIN, hass)
component._platforms = {}
yield from component.async_setup({
DOMAIN: {
'platform': 'platform',
}
})
handle = list(component._platforms.values())[-1]
assert handle.parallel_updates is not None