mirror of
https://github.com/home-assistant/core.git
synced 2025-05-01 20:57:51 +00:00
Increase coverage and optimize history skip_initial_state (#42014)
Avoids a database query when all states have not changed in the time window when skip_initial_state is set.
This commit is contained in:
parent
388a5d8c91
commit
9649525fe6
@ -1,11 +1,11 @@
|
|||||||
"""Provide pre-made queries on top of the recorder component."""
|
"""Provide pre-made queries on top of the recorder component."""
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import timedelta
|
from datetime import datetime as dt, timedelta
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
from typing import Optional, cast
|
from typing import Iterable, Optional, cast
|
||||||
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from sqlalchemy import and_, bindparam, func, not_, or_
|
from sqlalchemy import and_, bindparam, func, not_, or_
|
||||||
@ -33,6 +33,7 @@ from homeassistant.helpers.entityfilter import (
|
|||||||
CONF_ENTITY_GLOBS,
|
CONF_ENTITY_GLOBS,
|
||||||
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA,
|
INCLUDE_EXCLUDE_BASE_FILTER_SCHEMA,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.typing import HomeAssistantType
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
# mypy: allow-untyped-defs, no-check-untyped-defs
|
||||||
@ -502,6 +503,13 @@ class HistoryPeriodView(HomeAssistantView):
|
|||||||
|
|
||||||
hass = request.app["hass"]
|
hass = request.app["hass"]
|
||||||
|
|
||||||
|
if (
|
||||||
|
not include_start_time_state
|
||||||
|
and entity_ids
|
||||||
|
and not _entities_may_have_state_changes_after(hass, entity_ids, start_time)
|
||||||
|
):
|
||||||
|
return self.json([])
|
||||||
|
|
||||||
return cast(
|
return cast(
|
||||||
web.Response,
|
web.Response,
|
||||||
await hass.async_add_executor_job(
|
await hass.async_add_executor_job(
|
||||||
@ -660,6 +668,19 @@ def _glob_to_like(glob_str):
|
|||||||
return States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS))
|
return States.entity_id.like(glob_str.translate(GLOB_TO_SQL_CHARS))
|
||||||
|
|
||||||
|
|
||||||
|
def _entities_may_have_state_changes_after(
|
||||||
|
hass: HomeAssistantType, entity_ids: Iterable, start_time: dt
|
||||||
|
) -> bool:
|
||||||
|
"""Check the state machine to see if entities have changed since start time."""
|
||||||
|
for entity_id in entity_ids:
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
|
||||||
|
if state is None or state.last_changed > start_time:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class LazyState(State):
|
class LazyState(State):
|
||||||
"""A lazy version of core State."""
|
"""A lazy version of core State."""
|
||||||
|
|
||||||
|
@ -970,3 +970,41 @@ async def test_entity_ids_limit_via_api(hass, hass_client):
|
|||||||
assert len(response_json) == 2
|
assert len(response_json) == 2
|
||||||
assert response_json[0][0]["entity_id"] == "light.kitchen"
|
assert response_json[0][0]["entity_id"] == "light.kitchen"
|
||||||
assert response_json[1][0]["entity_id"] == "light.cow"
|
assert response_json[1][0]["entity_id"] == "light.cow"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entity_ids_limit_via_api_with_skip_initial_state(hass, hass_client):
|
||||||
|
"""Test limiting history to entity_ids with skip_initial_state."""
|
||||||
|
await hass.async_add_executor_job(init_recorder_component, hass)
|
||||||
|
await async_setup_component(
|
||||||
|
hass,
|
||||||
|
"history",
|
||||||
|
{"history": {}},
|
||||||
|
)
|
||||||
|
await hass.async_add_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
hass.states.async_set("light.kitchen", "on")
|
||||||
|
hass.states.async_set("light.cow", "on")
|
||||||
|
hass.states.async_set("light.nomatch", "on")
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
await hass.async_add_executor_job(trigger_db_commit, hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
await hass.async_add_executor_job(hass.data[recorder.DATA_INSTANCE].block_till_done)
|
||||||
|
|
||||||
|
client = await hass_client()
|
||||||
|
response = await client.get(
|
||||||
|
f"/api/history/period/{dt_util.utcnow().isoformat()}?filter_entity_id=light.kitchen,light.cow&skip_initial_state",
|
||||||
|
)
|
||||||
|
assert response.status == 200
|
||||||
|
response_json = await response.json()
|
||||||
|
assert len(response_json) == 0
|
||||||
|
|
||||||
|
when = dt_util.utcnow() - timedelta(minutes=1)
|
||||||
|
response = await client.get(
|
||||||
|
f"/api/history/period/{when.isoformat()}?filter_entity_id=light.kitchen,light.cow&skip_initial_state",
|
||||||
|
)
|
||||||
|
assert response.status == 200
|
||||||
|
response_json = await response.json()
|
||||||
|
assert len(response_json) == 2
|
||||||
|
assert response_json[0][0]["entity_id"] == "light.kitchen"
|
||||||
|
assert response_json[1][0]["entity_id"] == "light.cow"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user