def test_run_information(hass_recorder): """Ensure run_information returns expected data.""" before_start_recording = dt_util.utcnow() hass = hass_recorder() run_info = run_information_from_instance(hass) assert isinstance(run_info, RecorderRuns) assert run_info.closed_incorrect is False with session_scope(hass=hass) as session: run_info = run_information_with_session(session) assert isinstance(run_info, RecorderRuns) assert run_info.closed_incorrect is False run_info = run_information(hass) assert isinstance(run_info, RecorderRuns) assert run_info.closed_incorrect is False hass.states.set("test.two", "on", {}) wait_recording_done(hass) run_info = run_information(hass) assert isinstance(run_info, RecorderRuns) assert run_info.closed_incorrect is False run_info = run_information(hass, before_start_recording) assert run_info is None run_info = run_information(hass, dt_util.utcnow()) assert isinstance(run_info, RecorderRuns) assert run_info.closed_incorrect is False
def test_end_incomplete_runs(hass_recorder, caplog): """Ensure we can end incomplete runs.""" hass = hass_recorder() with session_scope(hass=hass) as session: run_info = run_information_with_session(session) assert isinstance(run_info, RecorderRuns) assert run_info.closed_incorrect is False now = dt_util.utcnow() now_without_tz = now.replace(tzinfo=None) end_incomplete_runs(session, now) run_info = run_information_with_session(session) assert run_info.closed_incorrect is True assert run_info.end == now_without_tz session.flush() later = dt_util.utcnow() end_incomplete_runs(session, later) run_info = run_information_with_session(session) assert run_info.end == now_without_tz assert "Ended unfinished session" in caplog.text
def _get_states_with_session( hass: HomeAssistant, session: Session, utc_point_in_time: datetime, entity_ids: list[str] | None = None, run: RecorderRuns | None = None, filters: Any | None = None, no_attributes: bool = False, ) -> list[LazyState]: """Return the states at a specific point in time.""" if entity_ids and len(entity_ids) == 1: return _get_single_entity_states_with_session(hass, session, utc_point_in_time, entity_ids[0], no_attributes) if (run is None and (run := (recorder.run_information_with_session( session, utc_point_in_time))) is None): # History did not run before utc_point_in_time return []
def _get_states_with_session( session, utc_point_in_time, entity_ids=None, run=None, filters=None ): """Return the states at a specific point in time.""" query = session.query(*QUERY_STATES) if entity_ids and len(entity_ids) == 1: # Use an entirely different (and extremely fast) query if we only # have a single entity id query = ( query.filter( States.last_updated < utc_point_in_time, States.entity_id.in_(entity_ids), ) .order_by(States.last_updated.desc()) .limit(1) ) return [LazyState(row) for row in execute(query)] if run is None: run = recorder.run_information_with_session(session, utc_point_in_time) # History did not run before utc_point_in_time if run is None: return [] # We have more than one entity to look at (most commonly we want # all entities,) so we need to do a search on all states since the # last recorder run started. most_recent_states_by_date = session.query( States.entity_id.label("max_entity_id"), func.max(States.last_updated).label("max_last_updated"), ).filter( (States.last_updated >= run.start) & (States.last_updated < utc_point_in_time) ) if entity_ids: most_recent_states_by_date.filter(States.entity_id.in_(entity_ids)) most_recent_states_by_date = most_recent_states_by_date.group_by(States.entity_id) most_recent_states_by_date = most_recent_states_by_date.subquery() most_recent_state_ids = session.query( func.max(States.state_id).label("max_state_id") ).join( most_recent_states_by_date, and_( States.entity_id == most_recent_states_by_date.c.max_entity_id, States.last_updated == most_recent_states_by_date.c.max_last_updated, ), ) most_recent_state_ids = most_recent_state_ids.group_by(States.entity_id) most_recent_state_ids = most_recent_state_ids.subquery() query = query.join( most_recent_state_ids, States.state_id == most_recent_state_ids.c.max_state_id, ).filter(~States.domain.in_(IGNORE_DOMAINS)) if filters: query = filters.apply(query, entity_ids) return [LazyState(row) for row in execute(query)]
def _get_states_with_session( hass, session, utc_point_in_time, entity_ids=None, run=None, filters=None, no_attributes=False, ): """Return the states at a specific point in time.""" if entity_ids and len(entity_ids) == 1: return _get_single_entity_states_with_session(hass, session, utc_point_in_time, entity_ids[0], no_attributes) if run is None: run = recorder.run_information_with_session(session, utc_point_in_time) # History did not run before utc_point_in_time if run is None: return [] # We have more than one entity to look at so we need to do a query on states # since the last recorder run started. query_keys = QUERY_STATE_NO_ATTR if no_attributes else QUERY_STATES query = session.query(*query_keys) if entity_ids: # We got an include-list of entities, accelerate the query by filtering already # in the inner query. most_recent_state_ids = (session.query( func.max(States.state_id).label("max_state_id"), ).filter( (States.last_updated >= run.start) & (States.last_updated < utc_point_in_time)).filter( States.entity_id.in_(entity_ids))) most_recent_state_ids = most_recent_state_ids.group_by( States.entity_id) most_recent_state_ids = most_recent_state_ids.subquery() query = query.join( most_recent_state_ids, States.state_id == most_recent_state_ids.c.max_state_id, ) if not no_attributes: query = query.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id)) else: # We did not get an include-list of entities, query all states in the inner # query, then filter out unwanted domains as well as applying the custom filter. # This filtering can't be done in the inner query because the domain column is # not indexed and we can't control what's in the custom filter. most_recent_states_by_date = (session.query( States.entity_id.label("max_entity_id"), func.max(States.last_updated).label("max_last_updated"), ).filter((States.last_updated >= run.start) & (States.last_updated < utc_point_in_time)).group_by( States.entity_id).subquery()) most_recent_state_ids = (session.query( func.max(States.state_id).label("max_state_id")).join( most_recent_states_by_date, and_( States.entity_id == most_recent_states_by_date.c.max_entity_id, States.last_updated == most_recent_states_by_date.c.max_last_updated, ), ).group_by(States.entity_id).subquery()) query = query.join( most_recent_state_ids, States.state_id == most_recent_state_ids.c.max_state_id, ) for entity_domain in IGNORE_DOMAINS_ENTITY_ID_LIKE: query = query.filter(~States.entity_id.like(entity_domain)) if filters: query = filters.apply(query) if not no_attributes: query = query.outerjoin( StateAttributes, (States.attributes_id == StateAttributes.attributes_id)) attr_cache = {} return [LazyState(row, attr_cache) for row in execute(query)]