From c211ce6b2f85705cd32daa63571c59c71cd9b4e7 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:41:06 +0200 Subject: [PATCH 01/18] Fix aiohttp connection reset errors --- homeassistant/helpers/aiohttp_client.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 71f3374f0c032..9927b6d57f480 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -112,17 +112,11 @@ async def async_aiohttp_proxy_stream(hass, request, stream, content_type, data = await stream.read(buffer_size) if not data: - await response.write_eof() break - await response.write(data) - except (asyncio.TimeoutError, aiohttp.ClientError): - # Something went wrong fetching data, close connection gracefully - await response.write_eof() - - except asyncio.CancelledError: - # The user closed the connection + except (asyncio.CancelledError, asyncio.TimeoutError, aiohttp.ClientError): + # Something went wrong fetching data, closed connection pass From 723691524bba47cba27548b96890219f9ae81edf Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:45:16 +0200 Subject: [PATCH 02/18] Update aiohttp_client.py --- homeassistant/helpers/aiohttp_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 9927b6d57f480..6603286d572d4 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -119,6 +119,7 @@ async def async_aiohttp_proxy_stream(hass, request, stream, content_type, # Something went wrong fetching data, closed connection pass + return response @callback def _async_register_clientsession_shutdown(hass, clientsession): From a712aa20b48f10a05cf78059128ea76ffb21b146 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:46:04 +0200 Subject: [PATCH 03/18] Update aiohttp_client.py --- homeassistant/helpers/aiohttp_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 6603286d572d4..ef0b845402448 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -73,7 +73,7 @@ def async_aiohttp_proxy_web(hass, request, web_coro, buffer_size=102400, """Stream websession request to aiohttp web response.""" try: with async_timeout.timeout(timeout, loop=hass.loop): - req = yield from web_coro + req = await web_coro except asyncio.CancelledError: # The user cancelled the request @@ -88,7 +88,7 @@ def async_aiohttp_proxy_web(hass, request, web_coro, buffer_size=102400, raise HTTPBadGateway() from err try: - yield from async_aiohttp_proxy_stream( + return await async_aiohttp_proxy_stream( hass, request, req.content, From e95b0bcab5db35524806782ffdd28e5673f21a24 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:56:52 +0200 Subject: [PATCH 04/18] Update __init__.py --- homeassistant/components/camera/__init__.py | 22 ++++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 22354b51956bc..afd6427718bd9 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -321,12 +321,9 @@ async def write_to_mjpeg_stream(img_bytes): except asyncio.CancelledError: _LOGGER.debug("Stream closed by frontend.") - response = None - raise + pass - finally: - if response is not None: - await response.write_eof() + return response async def handle_async_mjpeg_stream(self, request): """Serve an HTTP MJPEG stream from the camera. @@ -409,10 +406,9 @@ async def get(self, request, entity_id): request.query.get('token') in camera.access_tokens) if not authenticated: - return web.Response(status=401) + raise web.HTTPUnauthorized() - response = await self.handle(request, camera) - return response + return await self.handle(request, camera) async def handle(self, request, camera): """Handle the camera request.""" @@ -435,7 +431,7 @@ async def handle(self, request, camera): return web.Response(body=image, content_type=camera.content_type) - return web.Response(status=500) + raise web.HTTPInternalServerError() class CameraMjpegStream(CameraView): @@ -448,8 +444,7 @@ async def handle(self, request, camera): """Serve camera stream, possibly with interval.""" interval = request.query.get('interval') if interval is None: - await camera.handle_async_mjpeg_stream(request) - return + return await camera.handle_async_mjpeg_stream(request) try: # Compose camera stream from stills @@ -457,10 +452,9 @@ async def handle(self, request, camera): if interval < MIN_STREAM_INTERVAL: raise ValueError("Stream interval must be be > {}" .format(MIN_STREAM_INTERVAL)) - await camera.handle_async_still_stream(request, interval) - return + return await camera.handle_async_still_stream(request, interval) except ValueError: - return web.Response(status=400) + raise web.HTTPBadRequest() @callback From caf5168990e73b1f7d7462ede6484dd60ab7fbc0 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:58:34 +0200 Subject: [PATCH 05/18] Update mjpeg.py --- homeassistant/components/camera/mjpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index a5ed0cdc02c49..f3b36e51a05de 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -135,7 +135,7 @@ def handle_async_mjpeg_stream(self, request): websession = async_get_clientsession(self.hass) stream_coro = websession.get(self._mjpeg_url, auth=self._auth) - yield from async_aiohttp_proxy_web(self.hass, request, stream_coro) + return yield from async_aiohttp_proxy_web(self.hass, request, stream_coro) @property def name(self): From ac40e711e310243bac169dd5635741fd7de43fe8 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:58:55 +0200 Subject: [PATCH 06/18] Update mjpeg.py --- homeassistant/components/camera/mjpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index f3b36e51a05de..6d4c9fbb7d65b 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -135,7 +135,7 @@ def handle_async_mjpeg_stream(self, request): websession = async_get_clientsession(self.hass) stream_coro = websession.get(self._mjpeg_url, auth=self._auth) - return yield from async_aiohttp_proxy_web(self.hass, request, stream_coro) + return await async_aiohttp_proxy_web(self.hass, request, stream_coro) @property def name(self): From a027996e9faebeb38f7fd8d9d354e15af1aa2090 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 18:59:47 +0200 Subject: [PATCH 07/18] Update ffmpeg.py --- homeassistant/components/camera/ffmpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 1bbd263e58555..96129406b5fe7 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -69,7 +69,7 @@ def handle_async_mjpeg_stream(self, request): yield from stream.open_camera( self._input, extra_cmd=self._extra_arguments) - yield from async_aiohttp_proxy_stream( + return await async_aiohttp_proxy_stream( self.hass, request, stream, 'multipart/x-mixed-replace;boundary=ffserver') yield from stream.close() From aad8218ecaa019d9bce864255d7be21ead71696f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 19:00:33 +0200 Subject: [PATCH 08/18] Update ffmpeg.py --- homeassistant/components/camera/ffmpeg.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 96129406b5fe7..a0ef096a2042f 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -69,10 +69,12 @@ def handle_async_mjpeg_stream(self, request): yield from stream.open_camera( self._input, extra_cmd=self._extra_arguments) - return await async_aiohttp_proxy_stream( - self.hass, request, stream, - 'multipart/x-mixed-replace;boundary=ffserver') - yield from stream.close() + try: + return await async_aiohttp_proxy_stream( + self.hass, request, stream, + 'multipart/x-mixed-replace;boundary=ffserver') + finaly: + await stream.close() @property def name(self): From b962e0b3f2279c60abefffabe69d8ebebb88b151 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 19:00:51 +0200 Subject: [PATCH 09/18] Update ffmpeg.py --- homeassistant/components/camera/ffmpeg.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index a0ef096a2042f..70d3f6465952a 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -73,7 +73,7 @@ def handle_async_mjpeg_stream(self, request): return await async_aiohttp_proxy_stream( self.hass, request, stream, 'multipart/x-mixed-replace;boundary=ffserver') - finaly: + finally: await stream.close() @property From b07fb49025afbddf65461f0f594d88ccced6ac66 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 19:03:27 +0200 Subject: [PATCH 10/18] Update proxy.py --- homeassistant/components/camera/proxy.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/homeassistant/components/camera/proxy.py b/homeassistant/components/camera/proxy.py index 6fe891b6b241f..86fec125e4586 100644 --- a/homeassistant/components/camera/proxy.py +++ b/homeassistant/components/camera/proxy.py @@ -192,7 +192,7 @@ async def handle_async_mjpeg_stream(self, request): if not self._stream_opts: await async_aiohttp_proxy_web(self.hass, request, stream_coro) - return + return aiohttp.web.HTTPBadRequest() response = aiohttp.web.StreamResponse() response.content_type = ('multipart/x-mixed-replace; ' @@ -232,12 +232,8 @@ async def write(img_bytes): except asyncio.CancelledError: _LOGGER.debug("Stream closed by frontend.") req.close() - response = None - raise - finally: - if response is not None: - await response.write_eof() + return response @property def name(self): From 6fe2a561c30407ab7290f2a265c78e429d94b1c2 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 22:31:18 +0200 Subject: [PATCH 11/18] Update __init__.py --- homeassistant/components/camera/__init__.py | 30 +++++++++------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index afd6427718bd9..acaa63436fe7e 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -301,27 +301,21 @@ async def write_to_mjpeg_stream(img_bytes): last_image = None - try: - while True: - img_bytes = await self.async_camera_image() - if not img_bytes: - break - - if img_bytes and img_bytes != last_image: - await write_to_mjpeg_stream(img_bytes) + while True: + img_bytes = await self.async_camera_image() + if not img_bytes: + break - # Chrome seems to always ignore first picture, - # print it twice. - if last_image is None: - await write_to_mjpeg_stream(img_bytes) + if img_bytes and img_bytes != last_image: + await write_to_mjpeg_stream(img_bytes) - last_image = img_bytes - - await asyncio.sleep(interval) + # Chrome seems to always ignore first picture, + # print it twice. + if last_image is None: + await write_to_mjpeg_stream(img_bytes) + last_image = img_bytes - except asyncio.CancelledError: - _LOGGER.debug("Stream closed by frontend.") - pass + await asyncio.sleep(interval) return response From 908c6b132a58d98b4e6598695a3ac61bf4d63f1d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 22:32:32 +0200 Subject: [PATCH 12/18] Update aiohttp_client.py --- homeassistant/helpers/aiohttp_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index ef0b845402448..599d4cdabc7b0 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -115,7 +115,7 @@ async def async_aiohttp_proxy_stream(hass, request, stream, content_type, break await response.write(data) - except (asyncio.CancelledError, asyncio.TimeoutError, aiohttp.ClientError): + except (asyncio.TimeoutError, aiohttp.ClientError): # Something went wrong fetching data, closed connection pass From ac85158f58331fae6dbe869ecd6028e328963d29 Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 22:38:11 +0200 Subject: [PATCH 13/18] Update aiohttp_client.py --- homeassistant/helpers/aiohttp_client.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index 599d4cdabc7b0..e135a88989859 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -66,10 +66,9 @@ def async_create_clientsession(hass, verify_ssl=True, auto_cleanup=True, return clientsession -@asyncio.coroutine @bind_hass -def async_aiohttp_proxy_web(hass, request, web_coro, buffer_size=102400, - timeout=10): +async def async_aiohttp_proxy_web(hass, request, web_coro, + buffer_size=102400, timeout=10): """Stream websession request to aiohttp web response.""" try: with async_timeout.timeout(timeout, loop=hass.loop): From 6d9fade59e45c1e0821f5b3826d109bbcb781f7d Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 22:39:03 +0200 Subject: [PATCH 14/18] Update proxy.py --- homeassistant/components/camera/proxy.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/camera/proxy.py b/homeassistant/components/camera/proxy.py index 86fec125e4586..598e0cc4a925c 100644 --- a/homeassistant/components/camera/proxy.py +++ b/homeassistant/components/camera/proxy.py @@ -191,8 +191,7 @@ async def handle_async_mjpeg_stream(self, request): stream_coro = websession.get(url, headers=self._headers) if not self._stream_opts: - await async_aiohttp_proxy_web(self.hass, request, stream_coro) - return aiohttp.web.HTTPBadRequest() + return await async_aiohttp_proxy_web(self.hass, request, stream_coro) response = aiohttp.web.StreamResponse() response.content_type = ('multipart/x-mixed-replace; ' From 542963139767ae3dbac0e9419793998b2c30f47f Mon Sep 17 00:00:00 2001 From: Pascal Vizeli Date: Fri, 20 Jul 2018 22:41:30 +0200 Subject: [PATCH 15/18] Update proxy.py --- homeassistant/components/camera/proxy.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/camera/proxy.py b/homeassistant/components/camera/proxy.py index 598e0cc4a925c..d88d52c4c8a04 100644 --- a/homeassistant/components/camera/proxy.py +++ b/homeassistant/components/camera/proxy.py @@ -191,7 +191,8 @@ async def handle_async_mjpeg_stream(self, request): stream_coro = websession.get(url, headers=self._headers) if not self._stream_opts: - return await async_aiohttp_proxy_web(self.hass, request, stream_coro) + return await async_aiohttp_proxy_web( + self.hass, request, stream_coro) response = aiohttp.web.StreamResponse() response.content_type = ('multipart/x-mixed-replace; ' @@ -228,8 +229,7 @@ async def write(img_bytes): _resize_image, image, self._stream_opts) await write(image) data = data[jpg_end + 2:] - except asyncio.CancelledError: - _LOGGER.debug("Stream closed by frontend.") + finally: req.close() return response From 0340d4e6c83bee330143e6f5cb0d6f71c5b00f5c Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 23 Jul 2018 12:26:25 +0200 Subject: [PATCH 16/18] Fix await inside coroutine --- homeassistant/components/camera/ffmpeg.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/camera/ffmpeg.py b/homeassistant/components/camera/ffmpeg.py index 70d3f6465952a..3da0f19fbf03d 100644 --- a/homeassistant/components/camera/ffmpeg.py +++ b/homeassistant/components/camera/ffmpeg.py @@ -29,8 +29,8 @@ }) -@asyncio.coroutine -def async_setup_platform(hass, config, async_add_devices, discovery_info=None): +async def async_setup_platform(hass, config, async_add_devices, + discovery_info=None): """Set up a FFmpeg camera.""" if not hass.data[DATA_FFMPEG].async_run_test(config.get(CONF_INPUT)): return @@ -49,24 +49,22 @@ def __init__(self, hass, config): self._input = config.get(CONF_INPUT) self._extra_arguments = config.get(CONF_EXTRA_ARGUMENTS) - @asyncio.coroutine - def async_camera_image(self): + async def async_camera_image(self): """Return a still image response from the camera.""" from haffmpeg import ImageFrame, IMAGE_JPEG ffmpeg = ImageFrame(self._manager.binary, loop=self.hass.loop) - image = yield from asyncio.shield(ffmpeg.get_image( + image = await asyncio.shield(ffmpeg.get_image( self._input, output_format=IMAGE_JPEG, extra_cmd=self._extra_arguments), loop=self.hass.loop) return image - @asyncio.coroutine - def handle_async_mjpeg_stream(self, request): + async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" from haffmpeg import CameraMjpeg stream = CameraMjpeg(self._manager.binary, loop=self.hass.loop) - yield from stream.open_camera( + await stream.open_camera( self._input, extra_cmd=self._extra_arguments) try: From 67596293d62d83bfd7a1d00fc99ea172f0650270 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 23 Jul 2018 13:53:59 +0200 Subject: [PATCH 17/18] Fix async syntax --- homeassistant/components/camera/mjpeg.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/camera/mjpeg.py b/homeassistant/components/camera/mjpeg.py index 6d4c9fbb7d65b..757a1b5fc0942 100644 --- a/homeassistant/components/camera/mjpeg.py +++ b/homeassistant/components/camera/mjpeg.py @@ -123,12 +123,11 @@ def camera_image(self): with closing(req) as response: return extract_image_from_mjpeg(response.iter_content(102400)) - @asyncio.coroutine - def handle_async_mjpeg_stream(self, request): + async def handle_async_mjpeg_stream(self, request): """Generate an HTTP MJPEG stream from the camera.""" # aiohttp don't support DigestAuth -> Fallback if self._authentication == HTTP_DIGEST_AUTHENTICATION: - yield from super().handle_async_mjpeg_stream(request) + await super().handle_async_mjpeg_stream(request) return # connect to stream From 839a5776084c95f5d2c7fce55440ab62007ed429 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 23 Jul 2018 14:13:36 +0200 Subject: [PATCH 18/18] Lint --- homeassistant/helpers/aiohttp_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/helpers/aiohttp_client.py b/homeassistant/helpers/aiohttp_client.py index e135a88989859..53b246c700d34 100644 --- a/homeassistant/helpers/aiohttp_client.py +++ b/homeassistant/helpers/aiohttp_client.py @@ -120,6 +120,7 @@ async def async_aiohttp_proxy_stream(hass, request, stream, content_type, return response + @callback def _async_register_clientsession_shutdown(hass, clientsession): """Register ClientSession close on Home Assistant shutdown.