1 """Classes to help gather user submissions."""
3 from __future__
import annotations
7 from collections
import defaultdict
8 from collections.abc
import Callable, Container, Hashable, Iterable, Mapping
9 from contextlib
import suppress
11 from dataclasses
import dataclass
12 from enum
import StrEnum
13 from functools
import partial
15 from types
import MappingProxyType
16 from typing
import Any, Generic, Required, TypedDict, cast
18 from typing_extensions
import TypeVar
19 import voluptuous
as vol
21 from .core
import HomeAssistant, callback
22 from .exceptions
import HomeAssistantError
23 from .helpers.deprecation
import (
24 DeprecatedConstantEnum,
25 all_with_deprecated_constants,
26 check_if_deprecated_constant,
27 dir_with_deprecated_constants,
29 from .helpers.frame
import ReportBehavior, report_usage
30 from .loader
import async_suggest_report_issue
31 from .util
import uuid
as uuid_util
33 _LOGGER = logging.getLogger(__name__)
37 """Result type for a data entry flow."""
40 CREATE_ENTRY =
"create_entry"
42 EXTERNAL_STEP =
"external"
43 EXTERNAL_STEP_DONE =
"external_done"
44 SHOW_PROGRESS =
"progress"
45 SHOW_PROGRESS_DONE =
"progress_done"
52 FlowResultType.CREATE_ENTRY,
"2025.1"
56 FlowResultType.EXTERNAL_STEP,
"2025.1"
59 FlowResultType.EXTERNAL_STEP_DONE,
"2025.1"
62 FlowResultType.SHOW_PROGRESS,
"2025.1"
65 FlowResultType.SHOW_PROGRESS_DONE,
"2025.1"
70 EVENT_DATA_ENTRY_FLOW_PROGRESSED =
"data_entry_flow_progressed"
72 FLOW_NOT_COMPLETE_STEPS = {
74 FlowResultType.EXTERNAL_STEP,
75 FlowResultType.EXTERNAL_STEP_DONE,
76 FlowResultType.SHOW_PROGRESS,
77 FlowResultType.SHOW_PROGRESS_DONE,
82 STEP_ID_OPTIONAL_STEPS = {
83 FlowResultType.EXTERNAL_STEP,
86 FlowResultType.SHOW_PROGRESS,
90 _FlowContextT = TypeVar(
"_FlowContextT", bound=
"FlowContext", default=
"FlowContext")
91 _FlowResultT = TypeVar(
92 "_FlowResultT", bound=
"FlowResult[Any, Any]", default=
"FlowResult"
94 _HandlerT = TypeVar(
"_HandlerT", default=str)
97 @dataclass(slots=True)
99 """Base class for discovery ServiceInfo."""
102 class FlowError(HomeAssistantError):
103 """Base class for data entry errors."""
107 """Unknown handler specified."""
111 """Unknown flow specified."""
115 """Unknown step specified."""
119 """Invalid data provided."""
124 path: list[Hashable] |
None,
125 error_message: str |
None,
126 schema_errors: dict[str, Any],
129 super().
__init__(message, path, error_message, **kwargs)
134 """Exception to indicate a flow needs to be aborted."""
137 self, reason: str, description_placeholders: Mapping[str, str] |
None =
None
139 """Initialize an abort flow exception."""
140 super().
__init__(f
"Flow aborted: {reason}")
146 """Typed context dict."""
148 show_advanced_options: bool
152 class FlowResult(TypedDict, Generic[_FlowContextT, _HandlerT], total=
False):
153 """Typed result dict."""
155 context: _FlowContextT
156 data_schema: vol.Schema |
None
157 data: Mapping[str, Any]
158 description_placeholders: Mapping[str, str] |
None
159 description: str |
None
160 errors: dict[str, str] |
None
162 flow_id: Required[str]
163 handler: Required[_HandlerT]
164 last_step: bool |
None
165 menu_options: Container[str]
168 progress_task: asyncio.Task[Any] |
None
174 translation_domain: str
180 schema_errors: dict[str, Any],
182 data_schema: vol.Schema,
184 """Map an error to the correct position in the schema_errors.
186 Raises ValueError if the error path could not be found in the schema.
187 Limitation: Nested schemas are not supported and a ValueError will be raised.
189 schema = data_schema.schema
190 error_path = error.path
191 if not error_path
or (path_part := error_path[0])
not in schema:
192 raise ValueError(
"Could not find path in schema")
194 if len(error_path) > 1:
195 raise ValueError(
"Nested schemas are not supported")
198 path_part_str =
str(path_part)
199 schema_errors[path_part_str] = error.error_message
202 class FlowManager(abc.ABC, Generic[_FlowContextT, _FlowResultT, _HandlerT]):
203 """Manage all the flows that are in progress."""
205 _flow_result: type[_FlowResultT] = FlowResult
211 """Initialize the flow manager."""
213 self._preview: set[_HandlerT] = set()
214 self._progress: dict[
215 str, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]
217 self._handler_progress_index: defaultdict[
218 _HandlerT, set[FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]]
220 self._init_data_process_index: defaultdict[
221 type, set[FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]]
227 handler_key: _HandlerT,
229 context: _FlowContextT |
None =
None,
230 data: dict[str, Any] |
None =
None,
231 ) -> FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]:
232 """Create a flow for specified handler.
234 Handler key is the domain of the component that we want to set up.
240 flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT],
241 result: _FlowResultT,
243 """Finish a data entry flow.
245 This method is called when a flow step returns FlowResultType.ABORT or
246 FlowResultType.CREATE_ENTRY.
251 flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT],
252 result: _FlowResultT,
254 """Entry has finished executing its first step asynchronously."""
258 """Return a flow in progress as a partial FlowResult."""
259 if (flow := self._progress.
get(flow_id))
is None:
264 def async_progress(self, include_uninitialized: bool =
False) -> list[_FlowResultT]:
265 """Return the flows in progress as a partial FlowResult."""
267 self._progress.values(), include_uninitialized
274 include_uninitialized: bool =
False,
275 match_context: dict[str, Any] |
None =
None,
276 ) -> list[_FlowResultT]:
277 """Return the flows in progress by handler as a partial FlowResult.
279 If match_context is specified, only return flows with a context that
280 is a superset of match_context.
284 include_uninitialized,
290 init_data_type: type,
291 matcher: Callable[[Any], bool],
292 include_uninitialized: bool =
False,
293 ) -> list[_FlowResultT]:
294 """Return flows in progress init matching by data type as a partial FlowResult."""
298 for progress
in self._init_data_process_index.
get(init_data_type, ())
299 if matcher(progress.init_data)
301 include_uninitialized,
306 self, handler: _HandlerT, match_context: dict[str, Any] |
None
307 ) -> list[FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]]:
308 """Return the flows in progress by handler.
310 If match_context is specified, only return flows with a context that
311 is a superset of match_context.
313 if not match_context:
314 return list(self._handler_progress_index.
get(handler, ()))
315 match_context_items = match_context.items()
318 for progress
in self._handler_progress_index.
get(handler, ())
319 if match_context_items <= progress.context.items()
326 context: _FlowContextT |
None =
None,
329 """Start a data entry flow."""
331 context = cast(_FlowContextT, {})
332 flow = await self.
async_create_flowasync_create_flow(handler, context=context, data=data)
335 flow.hass = self.
hasshass
336 flow.handler = handler
337 flow.flow_id = uuid_util.random_uuid_hex()
338 flow.context = context
339 flow.init_data = data
344 if result[
"type"] != FlowResultType.ABORT:
350 self, flow_id: str, user_input: dict |
None =
None
352 """Continue a data entry flow."""
353 result: _FlowResultT |
None =
None
358 flow = self._progress.
get(flow_id)
359 if flow
and flow.deprecated_show_progress:
360 if (cur_step := flow.cur_step)
and cur_step[
362 ] == FlowResultType.SHOW_PROGRESS:
364 await asyncio.sleep(0)
366 while not result
or result[
"type"] == FlowResultType.SHOW_PROGRESS_DONE:
368 flow = self._progress.
get(flow_id)
369 if flow
and flow.deprecated_show_progress:
374 self, flow_id: str, user_input: dict |
None =
None
376 """Continue a data entry flow."""
377 if (flow := self._progress.
get(flow_id))
is None:
380 cur_step = flow.cur_step
381 assert cur_step
is not None
384 data_schema := cur_step.get(
"data_schema")
385 )
is not None and user_input
is not None:
386 data_schema = cast(vol.Schema, data_schema)
389 except vol.Invalid
as ex:
391 if isinstance(ex, vol.MultipleInvalid):
392 raised_errors = ex.errors
394 schema_errors: dict[str, Any] = {}
395 for error
in raised_errors:
400 schema_errors.setdefault(
"base", []).append(
str(error))
402 "Schema validation failed",
404 error_message=ex.error_message,
405 schema_errors=schema_errors,
409 if cur_step[
"type"] == FlowResultType.MENU
and user_input:
411 flow, user_input[
"next_step_id"],
None
415 flow, cur_step[
"step_id"], user_input
418 if cur_step[
"type"]
in (
419 FlowResultType.EXTERNAL_STEP,
420 FlowResultType.SHOW_PROGRESS,
422 if cur_step[
"type"] == FlowResultType.EXTERNAL_STEP
and result[
425 FlowResultType.EXTERNAL_STEP,
426 FlowResultType.EXTERNAL_STEP_DONE,
429 "External step can only transition to "
430 "external step or external step done."
432 if cur_step[
"type"] == FlowResultType.SHOW_PROGRESS
and result[
435 FlowResultType.SHOW_PROGRESS,
436 FlowResultType.SHOW_PROGRESS_DONE,
439 "Show progress can only transition to show progress or show"
448 if cur_step[
"step_id"] != result.get(
"step_id")
or (
449 result[
"type"] == FlowResultType.SHOW_PROGRESS
451 cur_step[
"progress_action"] != result.get(
"progress_action")
452 or cur_step[
"description_placeholders"]
453 != result.get(
"description_placeholders")
457 self.
hasshass.bus.async_fire_internal(
458 EVENT_DATA_ENTRY_FLOW_PROGRESSED,
459 {
"handler": flow.handler,
"flow_id": flow_id,
"refresh":
True},
471 self, flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]
473 """Add a flow to in progress."""
474 if flow.init_data
is not None:
475 self._init_data_process_index[type(flow.init_data)].
add(flow)
476 self._progress[flow.flow_id] = flow
477 self._handler_progress_index[flow.handler].
add(flow)
481 self, flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]
483 """Remove a flow from in progress."""
484 if flow.init_data
is not None:
485 init_data_type = type(flow.init_data)
486 self._init_data_process_index[init_data_type].
remove(flow)
487 if not self._init_data_process_index[init_data_type]:
488 del self._init_data_process_index[init_data_type]
489 handler = flow.handler
490 self._handler_progress_index[handler].
remove(flow)
491 if not self._handler_progress_index[handler]:
492 del self._handler_progress_index[handler]
496 """Remove a flow from in progress."""
497 if (flow := self._progress.pop(flow_id,
None))
is None:
500 flow.async_cancel_progress_task()
504 _LOGGER.exception(
"Error removing %s flow", flow.handler)
508 flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT],
510 user_input: dict | BaseServiceInfo |
None,
512 """Handle a step of a flow."""
515 method = f
"async_step_{step_id}"
517 result: _FlowResultT = await getattr(flow, method)(user_input)
518 except AbortFlow
as err:
519 result = self._flow_result(
520 type=FlowResultType.ABORT,
521 flow_id=flow.flow_id,
522 handler=flow.handler,
524 description_placeholders=err.description_placeholders,
528 if result.get(
"preview")
is not None:
531 if not isinstance(result[
"type"], FlowResultType):
534 "does not use FlowResultType enum for data entry flow result type",
535 core_behavior=ReportBehavior.LOG,
536 breaks_in_ha_version=
"2025.1",
540 result[
"type"] == FlowResultType.SHOW_PROGRESS
542 and (progress_task := result.pop(
"progress_task",
None))
543 and progress_task != flow.async_get_progress_task()
546 async
def call_configure() -> None:
547 with suppress(UnknownFlow):
550 def schedule_configure(_: asyncio.Task) ->
None:
551 self.
hasshass.async_create_task(call_configure())
554 progress_task.add_done_callback(schedule_configure)
555 flow.async_set_progress_task(progress_task)
557 elif result[
"type"] != FlowResultType.SHOW_PROGRESS:
558 flow.async_cancel_progress_task()
560 if result[
"type"]
in STEP_ID_OPTIONAL_STEPS:
561 if "step_id" not in result:
562 result[
"step_id"] = step_id
564 if result[
"type"]
in FLOW_NOT_COMPLETE_STEPS:
566 flow.cur_step = result
573 if result[
"type"] == FlowResultType.FORM:
574 flow.cur_step = result
583 self, flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT], step_id: str
585 """Raise if the step does not exist."""
586 method = f
"async_step_{step_id}"
588 if not hasattr(flow, method):
591 f
"Handler {self.__class__.__name__} doesn't support step {step_id}"
595 self, flow: FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]
597 """Set up preview for a flow handler."""
598 if flow.handler
not in self._preview:
599 self._preview.
add(flow.handler)
600 await flow.async_setup_preview(self.
hasshass)
605 flows: Iterable[FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]],
606 include_uninitialized: bool,
607 ) -> list[_FlowResultT]:
608 """Convert a list of FlowHandler to a partial FlowResult that can be serialized."""
611 flow_id=flow.flow_id,
612 handler=flow.handler,
613 context=flow.context,
614 step_id=flow.cur_step[
"step_id"],
617 else self._flow_result(
618 flow_id=flow.flow_id,
619 handler=flow.handler,
620 context=flow.context,
623 if include_uninitialized
or flow.cur_step
is not None
627 class FlowHandler(Generic[_FlowContextT, _FlowResultT, _HandlerT]):
628 """Handle a data entry flow."""
630 _flow_result: type[_FlowResultT] = FlowResult
633 cur_step: _FlowResultT |
None =
None
638 hass: HomeAssistant =
None
639 handler: _HandlerT =
None
641 context: _FlowContextT = MappingProxyType({})
647 init_data: Any =
None
653 __progress_task: asyncio.Task[Any] |
None =
None
654 __no_progress_task_reported =
False
655 deprecated_show_progress =
False
659 """Source that initialized the flow."""
660 return self.context.
get(
"source",
None)
664 """If we should show advanced options."""
665 return self.context.
get(
"show_advanced_options",
False)
668 self, data_schema: vol.Schema, suggested_values: Mapping[str, Any] |
None
670 """Make a copy of the schema, populated with suggested values.
672 For each schema marker matching items in `suggested_values`,
673 the `suggested_value` will be set. The existing `suggested_value` will
674 be left untouched if there is no matching item.
677 for key, val
in data_schema.schema.items():
678 if isinstance(key, vol.Marker):
682 and key.description.get(
"advanced")
690 and key
in suggested_values
691 and isinstance(key, vol.Marker)
694 new_key = copy.copy(key)
695 new_key.description = {
"suggested_value": suggested_values[key.schema]}
696 schema[new_key] = val
697 return vol.Schema(schema)
703 step_id: str |
None =
None,
704 data_schema: vol.Schema |
None =
None,
705 errors: dict[str, str] |
None =
None,
706 description_placeholders: Mapping[str, str] |
None =
None,
707 last_step: bool |
None =
None,
708 preview: str |
None =
None,
710 """Return the definition of a form to gather user input.
712 The step_id parameter is deprecated and will be removed in a future release.
714 flow_result = self._flow_result(
715 type=FlowResultType.FORM,
716 flow_id=self.flow_id,
717 handler=self.handler,
718 data_schema=data_schema,
720 description_placeholders=description_placeholders,
724 if step_id
is not None:
725 flow_result[
"step_id"] = step_id
732 title: str |
None =
None,
733 data: Mapping[str, Any],
734 description: str |
None =
None,
735 description_placeholders: Mapping[str, str] |
None =
None,
738 flow_result = self._flow_result(
739 type=FlowResultType.CREATE_ENTRY,
740 flow_id=self.flow_id,
741 handler=self.handler,
743 description=description,
744 description_placeholders=description_placeholders,
745 context=self.context,
747 if title
is not None:
748 flow_result[
"title"] = title
756 description_placeholders: Mapping[str, str] |
None =
None,
758 """Abort the flow."""
759 return self._flow_result(
760 type=FlowResultType.ABORT,
761 flow_id=self.flow_id,
762 handler=self.handler,
764 description_placeholders=description_placeholders,
771 step_id: str |
None =
None,
773 description_placeholders: Mapping[str, str] |
None =
None,
775 """Return the definition of an external step for the user to take.
777 The step_id parameter is deprecated and will be removed in a future release.
779 flow_result = self._flow_result(
780 type=FlowResultType.EXTERNAL_STEP,
781 flow_id=self.flow_id,
782 handler=self.handler,
784 description_placeholders=description_placeholders,
786 if step_id
is not None:
787 flow_result[
"step_id"] = step_id
792 """Return the definition of an external step for the user to take."""
793 return self._flow_result(
794 type=FlowResultType.EXTERNAL_STEP_DONE,
795 flow_id=self.flow_id,
796 handler=self.handler,
797 step_id=next_step_id,
804 step_id: str |
None =
None,
805 progress_action: str,
806 description_placeholders: Mapping[str, str] |
None =
None,
807 progress_task: asyncio.Task[Any] |
None =
None,
809 """Show a progress message to the user, without user input allowed.
811 The step_id parameter is deprecated and will be removed in a future release.
819 "%s::%s calls async_show_progress without passing a progress task, "
820 "this is not valid and will break in Home Assistant Core 2024.8. "
828 if progress_task
is None:
831 flow_result = self._flow_result(
832 type=FlowResultType.SHOW_PROGRESS,
833 flow_id=self.flow_id,
834 handler=self.handler,
835 progress_action=progress_action,
836 description_placeholders=description_placeholders,
837 progress_task=progress_task,
839 if step_id
is not None:
840 flow_result[
"step_id"] = step_id
845 """Mark the progress done."""
846 return self._flow_result(
847 type=FlowResultType.SHOW_PROGRESS_DONE,
848 flow_id=self.flow_id,
849 handler=self.handler,
850 step_id=next_step_id,
857 step_id: str |
None =
None,
858 menu_options: Container[str],
859 description_placeholders: Mapping[str, str] |
None =
None,
861 """Show a navigation menu to the user.
863 Options dict maps step_id => i18n label
864 The step_id parameter is deprecated and will be removed in a future release.
866 flow_result = self._flow_result(
867 type=FlowResultType.MENU,
868 flow_id=self.flow_id,
869 handler=self.handler,
870 data_schema=vol.Schema({
"next_step_id": vol.In(menu_options)}),
871 menu_options=menu_options,
872 description_placeholders=description_placeholders,
874 if step_id
is not None:
875 flow_result[
"step_id"] = step_id
880 """Notification that the flow has been removed."""
884 """Set up preview."""
888 """Cancel in progress task."""
895 """Get in progress task."""
901 progress_task: asyncio.Task[Any],
903 """Set in progress task."""
908 """Class to represent a section config."""
914 """Data entry flow section."""
916 CONFIG_SCHEMA = vol.Schema(
918 vol.Optional(
"collapsed", default=
False): bool,
923 self, schema: vol.Schema, options: SectionConfig |
None =
None
927 self.options: SectionConfig = self.
CONFIG_SCHEMACONFIG_SCHEMA(options
or {})
930 """Validate input."""
931 return self.
schemaschema(value)
935 __getattr__ = partial(check_if_deprecated_constant, module_globals=globals())
937 dir_with_deprecated_constants, module_globals_keys=[*globals().keys()]
None __init__(self, str reason, Mapping[str, str]|None description_placeholders=None)
None async_setup_preview(HomeAssistant hass)
_FlowResultT async_external_step(self, *str|None step_id=None, str url, Mapping[str, str]|None description_placeholders=None)
None async_cancel_progress_task(self)
asyncio.Task[Any]|None async_get_progress_task(self)
bool show_advanced_options(self)
__no_progress_task_reported
vol.Schema add_suggested_values_to_schema(self, vol.Schema data_schema, Mapping[str, Any]|None suggested_values)
bool deprecated_show_progress
None async_set_progress_task(self, asyncio.Task[Any] progress_task)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
bool __no_progress_task_reported
_FlowResultT async_external_step_done(self, *str next_step_id)
_FlowResultT async_show_progress(self, *str|None step_id=None, str progress_action, Mapping[str, str]|None description_placeholders=None, asyncio.Task[Any]|None progress_task=None)
_FlowResultT async_show_menu(self, *str|None step_id=None, Container[str] menu_options, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_show_progress_done(self, *str next_step_id)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
_FlowResultT async_abort(self, *str reason, Mapping[str, str]|None description_placeholders=None)
None _async_remove_flow_from_index(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow)
None _raise_if_step_does_not_exist(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow, str step_id)
list[_FlowResultT] async_progress_by_init_data_type(self, type init_data_type, Callable[[Any], bool] matcher, bool include_uninitialized=False)
_FlowResultT async_finish_flow(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow, _FlowResultT result)
list[_FlowResultT] async_progress_by_handler(self, _HandlerT handler, bool include_uninitialized=False, dict[str, Any]|None match_context=None)
FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] async_create_flow(self, _HandlerT handler_key, *_FlowContextT|None context=None, dict[str, Any]|None data=None)
None _async_remove_flow_progress(self, str flow_id)
None _async_setup_preview(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow)
_FlowResultT async_get(self, str flow_id)
_FlowResultT async_init(self, _HandlerT handler, *_FlowContextT|None context=None, Any data=None)
_FlowResultT _async_configure(self, str flow_id, dict|None user_input=None)
None __init__(self, HomeAssistant hass)
_FlowResultT _async_handle_step(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow, str step_id, dict|BaseServiceInfo|None user_input)
list[_FlowResultT] async_progress(self, bool include_uninitialized=False)
list[FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]] _async_progress_by_handler(self, _HandlerT handler, dict[str, Any]|None match_context)
_FlowResultT async_configure(self, str flow_id, dict|None user_input=None)
None async_post_init(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow, _FlowResultT result)
None async_abort(self, str flow_id)
None _async_add_flow_progress(self, FlowHandler[_FlowContextT, _FlowResultT, _HandlerT] flow)
list[_FlowResultT] _async_flow_handler_to_flow_result(self, Iterable[FlowHandler[_FlowContextT, _FlowResultT, _HandlerT]] flows, bool include_uninitialized)
None __init__(self, str message, list[Hashable]|None path, str|None error_message, dict[str, Any] schema_errors, **Any kwargs)
Any __call__(self, Any value)
None __init__(self, vol.Schema schema, SectionConfig|None options=None)
bool add(self, _T matcher)
bool remove(self, _T matcher)
web.Response get(self, web.Request request, str config_key)
None _map_error_to_schema_errors(dict[str, Any] schema_errors, vol.Invalid error, vol.Schema data_schema)
list[str] all_with_deprecated_constants(dict[str, Any] module_globals)
None report_usage(str what, *str|None breaks_in_ha_version=None, ReportBehavior core_behavior=ReportBehavior.ERROR, ReportBehavior core_integration_behavior=ReportBehavior.LOG, ReportBehavior custom_integration_behavior=ReportBehavior.LOG, set[str]|None exclude_integrations=None, str|None integration_domain=None, int level=logging.WARNING)
str async_suggest_report_issue(HomeAssistant|None hass, *Integration|None integration=None, str|None integration_domain=None, str|None module=None)