Refactor logbook helpers to reduce splits and lookups (#108933)

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
pull/108942/head
J. Nick Koston 2024-01-26 20:07:24 -10:00 committed by GitHub
parent 61c6c70a7d
commit 5dac5d5c7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 36 additions and 29 deletions

View File

@ -39,7 +39,8 @@ def async_filter_entities(hass: HomeAssistant, entity_ids: list[str]) -> list[st
return [
entity_id
for entity_id in entity_ids
if not _is_entity_id_filtered(hass, ent_reg, entity_id)
if split_entity_id(entity_id)[0] not in ALWAYS_CONTINUOUS_DOMAINS
and not is_sensor_continuous(hass, ent_reg, entity_id)
]
@ -216,18 +217,37 @@ def async_subscribe_events(
)
def is_sensor_continuous(ent_reg: er.EntityRegistry, entity_id: str) -> bool:
"""Determine if a sensor is continuous by checking its state class.
def is_sensor_continuous(
hass: HomeAssistant, ent_reg: er.EntityRegistry, entity_id: str
) -> bool:
"""Determine if a sensor is continuous.
Sensors with a unit_of_measurement are also considered continuous, but are filtered
already by the SQL query generated by _get_events
Sensors with a unit_of_measurement or state_class are considered continuous.
The unit_of_measurement check will already happen if this is
called for historical data because the SQL query generated by _get_events
will filter out any sensors with a unit_of_measurement.
If the state still exists in the state machine, this function still
checks for ATTR_UNIT_OF_MEASUREMENT since the live mode is not filtered
by the SQL query.
"""
if not (entry := ent_reg.async_get(entity_id)):
# Entity not registered, so can't have a state class
return False
return (
entry.capabilities is not None
and entry.capabilities.get(ATTR_STATE_CLASS) is not None
# If it is in the state machine we can quick check if it
# has a unit_of_measurement or state_class, and filter if
# it does
if (state := hass.states.get(entity_id)) and (attributes := state.attributes):
return ATTR_UNIT_OF_MEASUREMENT in attributes or ATTR_STATE_CLASS in attributes
# If its not in the state machine, we need to check
# the entity registry to see if its a sensor
# filter with a state class. We do not check
# for unit_of_measurement since the SQL query
# will filter out any sensors with a unit_of_measurement
# and we should never get here in live mode because
# the state machine will always have the state.
return bool(
(entry := ent_reg.async_get(entity_id))
and entry.capabilities
and entry.capabilities.get(ATTR_STATE_CLASS)
)
@ -239,24 +259,8 @@ def _is_state_filtered(new_state: State, old_state: State) -> bool:
"""
return bool(
new_state.state == old_state.state
or split_entity_id(new_state.entity_id)[0] in ALWAYS_CONTINUOUS_DOMAINS
or new_state.last_changed != new_state.last_updated
or new_state.domain in ALWAYS_CONTINUOUS_DOMAINS
or ATTR_UNIT_OF_MEASUREMENT in new_state.attributes
or ATTR_STATE_CLASS in new_state.attributes
)
def _is_entity_id_filtered(
hass: HomeAssistant, ent_reg: er.EntityRegistry, entity_id: str
) -> bool:
"""Check if the logbook should filter an entity.
Used to setup listeners and which entities to select
from the database when a list of entities is requested.
"""
return bool(
split_entity_id(entity_id)[0] in ALWAYS_CONTINUOUS_DOMAINS
or (state := hass.states.get(entity_id))
and (ATTR_UNIT_OF_MEASUREMENT in state.attributes)
or is_sensor_continuous(ent_reg, entity_id)
)

View File

@ -175,6 +175,7 @@ class EventProcessor:
"""Humanify rows."""
return list(
_humanify(
self.hass,
rows,
self.ent_reg,
self.logbook_run,
@ -184,6 +185,7 @@ class EventProcessor:
def _humanify(
hass: HomeAssistant,
rows: Generator[EventAsRow, None, None] | Sequence[Row] | Result,
ent_reg: er.EntityRegistry,
logbook_run: LogbookRun,
@ -219,7 +221,7 @@ def _humanify(
if (
is_continuous := continuous_sensors.get(entity_id)
) is None and split_entity_id(entity_id)[0] == SENSOR_DOMAIN:
is_continuous = is_sensor_continuous(ent_reg, entity_id)
is_continuous = is_sensor_continuous(hass, ent_reg, entity_id)
continuous_sensors[entity_id] = is_continuous
if is_continuous:
continue

View File

@ -77,6 +77,7 @@ def mock_humanify(hass_, rows):
context_augmenter = processor.ContextAugmenter(logbook_run)
return list(
processor._humanify(
hass_,
rows,
ent_reg,
logbook_run,