diff --git a/homeassistant/components/scene/__init__.py b/homeassistant/components/scene/__init__.py index 5ac7a2d9c86..7934f4b610b 100644 --- a/homeassistant/components/scene/__init__.py +++ b/homeassistant/components/scene/__init__.py @@ -4,6 +4,7 @@ Allow users to set and activate scenes. For more details about this component, please refer to the documentation at https://home-assistant.io/components/scene/ """ +import asyncio import logging from collections import namedtuple @@ -39,7 +40,8 @@ def activate(hass, entity_id=None): hass.services.call(DOMAIN, SERVICE_TURN_ON, data) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Setup scenes.""" logger = logging.getLogger(__name__) @@ -59,17 +61,21 @@ def setup(hass, config): component = EntityComponent(logger, DOMAIN, hass) - component.setup(config) + yield from component.async_setup(config) - def handle_scene_service(service): + @asyncio.coroutine + def async_handle_scene_service(service): """Handle calls to the switch services.""" - target_scenes = component.extract_from_service(service) + target_scenes = component.async_extract_from_service(service) + print(target_scenes) + print(component.entities) + tasks = [scene.async_activate() for scene in target_scenes] + if tasks: + yield from asyncio.wait(tasks, loop=hass.loop) - for scene in target_scenes: - scene.activate() - - hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_scene_service, - schema=SCENE_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_TURN_ON, async_handle_scene_service, + schema=SCENE_SERVICE_SCHEMA) return True @@ -89,4 +95,12 @@ class Scene(Entity): def activate(self): """Activate scene. Try to get entities into requested state.""" - raise NotImplementedError + raise NotImplementedError() + + @asyncio.coroutine + def async_activate(self): + """Activate scene. Try to get entities into requested state. + + This method is a coroutine. + """ + yield from self.hass.loop.run_in_executor(None, self.activate) diff --git a/homeassistant/components/scene/homeassistant.py b/homeassistant/components/scene/homeassistant.py index e507c664bef..c7365ea65d9 100644 --- a/homeassistant/components/scene/homeassistant.py +++ b/homeassistant/components/scene/homeassistant.py @@ -4,13 +4,14 @@ Allow users to set and activate scenes. For more details about this component, please refer to the documentation at https://home-assistant.io/components/scene/ """ +import asyncio from collections import namedtuple from homeassistant.components.scene import Scene from homeassistant.const import ( ATTR_ENTITY_ID, STATE_OFF, STATE_ON) from homeassistant.core import State -from homeassistant.helpers.state import reproduce_state +from homeassistant.helpers.state import async_reproduce_state DEPENDENCIES = ['group'] STATE = 'scening' @@ -20,21 +21,24 @@ CONF_ENTITIES = "entities" SceneConfig = namedtuple('SceneConfig', ['name', 'states']) -def setup_platform(hass, config, add_devices, discovery_info=None): +@asyncio.coroutine +def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Setup home assistant scene entries.""" scene_config = config.get("states") if not isinstance(scene_config, list): scene_config = [scene_config] - add_devices(HomeAssistantScene(hass, _process_config(scene)) - for scene in scene_config) - + yield from async_add_devices(HomeAssistantScene( + hass, _process_config(scene)) for scene in scene_config) return True def _process_config(scene_config): - """Process passed in config into a format to work with.""" + """Process passed in config into a format to work with. + + Async friendly. + """ name = scene_config.get('name') states = {} @@ -81,6 +85,8 @@ class HomeAssistantScene(Scene): ATTR_ENTITY_ID: list(self.scene_config.states.keys()), } - def activate(self): + @asyncio.coroutine + def async_activate(self): """Activate scene. Try to get entities into requested state.""" - reproduce_state(self.hass, self.scene_config.states.values(), True) + yield from async_reproduce_state( + self.hass, self.scene_config.states.values(), True) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 10364eff815..9980ad11a8d 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -1,4 +1,5 @@ """Helpers that help with state related things.""" +import asyncio import json import logging from collections import defaultdict @@ -33,6 +34,7 @@ from homeassistant.const import ( STATE_OFF, STATE_ON, STATE_OPEN, STATE_PAUSED, STATE_PLAYING, STATE_UNKNOWN, STATE_UNLOCKED) from homeassistant.core import State +from homeassistant.util.async import run_coroutine_threadsafe _LOGGER = logging.getLogger(__name__) @@ -111,6 +113,13 @@ def get_changed_since(states, utc_point_in_time): def reproduce_state(hass, states, blocking=False): + """Reproduce given state.""" + return run_coroutine_threadsafe( + async_reproduce_state(hass, states, blocking), hass.loop).result() + + +@asyncio.coroutine +def async_reproduce_state(hass, states, blocking=False): """Reproduce given state.""" if isinstance(states, State): states = [states] @@ -129,7 +138,7 @@ def reproduce_state(hass, states, blocking=False): else: service_domain = state.domain - domain_services = hass.services.services[service_domain] + domain_services = hass.services.async_services()[service_domain] service = None for _service in domain_services.keys(): @@ -157,7 +166,8 @@ def reproduce_state(hass, states, blocking=False): for (service_domain, service, service_data), entity_ids in to_call.items(): data = json.loads(service_data) data[ATTR_ENTITY_ID] = entity_ids - hass.services.call(service_domain, service, data, blocking) + yield from hass.services.async_call( + service_domain, service, data, blocking) def state_as_number(state): diff --git a/tests/components/scene/__init__.py b/tests/components/scene/__init__.py new file mode 100644 index 00000000000..6491c2ef020 --- /dev/null +++ b/tests/components/scene/__init__.py @@ -0,0 +1 @@ +"""Tests for scene component.""" diff --git a/tests/components/test_scene.py b/tests/components/scene/test_init.py similarity index 100% rename from tests/components/test_scene.py rename to tests/components/scene/test_init.py