mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 21:06:19 +00:00
Reinitialize zeroconf discovery flow on unignore (#125753)
* Reinitialize zeroconf discovery flow on unignore * Adjust tests * Improve comments * Fix logic for updating discovery keys * Add tests * Use mock_config_flow helper in new config_entries test * Add discovery_keys attribute to ConfigEntry * Update zeroconf rediscovery * Change type of ConfigEntry.discovery_keys * Update tests * Fix DiscoveryKey.from_json_dict and add tests * Fix test --------- Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
@@ -49,6 +49,7 @@ from .exceptions import (
|
||||
)
|
||||
from .helpers import device_registry, entity_registry, issue_registry as ir, storage
|
||||
from .helpers.debounce import Debouncer
|
||||
from .helpers.discovery_flow import DiscoveryKey
|
||||
from .helpers.dispatcher import SignalType, async_dispatcher_send_internal
|
||||
from .helpers.event import (
|
||||
RANDOM_MICROSECOND_MAX,
|
||||
@@ -120,7 +121,7 @@ HANDLERS: Registry[str, type[ConfigFlow]] = Registry()
|
||||
|
||||
STORAGE_KEY = "core.config_entries"
|
||||
STORAGE_VERSION = 1
|
||||
STORAGE_VERSION_MINOR = 3
|
||||
STORAGE_VERSION_MINOR = 4
|
||||
|
||||
SAVE_DELAY = 1
|
||||
|
||||
@@ -317,6 +318,7 @@ class ConfigEntry(Generic[_DataT]):
|
||||
_tries: int
|
||||
created_at: datetime
|
||||
modified_at: datetime
|
||||
discovery_keys: tuple[DiscoveryKey, ...]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -324,6 +326,7 @@ class ConfigEntry(Generic[_DataT]):
|
||||
created_at: datetime | None = None,
|
||||
data: Mapping[str, Any],
|
||||
disabled_by: ConfigEntryDisabler | None = None,
|
||||
discovery_keys: tuple[DiscoveryKey, ...],
|
||||
domain: str,
|
||||
entry_id: str | None = None,
|
||||
minor_version: int,
|
||||
@@ -422,6 +425,7 @@ class ConfigEntry(Generic[_DataT]):
|
||||
_setter(self, "_tries", 0)
|
||||
_setter(self, "created_at", created_at or utcnow())
|
||||
_setter(self, "modified_at", modified_at or utcnow())
|
||||
_setter(self, "discovery_keys", discovery_keys)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Representation of ConfigEntry."""
|
||||
@@ -951,6 +955,7 @@ class ConfigEntry(Generic[_DataT]):
|
||||
return {
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"data": dict(self.data),
|
||||
"discovery_keys": self.discovery_keys,
|
||||
"disabled_by": self.disabled_by,
|
||||
"domain": self.domain,
|
||||
"entry_id": self.entry_id,
|
||||
@@ -1364,6 +1369,30 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager[ConfigFlowResult]):
|
||||
ir.async_delete_issue(self.hass, HOMEASSISTANT_DOMAIN, issue_id)
|
||||
|
||||
if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY:
|
||||
# If there's an ignored config entry with a matching unique ID,
|
||||
# update the discovery key.
|
||||
if (
|
||||
(discovery_key := flow.context.get("discovery_key"))
|
||||
and (unique_id := flow.unique_id) is not None
|
||||
and (
|
||||
entry := self.config_entries.async_entry_for_domain_unique_id(
|
||||
result["handler"], unique_id
|
||||
)
|
||||
)
|
||||
and entry.source == SOURCE_IGNORE
|
||||
and discovery_key not in (known_discovery_keys := entry.discovery_keys)
|
||||
):
|
||||
new_discovery_keys = tuple([*known_discovery_keys, discovery_key][-10:])
|
||||
_LOGGER.debug(
|
||||
"Updating discovery keys for %s entry %s %s -> %s",
|
||||
entry.domain,
|
||||
unique_id,
|
||||
known_discovery_keys,
|
||||
new_discovery_keys,
|
||||
)
|
||||
self.config_entries.async_update_entry(
|
||||
entry, discovery_keys=new_discovery_keys
|
||||
)
|
||||
return result
|
||||
|
||||
# Avoid adding a config entry for a integration
|
||||
@@ -1420,8 +1449,11 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager[ConfigFlowResult]):
|
||||
if existing_entry is not None and existing_entry.state.recoverable:
|
||||
await self.config_entries.async_unload(existing_entry.entry_id)
|
||||
|
||||
discovery_key = flow.context.get("discovery_key")
|
||||
discovery_keys = (discovery_key,) if discovery_key else ()
|
||||
entry = ConfigEntry(
|
||||
data=result["data"],
|
||||
discovery_keys=discovery_keys,
|
||||
domain=result["handler"],
|
||||
minor_version=result["minor_version"],
|
||||
options=result["options"],
|
||||
@@ -1649,6 +1681,11 @@ class ConfigEntryStore(storage.Store[dict[str, list[dict[str, Any]]]]):
|
||||
for entry in data["entries"]:
|
||||
entry["created_at"] = entry["modified_at"] = created_at
|
||||
|
||||
if old_minor_version < 4:
|
||||
# Version 1.4 adds discovery_keys
|
||||
for entry in data["entries"]:
|
||||
entry["discovery_keys"] = []
|
||||
|
||||
if old_major_version > 1:
|
||||
raise NotImplementedError
|
||||
return data
|
||||
@@ -1836,6 +1873,9 @@ class ConfigEntries:
|
||||
created_at=datetime.fromisoformat(entry["created_at"]),
|
||||
data=entry["data"],
|
||||
disabled_by=try_parse_enum(ConfigEntryDisabler, entry["disabled_by"]),
|
||||
discovery_keys=tuple(
|
||||
DiscoveryKey.from_json_dict(key) for key in entry["discovery_keys"]
|
||||
),
|
||||
domain=entry["domain"],
|
||||
entry_id=entry_id,
|
||||
minor_version=entry["minor_version"],
|
||||
@@ -1992,6 +2032,7 @@ class ConfigEntries:
|
||||
entry: ConfigEntry,
|
||||
*,
|
||||
data: Mapping[str, Any] | UndefinedType = UNDEFINED,
|
||||
discovery_keys: tuple[DiscoveryKey, ...] | UndefinedType = UNDEFINED,
|
||||
minor_version: int | UndefinedType = UNDEFINED,
|
||||
options: Mapping[str, Any] | UndefinedType = UNDEFINED,
|
||||
pref_disable_new_entities: bool | UndefinedType = UNDEFINED,
|
||||
@@ -2021,6 +2062,7 @@ class ConfigEntries:
|
||||
changed = True
|
||||
|
||||
for attr, value in (
|
||||
("discovery_keys", discovery_keys),
|
||||
("minor_version", minor_version),
|
||||
("pref_disable_new_entities", pref_disable_new_entities),
|
||||
("pref_disable_polling", pref_disable_polling),
|
||||
@@ -2451,7 +2493,20 @@ class ConfigFlow(ConfigEntryBaseFlow):
|
||||
]
|
||||
|
||||
async def async_step_ignore(self, user_input: dict[str, Any]) -> ConfigFlowResult:
|
||||
"""Ignore this config flow."""
|
||||
"""Ignore this config flow.
|
||||
|
||||
Ignoring a config flow works by creating a config entry with source set to
|
||||
SOURCE_IGNORE.
|
||||
|
||||
There will only be a single active discovery flow per device, also when the
|
||||
integration has multiple discovery sources for the same device. This method
|
||||
is called when the user ignores a discovered device or service, we then store
|
||||
the key for the flow being ignored.
|
||||
|
||||
Once the ignore config entry is created, ConfigEntriesFlowManager.async_finish_flow
|
||||
will make sure the discovery key is kept up to date since it may not be stable
|
||||
unlike the unique id.
|
||||
"""
|
||||
await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False)
|
||||
return self.async_create_entry(title=user_input["title"], data={})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user