From ee37bc476fde4d34acce0a438228e0fbb75e0062 Mon Sep 17 00:00:00 2001 From: Nathan Spencer Date: Sat, 1 Feb 2025 05:53:04 -0700 Subject: [PATCH] Raise HomeAssistantError from camera snapshot service (#137051) * Raise HomeAssistantError from camera snapshot service * Improve error message --------- Co-authored-by: Martin Hjelmare --- homeassistant/components/camera/__init__.py | 19 ++++++++++++------- tests/components/camera/test_init.py | 21 +++++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 556f8d75fc4..aa5d766c874 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -1175,12 +1175,17 @@ async def async_handle_snapshot_service( f"Cannot write `{snapshot_file}`, no access to path; `allowlist_external_dirs` may need to be adjusted in `configuration.yaml`" ) - async with asyncio.timeout(CAMERA_IMAGE_TIMEOUT): - image = ( - await _async_get_stream_image(camera, wait_for_next_keyframe=True) - if camera.use_stream_for_stills - else await camera.async_camera_image() - ) + try: + async with asyncio.timeout(CAMERA_IMAGE_TIMEOUT): + image = ( + await _async_get_stream_image(camera, wait_for_next_keyframe=True) + if camera.use_stream_for_stills + else await camera.async_camera_image() + ) + except TimeoutError as err: + raise HomeAssistantError( + f"Unable to get snapshot: Timed out after {CAMERA_IMAGE_TIMEOUT} seconds" + ) from err if image is None: return @@ -1194,7 +1199,7 @@ async def async_handle_snapshot_service( try: await hass.async_add_executor_job(_write_image, snapshot_file, image) except OSError as err: - _LOGGER.error("Can't write image to file: %s", err) + raise HomeAssistantError(f"Can't write image to file: {err}") from err async def async_handle_play_stream_service( diff --git a/tests/components/camera/test_init.py b/tests/components/camera/test_init.py index 5a26e3b44f6..7fd469fa51a 100644 --- a/tests/components/camera/test_init.py +++ b/tests/components/camera/test_init.py @@ -301,13 +301,24 @@ async def test_snapshot_service_not_allowed_path(hass: HomeAssistant) -> None: @pytest.mark.usefixtures("mock_camera") -async def test_snapshot_service_os_error( - hass: HomeAssistant, caplog: pytest.LogCaptureFixture +@pytest.mark.parametrize( + ("target", "side_effect"), + [ + ("homeassistant.components.camera.os.makedirs", OSError), + ( + "homeassistant.components.demo.camera.DemoCamera.async_camera_image", + TimeoutError, + ), + ], +) +async def test_snapshot_service_error( + hass: HomeAssistant, target: str, side_effect: Exception ) -> None: - """Test snapshot service with os error.""" + """Test snapshot service with error.""" with ( patch.object(hass.config, "is_allowed_path", return_value=True), - patch("homeassistant.components.camera.os.makedirs", side_effect=OSError), + patch(target, side_effect=side_effect), + pytest.raises(HomeAssistantError), ): await hass.services.async_call( camera.DOMAIN, @@ -319,8 +330,6 @@ async def test_snapshot_service_os_error( blocking=True, ) - assert "Can't write image to file:" in caplog.text - @pytest.mark.usefixtures("mock_camera", "mock_stream") async def test_websocket_stream_no_source(