Add visual image preview during generic camera options flow (#80392)
Co-authored-by: Dave T <davet2001@users.noreply.github.com>pull/80936/head
parent
6b1f503a79
commit
e5716efa9c
|
@ -394,8 +394,7 @@ class GenericOptionsFlowHandler(OptionsFlow):
|
|||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
"""Initialize Generic IP Camera options flow."""
|
||||
self.config_entry = config_entry
|
||||
self.cached_user_input: dict[str, Any] = {}
|
||||
self.cached_title = ""
|
||||
self.user_input: dict[str, Any] = {}
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
|
@ -410,9 +409,7 @@ class GenericOptionsFlowHandler(OptionsFlow):
|
|||
)
|
||||
errors = errors | await async_test_stream(hass, user_input)
|
||||
still_url = user_input.get(CONF_STILL_IMAGE_URL)
|
||||
stream_url = user_input.get(CONF_STREAM_SOURCE)
|
||||
if not errors:
|
||||
title = slug(hass, still_url) or slug(hass, stream_url) or DEFAULT_NAME
|
||||
if still_url is None:
|
||||
# If user didn't specify a still image URL,
|
||||
# The automatically generated still image that stream generates
|
||||
|
@ -438,10 +435,10 @@ class GenericOptionsFlowHandler(OptionsFlow):
|
|||
),
|
||||
),
|
||||
}
|
||||
return self.async_create_entry(
|
||||
title=title,
|
||||
data=data,
|
||||
)
|
||||
self.user_input = data
|
||||
# temporary preview for user to check the image
|
||||
self.context["preview_cam"] = data
|
||||
return await self.async_step_confirm_still()
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=build_schema(
|
||||
|
@ -452,6 +449,30 @@ class GenericOptionsFlowHandler(OptionsFlow):
|
|||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_confirm_still(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle user clicking confirm after still preview."""
|
||||
if user_input:
|
||||
if not user_input.get(CONF_CONFIRMED_OK):
|
||||
return await self.async_step_init()
|
||||
return self.async_create_entry(
|
||||
title=self.config_entry.title,
|
||||
data=self.user_input,
|
||||
)
|
||||
register_preview(self.hass)
|
||||
preview_url = f"/api/generic/preview_flow_image/{self.flow_id}?t={datetime.now().isoformat()}"
|
||||
return self.async_show_form(
|
||||
step_id="confirm_still",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_CONFIRMED_OK, default=False): bool,
|
||||
}
|
||||
),
|
||||
description_placeholders={"preview_url": preview_url},
|
||||
errors=None,
|
||||
)
|
||||
|
||||
|
||||
class CameraImagePreview(HomeAssistantView):
|
||||
"""Camera view to temporarily serve an image."""
|
||||
|
@ -468,9 +489,13 @@ class CameraImagePreview(HomeAssistantView):
|
|||
"""Start a GET request."""
|
||||
_LOGGER.debug("processing GET request for flow_id=%s", flow_id)
|
||||
try:
|
||||
flow: FlowResult = self.hass.config_entries.flow.async_get(flow_id)
|
||||
except UnknownFlow as exc:
|
||||
raise web.HTTPNotFound() from exc
|
||||
flow = self.hass.config_entries.flow.async_get(flow_id)
|
||||
except UnknownFlow:
|
||||
try:
|
||||
flow = self.hass.config_entries.options.async_get(flow_id)
|
||||
except UnknownFlow as exc:
|
||||
_LOGGER.warning("Unknown flow while getting image preview")
|
||||
raise web.HTTPNotFound() from exc
|
||||
user_input = flow["context"]["preview_cam"]
|
||||
camera = GenericCamera(self.hass, user_input, flow_id, "preview")
|
||||
if not camera.is_on:
|
||||
|
|
|
@ -73,6 +73,13 @@
|
|||
"data": {
|
||||
"content_type": "[%key:component::generic::config::step::content_type::data::content_type%]"
|
||||
}
|
||||
},
|
||||
"confirm_still": {
|
||||
"title": "[%key:component::generic::config::step::user_confirm_still::title%]",
|
||||
"description": "[%key:component::generic::config::step::user_confirm_still::description%]",
|
||||
"data": {
|
||||
"confirmed_ok": "[%key:component::generic::config::step::user_confirm_still::data::confirmed_ok%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
|
|
|
@ -596,7 +596,13 @@ async def test_options_template_error(hass, fakeimgbytes_png, mock_create_stream
|
|||
result["flow_id"],
|
||||
user_input=data,
|
||||
)
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result2["step_id"] == "confirm_still"
|
||||
|
||||
result2a = await hass.config_entries.options.async_configure(
|
||||
result2["flow_id"], user_input={CONF_CONFIRMED_OK: True}
|
||||
)
|
||||
assert result2a["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
|
||||
result3 = await hass.config_entries.options.async_init(mock_entry.entry_id)
|
||||
assert result3["type"] == data_entry_flow.FlowResultType.FORM
|
||||
|
@ -681,10 +687,16 @@ async def test_options_only_stream(hass, fakeimgbytes_png, mock_create_stream):
|
|||
|
||||
# try updating the config options
|
||||
with mock_create_stream:
|
||||
result3 = await hass.config_entries.options.async_configure(
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=data,
|
||||
)
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result2["step_id"] == "confirm_still"
|
||||
|
||||
result3 = await hass.config_entries.options.async_configure(
|
||||
result2["flow_id"], user_input={CONF_CONFIRMED_OK: True}
|
||||
)
|
||||
assert result3["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result3["data"][CONF_CONTENT_TYPE] == "image/jpeg"
|
||||
|
||||
|
@ -809,4 +821,24 @@ async def test_use_wallclock_as_timestamps_option(
|
|||
result["flow_id"],
|
||||
user_input={CONF_USE_WALLCLOCK_AS_TIMESTAMPS: True, **TESTDATA},
|
||||
)
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
assert result2["type"] == data_entry_flow.FlowResultType.FORM
|
||||
# Test what happens if user rejects the preview
|
||||
result3 = await hass.config_entries.options.async_configure(
|
||||
result2["flow_id"], user_input={CONF_CONFIRMED_OK: False}
|
||||
)
|
||||
assert result3["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result3["step_id"] == "init"
|
||||
with patch(
|
||||
"homeassistant.components.generic.async_setup_entry", return_value=True
|
||||
), mock_create_stream:
|
||||
result4 = await hass.config_entries.options.async_configure(
|
||||
result3["flow_id"],
|
||||
user_input={CONF_USE_WALLCLOCK_AS_TIMESTAMPS: True, **TESTDATA},
|
||||
)
|
||||
assert result4["type"] == data_entry_flow.FlowResultType.FORM
|
||||
assert result4["step_id"] == "confirm_still"
|
||||
result5 = await hass.config_entries.options.async_configure(
|
||||
result4["flow_id"],
|
||||
user_input={CONF_CONFIRMED_OK: True},
|
||||
)
|
||||
assert result5["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
|
||||
|
|
Loading…
Reference in New Issue