diff --git a/homeassistant/components/logbook/helpers.py b/homeassistant/components/logbook/helpers.py index 6bfd88c976a0a1..839a742224f485 100644 --- a/homeassistant/components/logbook/helpers.py +++ b/homeassistant/components/logbook/helpers.py @@ -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 _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) ) @@ -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) - ) diff --git a/homeassistant/components/logbook/processor.py b/homeassistant/components/logbook/processor.py index a36c887b5993c0..02a6dae3ce680a 100644 --- a/homeassistant/components/logbook/processor.py +++ b/homeassistant/components/logbook/processor.py @@ -175,6 +175,7 @@ def humanify( """Humanify rows.""" return list( _humanify( + self.hass, rows, self.ent_reg, self.logbook_run, @@ -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, @@ -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 diff --git a/tests/components/logbook/common.py b/tests/components/logbook/common.py index 9fe6c2b60a8a1d..824bbbde21dbfb 100644 --- a/tests/components/logbook/common.py +++ b/tests/components/logbook/common.py @@ -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,