Fix generic camera error when template renders to an invalid URL (#109737)

This commit is contained in:
Jan Bouwhuis 2024-02-05 20:19:38 +01:00 committed by GitHub
parent 45f44e9216
commit 94ccd59123
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 3 deletions

View File

@ -8,6 +8,7 @@ import logging
from typing import Any from typing import Any
import httpx import httpx
import voluptuous as vol
import yarl import yarl
from homeassistant.components.camera import Camera, CameraEntityFeature from homeassistant.components.camera import Camera, CameraEntityFeature
@ -140,6 +141,12 @@ class GenericCamera(Camera):
_LOGGER.error("Error parsing template %s: %s", self._still_image_url, err) _LOGGER.error("Error parsing template %s: %s", self._still_image_url, err)
return self._last_image return self._last_image
try:
vol.Schema(vol.Url())(url)
except vol.Invalid as err:
_LOGGER.warning("Invalid URL '%s': %s, returning last image", url, err)
return self._last_image
if url == self._last_url and self._limit_refetch: if url == self._last_url and self._limit_refetch:
return self._last_image return self._last_image

View File

@ -70,15 +70,20 @@ async def help_setup_mock_config_entry(
@respx.mock @respx.mock
async def test_fetching_url( async def test_fetching_url(
hass: HomeAssistant, hass_client: ClientSessionGenerator, fakeimgbytes_png hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fakeimgbytes_png,
caplog: pytest.CaptureFixture,
) -> None: ) -> None:
"""Test that it fetches the given url.""" """Test that it fetches the given url."""
respx.get("http://example.com").respond(stream=fakeimgbytes_png) hass.states.async_set("sensor.temp", "http://example.com/0a")
respx.get("http://example.com/0a").respond(stream=fakeimgbytes_png)
respx.get("http://example.com/1a").respond(stream=fakeimgbytes_png)
options = { options = {
"name": "config_test", "name": "config_test",
"platform": "generic", "platform": "generic",
"still_image_url": "http://example.com", "still_image_url": "{{ states.sensor.temp.state }}",
"username": "user", "username": "user",
"password": "pass", "password": "pass",
"authentication": "basic", "authentication": "basic",
@ -101,6 +106,25 @@ async def test_fetching_url(
resp = await client.get("/api/camera_proxy/camera.config_test") resp = await client.get("/api/camera_proxy/camera.config_test")
assert respx.calls.call_count == 2 assert respx.calls.call_count == 2
# If the template renders to an invalid URL we return the last image from cache
hass.states.async_set("sensor.temp", "invalid url")
# sleep another .1 seconds to make cached image expire
await asyncio.sleep(0.1)
resp = await client.get("/api/camera_proxy/camera.config_test")
assert resp.status == HTTPStatus.OK
assert respx.calls.call_count == 2
assert (
"Invalid URL 'invalid url': expected a URL, returning last image" in caplog.text
)
# Restore a valid URL
hass.states.async_set("sensor.temp", "http://example.com/1a")
await asyncio.sleep(0.1)
resp = await client.get("/api/camera_proxy/camera.config_test")
assert resp.status == HTTPStatus.OK
assert respx.calls.call_count == 3
@respx.mock @respx.mock
async def test_image_caching( async def test_image_caching(