Skip to content

Commit

Permalink
Refactor logbook helpers to reduce splits and lookups (#108933)
Browse files Browse the repository at this point in the history
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
  • Loading branch information
bdraco and balloob committed Jan 27, 2024
1 parent 61c6c70 commit 5dac5d5
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 29 deletions.
60 changes: 32 additions & 28 deletions homeassistant/components/logbook/helpers.py
Expand Up @@ -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)
]


Expand Down Expand Up @@ -216,18 +217,37 @@ def _forward_state_events_filtered(event: EventType[EventStateChangedData]) -> N
)


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 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.
Sensors with a unit_of_measurement are also considered continuous, but are filtered
already by the SQL query generated by _get_events
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)
)


Expand All @@ -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)
)
4 changes: 3 additions & 1 deletion homeassistant/components/logbook/processor.py
Expand Up @@ -175,6 +175,7 @@ def humanify(
"""Humanify rows."""
return list(
_humanify(
self.hass,
rows,
self.ent_reg,
self.logbook_run,
Expand All @@ -184,6 +185,7 @@ def humanify(


def _humanify(
hass: HomeAssistant,
rows: Generator[EventAsRow, None, None] | Sequence[Row] | Result,
ent_reg: er.EntityRegistry,
logbook_run: LogbookRun,
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/components/logbook/common.py
Expand Up @@ -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,
Expand Down

0 comments on commit 5dac5d5

Please sign in to comment.