Improve user error messages for generic camera (#112814)

* Generic camera: Insufficient error message when configuration fails
Fixes #112279

* Add tests

* Fix typo in string

* Add new error strings to options flow.

* Group and improve error strings following PR review
This commit is contained in:
Dave T 2024-03-21 21:54:45 +00:00 committed by GitHub
parent 0c791051b8
commit a6d98c1857
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 59 additions and 8 deletions

View File

@ -183,14 +183,27 @@ async def async_test_still(
except (
TimeoutError,
RequestError,
HTTPStatusError,
TimeoutException,
) as err:
_LOGGER.error("Error getting camera image from %s: %s", url, type(err).__name__)
return {CONF_STILL_IMAGE_URL: "unable_still_load"}, None
except HTTPStatusError as err:
_LOGGER.error(
"Error getting camera image from %s: %s %s",
url,
type(err).__name__,
err.response.text,
)
if err.response.status_code in [401, 403]:
return {CONF_STILL_IMAGE_URL: "unable_still_load_auth"}, None
if err.response.status_code in [404]:
return {CONF_STILL_IMAGE_URL: "unable_still_load_not_found"}, None
if err.response.status_code in [500, 503]:
return {CONF_STILL_IMAGE_URL: "unable_still_load_server_error"}, None
return {CONF_STILL_IMAGE_URL: "unable_still_load"}, None
if not image:
return {CONF_STILL_IMAGE_URL: "unable_still_load"}, None
return {CONF_STILL_IMAGE_URL: "unable_still_load_no_image"}, None
fmt = get_image_type(image)
_LOGGER.debug(
"Still image at '%s' detected format: %s",

View File

@ -5,6 +5,10 @@
"unknown": "[%key:common::config_flow::error::unknown%]",
"already_exists": "A camera with these URL settings already exists.",
"unable_still_load": "Unable to load valid image from still image URL (e.g. invalid host, URL or authentication failure). Review log for more info.",
"unable_still_load_auth": "Unable to load valid image from still image URL: The camera may require a user name and password, or they are not correct.",
"unable_still_load_not_found": "Unable to load valid image from still image URL: The URL was not found on the server.",
"unable_still_load_server_error": "Unable to load valid image from still image URL: The camera replied with a server error.",
"unable_still_load_no_image": "Unable to load valid image from still image URL: No image was returned.",
"no_still_image_or_stream_url": "You must specify at least a still image or stream URL",
"invalid_still_image": "URL did not return a valid still image",
"malformed_url": "Malformed URL",
@ -73,6 +77,10 @@
"unknown": "[%key:common::config_flow::error::unknown%]",
"already_exists": "[%key:component::generic::config::error::already_exists%]",
"unable_still_load": "[%key:component::generic::config::error::unable_still_load%]",
"unable_still_load_auth": "[%key:component::generic::config::error::unable_still_load_auth%]",
"unable_still_load_not_found": "[%key:component::generic::config::error::unable_still_load_not_found%]",
"unable_still_load_server_error": "[%key:component::generic::config::error::unable_still_load_server_error%]",
"unable_still_load_no_image": "[%key:component::generic::config::error::unable_still_load_no_image%]",
"no_still_image_or_stream_url": "[%key:component::generic::config::error::no_still_image_or_stream_url%]",
"invalid_still_image": "[%key:component::generic::config::error::invalid_still_image%]",
"malformed_url": "[%key:component::generic::config::error::malformed_url%]",

View File

@ -449,12 +449,42 @@ async def test_form_still_and_stream_not_provided(
@respx.mock
async def test_form_image_timeout(
hass: HomeAssistant, user_flow, mock_create_stream
@pytest.mark.parametrize(
("side_effect", "expected_message"),
[
(httpx.TimeoutException, {"still_image_url": "unable_still_load"}),
(
httpx.HTTPStatusError("", request=None, response=httpx.Response(401)),
{"still_image_url": "unable_still_load_auth"},
),
(
httpx.HTTPStatusError("", request=None, response=httpx.Response(403)),
{"still_image_url": "unable_still_load_auth"},
),
(
httpx.HTTPStatusError("", request=None, response=httpx.Response(404)),
{"still_image_url": "unable_still_load_not_found"},
),
(
httpx.HTTPStatusError("", request=None, response=httpx.Response(500)),
{"still_image_url": "unable_still_load_server_error"},
),
(
httpx.HTTPStatusError("", request=None, response=httpx.Response(503)),
{"still_image_url": "unable_still_load_server_error"},
),
( # Errors without specific handler should show the general message.
httpx.HTTPStatusError("", request=None, response=httpx.Response(507)),
{"still_image_url": "unable_still_load"},
),
],
)
async def test_form_image_http_exceptions(
side_effect, expected_message, hass: HomeAssistant, user_flow, mock_create_stream
) -> None:
"""Test we handle invalid image timeout."""
"""Test we handle image http exceptions."""
respx.get("http://127.0.0.1/testurl/1").side_effect = [
httpx.TimeoutException,
side_effect,
]
with mock_create_stream:
@ -465,7 +495,7 @@ async def test_form_image_timeout(
await hass.async_block_till_done()
assert result2["type"] == "form"
assert result2["errors"] == {"still_image_url": "unable_still_load"}
assert result2["errors"] == expected_message
@respx.mock
@ -499,7 +529,7 @@ async def test_form_stream_invalidimage2(
await hass.async_block_till_done()
assert result2["type"] == "form"
assert result2["errors"] == {"still_image_url": "unable_still_load"}
assert result2["errors"] == {"still_image_url": "unable_still_load_no_image"}
@respx.mock