Feedreader configurable update interval and max entries (#14487)
This commit is contained in:
parent
1c3293ac85
commit
a3777c4ea8
2 changed files with 75 additions and 27 deletions
|
@ -4,7 +4,7 @@ Support for RSS/Atom feeds.
|
|||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/feedreader/
|
||||
"""
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from logging import getLogger
|
||||
from os.path import exists
|
||||
from threading import Lock
|
||||
|
@ -12,8 +12,8 @@ import pickle
|
|||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
from homeassistant.helpers.event import track_utc_time_change
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START, CONF_SCAN_INTERVAL
|
||||
from homeassistant.helpers.event import track_time_interval
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['feedparser==5.2.1']
|
||||
|
@ -21,16 +21,22 @@ REQUIREMENTS = ['feedparser==5.2.1']
|
|||
_LOGGER = getLogger(__name__)
|
||||
|
||||
CONF_URLS = 'urls'
|
||||
CONF_MAX_ENTRIES = 'max_entries'
|
||||
|
||||
DEFAULT_MAX_ENTRIES = 20
|
||||
DEFAULT_SCAN_INTERVAL = timedelta(hours=1)
|
||||
|
||||
DOMAIN = 'feedreader'
|
||||
|
||||
EVENT_FEEDREADER = 'feedreader'
|
||||
|
||||
MAX_ENTRIES = 20
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: {
|
||||
vol.Required(CONF_URLS): vol.All(cv.ensure_list, [cv.url]),
|
||||
vol.Optional(CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL):
|
||||
cv.time_period,
|
||||
vol.Optional(CONF_MAX_ENTRIES, default=DEFAULT_MAX_ENTRIES):
|
||||
cv.positive_int
|
||||
}
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
@ -38,18 +44,23 @@ CONFIG_SCHEMA = vol.Schema({
|
|||
def setup(hass, config):
|
||||
"""Set up the Feedreader component."""
|
||||
urls = config.get(DOMAIN)[CONF_URLS]
|
||||
scan_interval = config.get(DOMAIN).get(CONF_SCAN_INTERVAL)
|
||||
max_entries = config.get(DOMAIN).get(CONF_MAX_ENTRIES)
|
||||
data_file = hass.config.path("{}.pickle".format(DOMAIN))
|
||||
storage = StoredData(data_file)
|
||||
feeds = [FeedManager(url, hass, storage) for url in urls]
|
||||
feeds = [FeedManager(url, scan_interval, max_entries, hass, storage) for
|
||||
url in urls]
|
||||
return len(feeds) > 0
|
||||
|
||||
|
||||
class FeedManager(object):
|
||||
"""Abstraction over Feedparser module."""
|
||||
|
||||
def __init__(self, url, hass, storage):
|
||||
"""Initialize the FeedManager object, poll every hour."""
|
||||
def __init__(self, url, scan_interval, max_entries, hass, storage):
|
||||
"""Initialize the FeedManager object, poll as per scan interval."""
|
||||
self._url = url
|
||||
self._scan_interval = scan_interval
|
||||
self._max_entries = max_entries
|
||||
self._feed = None
|
||||
self._hass = hass
|
||||
self._firstrun = True
|
||||
|
@ -69,8 +80,8 @@ class FeedManager(object):
|
|||
|
||||
def _init_regular_updates(self, hass):
|
||||
"""Schedule regular updates at the top of the clock."""
|
||||
track_utc_time_change(
|
||||
hass, lambda now: self._update(), minute=0, second=0)
|
||||
track_time_interval(hass, lambda now: self._update(),
|
||||
self._scan_interval)
|
||||
|
||||
@property
|
||||
def last_update_successful(self):
|
||||
|
@ -116,10 +127,10 @@ class FeedManager(object):
|
|||
|
||||
def _filter_entries(self):
|
||||
"""Filter the entries provided and return the ones to keep."""
|
||||
if len(self._feed.entries) > MAX_ENTRIES:
|
||||
if len(self._feed.entries) > self._max_entries:
|
||||
_LOGGER.debug("Processing only the first %s entries "
|
||||
"in feed %s", MAX_ENTRIES, self._url)
|
||||
self._feed.entries = self._feed.entries[0:MAX_ENTRIES]
|
||||
"in feed %s", self._max_entries, self._url)
|
||||
self._feed.entries = self._feed.entries[0:self._max_entries]
|
||||
|
||||
def _update_and_fire_entry(self, entry):
|
||||
"""Update last_entry_timestamp and fire entry."""
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""The tests for the feedreader component."""
|
||||
import time
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import unittest
|
||||
from genericpath import exists
|
||||
|
@ -11,12 +11,12 @@ from unittest.mock import patch
|
|||
|
||||
from homeassistant.components import feedreader
|
||||
from homeassistant.components.feedreader import CONF_URLS, FeedManager, \
|
||||
StoredData, EVENT_FEEDREADER
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START
|
||||
StoredData, EVENT_FEEDREADER, DEFAULT_SCAN_INTERVAL, CONF_MAX_ENTRIES, \
|
||||
DEFAULT_MAX_ENTRIES
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_START, CONF_SCAN_INTERVAL
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.setup import setup_component
|
||||
from tests.common import get_test_home_assistant, assert_setup_component, \
|
||||
load_fixture
|
||||
from tests.common import get_test_home_assistant, load_fixture
|
||||
|
||||
_LOGGER = getLogger(__name__)
|
||||
|
||||
|
@ -26,6 +26,18 @@ VALID_CONFIG_1 = {
|
|||
CONF_URLS: [URL]
|
||||
}
|
||||
}
|
||||
VALID_CONFIG_2 = {
|
||||
feedreader.DOMAIN: {
|
||||
CONF_URLS: [URL],
|
||||
CONF_SCAN_INTERVAL: 60
|
||||
}
|
||||
}
|
||||
VALID_CONFIG_3 = {
|
||||
feedreader.DOMAIN: {
|
||||
CONF_URLS: [URL],
|
||||
CONF_MAX_ENTRIES: 100
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TestFeedreaderComponent(unittest.TestCase):
|
||||
|
@ -45,11 +57,28 @@ class TestFeedreaderComponent(unittest.TestCase):
|
|||
|
||||
def test_setup_one_feed(self):
|
||||
"""Test the general setup of this component."""
|
||||
with assert_setup_component(1, 'feedreader'):
|
||||
with patch("homeassistant.components.feedreader."
|
||||
"track_time_interval") as track_method:
|
||||
self.assertTrue(setup_component(self.hass, feedreader.DOMAIN,
|
||||
VALID_CONFIG_1))
|
||||
track_method.assert_called_once_with(self.hass, mock.ANY,
|
||||
DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
def setup_manager(self, feed_data):
|
||||
def test_setup_scan_interval(self):
|
||||
"""Test the setup of this component with scan interval."""
|
||||
with patch("homeassistant.components.feedreader."
|
||||
"track_time_interval") as track_method:
|
||||
self.assertTrue(setup_component(self.hass, feedreader.DOMAIN,
|
||||
VALID_CONFIG_2))
|
||||
track_method.assert_called_once_with(self.hass, mock.ANY,
|
||||
timedelta(seconds=60))
|
||||
|
||||
def test_setup_max_entries(self):
|
||||
"""Test the setup of this component with max entries."""
|
||||
self.assertTrue(setup_component(self.hass, feedreader.DOMAIN,
|
||||
VALID_CONFIG_3))
|
||||
|
||||
def setup_manager(self, feed_data, max_entries=DEFAULT_MAX_ENTRIES):
|
||||
"""Generic test setup method."""
|
||||
events = []
|
||||
|
||||
|
@ -67,12 +96,13 @@ class TestFeedreaderComponent(unittest.TestCase):
|
|||
feedreader.DOMAIN))
|
||||
storage = StoredData(data_file)
|
||||
with patch("homeassistant.components.feedreader."
|
||||
"track_utc_time_change") as track_method:
|
||||
manager = FeedManager(feed_data, self.hass, storage)
|
||||
"track_time_interval") as track_method:
|
||||
manager = FeedManager(feed_data, DEFAULT_SCAN_INTERVAL,
|
||||
max_entries, self.hass, storage)
|
||||
# Can't use 'assert_called_once' here because it's not available
|
||||
# in Python 3.5 yet.
|
||||
track_method.assert_called_once_with(self.hass, mock.ANY, minute=0,
|
||||
second=0)
|
||||
track_method.assert_called_once_with(self.hass, mock.ANY,
|
||||
DEFAULT_SCAN_INTERVAL)
|
||||
# Artificially trigger update.
|
||||
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
|
||||
# Collect events.
|
||||
|
@ -116,12 +146,18 @@ class TestFeedreaderComponent(unittest.TestCase):
|
|||
manager3, events3 = self.setup_manager(feed_data3)
|
||||
assert len(events3) == 0
|
||||
|
||||
def test_feed_max_length(self):
|
||||
"""Test long feed beyond the 20 entry limit."""
|
||||
def test_feed_default_max_length(self):
|
||||
"""Test long feed beyond the default 20 entry limit."""
|
||||
feed_data = load_fixture('feedreader2.xml')
|
||||
manager, events = self.setup_manager(feed_data)
|
||||
assert len(events) == 20
|
||||
|
||||
def test_feed_max_length(self):
|
||||
"""Test long feed beyond a configured 5 entry limit."""
|
||||
feed_data = load_fixture('feedreader2.xml')
|
||||
manager, events = self.setup_manager(feed_data, max_entries=5)
|
||||
assert len(events) == 5
|
||||
|
||||
def test_feed_without_publication_date(self):
|
||||
"""Test simple feed with entry without publication date."""
|
||||
feed_data = load_fixture('feedreader3.xml')
|
||||
|
@ -141,7 +177,8 @@ class TestFeedreaderComponent(unittest.TestCase):
|
|||
data_file = self.hass.config.path("{}.pickle".format(
|
||||
feedreader.DOMAIN))
|
||||
storage = StoredData(data_file)
|
||||
manager = FeedManager("FEED DATA", self.hass, storage)
|
||||
manager = FeedManager("FEED DATA", DEFAULT_SCAN_INTERVAL,
|
||||
DEFAULT_MAX_ENTRIES, self.hass, storage)
|
||||
# Artificially trigger update.
|
||||
self.hass.bus.fire(EVENT_HOMEASSISTANT_START)
|
||||
# Collect events.
|
||||
|
|
Loading…
Add table
Reference in a new issue