def test_update_error(self, mock_session, mock_request): """Test updating feed results in error.""" home_coordinates = (-31.0, 151.0) mock_session.return_value.__enter__.return_value.send.return_value.ok = False feed = GenericFeed(home_coordinates, None) status, entries = feed.update() assert status == UPDATE_ERROR self.assertIsNone(feed.last_timestamp)
def test_update_with_request_exception(self, mock_session, mock_request): """Test updating feed raises exception.""" home_coordinates = (-31.0, 151.0) mock_session.return_value.__enter__.return_value.send\ .side_effect = requests.exceptions.RequestException feed = GenericFeed(home_coordinates, None) status, entries = feed.update() assert status == UPDATE_ERROR self.assertIsNone(entries)
def test_update_with_json_decode_error(self, mock_session, mock_request): """Test updating feed raises exception.""" home_coordinates = (-31.0, 151.0) mock_session.return_value.__enter__.return_value.send\ .side_effect = JSONDecodeError("", "", 0) feed = GenericFeed(home_coordinates, None) status, entries = feed.update() assert status == UPDATE_ERROR self.assertIsNone(entries)
def __init__(self, hass, add_entities, scan_interval, url, radius_in_km): """Initialize the GeoJSON Feed Manager.""" from geojson_client.generic_feed import GenericFeed self._hass = hass self._feed = GenericFeed( (hass.config.latitude, hass.config.longitude), filter_radius=radius_in_km, url=url) self._add_entities = add_entities self._scan_interval = scan_interval self.feed_entries = {} self._managed_external_ids = set()
def __init__(self, hass, add_entities, scan_interval, url, radius_in_km): """Initialize the GeoJSON Feed Manager.""" from geojson_client.generic_feed import GenericFeed self._hass = hass self._feed = GenericFeed((hass.config.latitude, hass.config.longitude), filter_radius=radius_in_km, url=url) self._add_entities = add_entities self._scan_interval = scan_interval self._feed_entries = [] self._managed_entities = [] hass.bus.listen_once(EVENT_HOMEASSISTANT_START, lambda _: self._update()) self._init_regular_updates()
def test_update_ok_with_filtering_override(self, mock_session, mock_request): """Test updating feed is ok.""" home_coordinates = (-37.0, 150.0) mock_session.return_value.__enter__.return_value.send.return_value.ok = True mock_session.return_value.__enter__.return_value.send.return_value.text = ( load_fixture("generic_feed_1.json") ) feed = GenericFeed(home_coordinates, None, filter_radius=90.0) status, entries = feed.update_override({"radius": 80.0}) assert status == UPDATE_OK self.assertIsNotNone(entries) assert len(entries) == 1 self.assertAlmostEqual(entries[0].distance_to_home, 77.0, 1)
def test_update_ok_with_filtering(self, mock_session, mock_request): """Test updating feed is ok.""" home_coordinates = (-37.0, 150.0) mock_session.return_value.__enter__.return_value.send\ .return_value.ok = True mock_session.return_value.__enter__.return_value.send\ .return_value.text = load_fixture('generic_feed_1.json') feed = GenericFeed(home_coordinates, None, filter_radius=90.0) status, entries = feed.update() assert status == UPDATE_OK self.assertIsNotNone(entries) assert len(entries) == 4 self.assertAlmostEqual(entries[0].distance_to_home, 82.0, 1) self.assertAlmostEqual(entries[1].distance_to_home, 77.0, 1) self.assertAlmostEqual(entries[2].distance_to_home, 84.6, 1)
def __init__(self, hass, add_entities, scan_interval, url, radius_in_km): """Initialize the GeoJSON Feed Manager.""" from geojson_client.generic_feed import GenericFeed self._hass = hass self._feed = GenericFeed((hass.config.latitude, hass.config.longitude), filter_radius=radius_in_km, url=url) self._add_entities = add_entities self._scan_interval = scan_interval self._feed_entries = [] self._managed_entities = [] hass.bus.listen_once( EVENT_HOMEASSISTANT_START, lambda _: self._update()) self._init_regular_updates()
def test_update_ok(self, mock_session, mock_request): """Test updating feed is ok.""" home_coordinates = (-31.0, 151.0) mock_session.return_value.__enter__.return_value.send.return_value.ok = True mock_session.return_value.__enter__.return_value.send.return_value.text = ( load_fixture("generic_feed_1.json") ) feed = GenericFeed(home_coordinates, None) assert ( repr(feed) == "<GenericFeed(home=(-31.0, 151.0), url=None, " "radius=None)>" ) status, entries = feed.update() assert status == UPDATE_OK self.assertIsNotNone(entries) assert len(entries) == 5 feed_entry = entries[0] assert feed_entry.title == "Title 1" assert feed_entry.external_id == "3456" assert feed_entry.coordinates == (-37.2345, 149.1234) self.assertAlmostEqual(feed_entry.distance_to_home, 714.4, 1) assert repr(feed_entry) == "<GenericFeedEntry(id=3456)>" feed_entry = entries[1] assert feed_entry.title == "Title 2" assert feed_entry.external_id == "4567" feed_entry = entries[2] assert feed_entry.title == "Title 3" assert feed_entry.external_id == "Title 3" feed_entry = entries[3] self.assertIsNone(feed_entry.title) assert feed_entry.external_id == hash(feed_entry.coordinates) feed_entry = entries[4] assert feed_entry.title == "Title 5" assert feed_entry.external_id == "7890"
class GeoJsonFeedManager: """Feed Manager for GeoJSON feeds.""" def __init__(self, hass, add_entities, scan_interval, url, radius_in_km): """Initialize the GeoJSON Feed Manager.""" from geojson_client.generic_feed import GenericFeed self._hass = hass self._feed = GenericFeed( (hass.config.latitude, hass.config.longitude), filter_radius=radius_in_km, url=url) self._add_entities = add_entities self._scan_interval = scan_interval self.feed_entries = {} self._managed_external_ids = set() def startup(self): """Start up this manager.""" self._update() self._init_regular_updates() def _init_regular_updates(self): """Schedule regular updates at the specified interval.""" track_time_interval( self._hass, lambda now: self._update(), self._scan_interval) def _update(self): """Update the feed and then update connected entities.""" import geojson_client status, feed_entries = self._feed.update() if status == geojson_client.UPDATE_OK: _LOGGER.debug("Data retrieved %s", feed_entries) # Keep a copy of all feed entries for future lookups by entities. self.feed_entries = {entry.external_id: entry for entry in feed_entries} # For entity management the external ids from the feed are used. feed_external_ids = set(self.feed_entries) remove_external_ids = self._managed_external_ids.difference( feed_external_ids) self._remove_entities(remove_external_ids) update_external_ids = self._managed_external_ids.intersection( feed_external_ids) self._update_entities(update_external_ids) create_external_ids = feed_external_ids.difference( self._managed_external_ids) self._generate_new_entities(create_external_ids) elif status == geojson_client.UPDATE_OK_NO_DATA: _LOGGER.debug( "Update successful, but no data received from %s", self._feed) else: _LOGGER.warning( "Update not successful, no data received from %s", self._feed) # Remove all entities. self._remove_entities(self._managed_external_ids.copy()) def _generate_new_entities(self, external_ids): """Generate new entities for events.""" new_entities = [] for external_id in external_ids: new_entity = GeoJsonLocationEvent(self, external_id) _LOGGER.debug("New entity added %s", external_id) new_entities.append(new_entity) self._managed_external_ids.add(external_id) # Add new entities to HA. self._add_entities(new_entities, True) def _update_entities(self, external_ids): """Update entities.""" for external_id in external_ids: _LOGGER.debug("Existing entity found %s", external_id) dispatcher_send( self._hass, SIGNAL_UPDATE_ENTITY.format(external_id)) def _remove_entities(self, external_ids): """Remove entities.""" for external_id in external_ids: _LOGGER.debug("Entity not current anymore %s", external_id) self._managed_external_ids.remove(external_id) dispatcher_send( self._hass, SIGNAL_DELETE_ENTITY.format(external_id))
class GeoJsonFeedManager: """Feed Manager for GeoJSON feeds.""" def __init__(self, hass, add_entities, scan_interval, url, radius_in_km): """Initialize the GeoJSON Feed Manager.""" from geojson_client.generic_feed import GenericFeed self._hass = hass self._feed = GenericFeed((hass.config.latitude, hass.config.longitude), filter_radius=radius_in_km, url=url) self._add_entities = add_entities self._scan_interval = scan_interval self._feed_entries = [] self._managed_entities = [] hass.bus.listen_once( EVENT_HOMEASSISTANT_START, lambda _: self._update()) self._init_regular_updates() def _init_regular_updates(self): """Schedule regular updates at the specified interval.""" track_time_interval(self._hass, lambda now: self._update(), self._scan_interval) def _update(self): """Update the feed and then update connected entities.""" import geojson_client status, feed_entries = self._feed.update() if status == geojson_client.UPDATE_OK: _LOGGER.debug("Data retrieved %s", feed_entries) # Keep a copy of all feed entries for future lookups by entities. self._feed_entries = feed_entries.copy() keep_entries = self._update_or_remove_entities(feed_entries) self._generate_new_entities(keep_entries) elif status == geojson_client.UPDATE_OK_NO_DATA: _LOGGER.debug("Update successful, but no data received from %s", self._feed) else: _LOGGER.warning("Update not successful, no data received from %s", self._feed) # Remove all entities. self._update_or_remove_entities([]) def _update_or_remove_entities(self, feed_entries): """Update existing entries and remove obsolete entities.""" _LOGGER.debug("Entries for updating: %s", feed_entries) remove_entry = None # Remove obsolete entities for events that have disappeared managed_entities = self._managed_entities.copy() for entity in managed_entities: # Remove entry from previous iteration - if applicable. if remove_entry: feed_entries.remove(remove_entry) remove_entry = None for entry in feed_entries: if entity.external_id == entry.external_id: # Existing entity - update details. _LOGGER.debug("Existing entity found %s", entity) remove_entry = entry entity.schedule_update_ha_state(True) break else: # Remove obsolete entity. _LOGGER.debug("Entity not current anymore %s", entity) self._managed_entities.remove(entity) self._hass.add_job(entity.async_remove()) # Remove entry from very last iteration - if applicable. if remove_entry: feed_entries.remove(remove_entry) # Return the remaining entries that new entities must be created for. return feed_entries def _generate_new_entities(self, entries): """Generate new entities for events.""" new_entities = [] for entry in entries: new_entity = GeoJsonLocationEvent(self, entry) _LOGGER.debug("New entity added %s", new_entity) new_entities.append(new_entity) # Add new entities to HA and keep track of them in this manager. self._add_entities(new_entities, True) self._managed_entities.extend(new_entities) def get_feed_entry(self, external_id): """Return a feed entry identified by external id.""" return next((entry for entry in self._feed_entries if entry.external_id == external_id), None)
class GeoJsonFeedManager: """Feed Manager for GeoJSON feeds.""" def __init__(self, hass, add_entities, scan_interval, url, radius_in_km): """Initialize the GeoJSON Feed Manager.""" from geojson_client.generic_feed import GenericFeed self._hass = hass self._feed = GenericFeed((hass.config.latitude, hass.config.longitude), filter_radius=radius_in_km, url=url) self._add_entities = add_entities self._scan_interval = scan_interval self._feed_entries = [] self._managed_entities = [] hass.bus.listen_once(EVENT_HOMEASSISTANT_START, lambda _: self._update()) self._init_regular_updates() def _init_regular_updates(self): """Schedule regular updates at the specified interval.""" track_time_interval(self._hass, lambda now: self._update(), self._scan_interval) def _update(self): """Update the feed and then update connected entities.""" import geojson_client status, feed_entries = self._feed.update() if status == geojson_client.UPDATE_OK: _LOGGER.debug("Data retrieved %s", feed_entries) # Keep a copy of all feed entries for future lookups by entities. self._feed_entries = feed_entries.copy() keep_entries = self._update_or_remove_entities(feed_entries) self._generate_new_entities(keep_entries) elif status == geojson_client.UPDATE_OK_NO_DATA: _LOGGER.debug("Update successful, but no data received from %s", self._feed) else: _LOGGER.warning("Update not successful, no data received from %s", self._feed) # Remove all entities. self._update_or_remove_entities([]) def _update_or_remove_entities(self, feed_entries): """Update existing entries and remove obsolete entities.""" _LOGGER.debug("Entries for updating: %s", feed_entries) remove_entry = None # Remove obsolete entities for events that have disappeared managed_entities = self._managed_entities.copy() for entity in managed_entities: # Remove entry from previous iteration - if applicable. if remove_entry: feed_entries.remove(remove_entry) remove_entry = None for entry in feed_entries: if entity.external_id == entry.external_id: # Existing entity - update details. _LOGGER.debug("Existing entity found %s", entity) remove_entry = entry entity.schedule_update_ha_state(True) break else: # Remove obsolete entity. _LOGGER.debug("Entity not current anymore %s", entity) self._managed_entities.remove(entity) self._hass.add_job(entity.async_remove()) # Remove entry from very last iteration - if applicable. if remove_entry: feed_entries.remove(remove_entry) # Return the remaining entries that new entities must be created for. return feed_entries def _generate_new_entities(self, entries): """Generate new entities for events.""" new_entities = [] for entry in entries: new_entity = GeoJsonLocationEvent(self, entry) _LOGGER.debug("New entity added %s", new_entity) new_entities.append(new_entity) # Add new entities to HA and keep track of them in this manager. self._add_entities(new_entities, True) self._managed_entities.extend(new_entities) def get_feed_entry(self, external_id): """Return a feed entry identified by external id.""" return next((entry for entry in self._feed_entries if entry.external_id == external_id), None)