From dff5e457614513eca71a016e36e136a5f76d14e9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" <nick@koston.org> Date: Thu, 25 Jan 2024 20:20:19 -1000 Subject: [PATCH] Small speed up to listing config entries in the websocket api (#108892) --- .../components/config/config_entries.py | 29 +++++++++---------- homeassistant/config_entries.py | 13 +++++++++ tests/test_config_entries.py | 7 +++++ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/homeassistant/components/config/config_entries.py b/homeassistant/components/config/config_entries.py index c289459a2af..b19c0101232 100644 --- a/homeassistant/components/config/config_entries.py +++ b/homeassistant/components/config/config_entries.py @@ -516,7 +516,7 @@ async def async_matching_config_entries( if not type_filter: return [entry_json(entry) for entry in entries] - integrations = {} + integrations: dict[str, Integration] = {} # Fetch all the integrations so we can check their type domains = {entry.domain for entry in entries} for domain_key, integration_or_exc in ( @@ -531,35 +531,32 @@ async def async_matching_config_entries( # when only helpers are requested, also filter out entries # from unknown integrations. This prevent them from showing # up in the helpers UI. - entries = [ - entry + filter_is_not_helper = type_filter != ["helper"] + filter_set = set(type_filter) + return [ + entry_json(entry) for entry in entries - if (type_filter != ["helper"] and entry.domain not in integrations) - or ( - entry.domain in integrations - and integrations[entry.domain].integration_type in type_filter + # If the filter is not 'helper', we still include the integration + # even if its not returned from async_get_integrations for backwards + # compatibility. + if ( + (integration := integrations.get(entry.domain)) + and integration.integration_type in filter_set ) + or (filter_is_not_helper and entry.domain not in integrations) ] - return [entry_json(entry) for entry in entries] - @callback def entry_json(entry: config_entries.ConfigEntry) -> dict[str, Any]: """Return JSON value of a config entry.""" - handler = config_entries.HANDLERS.get(entry.domain) - # work out if handler has support for options flow - supports_options = handler is not None and handler.async_supports_options_flow( - entry - ) - return { "entry_id": entry.entry_id, "domain": entry.domain, "title": entry.title, "source": entry.source, "state": entry.state.value, - "supports_options": supports_options, + "supports_options": entry.supports_options, "supports_remove_device": entry.supports_remove_device or False, "supports_unload": entry.supports_unload or False, "pref_disable_new_entities": entry.pref_disable_new_entities, diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index 7eee83953a7..d9cfbd08886 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -54,6 +54,7 @@ if TYPE_CHECKING: from .components.zeroconf import ZeroconfServiceInfo from .helpers.service_info.mqtt import MqttServiceInfo + _LOGGER = logging.getLogger(__name__) SOURCE_BLUETOOTH = "bluetooth" @@ -238,6 +239,7 @@ class ConfigEntry: "_integration_for_domain", "_tries", "_setup_again_job", + "_supports_options", ) def __init__( @@ -318,6 +320,9 @@ class ConfigEntry: # Supports remove device self.supports_remove_device: bool | None = None + # Supports options + self._supports_options: bool | None = None + # Listeners to call on update self.update_listeners: list[UpdateListenerType] = [] @@ -351,6 +356,14 @@ class ConfigEntry: f"title={self.title} state={self.state} unique_id={self.unique_id}>" ) + @property + def supports_options(self) -> bool: + """Return if entry supports config options.""" + if self._supports_options is None and (handler := HANDLERS.get(self.domain)): + # work out if handler has support for options flow + self._supports_options = handler.async_supports_options_flow(self) + return self._supports_options or False + async def async_setup( self, hass: HomeAssistant, diff --git a/tests/test_config_entries.py b/tests/test_config_entries.py index db382ac35f4..e9e1437c06c 100644 --- a/tests/test_config_entries.py +++ b/tests/test_config_entries.py @@ -734,6 +734,7 @@ async def test_as_dict(snapshot: SnapshotAssertion) -> None: "_integration_for_domain", "_tries", "_setup_again_job", + "_supports_options", } entry = MockConfigEntry(entry_id="mock-entry") @@ -1176,6 +1177,7 @@ async def test_create_entry_options( entries = hass.config_entries.async_entries("comp") assert len(entries) == 1 + assert entries[0].supports_options is False assert entries[0].data == {"example": "data"} assert entries[0].options == {"example": "option"} @@ -1202,6 +1204,10 @@ async def test_entry_options( return OptionsFlowHandler() + def async_supports_options_flow(self, entry: MockConfigEntry) -> bool: + """Test options flow.""" + return True + config_entries.HANDLERS["test"] = TestFlow() flow = await manager.options.async_create_flow( entry.entry_id, context={"source": "test"}, data=None @@ -1216,6 +1222,7 @@ async def test_entry_options( assert entry.data == {"first": True} assert entry.options == {"second": True} + assert entry.supports_options is True async def test_entry_options_abort( -- GitLab