mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Add data entry flow show progress step (#42419)
This commit is contained in:
parent
3380b69d54
commit
1338c4a425
@ -14,8 +14,10 @@ RESULT_TYPE_CREATE_ENTRY = "create_entry"
|
||||
RESULT_TYPE_ABORT = "abort"
|
||||
RESULT_TYPE_EXTERNAL_STEP = "external"
|
||||
RESULT_TYPE_EXTERNAL_STEP_DONE = "external_done"
|
||||
RESULT_TYPE_SHOW_PROGRESS = "progress"
|
||||
RESULT_TYPE_SHOW_PROGRESS_DONE = "progress_done"
|
||||
|
||||
# Event that is fired when a flow is progressed via external source.
|
||||
# Event that is fired when a flow is progressed via external or progress source.
|
||||
EVENT_DATA_ENTRY_FLOW_PROGRESSED = "data_entry_flow_progressed"
|
||||
|
||||
|
||||
@ -152,8 +154,8 @@ class FlowManager(abc.ABC):
|
||||
|
||||
result = await self._async_handle_step(flow, cur_step["step_id"], user_input)
|
||||
|
||||
if cur_step["type"] == RESULT_TYPE_EXTERNAL_STEP:
|
||||
if result["type"] not in (
|
||||
if cur_step["type"] in (RESULT_TYPE_EXTERNAL_STEP, RESULT_TYPE_SHOW_PROGRESS):
|
||||
if cur_step["type"] == RESULT_TYPE_EXTERNAL_STEP and result["type"] not in (
|
||||
RESULT_TYPE_EXTERNAL_STEP,
|
||||
RESULT_TYPE_EXTERNAL_STEP_DONE,
|
||||
):
|
||||
@ -161,10 +163,20 @@ class FlowManager(abc.ABC):
|
||||
"External step can only transition to "
|
||||
"external step or external step done."
|
||||
)
|
||||
if cur_step["type"] == RESULT_TYPE_SHOW_PROGRESS and result["type"] not in (
|
||||
RESULT_TYPE_SHOW_PROGRESS,
|
||||
RESULT_TYPE_SHOW_PROGRESS_DONE,
|
||||
):
|
||||
raise ValueError(
|
||||
"Show progress can only transition to show progress or show progress done."
|
||||
)
|
||||
|
||||
# If the result has changed from last result, fire event to update
|
||||
# the frontend.
|
||||
if cur_step["step_id"] != result.get("step_id"):
|
||||
if (
|
||||
cur_step["step_id"] != result.get("step_id")
|
||||
or result["type"] == RESULT_TYPE_SHOW_PROGRESS
|
||||
):
|
||||
# Tell frontend to reload the flow state.
|
||||
self.hass.bus.async_fire(
|
||||
EVENT_DATA_ENTRY_FLOW_PROGRESSED,
|
||||
@ -217,6 +229,8 @@ class FlowManager(abc.ABC):
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_EXTERNAL_STEP_DONE,
|
||||
RESULT_TYPE_SHOW_PROGRESS,
|
||||
RESULT_TYPE_SHOW_PROGRESS_DONE,
|
||||
):
|
||||
raise ValueError(f"Handler returned incorrect type: {result['type']}")
|
||||
|
||||
@ -224,6 +238,8 @@ class FlowManager(abc.ABC):
|
||||
RESULT_TYPE_FORM,
|
||||
RESULT_TYPE_EXTERNAL_STEP,
|
||||
RESULT_TYPE_EXTERNAL_STEP_DONE,
|
||||
RESULT_TYPE_SHOW_PROGRESS,
|
||||
RESULT_TYPE_SHOW_PROGRESS_DONE,
|
||||
):
|
||||
flow.cur_step = result
|
||||
return result
|
||||
@ -348,6 +364,34 @@ class FlowHandler:
|
||||
"step_id": next_step_id,
|
||||
}
|
||||
|
||||
@callback
|
||||
def async_show_progress(
|
||||
self,
|
||||
*,
|
||||
step_id: str,
|
||||
progress_action: str,
|
||||
description_placeholders: Optional[Dict] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""Show a progress message to the user, without user input allowed."""
|
||||
return {
|
||||
"type": RESULT_TYPE_SHOW_PROGRESS,
|
||||
"flow_id": self.flow_id,
|
||||
"handler": self.handler,
|
||||
"step_id": step_id,
|
||||
"progress_action": progress_action,
|
||||
"description_placeholders": description_placeholders,
|
||||
}
|
||||
|
||||
@callback
|
||||
def async_show_progress_done(self, *, next_step_id: str) -> Dict[str, Any]:
|
||||
"""Mark the progress done."""
|
||||
return {
|
||||
"type": RESULT_TYPE_SHOW_PROGRESS_DONE,
|
||||
"flow_id": self.flow_id,
|
||||
"handler": self.handler,
|
||||
"step_id": next_step_id,
|
||||
}
|
||||
|
||||
|
||||
@callback
|
||||
def _create_abort_data(
|
||||
|
@ -285,6 +285,76 @@ async def test_external_step(hass, manager):
|
||||
assert result["title"] == "Hello"
|
||||
|
||||
|
||||
async def test_show_progress(hass, manager):
|
||||
"""Test show progress logic."""
|
||||
manager.hass = hass
|
||||
|
||||
@manager.mock_reg_handler("test")
|
||||
class TestFlow(data_entry_flow.FlowHandler):
|
||||
VERSION = 5
|
||||
data = None
|
||||
task_one_done = False
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
if not user_input:
|
||||
if not self.task_one_done:
|
||||
self.task_one_done = True
|
||||
progress_action = "task_one"
|
||||
else:
|
||||
progress_action = "task_two"
|
||||
return self.async_show_progress(
|
||||
step_id="init",
|
||||
progress_action=progress_action,
|
||||
)
|
||||
|
||||
self.data = user_input
|
||||
return self.async_show_progress_done(next_step_id="finish")
|
||||
|
||||
async def async_step_finish(self, user_input=None):
|
||||
return self.async_create_entry(title=self.data["title"], data=self.data)
|
||||
|
||||
events = async_capture_events(
|
||||
hass, data_entry_flow.EVENT_DATA_ENTRY_FLOW_PROGRESSED
|
||||
)
|
||||
|
||||
result = await manager.async_init("test")
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS
|
||||
assert result["progress_action"] == "task_one"
|
||||
assert len(manager.async_progress()) == 1
|
||||
|
||||
# Mimic task one done and moving to task two
|
||||
# Called by integrations: `hass.config_entries.flow.async_configure(…)`
|
||||
result = await manager.async_configure(result["flow_id"])
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS
|
||||
assert result["progress_action"] == "task_two"
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(events) == 1
|
||||
assert events[0].data == {
|
||||
"handler": "test",
|
||||
"flow_id": result["flow_id"],
|
||||
"refresh": True,
|
||||
}
|
||||
|
||||
# Mimic task two done and continuing step
|
||||
# Called by integrations: `hass.config_entries.flow.async_configure(…)`
|
||||
result = await manager.async_configure(result["flow_id"], {"title": "Hello"})
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_SHOW_PROGRESS_DONE
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(events) == 2
|
||||
assert events[1].data == {
|
||||
"handler": "test",
|
||||
"flow_id": result["flow_id"],
|
||||
"refresh": True,
|
||||
}
|
||||
|
||||
# Frontend refreshes the flow
|
||||
result = await manager.async_configure(result["flow_id"])
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "Hello"
|
||||
|
||||
|
||||
async def test_abort_flow_exception(manager):
|
||||
"""Test that the AbortFlow exception works."""
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user