From f63874eb8cfea537861b2cf142e6372191543676 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Thu, 2 Feb 2017 21:39:13 +0100 Subject: [PATCH] Migrate cover to async. (#5717) --- homeassistant/components/cover/__init__.py | 125 ++++++++++++++++---- homeassistant/components/cover/demo.py | 4 +- homeassistant/components/cover/garadget.py | 3 +- homeassistant/components/cover/mqtt.py | 4 +- homeassistant/components/cover/mysensors.py | 6 +- 5 files changed, 110 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/cover/__init__.py b/homeassistant/components/cover/__init__.py index da473df111e..1dc6101f1e9 100644 --- a/homeassistant/components/cover/__init__.py +++ b/homeassistant/components/cover/__init__.py @@ -4,9 +4,11 @@ Support for Cover devices. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/cover/ """ -import os +import asyncio from datetime import timedelta +import functools as ft import logging +import os import voluptuous as vol @@ -53,17 +55,17 @@ COVER_SET_COVER_TILT_POSITION_SCHEMA = COVER_SERVICE_SCHEMA.extend({ }) SERVICE_TO_METHOD = { - SERVICE_OPEN_COVER: {'method': 'open_cover'}, - SERVICE_CLOSE_COVER: {'method': 'close_cover'}, + SERVICE_OPEN_COVER: {'method': 'async_open_cover'}, + SERVICE_CLOSE_COVER: {'method': 'async_close_cover'}, SERVICE_SET_COVER_POSITION: { - 'method': 'set_cover_position', + 'method': 'async_set_cover_position', 'schema': COVER_SET_COVER_POSITION_SCHEMA}, - SERVICE_STOP_COVER: {'method': 'stop_cover'}, - SERVICE_OPEN_COVER_TILT: {'method': 'open_cover_tilt'}, - SERVICE_CLOSE_COVER_TILT: {'method': 'close_cover_tilt'}, - SERVICE_STOP_COVER_TILT: {'method': 'stop_cover_tilt'}, + SERVICE_STOP_COVER: {'method': 'async_stop_cover'}, + SERVICE_OPEN_COVER_TILT: {'method': 'async_open_cover_tilt'}, + SERVICE_CLOSE_COVER_TILT: {'method': 'async_close_cover_tilt'}, + SERVICE_STOP_COVER_TILT: {'method': 'async_stop_cover_tilt'}, SERVICE_SET_COVER_TILT_POSITION: { - 'method': 'set_cover_tilt_position', + 'method': 'async_set_cover_tilt_position', 'schema': COVER_SET_COVER_TILT_POSITION_SCHEMA}, } @@ -124,40 +126,53 @@ def stop_cover_tilt(hass, entity_id=None): hass.services.call(DOMAIN, SERVICE_STOP_COVER_TILT, data) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Track states and offer events for covers.""" component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_COVERS) - component.setup(config) - def handle_cover_service(service): + yield from component.async_setup(config) + + @asyncio.coroutine + def async_handle_cover_service(service): """Handle calls to the cover services.""" + covers = component.async_extract_from_service(service) method = SERVICE_TO_METHOD.get(service.service) params = service.data.copy() params.pop(ATTR_ENTITY_ID, None) - if not method: - return - - covers = component.extract_from_service(service) - + # call method for cover in covers: - getattr(cover, method['method'])(**params) + yield from getattr(cover, method['method'])(**params) + + update_tasks = [] for cover in covers: if not cover.should_poll: continue - cover.update_ha_state(True) + update_coro = hass.loop.create_task( + cover.async_update_ha_state(True)) + if hasattr(cover, 'async_update'): + update_tasks.append(update_coro) + else: + yield from update_coro - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) + if update_tasks: + yield from asyncio.wait(update_tasks, loop=hass.loop) + + descriptions = yield from hass.loop.run_in_executor( + None, load_yaml_config_file, os.path.join( + os.path.dirname(__file__), 'services.yaml')) for service_name in SERVICE_TO_METHOD: schema = SERVICE_TO_METHOD[service_name].get( 'schema', COVER_SERVICE_SCHEMA) - hass.services.register(DOMAIN, service_name, handle_cover_service, - descriptions.get(service_name), schema=schema) + hass.services.async_register( + DOMAIN, service_name, async_handle_cover_service, + descriptions.get(service_name), schema=schema) + return True @@ -215,30 +230,94 @@ class CoverDevice(Entity): """Open the cover.""" raise NotImplementedError() + def async_open_cover(self, **kwargs): + """Open the cover. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.open_cover, **kwargs)) + def close_cover(self, **kwargs): """Close cover.""" raise NotImplementedError() + def async_close_cover(self, **kwargs): + """Close cover. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.close_cover, **kwargs)) + def set_cover_position(self, **kwargs): """Move the cover to a specific position.""" pass + def async_set_cover_position(self, **kwargs): + """Move the cover to a specific position. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.set_cover_position, **kwargs)) + def stop_cover(self, **kwargs): """Stop the cover.""" pass + def async_stop_cover(self, **kwargs): + """Stop the cover. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.stop_cover, **kwargs)) + def open_cover_tilt(self, **kwargs): """Open the cover tilt.""" pass + def async_open_cover_tilt(self, **kwargs): + """Open the cover tilt. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.open_cover_tilt, **kwargs)) + def close_cover_tilt(self, **kwargs): """Close the cover tilt.""" pass + def async_close_cover_tilt(self, **kwargs): + """Close the cover tilt. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.close_cover_tilt, **kwargs)) + def set_cover_tilt_position(self, **kwargs): """Move the cover tilt to a specific position.""" pass + def async_set_cover_tilt_position(self, **kwargs): + """Move the cover tilt to a specific position. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.set_cover_tilt_position, **kwargs)) + def stop_cover_tilt(self, **kwargs): """Stop the cover.""" pass + + def async_stop_cover_tilt(self, **kwargs): + """Stop the cover. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.stop_cover_tilt, **kwargs)) diff --git a/homeassistant/components/cover/demo.py b/homeassistant/components/cover/demo.py index 5929ab1851a..070c37a0e3c 100644 --- a/homeassistant/components/cover/demo.py +++ b/homeassistant/components/cover/demo.py @@ -149,7 +149,7 @@ class DemoCover(CoverDevice): if self._position in (100, 0, self._set_position): self.stop_cover() - self.update_ha_state() + self.schedule_update_ha_state() def _listen_cover_tilt(self): """Listen for changes in cover tilt.""" @@ -167,4 +167,4 @@ class DemoCover(CoverDevice): if self._tilt_position in (100, 0, self._set_tilt_position): self.stop_cover_tilt() - self.update_ha_state() + self.schedule_update_ha_state() diff --git a/homeassistant/components/cover/garadget.py b/homeassistant/components/cover/garadget.py index 813ddea7170..1847e58c4a5 100644 --- a/homeassistant/components/cover/garadget.py +++ b/homeassistant/components/cover/garadget.py @@ -199,8 +199,7 @@ class GaradgetCover(CoverDevice): def _check_state(self, now): """Check the state of the service during an operation.""" - self.update() - self.update_ha_state() + self.schedule_update_ha_state(True) def close_cover(self): """Close the cover.""" diff --git a/homeassistant/components/cover/mqtt.py b/homeassistant/components/cover/mqtt.py index 44b59133d21..aa549986533 100644 --- a/homeassistant/components/cover/mqtt.py +++ b/homeassistant/components/cover/mqtt.py @@ -151,7 +151,7 @@ class MqttCover(CoverDevice): if self._optimistic: # Optimistically assume that cover has changed state. self._state = False - self.update_ha_state() + self.schedule_update_ha_state() def close_cover(self, **kwargs): """Move the cover down.""" @@ -160,7 +160,7 @@ class MqttCover(CoverDevice): if self._optimistic: # Optimistically assume that cover has changed state. self._state = True - self.update_ha_state() + self.schedule_update_ha_state() def stop_cover(self, **kwargs): """Stop the device.""" diff --git a/homeassistant/components/cover/mysensors.py b/homeassistant/components/cover/mysensors.py index a75ad36354b..7daadebadad 100644 --- a/homeassistant/components/cover/mysensors.py +++ b/homeassistant/components/cover/mysensors.py @@ -75,7 +75,7 @@ class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice): self._values[set_req.V_DIMMER] = 100 else: self._values[set_req.V_LIGHT] = STATE_ON - self.update_ha_state() + self.schedule_update_ha_state() def close_cover(self, **kwargs): """Move the cover down.""" @@ -88,7 +88,7 @@ class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice): self._values[set_req.V_DIMMER] = 0 else: self._values[set_req.V_LIGHT] = STATE_OFF - self.update_ha_state() + self.schedule_update_ha_state() def set_cover_position(self, **kwargs): """Move the cover to a specific position.""" @@ -99,7 +99,7 @@ class MySensorsCover(mysensors.MySensorsDeviceEntity, CoverDevice): if self.gateway.optimistic: # Optimistically assume that cover has changed state. self._values[set_req.V_DIMMER] = position - self.update_ha_state() + self.schedule_update_ha_state() def stop_cover(self, **kwargs): """Stop the device."""