def get_states(utc_point_in_time, entity_ids=None, run=None, filters=None): """Return the states at a specific point in time.""" if run is None: run = recorder.run_information(utc_point_in_time) # History did not run before utc_point_in_time if run is None: return [] from sqlalchemy import and_, func states = recorder.get_model('States') most_recent_state_ids = recorder.query( func.max(states.state_id).label('max_state_id') ).filter( (states.created >= run.start) & (states.created < utc_point_in_time) & (~states.domain.in_(IGNORE_DOMAINS))) if filters: most_recent_state_ids = filters.apply(most_recent_state_ids, entity_ids) most_recent_state_ids = most_recent_state_ids.group_by( states.entity_id).subquery() query = recorder.query('States').join(most_recent_state_ids, and_( states.state_id == most_recent_state_ids.c.max_state_id)) for state in recorder.execute(query): if not state.attributes.get(ATTR_HIDDEN, False): yield state
def get_states(utc_point_in_time, entity_ids=None, run=None): """Return the states at a specific point in time.""" if run is None: run = recorder.run_information(utc_point_in_time) # History did not run before utc_point_in_time if run is None: return [] from sqlalchemy import and_, func states = recorder.get_model("States") most_recent_state_ids = recorder.query(func.max(states.state_id).label("max_state_id")).filter( (states.created >= run.start) & (states.created < utc_point_in_time) ) if entity_ids is not None: most_recent_state_ids = most_recent_state_ids.filter(states.entity_id.in_(entity_ids)) most_recent_state_ids = most_recent_state_ids.group_by(states.entity_id).subquery() query = recorder.query("States").join( most_recent_state_ids, and_(states.state_id == most_recent_state_ids.c.max_state_id) ) return recorder.execute(query)
def get_significant_states(start_time, end_time=None, entity_id=None, filters=None): """ Return states changes during UTC period start_time - end_time. Significant states are all states where there is a state change, as well as all states from certain domains (for instance thermostat so that we get current temperature in our graphs). """ entity_ids = (entity_id.lower(), ) if entity_id is not None else None states = recorder.get_model('States') query = recorder.query( states).filter((states.domain.in_(SIGNIFICANT_DOMAINS) | (states.last_changed == states.last_updated)) & (states.last_updated > start_time)) if filters: query = filters.apply(query, entity_ids) if end_time is not None: query = query.filter(states.last_updated < end_time) states = (state for state in recorder.execute( query.order_by(states.entity_id, states.last_updated)) if (_is_significant(state) and not state.attributes.get(ATTR_HIDDEN, False))) return states_to_json(states, start_time, entity_id, filters)
def get_significant_states(start_time, end_time=None, entity_id=None): """ Return states changes during UTC period start_time - end_time. Significant states are all states where there is a state change, as well as all states from certain domains (for instance thermostat so that we get current temperature in our graphs). """ states = recorder.get_model("States") query = recorder.query("States").filter( (states.domain.in_(SIGNIFICANT_DOMAINS) | (states.last_changed == states.last_updated)) & ((~states.domain.in_(IGNORE_DOMAINS)) & (states.last_updated > start_time)) ) if end_time is not None: query = query.filter(states.last_updated < end_time) if entity_id is not None: query = query.filter_by(entity_id=entity_id.lower()) states = ( state for state in recorder.execute(query.order_by(states.entity_id, states.last_updated)) if _is_significant(state) ) return states_to_json(states, start_time, entity_id)
def update(self): """Get the latest data and update the states.""" data = self.speedtest_client.data if data is None: entity_id = 'sensor.speedtest_' + self._name.lower() states = recorder.get_model('States') try: last_state = recorder.execute( recorder.query('States').filter( (states.entity_id == entity_id) & (states.last_changed == states.last_updated) & (states.state != 'unknown')).order_by( states.state_id.desc()).limit(1)) except TypeError: return except RuntimeError: return if not last_state: return self._state = last_state[0].state elif self.type == 'ping': self._state = data['ping'] elif self.type == 'download': self._state = data['download'] elif self.type == 'upload': self._state = data['upload']
def get_significant_states(start_time, end_time=None, entity_id=None, filters=None): """ Return states changes during UTC period start_time - end_time. Significant states are all states where there is a state change, as well as all states from certain domains (for instance thermostat so that we get current temperature in our graphs). """ entity_ids = (entity_id.lower(), ) if entity_id is not None else None states = recorder.get_model('States') query = recorder.query('States').filter( (states.domain.in_(SIGNIFICANT_DOMAINS) | (states.last_changed == states.last_updated)) & (states.last_updated > start_time)) if filters: query = filters.apply(query, entity_ids) if end_time is not None: query = query.filter(states.last_updated < end_time) states = ( state for state in recorder.execute( query.order_by(states.entity_id, states.last_updated)) if (_is_significant(state) and not state.attributes.get(ATTR_HIDDEN, False))) return states_to_json(states, start_time, entity_id, filters)
def update(self): """Get the latest data and update the states.""" data = self.speedtest_client.data if data is None: entity_id = 'sensor.speedtest_' + self._name.lower() states = recorder.get_model('States') try: last_state = recorder.execute( recorder.query('States').filter( (states.entity_id == entity_id) & (states.last_changed == states.last_updated) & (states.state != 'unknown') ).order_by(states.state_id.desc()).limit(1)) except TypeError: return except RuntimeError: return if not last_state: return self._state = last_state[0].state elif self.type == 'ping': self._state = data['ping'] elif self.type == 'download': self._state = data['download'] elif self.type == 'upload': self._state = data['upload']
def test_saving_event(self): """Test saving and restoring an event.""" event_type = 'EVENT_TEST' event_data = {'test_attr': 5, 'test_attr_10': 'nice'} events = [] def event_listener(event): """Record events from eventbus.""" if event.event_type == event_type: events.append(event) self.hass.bus.listen(MATCH_ALL, event_listener) self.hass.bus.fire(event_type, event_data) self.hass.block_till_done() recorder._INSTANCE.block_till_done() db_events = recorder.execute( recorder.query('Events').filter_by(event_type=event_type)) assert len(events) == 1 assert len(db_events) == 1 event = events[0] db_event = db_events[0] assert event.event_type == db_event.event_type assert event.data == db_event.data assert event.origin == db_event.origin # Recorder uses SQLite and stores datetimes as integer unix timestamps assert event.time_fired.replace(microsecond=0) == \ db_event.time_fired.replace(microsecond=0)
def get_significant_states(start_time, end_time=None, entity_id=None): """ Return states changes during UTC period start_time - end_time. Significant states are all states where there is a state change, as well as all states from certain domains (for instance thermostat so that we get current temperature in our graphs). """ states = recorder.get_model('States') query = recorder.query( 'States').filter((states.domain.in_(SIGNIFICANT_DOMAINS) | (states.last_changed == states.last_updated)) & ((~states.domain.in_(IGNORE_DOMAINS)) & (states.last_updated > start_time))) if end_time is not None: query = query.filter(states.last_updated < end_time) if entity_id is not None: query = query.filter_by(entity_id=entity_id.lower()) states = (state for state in recorder.execute( query.order_by(states.entity_id, states.last_updated)) if _is_significant(state)) return states_to_json(states, start_time, entity_id)
def get_results(): """Query DB for results.""" events = recorder.get_model('Events') query = recorder.query('Events').filter( (events.time_fired > start_day) & (events.time_fired < end_day)) events = recorder.execute(query) return _exclude_events(events, self.config)
def get_results(): """Query DB for results.""" events = recorder.get_model('Events') query = recorder.query('Events').order_by( events.time_fired).filter((events.time_fired > start_day) & (events.time_fired < end_day)) events = recorder.execute(query) return _exclude_events(events, self.config)
def test_purge_disabled(self): """Test leaving purge_days disabled.""" self._add_test_states() self._add_test_events() # make sure we start with 5 states and events states = recorder.query('States') events = recorder.query('Events').filter( recorder.get_model('Events').event_type.like("EVENT_TEST%")) self.assertEqual(states.count(), 5) self.assertEqual(events.count(), 5) # run purge_old_data() recorder._INSTANCE.purge_days = None recorder._INSTANCE._purge_old_data() # we should have all of our states still self.assertEqual(states.count(), 5) self.assertEqual(events.count(), 5)
def last_5_states(entity_id): """Return the last 5 states for entity_id.""" entity_id = entity_id.lower() states = recorder.get_model('States') return recorder.execute( recorder.query('States').filter((states.entity_id == entity_id) & ( states.last_changed == states.last_updated)).order_by( states.state_id.desc()).limit(5))
def last_5_states(entity_id): """Return the last 5 states for entity_id.""" entity_id = entity_id.lower() states = recorder.get_model('States') return recorder.execute( recorder.query('States').filter( (states.entity_id == entity_id) & (states.last_changed == states.last_updated) ).order_by(states.state_id.desc()).limit(5))
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 _add_entities(hass, entity_ids): """Add entities.""" attributes = {'test_attr': 5, 'test_attr_10': 'nice'} for idx, entity_id in enumerate(entity_ids): hass.states.set(entity_id, 'state{}'.format(idx), attributes) hass.block_till_done() recorder._INSTANCE.block_till_done() db_states = recorder.query('States') states = recorder.execute(db_states) assert db_states[0].event_id is not None return states
def get(self, request, datetime=None): """Retrieve logbook entries.""" start_day = dt_util.as_utc(datetime or dt_util.start_of_local_day()) end_day = start_day + timedelta(days=1) events = recorder.get_model('Events') query = recorder.query('Events').filter( (events.time_fired > start_day) & (events.time_fired < end_day)) events = recorder.execute(query) return self.json(humanify(events))
def _add_test_events(self): """Add a few events for testing.""" now = int(time.time()) five_days_ago = now - (60*60*24*5) event_data = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.pool.block_till_done() recorder._INSTANCE.block_till_done() 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' recorder.query("INSERT INTO events" "(event_type, event_data, origin, created," "time_fired, utc_offset)" "VALUES (?, ?, ?, ?, ?, ?)", (event_type, json.dumps(event_data), 'LOCAL', timestamp, timestamp, -18000))
def test_purge_old_events(self): """Test deleting old events.""" self._add_test_events() events = recorder.query('Events').filter( recorder.get_model('Events').event_type.like("EVENT_TEST%")) self.assertEqual(events.count(), 5) # run purge_old_data() recorder._INSTANCE.purge_days = 4 recorder._INSTANCE._purge_old_data() # now we should only have 3 events left self.assertEqual(events.count(), 3)
def test_purge_old_states(self): """Test deleting old states.""" self._add_test_states() # make sure we start with 5 states states = recorder.query('States') self.assertEqual(states.count(), 5) # run purge_old_data() recorder._INSTANCE.purge_days = 4 recorder._INSTANCE._purge_old_data() # we should only have 2 states left after purging self.assertEqual(states.count(), 2)
def _add_test_states(self): """Add multiple states to the db for testing.""" now = int(time.time()) five_days_ago = now - (60*60*24*5) attributes = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.pool.block_till_done() recorder._INSTANCE.block_till_done() for event_id in range(5): if event_id < 3: timestamp = five_days_ago state = 'purgeme' else: timestamp = now state = 'dontpurgeme' recorder.query("INSERT INTO states (" "entity_id, domain, state, attributes," "last_changed, last_updated, created," "utc_offset, event_id)" "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", ('test.recorder2', 'sensor', state, json.dumps(attributes), timestamp, timestamp, timestamp, -18000, event_id + 1000))
def state_changes_during_period(start_time, end_time=None, entity_id=None): """Return states changes during UTC period start_time - end_time.""" states = recorder.get_model("States") query = recorder.query("States").filter( (states.last_changed == states.last_updated) & (states.last_changed > start_time) ) if end_time is not None: query = query.filter(states.last_updated < end_time) if entity_id is not None: query = query.filter_by(entity_id=entity_id.lower()) states = recorder.execute(query.order_by(states.entity_id, states.last_updated)) return states_to_json(states, start_time, entity_id)
def state_changes_during_period(start_time, end_time=None, entity_id=None): """Return states changes during UTC period start_time - end_time.""" states = recorder.get_model('States') query = recorder.query('States').filter( (states.last_changed == states.last_updated) & (states.last_changed > start_time)) if end_time is not None: query = query.filter(states.last_updated < end_time) if entity_id is not None: query = query.filter_by(entity_id=entity_id.lower()) states = recorder.execute( query.order_by(states.entity_id, states.last_updated)) return states_to_json(states, start_time, entity_id)
def test_saving_state(self): """Test saving and restoring a state.""" entity_id = 'test.recorder' state = 'restoring_from_db' attributes = {'test_attr': 5, 'test_attr_10': 'nice'} self.hass.states.set(entity_id, state, attributes) self.hass.block_till_done() recorder._INSTANCE.block_till_done() db_states = recorder.query('States') states = recorder.execute(db_states) assert db_states[0].event_id is not None self.assertEqual(1, len(states)) self.assertEqual(self.hass.states.get(entity_id), states[0])
def test_saving_event(self): """Test saving and restoring an event.""" event_type = 'EVENT_TEST' event_data = {'test_attr': 5, 'test_attr_10': 'nice'} events = [] @callback def event_listener(event): """Record events from eventbus.""" if event.event_type == event_type: events.append(event) self.hass.bus.listen(MATCH_ALL, event_listener) self.hass.bus.fire(event_type, event_data) self.hass.block_till_done() recorder._INSTANCE.block_till_done() db_events = recorder.execute( recorder.query('Events').filter_by( event_type=event_type)) assert len(events) == 1 assert len(db_events) == 1 event = events[0] db_event = db_events[0] assert event.event_type == db_event.event_type assert event.data == db_event.data assert event.origin == db_event.origin # Recorder uses SQLite and stores datetimes as integer unix timestamps assert event.time_fired.replace(microsecond=0) == \ db_event.time_fired.replace(microsecond=0)
def _restore_states(service): """Restore states.""" run = recorder.run_information(dt_util.utcnow()) if run is None: return from sqlalchemy import and_, func states = recorder.get_model('States') most_recent_state_ids = recorder.query( func.max(states.state_id).label('max_state_id')) most_recent_state_ids = most_recent_state_ids.group_by( states.entity_id).subquery() query = recorder.query('States').join( most_recent_state_ids, and_(states.state_id == most_recent_state_ids.c.max_state_id)) states = recorder.execute(query) data = {ATTR_ENTITY_ID: 'group.all_automations'} hass.services.call('homeassistant', SERVICE_TURN_OFF, data, True) last_services = [] for state in states: if state.domain == group.DOMAIN: continue if state.domain == input_number.DOMAIN: data = { ATTR_ENTITY_ID: state.entity_id, input_number.ATTR_VALUE: state.state } service = input_number.SERVICE_SELECT_VALUE elif state.domain == input_select.DOMAIN: data = { ATTR_ENTITY_ID: state.entity_id, input_select.ATTR_OPTION: state.state } service = input_select.SERVICE_SELECT_OPTION elif state.domain == climate.DOMAIN: data = { ATTR_ENTITY_ID: state.entity_id, climate.ATTR_TEMPERATURE: state.attributes.get('temperature') } service = climate.SERVICE_SET_TEMPERATURE elif (state.domain in [input_boolean.DOMAIN, automation.DOMAIN]): #or state.attributes.get('assumed_state')): data = {ATTR_ENTITY_ID: state.entity_id} if state.state == STATE_OFF: service = SERVICE_TURN_OFF if state.state == STATE_ON: service = SERVICE_TURN_ON else: continue if state.domain == light.DOMAIN: continue if state.domain == automation.DOMAIN: last_services.append((state.domain, service, data)) continue elif (state.domain in [switch.DOMAIN]): continue else: continue if hass.states.get(state.entity_id) is None: continue hass.services.call(state.domain, service, data, True) for (domain, service, data) in last_services: hass.services.call(domain, service, data, True)