Bootstrap / Component setup async (#6264)

* Bootstrap / Entiy setup async

* Cleanup add_job stuff / return task/future object

* Address paulus comments / part 1

* fix install pip

* Cleanup bootstrap / move config stuff to config.py

* Make demo async

* Further bootstrap improvement

* Address Martin's comments

* Fix initial tests

* Fix final tests

* Fix bug with prepare loader

* Remove no longer needed things

* Log error when invalid config

* More cleanup

* Cleanups platform events & fix lint

* Use a non blocking add_entities callback for platform

* Fix Autoamtion is setup befor entity is ready

* Better automation fix

* Address paulus comments

* Typo

* fix lint

* rename functions

* fix tests

* fix test

* change exceptions

* fix spell
This commit is contained in:
Pascal Vizeli 2017-03-01 05:33:19 +01:00 committed by Paulus Schoutsen
parent 383b0914b3
commit 41f558b181
109 changed files with 764 additions and 848 deletions

View file

@ -3,8 +3,7 @@ import asyncio
from datetime import timedelta
from homeassistant import config as conf_util
from homeassistant.bootstrap import (
async_prepare_setup_platform, async_prepare_setup_component)
from homeassistant.bootstrap import async_prepare_setup_platform
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_SCAN_INTERVAL, CONF_ENTITY_NAMESPACE,
DEVICE_DEFAULT_NAME)
@ -49,12 +48,9 @@ class EntityComponent(object):
def setup(self, config):
"""Set up a full entity component.
Loads the platforms from the config and will listen for supported
discovered platforms.
This doesn't block the executor to protect from deadlocks.
"""
run_coroutine_threadsafe(
self.async_setup(config), self.hass.loop
).result()
self.hass.add_job(self.async_setup(config))
@asyncio.coroutine
def async_setup(self, config):
@ -143,14 +139,16 @@ class EntityComponent(object):
if getattr(platform, 'async_setup_platform', None):
yield from platform.async_setup_platform(
self.hass, platform_config,
entity_platform.async_add_entities, discovery_info
entity_platform.async_schedule_add_entities, discovery_info
)
else:
yield from self.hass.loop.run_in_executor(
None, platform.setup_platform, self.hass, platform_config,
entity_platform.add_entities, discovery_info
entity_platform.schedule_add_entities, discovery_info
)
yield from entity_platform.async_block_entities_done()
self.hass.config.components.add(
'{}.{}'.format(self.domain, platform_type))
except Exception: # pylint: disable=broad-except
@ -275,7 +273,7 @@ class EntityComponent(object):
self.logger.error(err)
return None
conf = yield from async_prepare_setup_component(
conf = conf_util.async_process_component_config(
self.hass, conf, self.domain)
if conf is None:
@ -295,9 +293,40 @@ class EntityPlatform(object):
self.scan_interval = scan_interval
self.entity_namespace = entity_namespace
self.platform_entities = []
self._tasks = []
self._async_unsub_polling = None
self._process_updates = asyncio.Lock(loop=component.hass.loop)
@asyncio.coroutine
def async_block_entities_done(self):
"""Wait until all entities add to hass."""
if self._tasks:
pending = [task for task in self._tasks if not task.done()]
self._tasks.clear()
if pending:
yield from asyncio.wait(pending, loop=self.component.hass.loop)
def schedule_add_entities(self, new_entities, update_before_add=False):
"""Add entities for a single platform."""
if update_before_add:
for entity in new_entities:
entity.update()
run_callback_threadsafe(
self.component.hass.loop,
self.async_schedule_add_entities, list(new_entities), False
).result()
@callback
def async_schedule_add_entities(self, new_entities,
update_before_add=False):
"""Add entities for a single platform async."""
self._tasks.append(self.component.hass.async_add_job(
self.async_add_entities(
new_entities, update_before_add=update_before_add)
))
def add_entities(self, new_entities, update_before_add=False):
"""Add entities for a single platform."""
if update_before_add:
@ -306,8 +335,7 @@ class EntityPlatform(object):
run_coroutine_threadsafe(
self.async_add_entities(list(new_entities), False),
self.component.hass.loop
).result()
self.component.hass.loop).result()
@asyncio.coroutine
def async_add_entities(self, new_entities, update_before_add=False):
@ -319,8 +347,16 @@ class EntityPlatform(object):
if not new_entities:
return
tasks = [self._async_process_entity(entity, update_before_add)
for entity in new_entities]
@asyncio.coroutine
def async_process_entity(new_entity):
"""Add entities to StateMachine."""
ret = yield from self.component.async_add_entity(
new_entity, self, update_before_add=update_before_add
)
if ret:
self.platform_entities.append(new_entity)
tasks = [async_process_entity(entity) for entity in new_entities]
yield from asyncio.wait(tasks, loop=self.component.hass.loop)
yield from self.component.async_update_group()
@ -334,15 +370,6 @@ class EntityPlatform(object):
self.component.hass, self._update_entity_states, self.scan_interval
)
@asyncio.coroutine
def _async_process_entity(self, new_entity, update_before_add):
"""Add entities to StateMachine."""
ret = yield from self.component.async_add_entity(
new_entity, self, update_before_add=update_before_add
)
if ret:
self.platform_entities.append(new_entity)
@asyncio.coroutine
def async_reset(self):
"""Remove all entities and reset data.