mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Add reconfigure support to config subentries
This commit is contained in:
parent
a12a42710f
commit
fa6cc8edfe
@ -326,6 +326,9 @@ class SubentryManagerFlowIndexView(
|
|||||||
"""Return context."""
|
"""Return context."""
|
||||||
context = super().get_context(data)
|
context = super().get_context(data)
|
||||||
context["source"] = config_entries.SOURCE_USER
|
context["source"] = config_entries.SOURCE_USER
|
||||||
|
if subentry_id := data.get("subentry_id"):
|
||||||
|
context["source"] = config_entries.SOURCE_RECONFIGURE
|
||||||
|
context["subentry_id"] = subentry_id
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -339,7 +339,14 @@ class ConfigSubentryDataWithId(ConfigSubentryData):
|
|||||||
subentry_id: str
|
subentry_id: str
|
||||||
|
|
||||||
|
|
||||||
class SubentryFlowResult(FlowResult[FlowContext, tuple[str, str]], total=False):
|
class SubentryFlowContext(FlowContext, total=False):
|
||||||
|
"""Typed context dict for config flow."""
|
||||||
|
|
||||||
|
entry_id: str
|
||||||
|
subentry_id: str
|
||||||
|
|
||||||
|
|
||||||
|
class SubentryFlowResult(FlowResult[SubentryFlowContext, tuple[str, str]], total=False):
|
||||||
"""Typed result dict for subentry flow."""
|
"""Typed result dict for subentry flow."""
|
||||||
|
|
||||||
unique_id: str | None
|
unique_id: str | None
|
||||||
@ -389,7 +396,7 @@ class ConfigEntry(Generic[_DataT]):
|
|||||||
supports_remove_device: bool | None
|
supports_remove_device: bool | None
|
||||||
_supports_options: bool | None
|
_supports_options: bool | None
|
||||||
_supports_reconfigure: bool | None
|
_supports_reconfigure: bool | None
|
||||||
_supported_subentries: tuple[str, ...] | None
|
_supported_subentry_flows: dict[str, dict[str, bool]] | None
|
||||||
update_listeners: list[UpdateListenerType]
|
update_listeners: list[UpdateListenerType]
|
||||||
_async_cancel_retry_setup: Callable[[], Any] | None
|
_async_cancel_retry_setup: Callable[[], Any] | None
|
||||||
_on_unload: list[Callable[[], Coroutine[Any, Any, None] | None]] | None
|
_on_unload: list[Callable[[], Coroutine[Any, Any, None] | None]] | None
|
||||||
@ -500,7 +507,7 @@ class ConfigEntry(Generic[_DataT]):
|
|||||||
_setter(self, "_supports_reconfigure", None)
|
_setter(self, "_supports_reconfigure", None)
|
||||||
|
|
||||||
# Supports subentries
|
# Supports subentries
|
||||||
_setter(self, "_supported_subentries", None)
|
_setter(self, "_supported_subentry_flows", None)
|
||||||
|
|
||||||
# Listeners to call on update
|
# Listeners to call on update
|
||||||
_setter(self, "update_listeners", [])
|
_setter(self, "update_listeners", [])
|
||||||
@ -575,16 +582,26 @@ class ConfigEntry(Generic[_DataT]):
|
|||||||
return self._supports_reconfigure or False
|
return self._supports_reconfigure or False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_subentries(self) -> tuple[str, ...]:
|
def supported_subentry_flows(self) -> dict[str, dict[str, bool]]:
|
||||||
"""Return supported subentries."""
|
"""Return supported subentries."""
|
||||||
if self._supported_subentries is None and (
|
if self._supported_subentry_flows is None and (
|
||||||
handler := HANDLERS.get(self.domain)
|
handler := HANDLERS.get(self.domain)
|
||||||
):
|
):
|
||||||
# work out sub entries supported by the handler
|
# work out sub entries supported by the handler
|
||||||
|
supported_flows = handler.async_get_supported_subentry_flows(self)
|
||||||
object.__setattr__(
|
object.__setattr__(
|
||||||
self, "_supported_subentries", handler.async_supported_subentries(self)
|
self,
|
||||||
|
"_supported_subentry_flows",
|
||||||
|
{
|
||||||
|
subentry_flow_type: {
|
||||||
|
"supports_reconfigure": hasattr(
|
||||||
|
subentry_flow_handler, "async_step_reconfigure"
|
||||||
)
|
)
|
||||||
return self._supported_subentries or ()
|
}
|
||||||
|
for subentry_flow_type, subentry_flow_handler in supported_flows.items()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return self._supported_subentry_flows or {}
|
||||||
|
|
||||||
def clear_state_cache(self) -> None:
|
def clear_state_cache(self) -> None:
|
||||||
"""Clear cached properties that are included in as_json_fragment."""
|
"""Clear cached properties that are included in as_json_fragment."""
|
||||||
@ -605,7 +622,7 @@ class ConfigEntry(Generic[_DataT]):
|
|||||||
"supports_remove_device": self.supports_remove_device or False,
|
"supports_remove_device": self.supports_remove_device or False,
|
||||||
"supports_unload": self.supports_unload or False,
|
"supports_unload": self.supports_unload or False,
|
||||||
"supports_reconfigure": self.supports_reconfigure,
|
"supports_reconfigure": self.supports_reconfigure,
|
||||||
"supported_subentries": self.supported_subentries,
|
"supported_subentry_flows": self.supported_subentry_flows,
|
||||||
"pref_disable_new_entities": self.pref_disable_new_entities,
|
"pref_disable_new_entities": self.pref_disable_new_entities,
|
||||||
"pref_disable_polling": self.pref_disable_polling,
|
"pref_disable_polling": self.pref_disable_polling,
|
||||||
"disabled_by": self.disabled_by,
|
"disabled_by": self.disabled_by,
|
||||||
@ -2378,6 +2395,11 @@ class ConfigEntries:
|
|||||||
|
|
||||||
_setter(entry, "modified_at", utcnow())
|
_setter(entry, "modified_at", utcnow())
|
||||||
|
|
||||||
|
self._async_save_and_notify(entry)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_save_and_notify(self, entry: ConfigEntry) -> None:
|
||||||
for listener in entry.update_listeners:
|
for listener in entry.update_listeners:
|
||||||
self.hass.async_create_task(
|
self.hass.async_create_task(
|
||||||
listener(self.hass, entry),
|
listener(self.hass, entry),
|
||||||
@ -2388,7 +2410,6 @@ class ConfigEntries:
|
|||||||
entry.clear_state_cache()
|
entry.clear_state_cache()
|
||||||
entry.clear_storage_cache()
|
entry.clear_storage_cache()
|
||||||
self._async_dispatch(ConfigEntryChange.UPDATED, entry)
|
self._async_dispatch(ConfigEntryChange.UPDATED, entry)
|
||||||
return True
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_subentry(self, entry: ConfigEntry, subentry: ConfigSubentry) -> bool:
|
def async_add_subentry(self, entry: ConfigEntry, subentry: ConfigSubentry) -> bool:
|
||||||
@ -2417,6 +2438,54 @@ class ConfigEntries:
|
|||||||
ent_reg.async_clear_config_subentry(entry.entry_id, subentry_id)
|
ent_reg.async_clear_config_subentry(entry.entry_id, subentry_id)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_subentry(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
subentry: ConfigSubentry,
|
||||||
|
*,
|
||||||
|
data: Mapping[str, Any] | UndefinedType = UNDEFINED,
|
||||||
|
title: str | UndefinedType = UNDEFINED,
|
||||||
|
unique_id: str | None | UndefinedType = UNDEFINED,
|
||||||
|
) -> bool:
|
||||||
|
"""Update a config entry.
|
||||||
|
|
||||||
|
If the entry was changed, the update_listeners are
|
||||||
|
fired and this function returns True
|
||||||
|
|
||||||
|
If the entry was not changed, the update_listeners are
|
||||||
|
not fired and this function returns False
|
||||||
|
"""
|
||||||
|
if entry.entry_id not in self._entries:
|
||||||
|
raise UnknownEntry(entry.entry_id)
|
||||||
|
if subentry.subentry_id not in entry.subentries:
|
||||||
|
raise UnknownSubEntry(subentry.subentry_id)
|
||||||
|
|
||||||
|
self.hass.verify_event_loop_thread("hass.config_entries.async_update_subentry")
|
||||||
|
changed = False
|
||||||
|
_setter = object.__setattr__
|
||||||
|
|
||||||
|
if unique_id is not UNDEFINED and subentry.unique_id != unique_id:
|
||||||
|
self._raise_if_subentry_unique_id_exists(entry, unique_id)
|
||||||
|
changed = True
|
||||||
|
_setter(subentry, "unique_id", unique_id)
|
||||||
|
|
||||||
|
if title is not UNDEFINED and subentry.title != title:
|
||||||
|
changed = True
|
||||||
|
_setter(subentry, "title", title)
|
||||||
|
|
||||||
|
if data is not UNDEFINED and subentry.data != data:
|
||||||
|
changed = True
|
||||||
|
_setter(subentry, "data", MappingProxyType(data))
|
||||||
|
|
||||||
|
if not changed:
|
||||||
|
return False
|
||||||
|
|
||||||
|
_setter(entry, "modified_at", utcnow())
|
||||||
|
|
||||||
|
self._async_save_and_notify(entry)
|
||||||
|
return True
|
||||||
|
|
||||||
def _raise_if_subentry_unique_id_exists(
|
def _raise_if_subentry_unique_id_exists(
|
||||||
self, entry: ConfigEntry, unique_id: str | None
|
self, entry: ConfigEntry, unique_id: str | None
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -2763,19 +2832,13 @@ class ConfigFlow(ConfigEntryBaseFlow):
|
|||||||
"""Return options flow support for this handler."""
|
"""Return options flow support for this handler."""
|
||||||
return cls.async_get_options_flow is not ConfigFlow.async_get_options_flow
|
return cls.async_get_options_flow is not ConfigFlow.async_get_options_flow
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(
|
|
||||||
config_entry: ConfigEntry, subentry_type: str
|
|
||||||
) -> ConfigSubentryFlow:
|
|
||||||
"""Get the subentry flow for this handler."""
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(cls, config_entry: ConfigEntry) -> tuple[str, ...]:
|
def async_get_supported_subentry_flows(
|
||||||
|
cls, config_entry: ConfigEntry
|
||||||
|
) -> dict[str, type[ConfigSubentryFlow]]:
|
||||||
"""Return subentries supported by this handler."""
|
"""Return subentries supported by this handler."""
|
||||||
return ()
|
return {}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_abort_entries_match(
|
def _async_abort_entries_match(
|
||||||
@ -3231,7 +3294,9 @@ class _ConfigSubFlowManager:
|
|||||||
|
|
||||||
|
|
||||||
class ConfigSubentryFlowManager(
|
class ConfigSubentryFlowManager(
|
||||||
data_entry_flow.FlowManager[FlowContext, SubentryFlowResult, tuple[str, str]],
|
data_entry_flow.FlowManager[
|
||||||
|
SubentryFlowContext, SubentryFlowResult, tuple[str, str]
|
||||||
|
],
|
||||||
_ConfigSubFlowManager,
|
_ConfigSubFlowManager,
|
||||||
):
|
):
|
||||||
"""Manage all the config subentry flows that are in progress."""
|
"""Manage all the config subentry flows that are in progress."""
|
||||||
@ -3255,18 +3320,19 @@ class ConfigSubentryFlowManager(
|
|||||||
entry_id, subentry_type = handler_key
|
entry_id, subentry_type = handler_key
|
||||||
entry = self._async_get_config_entry(entry_id)
|
entry = self._async_get_config_entry(entry_id)
|
||||||
handler = await _async_get_flow_handler(self.hass, entry.domain, {})
|
handler = await _async_get_flow_handler(self.hass, entry.domain, {})
|
||||||
if subentry_type not in handler.async_supported_subentries(entry):
|
subentry_flows = handler.async_get_supported_subentry_flows(entry)
|
||||||
|
if subentry_type not in subentry_flows:
|
||||||
raise data_entry_flow.UnknownHandler(
|
raise data_entry_flow.UnknownHandler(
|
||||||
f"Config entry '{entry.domain}' does not support subentry '{subentry_type}'"
|
f"Config entry '{entry.domain}' does not support subentry '{subentry_type}'"
|
||||||
)
|
)
|
||||||
subentry_flow = handler.async_get_subentry_flow(entry, subentry_type)
|
subentry_flow = subentry_flows[subentry_type]()
|
||||||
subentry_flow.init_step = context["source"]
|
subentry_flow.init_step = context["source"]
|
||||||
return subentry_flow
|
return subentry_flow
|
||||||
|
|
||||||
async def async_finish_flow(
|
async def async_finish_flow(
|
||||||
self,
|
self,
|
||||||
flow: data_entry_flow.FlowHandler[
|
flow: data_entry_flow.FlowHandler[
|
||||||
FlowContext, SubentryFlowResult, tuple[str, str]
|
SubentryFlowContext, SubentryFlowResult, tuple[str, str]
|
||||||
],
|
],
|
||||||
result: SubentryFlowResult,
|
result: SubentryFlowResult,
|
||||||
) -> SubentryFlowResult:
|
) -> SubentryFlowResult:
|
||||||
@ -3302,7 +3368,9 @@ class ConfigSubentryFlowManager(
|
|||||||
|
|
||||||
|
|
||||||
class ConfigSubentryFlow(
|
class ConfigSubentryFlow(
|
||||||
data_entry_flow.FlowHandler[FlowContext, SubentryFlowResult, tuple[str, str]]
|
data_entry_flow.FlowHandler[
|
||||||
|
SubentryFlowContext, SubentryFlowResult, tuple[str, str]
|
||||||
|
]
|
||||||
):
|
):
|
||||||
"""Base class for config subentry flows."""
|
"""Base class for config subentry flows."""
|
||||||
|
|
||||||
@ -3320,6 +3388,9 @@ class ConfigSubentryFlow(
|
|||||||
unique_id: str | None = None,
|
unique_id: str | None = None,
|
||||||
) -> SubentryFlowResult:
|
) -> SubentryFlowResult:
|
||||||
"""Finish config flow and create a config entry."""
|
"""Finish config flow and create a config entry."""
|
||||||
|
if self.source != SOURCE_USER:
|
||||||
|
raise ValueError(f"Source is {self.source}, expected {SOURCE_USER}")
|
||||||
|
|
||||||
result = super().async_create_entry(
|
result = super().async_create_entry(
|
||||||
title=title,
|
title=title,
|
||||||
data=data,
|
data=data,
|
||||||
@ -3331,6 +3402,70 @@ class ConfigSubentryFlow(
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_update_and_abort(
|
||||||
|
self,
|
||||||
|
entry: ConfigEntry,
|
||||||
|
subentry: ConfigSubentry,
|
||||||
|
*,
|
||||||
|
unique_id: str | None | UndefinedType = UNDEFINED,
|
||||||
|
title: str | UndefinedType = UNDEFINED,
|
||||||
|
data: Mapping[str, Any] | UndefinedType = UNDEFINED,
|
||||||
|
data_updates: Mapping[str, Any] | UndefinedType = UNDEFINED,
|
||||||
|
) -> SubentryFlowResult:
|
||||||
|
"""Update config subentry and finish subentry flow.
|
||||||
|
|
||||||
|
:param data: replace the subentry data with new data
|
||||||
|
:param data_updates: add items from data_updates to subentry data - existing
|
||||||
|
keys are overridden
|
||||||
|
:param title: replace the title of the subentry
|
||||||
|
:param unique_id: replace the unique_id of the subentry
|
||||||
|
"""
|
||||||
|
if data_updates is not UNDEFINED:
|
||||||
|
if data is not UNDEFINED:
|
||||||
|
raise ValueError("Cannot set both data and data_updates")
|
||||||
|
data = entry.data | data_updates
|
||||||
|
self.hass.config_entries.async_update_subentry(
|
||||||
|
entry=entry,
|
||||||
|
subentry=subentry,
|
||||||
|
unique_id=unique_id,
|
||||||
|
title=title,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
return self.async_abort(reason="reconfigure_successful")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _reconfigure_entry_id(self) -> str:
|
||||||
|
"""Return reconfigure entry id."""
|
||||||
|
if self.source != SOURCE_RECONFIGURE:
|
||||||
|
raise ValueError(f"Source is {self.source}, expected {SOURCE_RECONFIGURE}")
|
||||||
|
return self.handler[0]
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _get_reconfigure_entry(self) -> ConfigEntry:
|
||||||
|
"""Return the reconfigure config entry linked to the current context."""
|
||||||
|
return self.hass.config_entries.async_get_known_entry(
|
||||||
|
self._reconfigure_entry_id
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _reconfigure_subentry_id(self) -> str:
|
||||||
|
"""Return reconfigure subentry id."""
|
||||||
|
if self.source != SOURCE_RECONFIGURE:
|
||||||
|
raise ValueError(f"Source is {self.source}, expected {SOURCE_RECONFIGURE}")
|
||||||
|
return self.context["subentry_id"]
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _get_reconfigure_subentry(self) -> ConfigSubentry:
|
||||||
|
"""Return the reconfigure config subentry linked to the current context."""
|
||||||
|
entry = self.hass.config_entries.async_get_known_entry(
|
||||||
|
self._reconfigure_entry_id
|
||||||
|
)
|
||||||
|
subentry_id = self._reconfigure_subentry_id
|
||||||
|
if subentry_id not in entry.subentries:
|
||||||
|
raise UnknownEntry
|
||||||
|
return entry.subentries[subentry_id]
|
||||||
|
|
||||||
|
|
||||||
class OptionsFlowManager(
|
class OptionsFlowManager(
|
||||||
data_entry_flow.FlowManager[ConfigFlowContext, ConfigFlowResult],
|
data_entry_flow.FlowManager[ConfigFlowContext, ConfigFlowResult],
|
||||||
|
@ -1090,6 +1090,28 @@ class MockConfigEntry(config_entries.ConfigEntry):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def start_subentry_reconfigure_flow(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
subentry_flow_type: str,
|
||||||
|
subentry_id: str,
|
||||||
|
*,
|
||||||
|
show_advanced_options: bool = False,
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Start a subnetry reconfiguration flow."""
|
||||||
|
if self.entry_id not in hass.config_entries._entries:
|
||||||
|
raise ValueError(
|
||||||
|
"Config entry must be added to hass to start reconfiguration flow"
|
||||||
|
)
|
||||||
|
return await hass.config_entries.subentries.async_init(
|
||||||
|
(self.entry_id, subentry_flow_type),
|
||||||
|
context={
|
||||||
|
"source": config_entries.SOURCE_RECONFIGURE,
|
||||||
|
"subentry_id": subentry_id,
|
||||||
|
"show_advanced_options": show_advanced_options,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def start_reauth_flow(
|
async def start_reauth_flow(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -143,7 +143,7 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": True,
|
"supports_options": True,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -163,7 +163,7 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": "Unsupported API",
|
"reason": "Unsupported API",
|
||||||
"source": "bla2",
|
"source": "bla2",
|
||||||
"state": core_ce.ConfigEntryState.SETUP_ERROR.value,
|
"state": core_ce.ConfigEntryState.SETUP_ERROR.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -183,7 +183,7 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -203,7 +203,7 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla4",
|
"source": "bla4",
|
||||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -223,7 +223,7 @@ async def test_get_entries(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla5",
|
"source": "bla5",
|
||||||
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
"state": core_ce.ConfigEntryState.NOT_LOADED.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -587,7 +587,7 @@ async def test_create_account(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": core_ce.SOURCE_USER,
|
"source": core_ce.SOURCE_USER,
|
||||||
"state": core_ce.ConfigEntryState.LOADED.value,
|
"state": core_ce.ConfigEntryState.LOADED.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -673,7 +673,7 @@ async def test_two_step_flow(hass: HomeAssistant, client: TestClient) -> None:
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": core_ce.SOURCE_USER,
|
"source": core_ce.SOURCE_USER,
|
||||||
"state": core_ce.ConfigEntryState.LOADED.value,
|
"state": core_ce.ConfigEntryState.LOADED.value,
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1108,9 +1108,6 @@ async def test_subentry_flow(hass: HomeAssistant, client) -> None:
|
|||||||
"""Test we can start a subentry flow."""
|
"""Test we can start a subentry flow."""
|
||||||
|
|
||||||
class TestFlow(core_ce.ConfigFlow):
|
class TestFlow(core_ce.ConfigFlow):
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(self, user_input=None):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -1124,12 +1121,12 @@ async def test_subentry_flow(hass: HomeAssistant, client) -> None:
|
|||||||
description_placeholders={"enabled": "Set to true to be true"},
|
description_placeholders={"enabled": "Set to true to be true"},
|
||||||
)
|
)
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(cls, config_entry):
|
def async_get_supported_subentry_flows(
|
||||||
return ("test",)
|
cls, config_entry: core_ce.ConfigEntry
|
||||||
|
) -> dict[str, type[core_ce.ConfigSubentryFlow]]:
|
||||||
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
mock_integration(hass, MockModule("test"))
|
mock_integration(hass, MockModule("test"))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
@ -1160,6 +1157,69 @@ async def test_subentry_flow(hass: HomeAssistant, client) -> None:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_subentry_reconfigure_flow(hass: HomeAssistant, client) -> None:
|
||||||
|
"""Test we can start a subentry reconfigure flow."""
|
||||||
|
|
||||||
|
class TestFlow(core_ce.ConfigFlow):
|
||||||
|
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
||||||
|
async def async_step_init(self, user_input=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def async_step_user(self, user_input=None):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def async_step_reconfigure(self, user_input=None):
|
||||||
|
schema = OrderedDict()
|
||||||
|
schema[vol.Required("enabled")] = bool
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reconfigure",
|
||||||
|
data_schema=schema,
|
||||||
|
description_placeholders={"enabled": "Set to true to be true"},
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@callback
|
||||||
|
def async_get_supported_subentry_flows(
|
||||||
|
cls, config_entry: core_ce.ConfigEntry
|
||||||
|
) -> dict[str, type[core_ce.ConfigSubentryFlow]]:
|
||||||
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
|
mock_integration(hass, MockModule("test"))
|
||||||
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
MockConfigEntry(
|
||||||
|
domain="test",
|
||||||
|
entry_id="test1",
|
||||||
|
source="bla",
|
||||||
|
subentries_data=[
|
||||||
|
core_ce.ConfigSubentryData(
|
||||||
|
data={}, subentry_id="mock_id", title="Title", unique_id=None
|
||||||
|
)
|
||||||
|
],
|
||||||
|
).add_to_hass(hass)
|
||||||
|
entry = hass.config_entries.async_entries()[0]
|
||||||
|
|
||||||
|
with patch.dict(HANDLERS, {"test": TestFlow}):
|
||||||
|
url = "/api/config/config_entries/subentries/flow"
|
||||||
|
resp = await client.post(
|
||||||
|
url, json={"handler": [entry.entry_id, "test"], "subentry_id": "mock_id"}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resp.status == HTTPStatus.OK
|
||||||
|
data = await resp.json()
|
||||||
|
|
||||||
|
data.pop("flow_id")
|
||||||
|
assert data == {
|
||||||
|
"type": "form",
|
||||||
|
"handler": ["test1", "test"],
|
||||||
|
"step_id": "reconfigure",
|
||||||
|
"data_schema": [{"name": "enabled", "required": True, "type": "boolean"}],
|
||||||
|
"description_placeholders": {"enabled": "Set to true to be true"},
|
||||||
|
"errors": None,
|
||||||
|
"last_step": None,
|
||||||
|
"preview": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("endpoint", "method"),
|
("endpoint", "method"),
|
||||||
[
|
[
|
||||||
@ -1174,9 +1234,6 @@ async def test_subentry_flow_unauth(
|
|||||||
"""Test unauthorized on subentry flow."""
|
"""Test unauthorized on subentry flow."""
|
||||||
|
|
||||||
class TestFlow(core_ce.ConfigFlow):
|
class TestFlow(core_ce.ConfigFlow):
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
||||||
async def async_step_init(self, user_input=None):
|
async def async_step_init(self, user_input=None):
|
||||||
schema = OrderedDict()
|
schema = OrderedDict()
|
||||||
@ -1187,12 +1244,12 @@ async def test_subentry_flow_unauth(
|
|||||||
description_placeholders={"enabled": "Set to true to be true"},
|
description_placeholders={"enabled": "Set to true to be true"},
|
||||||
)
|
)
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(cls, config_entry):
|
def async_get_supported_subentry_flows(
|
||||||
return ("test",)
|
cls, config_entry: core_ce.ConfigEntry
|
||||||
|
) -> dict[str, type[core_ce.ConfigSubentryFlow]]:
|
||||||
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
mock_integration(hass, MockModule("test"))
|
mock_integration(hass, MockModule("test"))
|
||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
@ -1219,9 +1276,6 @@ async def test_two_step_subentry_flow(hass: HomeAssistant, client) -> None:
|
|||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
class TestFlow(core_ce.ConfigFlow):
|
class TestFlow(core_ce.ConfigFlow):
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
return await self.async_step_finish()
|
return await self.async_step_finish()
|
||||||
@ -1236,12 +1290,12 @@ async def test_two_step_subentry_flow(hass: HomeAssistant, client) -> None:
|
|||||||
step_id="finish", data_schema=vol.Schema({"enabled": bool})
|
step_id="finish", data_schema=vol.Schema({"enabled": bool})
|
||||||
)
|
)
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(cls, config_entry):
|
def async_get_supported_subentry_flows(
|
||||||
return ("test",)
|
cls, config_entry: core_ce.ConfigEntry
|
||||||
|
) -> dict[str, type[core_ce.ConfigSubentryFlow]]:
|
||||||
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
MockConfigEntry(
|
MockConfigEntry(
|
||||||
domain="test",
|
domain="test",
|
||||||
@ -1300,9 +1354,6 @@ async def test_subentry_flow_with_invalid_data(hass: HomeAssistant, client) -> N
|
|||||||
mock_platform(hass, "test.config_flow", None)
|
mock_platform(hass, "test.config_flow", None)
|
||||||
|
|
||||||
class TestFlow(core_ce.ConfigFlow):
|
class TestFlow(core_ce.ConfigFlow):
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
class SubentryFlowHandler(core_ce.ConfigSubentryFlow):
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
@ -1317,16 +1368,14 @@ async def test_subentry_flow_with_invalid_data(hass: HomeAssistant, client) -> N
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def async_step_finish(self, user_input=None):
|
async def async_step_finish(self, user_input=None):
|
||||||
return self.async_create_entry(
|
return self.async_create_entry(title="Enable disable", data=user_input)
|
||||||
title="Enable disable", data=user_input
|
|
||||||
)
|
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(cls, config_entry):
|
def async_get_supported_subentry_flows(
|
||||||
return ("test",)
|
cls, config_entry: core_ce.ConfigEntry
|
||||||
|
) -> dict[str, type[core_ce.ConfigSubentryFlow]]:
|
||||||
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
MockConfigEntry(
|
MockConfigEntry(
|
||||||
domain="test",
|
domain="test",
|
||||||
@ -1409,7 +1458,7 @@ async def test_get_single(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "user",
|
"source": "user",
|
||||||
"state": "loaded",
|
"state": "loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1771,7 +1820,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1792,7 +1841,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": "Unsupported API",
|
"reason": "Unsupported API",
|
||||||
"source": "bla2",
|
"source": "bla2",
|
||||||
"state": "setup_error",
|
"state": "setup_error",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1813,7 +1862,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1834,7 +1883,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla4",
|
"source": "bla4",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1855,7 +1904,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla5",
|
"source": "bla5",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1887,7 +1936,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1918,7 +1967,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla4",
|
"source": "bla4",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1939,7 +1988,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla5",
|
"source": "bla5",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1970,7 +2019,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -1991,7 +2040,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2028,7 +2077,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2049,7 +2098,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": "Unsupported API",
|
"reason": "Unsupported API",
|
||||||
"source": "bla2",
|
"source": "bla2",
|
||||||
"state": "setup_error",
|
"state": "setup_error",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2070,7 +2119,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2091,7 +2140,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla4",
|
"source": "bla4",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2112,7 +2161,7 @@ async def test_get_matching_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla5",
|
"source": "bla5",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2221,7 +2270,7 @@ async def test_subscribe_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2245,7 +2294,7 @@ async def test_subscribe_entries_ws(
|
|||||||
"reason": "Unsupported API",
|
"reason": "Unsupported API",
|
||||||
"source": "bla2",
|
"source": "bla2",
|
||||||
"state": "setup_error",
|
"state": "setup_error",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2269,7 +2318,7 @@ async def test_subscribe_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2299,7 +2348,7 @@ async def test_subscribe_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2330,7 +2379,7 @@ async def test_subscribe_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2360,7 +2409,7 @@ async def test_subscribe_entries_ws(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2452,7 +2501,7 @@ async def test_subscribe_entries_ws_filtered(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2476,7 +2525,7 @@ async def test_subscribe_entries_ws_filtered(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2508,7 +2557,7 @@ async def test_subscribe_entries_ws_filtered(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2536,7 +2585,7 @@ async def test_subscribe_entries_ws_filtered(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla3",
|
"source": "bla3",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2568,7 +2617,7 @@ async def test_subscribe_entries_ws_filtered(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
@ -2598,7 +2647,7 @@ async def test_subscribe_entries_ws_filtered(
|
|||||||
"reason": None,
|
"reason": None,
|
||||||
"source": "bla",
|
"source": "bla",
|
||||||
"state": "not_loaded",
|
"state": "not_loaded",
|
||||||
"supported_subentries": [],
|
"supported_subentry_flows": {},
|
||||||
"supports_options": False,
|
"supports_options": False,
|
||||||
"supports_reconfigure": False,
|
"supports_reconfigure": False,
|
||||||
"supports_remove_device": False,
|
"supports_remove_device": False,
|
||||||
|
@ -1959,7 +1959,7 @@ async def test_create_entry_subentries(
|
|||||||
|
|
||||||
entries = hass.config_entries.async_entries("comp")
|
entries = hass.config_entries.async_entries("comp")
|
||||||
assert len(entries) == 1
|
assert len(entries) == 1
|
||||||
assert entries[0].supported_subentries == ()
|
assert entries[0].supported_subentry_flows == {}
|
||||||
assert entries[0].data == {"example": "data"}
|
assert entries[0].data == {"example": "data"}
|
||||||
assert len(entries[0].subentries) == 1
|
assert len(entries[0].subentries) == 1
|
||||||
subentry_id = list(entries[0].subentries)[0]
|
subentry_id = list(entries[0].subentries)[0]
|
||||||
@ -1984,22 +1984,15 @@ async def test_entry_subentry(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with mock_config_flow("test", TestFlow):
|
with mock_config_flow("test", TestFlow):
|
||||||
flow = await manager.subentries.async_create_flow(
|
flow = await manager.subentries.async_create_flow(
|
||||||
@ -2029,7 +2022,9 @@ async def test_entry_subentry(
|
|||||||
unique_id="test",
|
unique_id="test",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
assert entry.supported_subentries == ("test",)
|
assert entry.supported_subentry_flows == {
|
||||||
|
"test": {"supports_reconfigure": False}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_entry_subentry_non_string(
|
async def test_entry_subentry_non_string(
|
||||||
@ -2044,22 +2039,15 @@ async def test_entry_subentry_non_string(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with mock_config_flow("test", TestFlow):
|
with mock_config_flow("test", TestFlow):
|
||||||
flow = await manager.subentries.async_create_flow(
|
flow = await manager.subentries.async_create_flow(
|
||||||
@ -2093,22 +2081,15 @@ async def test_entry_subentry_no_context(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with mock_config_flow("test", TestFlow), pytest.raises(KeyError):
|
with mock_config_flow("test", TestFlow), pytest.raises(KeyError):
|
||||||
await manager.subentries.async_create_flow(
|
await manager.subentries.async_create_flow(
|
||||||
@ -2146,22 +2127,15 @@ async def test_entry_subentry_duplicate(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with mock_config_flow("test", TestFlow):
|
with mock_config_flow("test", TestFlow):
|
||||||
flow = await manager.subentries.async_create_flow(
|
flow = await manager.subentries.async_create_flow(
|
||||||
@ -2194,22 +2168,15 @@ async def test_entry_subentry_abort(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with mock_config_flow("test", TestFlow):
|
with mock_config_flow("test", TestFlow):
|
||||||
flow = await manager.subentries.async_create_flow(
|
flow = await manager.subentries.async_create_flow(
|
||||||
@ -2248,22 +2215,15 @@ async def test_entry_subentry_deleted_config_entry(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with mock_config_flow("test", TestFlow):
|
with mock_config_flow("test", TestFlow):
|
||||||
flow = await manager.subentries.async_create_flow(
|
flow = await manager.subentries.async_create_flow(
|
||||||
@ -2286,7 +2246,7 @@ async def test_entry_subentry_deleted_config_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_entry_subentry_unsupported(
|
async def test_entry_subentry_unsupported_subentry_type(
|
||||||
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test attempting to start a subentry flow for a config entry without support."""
|
"""Test attempting to start a subentry flow for a config entry without support."""
|
||||||
@ -2298,22 +2258,15 @@ async def test_entry_subentry_unsupported(
|
|||||||
class TestFlow(config_entries.ConfigFlow):
|
class TestFlow(config_entries.ConfigFlow):
|
||||||
"""Test flow."""
|
"""Test flow."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
@callback
|
|
||||||
def async_get_subentry_flow(config_entry, subentry_type: str):
|
|
||||||
"""Test subentry flow."""
|
|
||||||
|
|
||||||
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
class SubentryFlowHandler(data_entry_flow.FlowHandler):
|
||||||
"""Test subentry flow handler."""
|
"""Test subentry flow handler."""
|
||||||
|
|
||||||
return SubentryFlowHandler()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@callback
|
@callback
|
||||||
def async_supported_subentries(
|
def async_get_supported_subentry_flows(
|
||||||
cls, config_entry: ConfigEntry
|
cls, config_entry: ConfigEntry
|
||||||
) -> tuple[str, ...]:
|
) -> dict[str, type[config_entries.ConfigSubentryFlow]]:
|
||||||
return ("test",)
|
return {"test": TestFlow.SubentryFlowHandler}
|
||||||
|
|
||||||
with (
|
with (
|
||||||
mock_config_flow("test", TestFlow),
|
mock_config_flow("test", TestFlow),
|
||||||
@ -2329,7 +2282,7 @@ async def test_entry_subentry_unsupported(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_entry_subentry_unsupported_subentry_type(
|
async def test_entry_subentry_unsupported(
|
||||||
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
hass: HomeAssistant, manager: config_entries.ConfigEntries
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test attempting to start a subentry flow for a config entry without support."""
|
"""Test attempting to start a subentry flow for a config entry without support."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user