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
import httpx
import voluptuous as vol
import yarl
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)
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:
return self._last_image

View file

@ -70,15 +70,20 @@ async def help_setup_mock_config_entry(
@respx.mock
async def test_fetching_url(
hass: HomeAssistant, hass_client: ClientSessionGenerator, fakeimgbytes_png
hass: HomeAssistant,
hass_client: ClientSessionGenerator,
fakeimgbytes_png,
caplog: pytest.CaptureFixture,
) -> None:
"""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 = {
"name": "config_test",
"platform": "generic",
"still_image_url": "http://example.com",
"still_image_url": "{{ states.sensor.temp.state }}",
"username": "user",
"password": "pass",
"authentication": "basic",
@ -101,6 +106,25 @@ async def test_fetching_url(
resp = await client.get("/api/camera_proxy/camera.config_test")
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
async def test_image_caching(