diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index a7d392b321e..790f0783a9a 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -4,7 +4,9 @@ Component to interface with various locks that can be controlled remotely. For more details about this component, please refer to the documentation at https://home-assistant.io/components/lock/ """ +import asyncio from datetime import timedelta +import functools as ft import logging import os @@ -67,38 +69,54 @@ def unlock(hass, entity_id=None, code=None): hass.services.call(DOMAIN, SERVICE_UNLOCK, data) -def setup(hass, config): +@asyncio.coroutine +def async_setup(hass, config): """Track states and offer events for locks.""" component = EntityComponent( _LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_LOCKS) - component.setup(config) - def handle_lock_service(service): + yield from component.async_setup(config) + + @asyncio.coroutine + def async_handle_lock_service(service): """Handle calls to the lock services.""" - target_locks = component.extract_from_service(service) + target_locks = component.async_extract_from_service(service) code = service.data.get(ATTR_CODE) - for item in target_locks: + for entity in target_locks: if service.service == SERVICE_LOCK: - item.lock(code=code) + yield from entity.async_lock(code=code) else: - item.unlock(code=code) + yield from entity.async_unlock(code=code) - for item in target_locks: - if not item.should_poll: + update_tasks = [] + + for entity in target_locks: + if not entity.should_poll: continue - item.update_ha_state(True) + update_coro = hass.loop.create_task( + entity.async_update_ha_state(True)) + if hasattr(entity, 'async_update'): + update_tasks.append(update_coro) + else: + yield from update_coro + + 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')) + + hass.services.async_register( + DOMAIN, SERVICE_UNLOCK, async_handle_lock_service, + descriptions.get(SERVICE_UNLOCK), schema=LOCK_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, SERVICE_LOCK, async_handle_lock_service, + descriptions.get(SERVICE_LOCK), schema=LOCK_SERVICE_SCHEMA) - descriptions = load_yaml_config_file( - os.path.join(os.path.dirname(__file__), 'services.yaml')) - hass.services.register(DOMAIN, SERVICE_UNLOCK, handle_lock_service, - descriptions.get(SERVICE_UNLOCK), - schema=LOCK_SERVICE_SCHEMA) - hass.services.register(DOMAIN, SERVICE_LOCK, handle_lock_service, - descriptions.get(SERVICE_LOCK), - schema=LOCK_SERVICE_SCHEMA) return True @@ -125,10 +143,26 @@ class LockDevice(Entity): """Lock the lock.""" raise NotImplementedError() + def async_lock(self, **kwargs): + """Lock the lock. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.lock, **kwargs)) + def unlock(self, **kwargs): """Unlock the lock.""" raise NotImplementedError() + def async_unlock(self, **kwargs): + """Unlock the lock. + + This method must be run in the event loop and returns a coroutine. + """ + return self.hass.loop.run_in_executor( + None, ft.partial(self.unlock, **kwargs)) + @property def state_attributes(self): """Return the state attributes.""" diff --git a/homeassistant/components/lock/demo.py b/homeassistant/components/lock/demo.py index 55929227039..fca922b11e2 100644 --- a/homeassistant/components/lock/demo.py +++ b/homeassistant/components/lock/demo.py @@ -43,9 +43,9 @@ class DemoLock(LockDevice): def lock(self, **kwargs): """Lock the device.""" self._state = STATE_LOCKED - self.update_ha_state() + self.schedule_update_ha_state() def unlock(self, **kwargs): """Unlock the device.""" self._state = STATE_UNLOCKED - self.update_ha_state() + self.schedule_update_ha_state() diff --git a/homeassistant/components/lock/mqtt.py b/homeassistant/components/lock/mqtt.py index da6e595914b..fde62c8695e 100644 --- a/homeassistant/components/lock/mqtt.py +++ b/homeassistant/components/lock/mqtt.py @@ -82,10 +82,10 @@ class MqttLock(LockDevice): payload) if payload == self._payload_lock: self._state = True - self.update_ha_state() + self.schedule_update_ha_state() elif payload == self._payload_unlock: self._state = False - self.update_ha_state() + self.schedule_update_ha_state() if self._state_topic is None: # Force into optimistic mode. @@ -121,7 +121,7 @@ class MqttLock(LockDevice): if self._optimistic: # Optimistically assume that switch has changed state. self._state = True - self.update_ha_state() + self.schedule_update_ha_state() def unlock(self, **kwargs): """Unlock the device.""" @@ -130,4 +130,4 @@ class MqttLock(LockDevice): if self._optimistic: # Optimistically assume that switch has changed state. self._state = False - self.update_ha_state() + self.schedule_update_ha_state() diff --git a/homeassistant/components/lock/vera.py b/homeassistant/components/lock/vera.py index 0307bbf4312..14606c0853c 100644 --- a/homeassistant/components/lock/vera.py +++ b/homeassistant/components/lock/vera.py @@ -35,13 +35,11 @@ class VeraLock(VeraDevice, LockDevice): """Lock the device.""" self.vera_device.lock() self._state = STATE_LOCKED - self.update_ha_state() def unlock(self, **kwargs): """Unlock the device.""" self.vera_device.unlock() self._state = STATE_UNLOCKED - self.update_ha_state() @property def is_locked(self):