From bcadccf7aa5f9d07119e93515ee620bf0352e51c Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 14 Mar 2021 22:49:21 -0700 Subject: [PATCH] Invalidate HLS Stream on nest url refresh failure (#47869) This will ensure that the HLS stream is re-created and fetches a new url. --- homeassistant/components/nest/camera_sdm.py | 3 ++ tests/components/nest/camera_sdm_test.py | 37 ++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/nest/camera_sdm.py b/homeassistant/components/nest/camera_sdm.py index cc2730fad8a..dd84b53d719 100644 --- a/homeassistant/components/nest/camera_sdm.py +++ b/homeassistant/components/nest/camera_sdm.py @@ -145,6 +145,9 @@ class NestCamera(Camera): _LOGGER.debug("Failed to extend stream: %s", err) # Next attempt to catch a url will get a new one self._stream = None + if self.stream: + self.stream.stop() + self.stream = None return # Update the stream worker with the latest valid url if self.stream: diff --git a/tests/components/nest/camera_sdm_test.py b/tests/components/nest/camera_sdm_test.py index 57747ad9f59..3c0c0fdb4db 100644 --- a/tests/components/nest/camera_sdm_test.py +++ b/tests/components/nest/camera_sdm_test.py @@ -256,7 +256,10 @@ async def test_refresh_expired_stream_token(hass, auth): # Request a stream for the camera entity to exercise nest cam + camera interaction # and shutdown on url expiration - await camera.async_request_stream(hass, cam.entity_id, "hls") + with patch("homeassistant.components.camera.create_stream") as create_stream: + hls_url = await camera.async_request_stream(hass, "camera.my_camera", fmt="hls") + assert hls_url.startswith("/api/hls/") # Includes access token + assert create_stream.called stream_source = await camera.async_get_stream_source(hass, "camera.my_camera") assert stream_source == "rtsp://some/url?auth=g.1.streamingToken" @@ -273,6 +276,13 @@ async def test_refresh_expired_stream_token(hass, auth): stream_source = await camera.async_get_stream_source(hass, "camera.my_camera") assert stream_source == "rtsp://some/url?auth=g.2.streamingToken" + # HLS stream is not re-created, just the source is updated + with patch("homeassistant.components.camera.create_stream") as create_stream: + hls_url1 = await camera.async_request_stream( + hass, "camera.my_camera", fmt="hls" + ) + assert hls_url == hls_url1 + # Next alarm is well before stream_2_expiration, no change next_update = now + datetime.timedelta(seconds=100) await fire_alarm(hass, next_update) @@ -285,6 +295,13 @@ async def test_refresh_expired_stream_token(hass, auth): stream_source = await camera.async_get_stream_source(hass, "camera.my_camera") assert stream_source == "rtsp://some/url?auth=g.3.streamingToken" + # HLS stream is still not re-created + with patch("homeassistant.components.camera.create_stream") as create_stream: + hls_url2 = await camera.async_request_stream( + hass, "camera.my_camera", fmt="hls" + ) + assert hls_url == hls_url2 + async def test_stream_response_already_expired(hass, auth): """Test a API response returning an expired stream url.""" @@ -363,12 +380,20 @@ async def test_refresh_expired_stream_failure(hass, auth): make_stream_url_response(expiration=stream_2_expiration, token_num=2), ] await async_setup_camera(hass, DEVICE_TRAITS, auth=auth) + assert await async_setup_component(hass, "stream", {}) assert len(hass.states.async_all()) == 1 cam = hass.states.get("camera.my_camera") assert cam is not None assert cam.state == STATE_IDLE + # Request an HLS stream + with patch("homeassistant.components.camera.create_stream") as create_stream: + + hls_url = await camera.async_request_stream(hass, "camera.my_camera", fmt="hls") + assert hls_url.startswith("/api/hls/") # Includes access token + assert create_stream.called + stream_source = await camera.async_get_stream_source(hass, "camera.my_camera") assert stream_source == "rtsp://some/url?auth=g.1.streamingToken" @@ -381,6 +406,16 @@ async def test_refresh_expired_stream_failure(hass, auth): stream_source = await camera.async_get_stream_source(hass, "camera.my_camera") assert stream_source == "rtsp://some/url?auth=g.2.streamingToken" + # Requesting an HLS stream will create an entirely new stream + with patch("homeassistant.components.camera.create_stream") as create_stream: + # The HLS stream endpoint was invalidated, with a new auth token + hls_url2 = await camera.async_request_stream( + hass, "camera.my_camera", fmt="hls" + ) + assert hls_url != hls_url2 + assert hls_url2.startswith("/api/hls/") # Includes access token + assert create_stream.called + async def test_camera_image_from_last_event(hass, auth): """Test an image generated from an event."""