def test_get_significant_states_only(self): """Test significant states when significant_states_only is set.""" self.test_setup() entity_id = "sensor.test" def set_state(state, **kwargs): """Set the state.""" self.hass.states.set(entity_id, state, **kwargs) wait_recording_done(self.hass) return self.hass.states.get(entity_id) start = dt_util.utcnow() - timedelta(minutes=4) points = [] for i in range(1, 4): points.append(start + timedelta(minutes=i)) states = [] with patch( "homeassistant.components.recorder.dt_util.utcnow", return_value=start ): set_state("123", attributes={"attribute": 10.64}) with patch( "homeassistant.components.recorder.dt_util.utcnow", return_value=points[0] ): # Attributes are different, state not states.append(set_state("123", attributes={"attribute": 21.42})) with patch( "homeassistant.components.recorder.dt_util.utcnow", return_value=points[1] ): # state is different, attributes not states.append(set_state("32", attributes={"attribute": 21.42})) with patch( "homeassistant.components.recorder.dt_util.utcnow", return_value=points[2] ): # everything is different states.append(set_state("412", attributes={"attribute": 54.23})) hist = history.get_significant_states( self.hass, start, significant_changes_only=True ) assert len(hist[entity_id]) == 2 assert states[0] not in hist[entity_id] assert states[1] in hist[entity_id] assert states[2] in hist[entity_id] hist = history.get_significant_states( self.hass, start, significant_changes_only=False ) assert len(hist[entity_id]) == 3 assert states == hist[entity_id]
def test_get_significant_states_with_initial(self): """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ zero, four, states = self.record_states() one = zero + timedelta(seconds=1) one_and_half = zero + timedelta(seconds=1.5) for entity_id in states: if entity_id == "media_player.test": states[entity_id] = states[entity_id][1:] for state in states[entity_id]: if state.last_changed == one: state.last_changed = one_and_half hist = history.get_significant_states( self.hass, one_and_half, four, filters=history.Filters(), include_start_time_state=True, ) assert states == hist
def test_get_significant_states(self): """test that only significant states are returned with get_significant_states. We inject a bunch of state updates from media player and thermostat. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ self.init_recorder() mp = 'media_player.test' therm = 'thermostat.test' def set_state(entity_id, state, **kwargs): self.hass.states.set(entity_id, state, **kwargs) self.wait_recording_done() return self.hass.states.get(entity_id) zero = dt_util.utcnow() one = zero + timedelta(seconds=1) two = one + timedelta(seconds=1) three = two + timedelta(seconds=1) four = three + timedelta(seconds=1) states = {therm: [], mp: []} with patch('homeassistant.components.recorder.dt_util.utcnow', return_value=one): states[mp].append( set_state(mp, 'idle', attributes={'media_title': str(sentinel.mt1)})) states[mp].append( set_state(mp, 'YouTube', attributes={'media_title': str(sentinel.mt2)})) states[therm].append( set_state(therm, 20, attributes={'current_temperature': 19.5})) with patch('homeassistant.components.recorder.dt_util.utcnow', return_value=two): # this state will be skipped only different in time set_state(mp, 'YouTube', attributes={'media_title': str(sentinel.mt3)}) states[therm].append( set_state(therm, 21, attributes={'current_temperature': 19.8})) with patch('homeassistant.components.recorder.dt_util.utcnow', return_value=three): states[mp].append( set_state(mp, 'Netflix', attributes={'media_title': str(sentinel.mt4)})) # attributes changed even though state is the same states[therm].append( set_state(therm, 21, attributes={'current_temperature': 20})) hist = history.get_significant_states(zero, four) self.assertEqual(states, hist)
def test_get_significant_states_minimal_response(self): """Test that only significant states are returned. When minimal responses is set only the first and last states return a complete state. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ zero, four, states = self.record_states() hist = history.get_significant_states(self.hass, zero, four, filters=history.Filters(), minimal_response=True) # The second media_player.test state is reduced # down to last_changed and state when minimal_response # is set. We use JSONEncoder to make sure that are # pre-encoded last_changed is always the same as what # will happen with encoding a native state input_state = states["media_player.test"][1] orig_last_changed = json.dumps( process_timestamp(input_state.last_changed), cls=JSONEncoder, ).replace('"', "") orig_state = input_state.state states["media_player.test"][1] = { "last_changed": orig_last_changed, "state": orig_state, } assert states == hist
def test_get_significant_states_without_initial(hass_history): """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ hass = hass_history zero, four, states = record_states(hass) one = zero + timedelta(seconds=1) one_and_half = zero + timedelta(seconds=1.5) for entity_id in states: states[entity_id] = list( filter(lambda s: s.last_changed != one, states[entity_id]) ) del states["media_player.test2"] hist = history.get_significant_states( hass, one_and_half, four, filters=history.Filters(), include_start_time_state=False, ) assert states == hist
def test_get_significant_states_are_ordered(self): """Test order of results from get_significant_states. When entity ids are given, the results should be returned with the data in the same order. """ zero, four, states = self.record_states() entity_ids = ["media_player.test", "media_player.test2"] hist = history.get_significant_states( self.hass, zero, four, entity_ids, filters=history.Filters() ) assert list(hist.keys()) == entity_ids entity_ids = ["media_player.test2", "media_player.test"] hist = history.get_significant_states( self.hass, zero, four, entity_ids, filters=history.Filters() ) assert list(hist.keys()) == entity_ids
def test_get_significant_states_multiple_entity_ids(self): """Test that only significant states are returned for one entity.""" zero, four, states = self.record_states() del states['media_player.test2'] del states['thermostat.test2'] del states['script.can_cancel_this_one'] hist = history.get_significant_states( self.hass, zero, four, ['media_player.test', 'thermostat.test'], filters=history.Filters()) assert states == hist
def test_get_significant_states(self): """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ zero, four, states = self.record_states() hist = history.get_significant_states( self.hass, zero, four, filters=history.Filters()) assert states == hist
def test_get_significant_states_entity_id(self): """Test that only significant states are returned for one entity.""" zero, four, states = self.record_states() del states["media_player.test2"] del states["thermostat.test"] del states["thermostat.test2"] del states["script.can_cancel_this_one"] hist = history.get_significant_states( self.hass, zero, four, ["media_player.test"], filters=history.Filters() ) assert states == hist
def check_significant_states(self, zero, four, states, config): """Check if significant states are retrieved.""" filters = history.Filters() exclude = config[history.DOMAIN].get(history.CONF_EXCLUDE) if exclude: filters.excluded_entities = exclude.get(history.CONF_ENTITIES, []) filters.excluded_domains = exclude.get(history.CONF_DOMAINS, []) include = config[history.DOMAIN].get(history.CONF_INCLUDE) if include: filters.included_entities = include.get(history.CONF_ENTITIES, []) filters.included_domains = include.get(history.CONF_DOMAINS, []) hist = history.get_significant_states(self.hass, zero, four, filters=filters) assert states == hist
def check_significant_states(self, zero, four, states, config): """Check if significant states are retrieved.""" filters = history.Filters() exclude = config[history.DOMAIN].get(history.CONF_EXCLUDE) if exclude: filters.excluded_entities = exclude[history.CONF_ENTITIES] filters.excluded_domains = exclude[history.CONF_DOMAINS] include = config[history.DOMAIN].get(history.CONF_INCLUDE) if include: filters.included_entities = include[history.CONF_ENTITIES] filters.included_domains = include[history.CONF_DOMAINS] hist = history.get_significant_states(zero, four, filters=filters) assert states == hist
def check_significant_states(self, zero, four, states, config): \ # pylint: disable=no-self-use """Check if significant states are retrieved.""" filters = history.Filters() exclude = config[history.DOMAIN].get(history.CONF_EXCLUDE) if exclude: filters.excluded_entities = exclude[history.CONF_ENTITIES] filters.excluded_domains = exclude[history.CONF_DOMAINS] include = config[history.DOMAIN].get(history.CONF_INCLUDE) if include: filters.included_entities = include[history.CONF_ENTITIES] filters.included_domains = include[history.CONF_DOMAINS] hist = history.get_significant_states(zero, four, filters=filters) assert states == hist
def check_significant_states(self, zero, four, states, config): \ # pylint: disable=no-self-use """Check if significant states are retrieved.""" filters = history.Filters() exclude = config[history.DOMAIN].get(history.CONF_EXCLUDE) if exclude: filters.excluded_entities = exclude.get(history.CONF_ENTITIES, []) filters.excluded_domains = exclude.get(history.CONF_DOMAINS, []) include = config[history.DOMAIN].get(history.CONF_INCLUDE) if include: filters.included_entities = include.get(history.CONF_ENTITIES, []) filters.included_domains = include.get(history.CONF_DOMAINS, []) hist = history.get_significant_states( self.hass, zero, four, filters=filters) assert states == hist
def test_get_significant_states_multiple_entity_ids(hass_history): """Test that only significant states are returned for one entity.""" hass = hass_history zero, four, states = record_states(hass) del states["media_player.test2"] del states["media_player.test3"] del states["thermostat.test2"] del states["script.can_cancel_this_one"] hist = history.get_significant_states( hass, zero, four, ["media_player.test", "thermostat.test"], filters=history.Filters(), ) assert states == hist
def test_get_significant_states_without_initial(self): """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ zero, four, states = self.record_states() one = zero + timedelta(seconds=1) one_and_half = zero + timedelta(seconds=1.5) for entity_id in states: states[entity_id] = list(filter( lambda s: s.last_changed != one, states[entity_id])) del states['media_player.test2'] hist = history.get_significant_states( self.hass, one_and_half, four, filters=history.Filters(), include_start_time_state=False) assert states == hist
def test_get_significant_states_with_initial(self): """Test that only significant states are returned. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ zero, four, states = self.record_states() one = zero + timedelta(seconds=1) one_and_half = zero + timedelta(seconds=1.5) for entity_id in states: if entity_id == 'media_player.test': states[entity_id] = states[entity_id][1:] for state in states[entity_id]: if state.last_changed == one: state.last_changed = one_and_half hist = history.get_significant_states( self.hass, one_and_half, four, filters=history.Filters(), include_start_time_state=True) assert states == hist
def test_get_significant_states(self): """Test that only significant states are returned. We inject a bunch of state updates from media player, zone and thermostat. We should get back every thermostat change that includes an attribute change, but only the state updates for media player (attribute changes are not significant and not returned). """ self.init_recorder() mp = 'media_player.test' therm = 'thermostat.test' zone = 'zone.home' script_nc = 'script.cannot_cancel_this_one' script_c = 'script.can_cancel_this_one' def set_state(entity_id, state, **kwargs): self.hass.states.set(entity_id, state, **kwargs) self.wait_recording_done() return self.hass.states.get(entity_id) zero = dt_util.utcnow() one = zero + timedelta(seconds=1) two = one + timedelta(seconds=1) three = two + timedelta(seconds=1) four = three + timedelta(seconds=1) states = {therm: [], mp: [], script_c: []} with patch('homeassistant.components.recorder.dt_util.utcnow', return_value=one): states[mp].append( set_state(mp, 'idle', attributes={'media_title': str(sentinel.mt1)})) states[mp].append( set_state(mp, 'YouTube', attributes={'media_title': str(sentinel.mt2)})) states[therm].append( set_state(therm, 20, attributes={'current_temperature': 19.5})) with patch('homeassistant.components.recorder.dt_util.utcnow', return_value=two): # This state will be skipped only different in time set_state(mp, 'YouTube', attributes={'media_title': str(sentinel.mt3)}) # This state will be skipped because domain blacklisted set_state(zone, 'zoning') set_state(script_nc, 'off') states[script_c].append( set_state(script_c, 'off', attributes={'can_cancel': True})) states[therm].append( set_state(therm, 21, attributes={'current_temperature': 19.8})) with patch('homeassistant.components.recorder.dt_util.utcnow', return_value=three): states[mp].append( set_state(mp, 'Netflix', attributes={'media_title': str(sentinel.mt4)})) # Attributes changed even though state is the same states[therm].append( set_state(therm, 21, attributes={'current_temperature': 20})) hist = history.get_significant_states(zero, four) assert states == hist
async def handle_presence_simulation(call, restart=False, entities_after_restart=None, delta_after_restart=None): """Start the presence simulation""" if call is not None: #if we are here, it is a call of the service, or a restart at the end of a cycle if isinstance(call.data.get("entity_id", entities), list): overridden_entities = call.data.get("entity_id", entities) else: overridden_entities = [call.data.get("entity_id", entities)] overridden_delta = call.data.get("delta", delta) overridden_restore = call.data.get("restore_states", restoreAfterStop) else: #if we are it is a call from the toggle service or from the turn_on action of the switch entity # or this is a restart and the simulation was launched after a restart of HA if entities_after_restart is not None: overridden_entities = entities_after_restart else: overridden_entities = entities if delta_after_restart is not None: overridden_delta = delta_after_restart else: overridden_delta = delta overridden_restore = restoreAfterStop #get the switch entity entity = hass.data[DOMAIN][SWITCH_PLATFORM][SWITCH] _LOGGER.debug("Is already running ? %s", entity.state) if is_running(): _LOGGER.warning( "Presence simulation already running. Doing nothing") return running = True #turn on the switch. Not calling turn_on() to avoid calling the start service again entity.internal_turn_on() _LOGGER.debug("setting restore states %s", overridden_restore) await entity.set_restore_states(overridden_restore) _LOGGER.debug("Presence simulation started") current_date = datetime.now(timezone.utc) #compute the start date that will be used in the query to get the historic of the entities minus_delta = current_date + timedelta(-overridden_delta) #expand the entitiies, meaning replace the groups with the entities in it try: expanded_entities = await async_expand_entities(overridden_entities ) except Exception as e: _LOGGER.error("Error during identifing entities") running = False entity.internal_turn_off() return if not restart: #set attribute on the switch try: await entity.set_start_datetime( datetime.now(hass.config.time_zone)) except Exception as e: _LOGGER.error( "Start datetime could not be set to HA timezone: ", e) await entity.set_start_datetime(datetime.now()) if overridden_restore: service_data = {} service_data["scene_id"] = RESTORE_SCENE service_data["snapshot_entities"] = expanded_entities _LOGGER.debug("Saving scene before launching the simulation") try: await hass.services.async_call("scene", "create", service_data, blocking=True) except Exception as e: _LOGGER.error( "Scene could not be created, continue without the restore functionality: %s", e) await entity.set_entities(expanded_entities) await entity.set_delta(overridden_delta) _LOGGER.debug("Getting the historic from %s for %s", minus_delta, expanded_entities) dic = history.get_significant_states(hass=hass, start_time=minus_delta, entity_ids=expanded_entities, significant_changes_only=False) _LOGGER.debug("history: %s", dic) for entity_id in dic: _LOGGER.debug('Entity %s', entity_id) #launch an async task by entity_id hass.async_create_task( simulate_single_entity(entity_id, dic[entity_id])) #launch an async task that will restart the simulation after the delay has passed hass.async_create_task( restart_presence_simulation( call, entities_after_restart=entities_after_restart, delta_after_restart=delta_after_restart)) _LOGGER.debug("All async tasks launched")