From d2a52230ff4e1863b7e867196e6f7db21b217c42 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 4 Sep 2023 15:51:19 -0500 Subject: [PATCH] Speed up responding to states being polled via API (#99621) * Speed up responding to states being polled via API Switch to using `as_dict_json` to avoid serializing states over and over when the states api is polled since the mobile app is already building the cache as it also polls the states via the websocket_api * Speed up responding to states being polled via API Switch to using `as_dict_json` to avoid serializing states over and over when the states api is polled since the mobile app is already building the cache as it also polls the states via the websocket_api * fix json * cover --- homeassistant/components/api/__init__.py | 29 ++++++++++++++++-------- tests/components/api/test_init.py | 4 +++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/api/__init__.py b/homeassistant/components/api/__init__.py index b427341546e..10cf63b701d 100644 --- a/homeassistant/components/api/__init__.py +++ b/homeassistant/components/api/__init__.py @@ -14,6 +14,7 @@ from homeassistant.auth.permissions.const import POLICY_READ from homeassistant.bootstrap import DATA_LOGGING from homeassistant.components.http import HomeAssistantView, require_admin from homeassistant.const import ( + CONTENT_TYPE_JSON, EVENT_HOMEASSISTANT_STOP, MATCH_ALL, URL_API, @@ -195,15 +196,19 @@ class APIStatesView(HomeAssistantView): user: User = request["hass_user"] hass: HomeAssistant = request.app["hass"] if user.is_admin: - return self.json([state.as_dict() for state in hass.states.async_all()]) - entity_perm = user.permissions.check_entity - return self.json( - [ - state.as_dict() + states = (state.as_dict_json() for state in hass.states.async_all()) + else: + entity_perm = user.permissions.check_entity + states = ( + state.as_dict_json() for state in hass.states.async_all() if entity_perm(state.entity_id, "read") - ] + ) + response = web.Response( + body=f'[{",".join(states)}]', content_type=CONTENT_TYPE_JSON ) + response.enable_compression() + return response class APIEntityStateView(HomeAssistantView): @@ -213,14 +218,18 @@ class APIEntityStateView(HomeAssistantView): name = "api:entity-state" @ha.callback - def get(self, request, entity_id): + def get(self, request: web.Request, entity_id: str) -> web.Response: """Retrieve state of entity.""" - user = request["hass_user"] + user: User = request["hass_user"] + hass: HomeAssistant = request.app["hass"] if not user.permissions.check_entity(entity_id, POLICY_READ): raise Unauthorized(entity_id=entity_id) - if state := request.app["hass"].states.get(entity_id): - return self.json(state) + if state := hass.states.get(entity_id): + return web.Response( + body=state.as_dict_json(), + content_type=CONTENT_TYPE_JSON, + ) return self.json_message("Entity not found.", HTTPStatus.NOT_FOUND) async def post(self, request, entity_id): diff --git a/tests/components/api/test_init.py b/tests/components/api/test_init.py index f61988eff5a..38528b335b0 100644 --- a/tests/components/api/test_init.py +++ b/tests/components/api/test_init.py @@ -575,11 +575,13 @@ async def test_states( ) -> None: """Test fetching all states as admin.""" hass.states.async_set("test.entity", "hello") + hass.states.async_set("test.entity2", "hello") resp = await mock_api_client.get(const.URL_API_STATES) assert resp.status == HTTPStatus.OK json = await resp.json() - assert len(json) == 1 + assert len(json) == 2 assert json[0]["entity_id"] == "test.entity" + assert json[1]["entity_id"] == "test.entity2" async def test_states_view_filters(