core/homeassistant/helpers/data_entry_flow.py

139 lines
4.6 KiB
Python
Raw Normal View History

"""Helpers for the data entry flow."""
2021-03-17 17:34:19 +00:00
from __future__ import annotations
from http import HTTPStatus
2025-01-14 15:23:15 +00:00
from typing import Any, Generic, TypeVar
from aiohttp import web
import voluptuous as vol
2022-03-16 21:14:50 +00:00
import voluptuous_serialize
from homeassistant import data_entry_flow
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http.data_validator import RequestDataValidator
from . import config_validation as cv
_FlowManagerT = TypeVar(
"_FlowManagerT",
Merge config subentry feature branch to dev (#136121) * Reapply "Add support for subentries to config entries" (#133470) (#136061) * Reapply "Add support for subentries to config entries" (#133470) This reverts commit ecb3bf79f32a2e25d141ff467e5958826ed9fc3a. * Update test snapshot * Add config subentry support to device registry (#128157) * Add config subentry support to device registry * Apply suggestions from code review * Update syrupy serializer * Update snapshots * Address review comments * Allow a device to be connected to no or a single subentry of a config entry * Update snapshots * Revert "Allow a device to be connected to no or a single subentry of a config entry" This reverts commit ec6f613151cb4a806b7961033c004b71b76510c2. * Update test snapshots * Bump release version in comments * Rename config_subentries to config_entries_subentries * Add config subentry support to entity registry (#128155) * Add config subentry support to entity registry * Update syrupy serializer * Update snapshots * Update snapshots * Accept suggested changes * Clean registries when removing subentry (#136671) * Clean up registries when removing subentry * Update tests * Clean up subentries from deleted devices when removing config entry (#136669) * Clean up subentries from deleted devices when removing config entry * Move * Add config subentry support to entity platform (#128161) * Add config subentry support to entity platform * Rename subentry_id to config_subentry_id * Store subentry type in subentry (#136687) * Add reconfigure support to config subentries (#133353) * Add reconfigure support to config subentries * Update test * Minor adjustment * Rename supported_subentry_flows to supported_subentry_types * Address review comments * Add subentry support to kitchen sink (#136755) * Add subentry support to kitchen sink * Add subentry reconfigure support to kitchen_sink * Update kitchen_sink tests with subentry type stored in config entry * Update kitchen_sink * Update kitchen_sink * Adjust kitchen sink tests * Fix hassfest * Apply suggestions from code review Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Improve docstrings and strings.json --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update snapshots * Update snapshots * Update snapshots * Update snapshots * Update snapshots * Update snapshots * Update snapshots --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-02-10 15:40:07 +00:00
bound=data_entry_flow.FlowManager[Any, Any, Any],
default=data_entry_flow.FlowManager,
)
2019-07-31 19:25:30 +00:00
class _BaseFlowManagerView(HomeAssistantView, Generic[_FlowManagerT]):
"""Foundation for flow manager views."""
def __init__(self, flow_mgr: _FlowManagerT) -> None:
"""Initialize the flow manager index view."""
self._flow_mgr = flow_mgr
def _prepare_result_json(
self, result: data_entry_flow.FlowResult
) -> data_entry_flow.FlowResult:
"""Convert result to JSON."""
if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY:
data = result.copy()
2019-07-31 19:25:30 +00:00
data.pop("result")
data.pop("data")
data.pop("context")
return data
2022-03-16 21:14:50 +00:00
if "data_schema" not in result:
return result
data = result.copy()
2021-10-17 18:08:11 +00:00
if (schema := data["data_schema"]) is None:
data["data_schema"] = [] # type: ignore[typeddict-item] # json result type
else:
2020-02-15 23:36:57 +00:00
data["data_schema"] = voluptuous_serialize.convert(
schema, custom_serializer=cv.custom_serializer
)
return data
class FlowManagerIndexView(_BaseFlowManagerView[_FlowManagerT]):
"""View to create config flows."""
2019-07-31 19:25:30 +00:00
@RequestDataValidator(
vol.Schema(
{
vol.Required("handler"): str,
vol.Optional("show_advanced_options", default=False): cv.boolean,
},
extra=vol.ALLOW_EXTRA,
)
2019-07-31 19:25:30 +00:00
)
2021-03-17 17:34:19 +00:00
async def post(self, request: web.Request, data: dict[str, Any]) -> web.Response:
"""Initialize a POST request.
Merge config subentry feature branch to dev (#136121) * Reapply "Add support for subentries to config entries" (#133470) (#136061) * Reapply "Add support for subentries to config entries" (#133470) This reverts commit ecb3bf79f32a2e25d141ff467e5958826ed9fc3a. * Update test snapshot * Add config subentry support to device registry (#128157) * Add config subentry support to device registry * Apply suggestions from code review * Update syrupy serializer * Update snapshots * Address review comments * Allow a device to be connected to no or a single subentry of a config entry * Update snapshots * Revert "Allow a device to be connected to no or a single subentry of a config entry" This reverts commit ec6f613151cb4a806b7961033c004b71b76510c2. * Update test snapshots * Bump release version in comments * Rename config_subentries to config_entries_subentries * Add config subentry support to entity registry (#128155) * Add config subentry support to entity registry * Update syrupy serializer * Update snapshots * Update snapshots * Accept suggested changes * Clean registries when removing subentry (#136671) * Clean up registries when removing subentry * Update tests * Clean up subentries from deleted devices when removing config entry (#136669) * Clean up subentries from deleted devices when removing config entry * Move * Add config subentry support to entity platform (#128161) * Add config subentry support to entity platform * Rename subentry_id to config_subentry_id * Store subentry type in subentry (#136687) * Add reconfigure support to config subentries (#133353) * Add reconfigure support to config subentries * Update test * Minor adjustment * Rename supported_subentry_flows to supported_subentry_types * Address review comments * Add subentry support to kitchen sink (#136755) * Add subentry support to kitchen sink * Add subentry reconfigure support to kitchen_sink * Update kitchen_sink tests with subentry type stored in config entry * Update kitchen_sink * Update kitchen_sink * Adjust kitchen sink tests * Fix hassfest * Apply suggestions from code review Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Improve docstrings and strings.json --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com> * Update snapshots * Update snapshots * Update snapshots * Update snapshots * Update snapshots * Update snapshots * Update snapshots --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
2025-02-10 15:40:07 +00:00
Override `post` and call `_post_impl` in subclasses which need
to implement their own `RequestDataValidator`
"""
return await self._post_impl(request, data)
async def _post_impl(
self, request: web.Request, data: dict[str, Any]
) -> web.Response:
"""Handle a POST request."""
try:
result = await self._flow_mgr.async_init(
data["handler"],
context=self.get_context(data),
2019-07-31 19:25:30 +00:00
)
except data_entry_flow.UnknownHandler:
return self.json_message("Invalid handler specified", HTTPStatus.NOT_FOUND)
except data_entry_flow.UnknownStep as err:
return self.json_message(str(err), HTTPStatus.BAD_REQUEST)
result = self._prepare_result_json(result)
return self.json(result)
def get_context(self, data: dict[str, Any]) -> dict[str, Any]:
"""Return context."""
return {"show_advanced_options": data["show_advanced_options"]}
class FlowManagerResourceView(_BaseFlowManagerView[_FlowManagerT]):
"""View to interact with the flow manager."""
async def get(self, request: web.Request, /, flow_id: str) -> web.Response:
"""Get the current state of a data_entry_flow."""
try:
result = await self._flow_mgr.async_configure(flow_id)
except data_entry_flow.UnknownFlow:
return self.json_message("Invalid flow specified", HTTPStatus.NOT_FOUND)
result = self._prepare_result_json(result)
return self.json(result)
@RequestDataValidator(vol.Schema(dict), allow_empty=True)
async def post(
2022-07-21 11:07:42 +00:00
self, request: web.Request, data: dict[str, Any], flow_id: str
) -> web.Response:
"""Handle a POST request."""
try:
result = await self._flow_mgr.async_configure(flow_id, data)
except data_entry_flow.UnknownFlow:
return self.json_message("Invalid flow specified", HTTPStatus.NOT_FOUND)
except data_entry_flow.InvalidData as ex:
return self.json({"errors": ex.schema_errors}, HTTPStatus.BAD_REQUEST)
result = self._prepare_result_json(result)
return self.json(result)
async def delete(self, request: web.Request, flow_id: str) -> web.Response:
"""Cancel a flow in progress."""
try:
self._flow_mgr.async_abort(flow_id)
except data_entry_flow.UnknownFlow:
return self.json_message("Invalid flow specified", HTTPStatus.NOT_FOUND)
2019-07-31 19:25:30 +00:00
return self.json_message("Flow aborted")