def test_closest_with_no_states_with_location(self): """Setup the tests.""" state = State('light.test', 'on') state2 = State('light.test', 'on', { ATTR_LATITUDE: 'invalid', ATTR_LONGITUDE: 123.45, }) state3 = State('light.test', 'on', { ATTR_LONGITUDE: 123.45, }) self.assertIsNone( location.closest(123.45, 123.45, [state, state2, state3]))
def test_closest_returns_closest(self): """Test .""" state = State('light.test', 'on', { ATTR_LATITUDE: 124.45, ATTR_LONGITUDE: 124.45, }) state2 = State('light.test', 'on', { ATTR_LATITUDE: 125.45, ATTR_LONGITUDE: 125.45, }) self.assertEqual(state, location.closest(123.45, 123.45, [state, state2]))
def _process_config(scene_config): """Process passed in config into a format to work with.""" name = scene_config.get('name') states = {} c_entities = dict(scene_config.get(CONF_ENTITIES, {})) for entity_id in c_entities: if isinstance(c_entities[entity_id], dict): entity_attrs = c_entities[entity_id].copy() state = entity_attrs.pop('state', None) attributes = entity_attrs else: state = c_entities[entity_id] attributes = {} # YAML translates 'on' to a boolean # http://yaml.org/type/bool.html if isinstance(state, bool): state = STATE_ON if state else STATE_OFF else: state = str(state) states[entity_id.lower()] = State(entity_id, state, attributes) return SceneConfig(name, states)
def test_has_location_with_states_with_valid_location(self): """Setup the tests.""" state = State('hello.world', 'invalid', { ATTR_LATITUDE: 123.12, ATTR_LONGITUDE: 123.12 }) self.assertTrue(location.has_location(state))
def row_to_state(row): """Convert a database row to a state.""" try: return State(row[1], row[2], json.loads(row[3]), dt_util.utc_from_timestamp(row[4]), dt_util.utc_from_timestamp(row[5])) except ValueError: # When json.loads fails _LOGGER.exception("Error converting row to state: %s", row) return None
def test_state_changed_event_sends_message(self, mock_utcnow, mock_pub): """"Test the sending of a new message if event changed.""" now = dt_util.as_utc(dt_util.now()) e_id = 'fake.entity' pub_topic = 'bar' mock_utcnow.return_value = now # Add the eventstream component for publishing events self.assertTrue(self.add_eventstream(pub_topic=pub_topic)) self.hass.pool.block_till_done() # Reset the mock because it will have already gotten calls for the # mqtt_eventstream state change on initialization, etc. mock_pub.reset_mock() # Set a state of an entity mock_state_change_event(self.hass, State(e_id, 'on')) self.hass.pool.block_till_done() # The order of the JSON is indeterminate, # so first just check that publish was called mock_pub.assert_called_with(self.hass, pub_topic, ANY) self.assertTrue(mock_pub.called) # Get the actual call to publish and make sure it was the one # we were looking for msg = mock_pub.call_args[0][2] event = {} event['event_type'] = EVENT_STATE_CHANGED new_state = { "last_updated": now.isoformat(), "state": "on", "entity_id": e_id, "attributes": {}, "last_changed": now.isoformat() } event['event_data'] = {"new_state": new_state, "entity_id": e_id} # Verify that the message received was that expected self.assertEqual(json.loads(msg), event)
def _event_receiver(topic, payload, qos): """Receive events published by and fire them on this hass instance.""" event = json.loads(payload) event_type = event.get('event_type') event_data = event.get('event_data') # Special case handling for event STATE_CHANGED # We will try to convert state dicts back to State objects # Copied over from the _handle_api_post_events_event method # of the api component. if event_type == EVENT_STATE_CHANGED and event_data: for key in ('old_state', 'new_state'): state = State.from_dict(event_data.get(key)) if state: event_data[key] = state hass.bus.fire( event_type, event_data=event_data, origin=EventOrigin.remote )
def humanify(events): """Generator that converts a list of events into Entry objects. Will try to group events if possible: - if 2+ sensor updates in GROUP_BY_MINUTES, show last - if home assistant stop and start happen in same minute call it restarted """ # pylint: disable=too-many-branches # Group events in batches of GROUP_BY_MINUTES for _, g_events in groupby( events, lambda event: event.time_fired.minute // GROUP_BY_MINUTES): events_batch = list(g_events) # Keep track of last sensor states last_sensor_event = {} # Group HA start/stop events # Maps minute of event to 1: stop, 2: stop + start start_stop_events = {} # Process events for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: entity_id = event.data.get('entity_id') if entity_id is None: continue if entity_id.startswith('sensor.'): last_sensor_event[entity_id] = event elif event.event_type == EVENT_BLUMATE_STOP: if event.time_fired.minute in start_stop_events: continue start_stop_events[event.time_fired.minute] = 1 elif event.event_type == EVENT_BLUMATE_START: if event.time_fired.minute not in start_stop_events: continue start_stop_events[event.time_fired.minute] = 2 # Yield entries for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: # Do not report on new entities if 'old_state' not in event.data: continue to_state = State.from_dict(event.data.get('new_state')) # If last_changed != last_updated only attributes have changed # we do not report on that yet. Also filter auto groups. if not to_state or \ to_state.last_changed != to_state.last_updated or \ to_state.domain == 'group' and \ to_state.attributes.get('auto', False): continue domain = to_state.domain # Skip all but the last sensor state if domain == 'sensor' and \ event != last_sensor_event[to_state.entity_id]: continue yield Entry(event.time_fired, name=to_state.name, message=_entry_message_from_state( domain, to_state), domain=domain, entity_id=to_state.entity_id) elif event.event_type == EVENT_BLUMATE_START: if start_stop_events.get(event.time_fired.minute) == 2: continue yield Entry(event.time_fired, "Home Assistant", "started", domain=HA_DOMAIN) elif event.event_type == EVENT_BLUMATE_STOP: if start_stop_events.get(event.time_fired.minute) == 2: action = "restarted" else: action = "stopped" yield Entry(event.time_fired, "Home Assistant", action, domain=HA_DOMAIN) elif event.event_type.lower() == EVENT_LOGBOOK_ENTRY: domain = event.data.get(ATTR_DOMAIN) entity_id = event.data.get(ATTR_ENTITY_ID) if domain is None and entity_id is not None: try: domain = split_entity_id(str(entity_id))[0] except IndexError: pass yield Entry(event.time_fired, event.data.get(ATTR_NAME), event.data.get(ATTR_MESSAGE), domain, entity_id)
def humanify(events): """Generator that converts a list of events into Entry objects. Will try to group events if possible: - if 2+ sensor updates in GROUP_BY_MINUTES, show last - if home assistant stop and start happen in same minute call it restarted """ # pylint: disable=too-many-branches # Group events in batches of GROUP_BY_MINUTES for _, g_events in groupby( events, lambda event: event.time_fired.minute // GROUP_BY_MINUTES): events_batch = list(g_events) # Keep track of last sensor states last_sensor_event = {} # Group HA start/stop events # Maps minute of event to 1: stop, 2: stop + start start_stop_events = {} # Process events for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: entity_id = event.data.get('entity_id') if entity_id is None: continue if entity_id.startswith('sensor.'): last_sensor_event[entity_id] = event elif event.event_type == EVENT_BLUMATE_STOP: if event.time_fired.minute in start_stop_events: continue start_stop_events[event.time_fired.minute] = 1 elif event.event_type == EVENT_BLUMATE_START: if event.time_fired.minute not in start_stop_events: continue start_stop_events[event.time_fired.minute] = 2 # Yield entries for event in events_batch: if event.event_type == EVENT_STATE_CHANGED: # Do not report on new entities if 'old_state' not in event.data: continue to_state = State.from_dict(event.data.get('new_state')) # If last_changed != last_updated only attributes have changed # we do not report on that yet. Also filter auto groups. if not to_state or \ to_state.last_changed != to_state.last_updated or \ to_state.domain == 'group' and \ to_state.attributes.get('auto', False): continue domain = to_state.domain # Skip all but the last sensor state if domain == 'sensor' and \ event != last_sensor_event[to_state.entity_id]: continue yield Entry( event.time_fired, name=to_state.name, message=_entry_message_from_state(domain, to_state), domain=domain, entity_id=to_state.entity_id) elif event.event_type == EVENT_BLUMATE_START: if start_stop_events.get(event.time_fired.minute) == 2: continue yield Entry( event.time_fired, "Home Assistant", "started", domain=HA_DOMAIN) elif event.event_type == EVENT_BLUMATE_STOP: if start_stop_events.get(event.time_fired.minute) == 2: action = "restarted" else: action = "stopped" yield Entry( event.time_fired, "Home Assistant", action, domain=HA_DOMAIN) elif event.event_type.lower() == EVENT_LOGBOOK_ENTRY: domain = event.data.get(ATTR_DOMAIN) entity_id = event.data.get(ATTR_ENTITY_ID) if domain is None and entity_id is not None: try: domain = split_entity_id(str(entity_id))[0] except IndexError: pass yield Entry( event.time_fired, event.data.get(ATTR_NAME), event.data.get(ATTR_MESSAGE), domain, entity_id)