From abf07b60f07d33266bf49186750c81880056002a Mon Sep 17 00:00:00 2001
From: Jason Hu <awarecan@users.noreply.github.com>
Date: Sat, 16 Jun 2018 07:49:11 -0700
Subject: [PATCH] Refactoring camera component to use async/await syntax.
 (#14990)

* Refactoring camera component to use async/await syntax

Also updated camera demo platform to encourage use of async

* Code review
---
 homeassistant/components/camera/__init__.py | 36 +++++++++------------
 homeassistant/components/camera/demo.py     |  5 +--
 2 files changed, 19 insertions(+), 22 deletions(-)

diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py
index c41020c3faf..ebda09de20c 100644
--- a/homeassistant/components/camera/__init__.py
+++ b/homeassistant/components/camera/__init__.py
@@ -96,6 +96,7 @@ def disable_motion_detection(hass, entity_id=None):
 
 
 @bind_hass
+@callback
 def async_snapshot(hass, filename, entity_id=None):
     """Make a snapshot from a camera."""
     data = {ATTR_ENTITY_ID: entity_id} if entity_id else {}
@@ -128,8 +129,7 @@ async def async_get_image(hass, entity_id, timeout=10):
     raise HomeAssistantError('Unable to get image')
 
 
-@asyncio.coroutine
-def async_setup(hass, config):
+async def async_setup(hass, config):
     """Set up the camera component."""
     component = hass.data[DOMAIN] = \
         EntityComponent(_LOGGER, DOMAIN, hass, SCAN_INTERVAL)
@@ -141,7 +141,7 @@ def async_setup(hass, config):
         SCHEMA_WS_CAMERA_THUMBNAIL
     )
 
-    yield from component.async_setup(config)
+    await component.async_setup(config)
 
     @callback
     def update_tokens(time):
@@ -153,27 +153,25 @@ def async_setup(hass, config):
     hass.helpers.event.async_track_time_interval(
         update_tokens, TOKEN_CHANGE_INTERVAL)
 
-    @asyncio.coroutine
-    def async_handle_camera_service(service):
+    async def async_handle_camera_service(service):
         """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_ENABLE_MOTION:
-                yield from camera.async_enable_motion_detection()
+                await camera.async_enable_motion_detection()
             elif service.service == SERVICE_DISABLE_MOTION:
-                yield from camera.async_disable_motion_detection()
+                await camera.async_disable_motion_detection()
 
             if not camera.should_poll:
                 continue
             update_tasks.append(camera.async_update_ha_state(True))
 
         if update_tasks:
-            yield from asyncio.wait(update_tasks, loop=hass.loop)
+            await asyncio.wait(update_tasks, loop=hass.loop)
 
-    @asyncio.coroutine
-    def async_handle_snapshot_service(service):
+    async def async_handle_snapshot_service(service):
         """Handle snapshot services calls."""
         target_cameras = component.async_extract_from_service(service)
         filename = service.data[ATTR_FILENAME]
@@ -189,7 +187,7 @@ def async_setup(hass, config):
                     "Can't write %s, no access to path!", snapshot_file)
                 continue
 
-            image = yield from camera.async_camera_image()
+            image = await camera.async_camera_image()
 
             def _write_image(to_file, image_data):
                 """Executor helper to write image."""
@@ -197,7 +195,7 @@ def async_setup(hass, config):
                     img_file.write(image_data)
 
             try:
-                yield from hass.async_add_job(
+                await hass.async_add_job(
                     _write_image, snapshot_file, image)
             except OSError as err:
                 _LOGGER.error("Can't write image to file: %s", err)
@@ -274,6 +272,7 @@ class Camera(Entity):
         """Return bytes of camera image."""
         raise NotImplementedError()
 
+    @callback
     def async_camera_image(self):
         """Return bytes of camera image.
 
@@ -397,8 +396,7 @@ class CameraView(HomeAssistantView):
         """Initialize a basic camera view."""
         self.component = component
 
-    @asyncio.coroutine
-    def get(self, request, entity_id):
+    async def get(self, request, entity_id):
         """Start a GET request."""
         camera = self.component.get_entity(entity_id)
 
@@ -412,11 +410,10 @@ class CameraView(HomeAssistantView):
         if not authenticated:
             return web.Response(status=401)
 
-        response = yield from self.handle(request, camera)
+        response = await self.handle(request, camera)
         return response
 
-    @asyncio.coroutine
-    def handle(self, request, camera):
+    async def handle(self, request, camera):
         """Handle the camera request."""
         raise NotImplementedError()
 
@@ -427,12 +424,11 @@ class CameraImageView(CameraView):
     url = '/api/camera_proxy/{entity_id}'
     name = 'api:camera:image'
 
-    @asyncio.coroutine
-    def handle(self, request, camera):
+    async def handle(self, request, camera):
         """Serve camera image."""
         with suppress(asyncio.CancelledError, asyncio.TimeoutError):
             with async_timeout.timeout(10, loop=request.app['hass'].loop):
-                image = yield from camera.async_camera_image()
+                image = await camera.async_camera_image()
 
             if image:
                 return web.Response(body=image,
diff --git a/homeassistant/components/camera/demo.py b/homeassistant/components/camera/demo.py
index d009f156e9d..3c1477d1828 100644
--- a/homeassistant/components/camera/demo.py
+++ b/homeassistant/components/camera/demo.py
@@ -12,9 +12,10 @@ from homeassistant.components.camera import Camera
 _LOGGER = logging.getLogger(__name__)
 
 
-def setup_platform(hass, config, add_devices, discovery_info=None):
+async def async_setup_platform(hass, config, async_add_devices,
+                               discovery_info=None):
     """Set up the Demo camera platform."""
-    add_devices([
+    async_add_devices([
         DemoCamera(hass, config, 'Demo camera')
     ])