async def _add_test_recorder_runs(hass: HomeAssistantType, instance: recorder.Recorder): """Add a few recorder_runs for testing.""" utcnow = dt_util.utcnow() five_days_ago = utcnow - timedelta(days=5) eleven_days_ago = utcnow - timedelta(days=11) await hass.async_block_till_done() await async_wait_recording_done(hass, instance) with recorder.session_scope(hass=hass) as session: for rec_id in range(6): if rec_id < 2: timestamp = eleven_days_ago elif rec_id < 4: timestamp = five_days_ago else: timestamp = utcnow session.add( RecorderRuns( start=timestamp, created=dt_util.utcnow(), end=timestamp + timedelta(days=1), ))
async def _add_test_events(hass: HomeAssistantType, instance: recorder.Recorder): """Add a few events for testing.""" utcnow = dt_util.utcnow() five_days_ago = utcnow - timedelta(days=5) eleven_days_ago = utcnow - timedelta(days=11) event_data = {"test_attr": 5, "test_attr_10": "nice"} await hass.async_block_till_done() await async_wait_recording_done(hass, instance) with recorder.session_scope(hass=hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago event_type = "EVENT_TEST_AUTOPURGE" elif event_id < 4: timestamp = five_days_ago event_type = "EVENT_TEST_PURGE" else: timestamp = utcnow event_type = "EVENT_TEST" session.add( Events( event_type=event_type, event_data=json.dumps(event_data), origin="LOCAL", created=timestamp, time_fired=timestamp, ))
def _add_data_in_last_run(entities): """Add test data in the last recorder_run.""" # pylint: disable=protected-access t_now = dt_util.utcnow() - timedelta(minutes=10) t_min_1 = t_now - timedelta(minutes=20) t_min_2 = t_now - timedelta(minutes=30) recorder_runs = recorder.get_model('RecorderRuns') states = recorder.get_model('States') with recorder.session_scope() as session: run = recorder_runs( start=t_min_2, end=t_now, created=t_min_2 ) recorder._INSTANCE._commit(session, run) for entity_id, state in entities.items(): dbstate = states( entity_id=entity_id, domain=split_entity_id(entity_id)[0], state=state, attributes='{}', last_changed=t_min_1, last_updated=t_min_1, created=t_min_1) recorder._INSTANCE._commit(session, dbstate)
async def _add_test_statistics(hass: HomeAssistant, instance: recorder.Recorder): """Add multiple statistics to the db for testing.""" utcnow = dt_util.utcnow() five_days_ago = utcnow - timedelta(days=5) eleven_days_ago = utcnow - timedelta(days=11) await hass.async_block_till_done() await async_wait_recording_done(hass, instance) with recorder.session_scope(hass=hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago state = "-11" elif event_id < 4: timestamp = five_days_ago state = "-5" else: timestamp = utcnow state = "0" session.add( StatisticsShortTerm( start=timestamp, state=state, ) )
def _add_db_entries(hass: HomeAssistant) -> None: with recorder.session_scope(hass=hass) as session: # Add states and state_changed events that should be purged # in the legacy format timestamp = dt_util.utcnow() - timedelta(days=5) event_id = 1021 session.add( States( entity_id="sensor.old_format", state=STATE_ON, attributes=json.dumps( {"old": "not_using_state_attributes"}), last_changed=timestamp, last_updated=timestamp, event_id=event_id, state_attributes=None, )) session.add( Events( event_id=event_id, event_type=EVENT_STATE_CHANGED, event_data="{}", origin="LOCAL", time_fired=timestamp, ))
def _add_db_entries(hass: ha.HomeAssistant, point: datetime, entity_ids: list[str]) -> None: with recorder.session_scope(hass=hass) as session: for idx, entity_id in enumerate(entity_ids): session.add( Events( event_id=1001 + idx, event_type="state_changed", event_data="{}", origin="LOCAL", time_fired=point, )) session.add( States( entity_id=entity_id, state="on", attributes='{"name":"the light"}', last_changed=point, last_updated=point, event_id=1001 + idx, attributes_id=1002 + idx, )) session.add( StateAttributes( shared_attrs='{"name":"the shared light"}', hash=1234 + idx, attributes_id=1002 + idx, ))
def _add_test_states(self): """Add multiple states to the db for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) attributes = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(5): if event_id < 3: timestamp = five_days_ago state = 'purgeme' else: timestamp = now state = 'dontpurgeme' session.add(States( entity_id='test.recorder2', domain='sensor', state=state, attributes=json.dumps(attributes), last_changed=timestamp, last_updated=timestamp, created=timestamp, event_id=event_id + 1000 ))
def _add_test_states(self): """Add multiple states to the db for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) attributes = {"test_attr": 5, "test_attr_10": "nice"} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago state = "autopurgeme" elif event_id < 4: timestamp = five_days_ago state = "purgeme" else: timestamp = now state = "dontpurgeme" session.add( States( entity_id="test.recorder2", domain="sensor", state=state, attributes=json.dumps(attributes), last_changed=timestamp, last_updated=timestamp, created=timestamp, event_id=event_id + 1000, ))
def _add_db_entries(hass: HomeAssistantType) -> None: with recorder.session_scope(hass=hass) as session: # Add events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) for event_id in range(1000, 1020): session.add( Events( event_id=event_id * days, event_type="EVENT_PURGE", event_data="{}", origin="LOCAL", created=timestamp, time_fired=timestamp, )) # Add states and state_changed events that should be keeped timestamp = dt_util.utcnow() - timedelta(days=1) for event_id in range(200, 210): _add_state_and_state_changed_event( session, "sensor.keep", "keep", timestamp, event_id, )
async def _add_db_entries(hass: HomeAssistant, timestamp: datetime) -> None: with recorder.session_scope(hass=hass) as session: session.add( Events( event_id=1001, event_type="EVENT_TEST_PURGE", event_data="{}", origin="LOCAL", time_fired=timestamp, )) session.add( States( entity_id="test.recorder2", state="purgeme", attributes="{}", last_changed=timestamp, last_updated=timestamp, event_id=1001, attributes_id=1002, )) session.add( StateAttributes( shared_attrs="{}", hash=1234, attributes_id=1002, ))
def _add_test_events(self): """Add a few events for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) event_data = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(5): if event_id < 2: timestamp = five_days_ago event_type = 'EVENT_TEST_PURGE' else: timestamp = now event_type = 'EVENT_TEST' session.add(Events( event_type=event_type, event_data=json.dumps(event_data), origin='LOCAL', created=timestamp, time_fired=timestamp, ))
def _add_test_events(self): """Add a few events for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) event_data = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(5): if event_id < 2: timestamp = five_days_ago event_type = 'EVENT_TEST_PURGE' else: timestamp = now event_type = 'EVENT_TEST' session.add( Events( event_type=event_type, event_data=json.dumps(event_data), origin='LOCAL', created=timestamp, time_fired=timestamp, ))
def _add_db_entries(hass: HomeAssistant) -> None: with recorder.session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) for event_id in range(1000, 1020): _add_state_and_state_changed_event( session, "sensor.excluded", "purgeme", timestamp, event_id * days, ) # Add events that should be keeped timestamp = dt_util.utcnow() - timedelta(days=1) for event_id in range(200, 210): session.add( Events( event_id=event_id, event_type="EVENT_KEEP", event_data="{}", origin="LOCAL", created=timestamp, time_fired=timestamp, ) ) # Add states with linked old_state_ids that need to be handled timestamp = dt_util.utcnow() - timedelta(days=0) state_1 = States( entity_id="sensor.linked_old_state_id", domain="sensor", state="keep", attributes="{}", last_changed=timestamp, last_updated=timestamp, created=timestamp, old_state_id=1, ) timestamp = dt_util.utcnow() - timedelta(days=4) state_2 = States( entity_id="sensor.linked_old_state_id", domain="sensor", state="keep", attributes="{}", last_changed=timestamp, last_updated=timestamp, created=timestamp, old_state_id=2, ) state_3 = States( entity_id="sensor.linked_old_state_id", domain="sensor", state="keep", attributes="{}", last_changed=timestamp, last_updated=timestamp, created=timestamp, old_state_id=62, # keep ) session.add_all((state_1, state_2, state_3))
def _add_test_states(self): """Add multiple states to the db for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) attributes = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(5): if event_id < 3: timestamp = five_days_ago state = 'purgeme' else: timestamp = now state = 'dontpurgeme' session.add( States(entity_id='test.recorder2', domain='sensor', state=state, attributes=json.dumps(attributes), last_changed=timestamp, last_updated=timestamp, created=timestamp, event_id=event_id + 1000))
def _add_test_recorder_runs(hass): """Add a few recorder_runs for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) hass.block_till_done() hass.data[DATA_INSTANCE].block_till_done() wait_recording_done(hass) with recorder.session_scope(hass=hass) as session: for rec_id in range(6): if rec_id < 2: timestamp = eleven_days_ago elif rec_id < 4: timestamp = five_days_ago else: timestamp = now session.add( RecorderRuns( start=timestamp, created=dt_util.utcnow(), end=timestamp + timedelta(days=1), ))
def _add_test_events(self): """Add a few events for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) event_data = {"test_attr": 5, "test_attr_10": "nice"} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago event_type = "EVENT_TEST_AUTOPURGE" elif event_id < 4: timestamp = five_days_ago event_type = "EVENT_TEST_PURGE" else: timestamp = now event_type = "EVENT_TEST" session.add( Events( event_type=event_type, event_data=json.dumps(event_data), origin="LOCAL", created=timestamp, time_fired=timestamp, ))
def _add_purge_records(hass: HomeAssistant) -> None: with recorder.session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) for event_id in range(1000, 1020): _add_state_and_state_changed_event( session, "sensor.purge_entity", "purgeme", timestamp, event_id * days, ) timestamp = dt_util.utcnow() - timedelta(days=days) for event_id in range(10000, 10020): _add_state_and_state_changed_event( session, "purge_domain.entity", "purgeme", timestamp, event_id * days, ) timestamp = dt_util.utcnow() - timedelta(days=days) for event_id in range(100000, 100020): _add_state_and_state_changed_event( session, "binary_sensor.purge_glob", "purgeme", timestamp, event_id * days, )
def last_recorder_run(): """Retireve the last closed recorder run from the DB.""" recorder.get_instance() rec_runs = recorder.get_model('RecorderRuns') with recorder.session_scope() as session: res = recorder.query(rec_runs).order_by(rec_runs.end.desc()).first() if res is None: return None session.expunge(res) return res
def last_recorder_run(): """Retireve the last closed recorder run from the DB.""" recorder.get_instance() rec_runs = recorder.get_model('RecorderRuns') with recorder.session_scope() as session: res = recorder.query(rec_runs).order_by(rec_runs.end.desc()).first() if res is None: return None session.expunge(res) return res
async def _add_db_entries(hass: HomeAssistant, cutoff: datetime, rows: int) -> None: timestamp_keep = cutoff timestamp_purge = cutoff - timedelta(microseconds=1) with recorder.session_scope(hass=hass) as session: session.add( Events( event_id=1000, event_type="KEEP", event_data="{}", origin="LOCAL", time_fired=timestamp_keep, )) session.add( States( entity_id="test.cutoff", state="keep", attributes="{}", last_changed=timestamp_keep, last_updated=timestamp_keep, event_id=1000, attributes_id=1000, )) session.add( StateAttributes( shared_attrs="{}", hash=1234, attributes_id=1000, )) for row in range(1, rows): session.add( Events( event_id=1000 + row, event_type="PURGE", event_data="{}", origin="LOCAL", time_fired=timestamp_purge, )) session.add( States( entity_id="test.cutoff", state="purge", attributes="{}", last_changed=timestamp_purge, last_updated=timestamp_purge, event_id=1000 + row, attributes_id=1000 + row, )) session.add( StateAttributes( shared_attrs="{}", hash=1234, attributes_id=1000 + row, ))
def _add_keep_records(hass: HomeAssistant) -> None: with recorder.session_scope(hass=hass) as session: # Add states and state_changed events that should be kept timestamp = dt_util.utcnow() - timedelta(days=2) for event_id in range(200, 210): _add_state_and_state_changed_event( session, "sensor.keep", "keep", timestamp, event_id, )
def _add_db_entries(hass: HomeAssistant) -> None: with recorder.session_scope(hass=hass) as session: # Add states and state_changed events that should be purged for days in range(1, 4): timestamp = dt_util.utcnow() - timedelta(days=days) for event_id in range(1000, 1020): _add_state_and_state_changed_event( session, "sensor.excluded", "purgeme", timestamp, event_id * days, )
def test_recorder_bad_commit(hass_recorder): """Bad _commit should retry 3 times.""" hass_recorder() def work(session): """Bad work.""" session.execute('select * from notthere') with patch('homeassistant.components.recorder.time.sleep') as e_mock, \ recorder.session_scope() as session: res = recorder._INSTANCE._commit(session, work) assert res is False assert e_mock.call_count == 3
def test_recorder_bad_commit(hass_recorder): """Bad _commit should retry 3 times.""" hass_recorder() def work(session): """Bad work.""" session.execute('select * from notthere') with patch('homeassistant.components.recorder.time.sleep') as e_mock, \ recorder.session_scope() as session: res = recorder._INSTANCE._commit(session, work) assert res is False assert e_mock.call_count == 3
def _add_test_states(self): """Add multiple states to the db for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) attributes = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago state = 'autopurgeme' elif event_id < 4: timestamp = five_days_ago state = 'purgeme' else: timestamp = now state = 'dontpurgeme' session.add(States( entity_id='test.recorder2', domain='sensor', state=state, attributes=json.dumps(attributes), last_changed=timestamp, last_updated=timestamp, created=timestamp, event_id=event_id + 1000 )) # if self._add_test_events was called, we added a special event # that should be protected from deletion, too protected_event_id = getattr(self, "_protected_event_id", 2000) # add a state that is old but the only state of its entity and # should be protected session.add(States( entity_id='test.rarely_updated_entity', domain='sensor', state='iamprotected', attributes=json.dumps(attributes), last_changed=eleven_days_ago, last_updated=eleven_days_ago, created=eleven_days_ago, event_id=protected_event_id ))
def _add_test_states(self): """Add multiple states to the db for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) attributes = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago state = 'autopurgeme' elif event_id < 4: timestamp = five_days_ago state = 'purgeme' else: timestamp = now state = 'dontpurgeme' session.add(States( entity_id='test.recorder2', domain='sensor', state=state, attributes=json.dumps(attributes), last_changed=timestamp, last_updated=timestamp, created=timestamp, event_id=event_id + 1000 )) # if self._add_test_events was called, we added a special event # that should be protected from deletion, too protected_event_id = getattr(self, "_protected_event_id", 2000) # add a state that is old but the only state of its entity and # should be protected session.add(States( entity_id='test.rarely_updated_entity', domain='sensor', state='iamprotected', attributes=json.dumps(attributes), last_changed=eleven_days_ago, last_updated=eleven_days_ago, created=eleven_days_ago, event_id=protected_event_id ))
async def _add_test_states(hass: HomeAssistantType, instance: recorder.Recorder): """Add multiple states to the db for testing.""" utcnow = dt_util.utcnow() five_days_ago = utcnow - timedelta(days=5) eleven_days_ago = utcnow - timedelta(days=11) attributes = {"test_attr": 5, "test_attr_10": "nice"} await hass.async_block_till_done() await async_wait_recording_done(hass, instance) with recorder.session_scope(hass=hass) as session: old_state_id = None for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago state = "autopurgeme" elif event_id < 4: timestamp = five_days_ago state = "purgeme" else: timestamp = utcnow state = "dontpurgeme" event = Events( event_type="state_changed", event_data="{}", origin="LOCAL", created=timestamp, time_fired=timestamp, ) session.add(event) session.flush() state = States( entity_id="test.recorder2", domain="sensor", state=state, attributes=json.dumps(attributes), last_changed=timestamp, last_updated=timestamp, created=timestamp, event_id=event.event_id, old_state_id=old_state_id, ) session.add(state) session.flush() old_state_id = state.state_id
def _add_test_events(self): """Add a few events for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) event_data = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago event_type = 'EVENT_TEST_AUTOPURGE' elif event_id < 4: timestamp = five_days_ago event_type = 'EVENT_TEST_PURGE' else: timestamp = now event_type = 'EVENT_TEST' session.add( Events( event_type=event_type, event_data=json.dumps(event_data), origin='LOCAL', created=timestamp, time_fired=timestamp, )) # Add an event for the protected state protected_event = Events( event_type='EVENT_TEST_FOR_PROTECTED', event_data=json.dumps(event_data), origin='LOCAL', created=eleven_days_ago, time_fired=eleven_days_ago, ) session.add(protected_event) session.flush() self._protected_event_id = protected_event.event_id
def _add_data_in_last_run(hass, entities): """Add test data in the last recorder_run.""" # pylint: disable=protected-access t_now = dt_util.utcnow() - timedelta(minutes=10) t_min_1 = t_now - timedelta(minutes=20) t_min_2 = t_now - timedelta(minutes=30) with recorder.session_scope(hass=hass) as session: session.add(RecorderRuns(start=t_min_2, end=t_now, created=t_min_2)) for entity_id, state in entities.items(): session.add( States(entity_id=entity_id, domain=split_entity_id(entity_id)[0], state=state, attributes='{}', last_changed=t_min_1, last_updated=t_min_1, created=t_min_1))
def _add_test_events(self): """Add a few events for testing.""" now = datetime.now() five_days_ago = now - timedelta(days=5) eleven_days_ago = now - timedelta(days=11) event_data = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.block_till_done() self.hass.data[DATA_INSTANCE].block_till_done() with recorder.session_scope(hass=self.hass) as session: for event_id in range(6): if event_id < 2: timestamp = eleven_days_ago event_type = 'EVENT_TEST_AUTOPURGE' elif event_id < 4: timestamp = five_days_ago event_type = 'EVENT_TEST_PURGE' else: timestamp = now event_type = 'EVENT_TEST' session.add(Events( event_type=event_type, event_data=json.dumps(event_data), origin='LOCAL', created=timestamp, time_fired=timestamp, )) # Add an event for the protected state protected_event = Events( event_type='EVENT_TEST_FOR_PROTECTED', event_data=json.dumps(event_data), origin='LOCAL', created=eleven_days_ago, time_fired=eleven_days_ago, ) session.add(protected_event) session.flush() self._protected_event_id = protected_event.event_id
def test_get_full_significant_states_with_session_entity_no_matches( hass_recorder): """Test getting states at a specific point in time for entities that never have been recorded.""" hass = hass_recorder() now = dt_util.utcnow() time_before_recorder_ran = now - timedelta(days=1000) with recorder.session_scope(hass=hass) as session: assert (history.get_full_significant_states_with_session( hass, session, time_before_recorder_ran, now, entity_ids=["demo.id"]) == {}) assert (history.get_full_significant_states_with_session( hass, session, time_before_recorder_ran, now, entity_ids=["demo.id", "demo.id2"], ) == {})
def test_recorder_errors_exceptions(hass_recorder): \ # pylint: disable=redefined-outer-name """Test session_scope and get_model errors.""" # Model cannot be resolved assert recorder.get_model('dont-exist') is None # Verify the instance fails before setup with pytest.raises(RuntimeError): recorder.get_instance() # Setup the recorder hass_recorder() recorder.get_instance() # Verify session scope raises (and prints) an exception with patch('homeassistant.components.recorder._LOGGER.error') as e_mock, \ pytest.raises(Exception) as err: with recorder.session_scope() as session: session.execute('select * from notthere') assert e_mock.call_count == 1 assert recorder.ERROR_QUERY[:-4] in e_mock.call_args[0][0] assert 'no such table' in str(err.value)
def test_recorder_errors_exceptions(hass_recorder): \ # pylint: disable=redefined-outer-name """Test session_scope and get_model errors.""" # Model cannot be resolved assert recorder.get_model('dont-exist') is None # Verify the instance fails before setup with pytest.raises(RuntimeError): recorder._verify_instance() # Setup the recorder hass_recorder() recorder._verify_instance() # Verify session scope raises (and prints) an exception with patch('homeassistant.components.recorder._LOGGER.error') as e_mock, \ pytest.raises(Exception) as err: with recorder.session_scope() as session: session.execute('select * from notthere') assert e_mock.call_count == 1 assert recorder.ERROR_QUERY[:-4] in e_mock.call_args[0][0] assert 'no such table' in str(err.value)
def _add_data_in_last_run(hass, entities): """Add test data in the last recorder_run.""" # pylint: disable=protected-access t_now = dt_util.utcnow() - timedelta(minutes=10) t_min_1 = t_now - timedelta(minutes=20) t_min_2 = t_now - timedelta(minutes=30) with recorder.session_scope(hass=hass) as session: session.add(RecorderRuns( start=t_min_2, end=t_now, created=t_min_2 )) for entity_id, state in entities.items(): session.add(States( entity_id=entity_id, domain=split_entity_id(entity_id)[0], state=state, attributes='{}', last_changed=t_min_1, last_updated=t_min_1, created=t_min_1))