From 3875fc59530d7794f80bb650c0d329499aade113 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 5 Jul 2022 23:08:39 -0500 Subject: [PATCH] Cache the response from config/device_registry/list (#74483) --- .../components/config/device_registry.py | 56 ++++++++++++++----- .../components/config/test_device_registry.py | 30 +++++++++- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/homeassistant/components/config/device_registry.py b/homeassistant/components/config/device_registry.py index e811d43d502e..587710a8c2a2 100644 --- a/homeassistant/components/config/device_registry.py +++ b/homeassistant/components/config/device_registry.py @@ -1,12 +1,23 @@ """HTTP views to interact with the device registry.""" +from __future__ import annotations + import voluptuous as vol from homeassistant import loader from homeassistant.components import websocket_api from homeassistant.components.websocket_api.decorators import require_admin -from homeassistant.core import HomeAssistant, callback +from homeassistant.components.websocket_api.messages import ( + IDEN_JSON_TEMPLATE, + IDEN_TEMPLATE, + message_to_json, +) +from homeassistant.core import Event, HomeAssistant, callback from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers.device_registry import DeviceEntryDisabler, async_get +from homeassistant.helpers.device_registry import ( + EVENT_DEVICE_REGISTRY_UPDATED, + DeviceEntryDisabler, + async_get, +) WS_TYPE_LIST = "config/device_registry/list" SCHEMA_WS_LIST = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( @@ -29,6 +40,36 @@ SCHEMA_WS_UPDATE = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( async def async_setup(hass): """Enable the Device Registry views.""" + + cached_list_devices: str | None = None + + @callback + def _async_clear_list_device_cache(event: Event) -> None: + nonlocal cached_list_devices + cached_list_devices = None + + @callback + def websocket_list_devices(hass, connection, msg): + """Handle list devices command.""" + nonlocal cached_list_devices + if not cached_list_devices: + registry = async_get(hass) + cached_list_devices = message_to_json( + websocket_api.result_message( + IDEN_TEMPLATE, + [_entry_dict(entry) for entry in registry.devices.values()], + ) + ) + connection.send_message( + cached_list_devices.replace(IDEN_JSON_TEMPLATE, str(msg["id"]), 1) + ) + + hass.bus.async_listen( + EVENT_DEVICE_REGISTRY_UPDATED, + _async_clear_list_device_cache, + run_immediately=True, + ) + websocket_api.async_register_command( hass, WS_TYPE_LIST, websocket_list_devices, SCHEMA_WS_LIST ) @@ -41,17 +82,6 @@ async def async_setup(hass): return True -@callback -def websocket_list_devices(hass, connection, msg): - """Handle list devices command.""" - registry = async_get(hass) - connection.send_message( - websocket_api.result_message( - msg["id"], [_entry_dict(entry) for entry in registry.devices.values()] - ) - ) - - @require_admin @callback def websocket_update_device(hass, connection, msg): diff --git a/tests/components/config/test_device_registry.py b/tests/components/config/test_device_registry.py index f923b3261009..f9289b6e3b3b 100644 --- a/tests/components/config/test_device_registry.py +++ b/tests/components/config/test_device_registry.py @@ -29,14 +29,14 @@ def registry(hass): async def test_list_devices(hass, client, registry): """Test list entries.""" - registry.async_get_or_create( + device1 = registry.async_get_or_create( config_entry_id="1234", connections={("ethernet", "12:34:56:78:90:AB:CD:EF")}, identifiers={("bridgeid", "0123")}, manufacturer="manufacturer", model="model", ) - registry.async_get_or_create( + device2 = registry.async_get_or_create( config_entry_id="1234", identifiers={("bridgeid", "1234")}, manufacturer="manufacturer", @@ -85,6 +85,32 @@ async def test_list_devices(hass, client, registry): }, ] + registry.async_remove_device(device2.id) + await hass.async_block_till_done() + + await client.send_json({"id": 6, "type": "config/device_registry/list"}) + msg = await client.receive_json() + + assert msg["result"] == [ + { + "area_id": None, + "config_entries": ["1234"], + "configuration_url": None, + "connections": [["ethernet", "12:34:56:78:90:AB:CD:EF"]], + "disabled_by": None, + "entry_type": None, + "hw_version": None, + "id": device1.id, + "identifiers": [["bridgeid", "0123"]], + "manufacturer": "manufacturer", + "model": "model", + "name": None, + "name_by_user": None, + "sw_version": None, + "via_device_id": None, + } + ] + @pytest.mark.parametrize( "payload_key,payload_value",