[feed] Minor improvements for Feed Binding (#8824)
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
This commit is contained in:
parent
44e3f9c90f
commit
333cae9e72
|
@ -4,9 +4,7 @@ This binding allows you to integrate feeds in the openHAB environment.
|
||||||
The Feed binding downloads the content, tracks for changes, and displays information like feed author, feed title and description, number of entries, last update date.
|
The Feed binding downloads the content, tracks for changes, and displays information like feed author, feed title and description, number of entries, last update date.
|
||||||
|
|
||||||
It can be used in combination with openHAB rules to trigger events on feed change.
|
It can be used in combination with openHAB rules to trigger events on feed change.
|
||||||
It uses the [ROME library](https://rometools.github.io/rome/index.html) for parsing
|
It uses the [ROME library](https://rometools.github.io/rome/index.html) for parsing and supports a wide range of popular feed formats - RSS 2.00, RSS 1.00, RSS 0.94, RSS 0.93, RSS 0.92, RSS 0.91 UserLand, RSS 0.91 Netscape, RSS 0.90, Atom 1.0, Atom 0.3.
|
||||||
and supports a wide range of popular feed formats - RSS 2.00, RSS 1.00, RSS 0.94, RSS 0.93, RSS 0.92, RSS 0.91 UserLand,
|
|
||||||
RSS 0.91 Netscape, RSS 0.90, Atom 1.0, Atom 0.3.
|
|
||||||
|
|
||||||
## Supported Things
|
## Supported Things
|
||||||
|
|
||||||
|
@ -24,11 +22,11 @@ No binding configuration required.
|
||||||
|
|
||||||
Required configuration:
|
Required configuration:
|
||||||
|
|
||||||
- **URL** - the URL of the feed (e.g <http://example.com/path/file>). The binding uses this URL to download data
|
- **URL** - the URL of the feed (e.g <http://example.com/path/file>). The binding uses this URL to download data.
|
||||||
|
|
||||||
Optional configuration:
|
Optional configuration:
|
||||||
|
|
||||||
- **refresh** - a refresh interval defines after how many minutes the binding will check, if new content is available. Default value is 20 minutes
|
- **refresh** - a refresh interval defines after how many minutes the binding will check, if new content is available. Default value is 20 minutes.
|
||||||
|
|
||||||
## Channels
|
## Channels
|
||||||
|
|
||||||
|
@ -39,18 +37,18 @@ The binding supports following channels
|
||||||
| latest-title | String | Contains the title of the last feed entry. |
|
| latest-title | String | Contains the title of the last feed entry. |
|
||||||
| latest-description | String | Contains the description of last feed entry. |
|
| latest-description | String | Contains the description of last feed entry. |
|
||||||
| latest-date | DateTime | Contains the published date of the last feed entry. |
|
| latest-date | DateTime | Contains the published date of the last feed entry. |
|
||||||
| author | String | The name of the feed author, if author is present |
|
| author | String | The name of the feed author, if author is present. |
|
||||||
| title | String | The title of the feed |
|
| title | String | The title of the feed. |
|
||||||
| description | String | Description of the feed |
|
| description | String | Description of the feed. |
|
||||||
| last-update | DateTime | The last update date of the feed |
|
| last-update | DateTime | The last update date of the feed. |
|
||||||
| number-of-entries | Number | Number of entries in the feed |
|
| number-of-entries | Number | Number of entries in the feed. |
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Things:
|
Things:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
feed:feed:bbc [ URL="http://feeds.bbci.co.uk/news/video_and_audio/news_front_page/rss.xml?edition=uk"]
|
feed:feed:bbc [ URL="http://feeds.bbci.co.uk/news/video_and_audio/news_front_page/rss.xml?edition=uk"]
|
||||||
feed:feed:techCrunch [ URL="http://feeds.feedburner.com/TechCrunch/", refresh=60]
|
feed:feed:techCrunch [ URL="http://feeds.feedburner.com/TechCrunch/", refresh=60]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.feed.internal;
|
package org.openhab.binding.feed.internal;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
|
||||||
|
@ -86,11 +84,11 @@ public class FeedBindingConstants {
|
||||||
/**
|
/**
|
||||||
* The default auto refresh time in minutes.
|
* The default auto refresh time in minutes.
|
||||||
*/
|
*/
|
||||||
public static final BigDecimal DEFAULT_REFRESH_TIME = new BigDecimal(20);
|
public static final long DEFAULT_REFRESH_TIME = 20;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum refresh time in milliseconds. Any REFRESH command send to a Thing, before this time has expired, will
|
* The minimum refresh time in milliseconds. Any REFRESH command send to a Thing, before this time has expired, will
|
||||||
* not trigger an attempt to dowload new data form the server.
|
* not trigger an attempt to download new data from the server.
|
||||||
**/
|
**/
|
||||||
public static final int MINIMUM_REFRESH_TIME = 3000;
|
public static final int MINIMUM_REFRESH_TIME = 3000;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,10 @@ package org.openhab.binding.feed.internal;
|
||||||
|
|
||||||
import static org.openhab.binding.feed.internal.FeedBindingConstants.FEED_THING_TYPE_UID;
|
import static org.openhab.binding.feed.internal.FeedBindingConstants.FEED_THING_TYPE_UID;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.binding.feed.internal.handler.FeedHandler;
|
import org.openhab.binding.feed.internal.handler.FeedHandler;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingTypeUID;
|
import org.openhab.core.thing.ThingTypeUID;
|
||||||
|
@ -32,9 +33,10 @@ import org.osgi.service.component.annotations.Component;
|
||||||
* @author Svilen Valkanov - Initial contribution
|
* @author Svilen Valkanov - Initial contribution
|
||||||
*/
|
*/
|
||||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.feed")
|
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.feed")
|
||||||
|
@NonNullByDefault
|
||||||
public class FeedHandlerFactory extends BaseThingHandlerFactory {
|
public class FeedHandlerFactory extends BaseThingHandlerFactory {
|
||||||
|
|
||||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(FEED_THING_TYPE_UID);
|
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(FEED_THING_TYPE_UID);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||||
|
@ -42,7 +44,7 @@ public class FeedHandlerFactory extends BaseThingHandlerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ThingHandler createHandler(Thing thing) {
|
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||||
|
|
||||||
if (thingTypeUID.equals(FEED_THING_TYPE_UID)) {
|
if (thingTypeUID.equals(FEED_THING_TYPE_UID)) {
|
||||||
|
|
|
@ -29,11 +29,12 @@ import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
import org.eclipse.jdt.annotation.Nullable;
|
||||||
import org.openhab.core.config.core.Configuration;
|
import org.openhab.core.config.core.Configuration;
|
||||||
import org.openhab.core.library.types.DateTimeType;
|
import org.openhab.core.library.types.DateTimeType;
|
||||||
import org.openhab.core.library.types.DecimalType;
|
import org.openhab.core.library.types.DecimalType;
|
||||||
import org.openhab.core.library.types.StringType;
|
import org.openhab.core.library.types.StringType;
|
||||||
import org.openhab.core.thing.Channel;
|
|
||||||
import org.openhab.core.thing.ChannelUID;
|
import org.openhab.core.thing.ChannelUID;
|
||||||
import org.openhab.core.thing.Thing;
|
import org.openhab.core.thing.Thing;
|
||||||
import org.openhab.core.thing.ThingStatus;
|
import org.openhab.core.thing.ThingStatus;
|
||||||
|
@ -57,73 +58,80 @@ import com.rometools.rome.io.SyndFeedInput;
|
||||||
*
|
*
|
||||||
* @author Svilen Valkanov - Initial contribution
|
* @author Svilen Valkanov - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public class FeedHandler extends BaseThingHandler {
|
public class FeedHandler extends BaseThingHandler {
|
||||||
|
|
||||||
private Logger logger = LoggerFactory.getLogger(FeedHandler.class);
|
private final Logger logger = LoggerFactory.getLogger(FeedHandler.class);
|
||||||
|
|
||||||
private String urlString;
|
private @Nullable URL url;
|
||||||
private BigDecimal refreshTime;
|
private long refreshTime;
|
||||||
private ScheduledFuture<?> refreshTask;
|
private @Nullable ScheduledFuture<?> refreshTask;
|
||||||
private SyndFeed currentFeedState;
|
private @Nullable SyndFeed currentFeedState;
|
||||||
private long lastRefreshTime;
|
private long lastRefreshTime;
|
||||||
|
|
||||||
public FeedHandler(Thing thing) {
|
public FeedHandler(Thing thing) {
|
||||||
super(thing);
|
super(thing);
|
||||||
currentFeedState = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
checkConfiguration();
|
if (checkConfiguration()) {
|
||||||
updateStatus(ThingStatus.UNKNOWN);
|
updateStatus(ThingStatus.UNKNOWN);
|
||||||
startAutomaticRefresh();
|
startAutomaticRefresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method checks if the provided configuration is valid.
|
* This method checks if the provided configuration is valid.
|
||||||
* When invalid parameter is found, default value is assigned.
|
* When invalid parameter is found, default value is assigned.
|
||||||
*/
|
*/
|
||||||
private void checkConfiguration() {
|
private boolean checkConfiguration() {
|
||||||
logger.debug("Start reading Feed Thing configuration.");
|
logger.debug("Start reading Feed Thing configuration.");
|
||||||
Configuration configuration = getConfig();
|
Configuration configuration = getConfig();
|
||||||
|
|
||||||
// It is not necessary to check if the URL is valid, this will be done in fetchFeedData() method
|
// It is not necessary to check if the URL is valid, this will be done in fetchFeedData() method
|
||||||
urlString = (String) configuration.get(URL);
|
String urlString = (String) configuration.get(URL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
refreshTime = (BigDecimal) configuration.get(REFRESH_TIME);
|
url = new URL(urlString);
|
||||||
if (refreshTime.intValue() <= 0) {
|
} catch (MalformedURLException e) {
|
||||||
|
logger.warn("Url '{}' is not valid: ", urlString, e);
|
||||||
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal localRefreshTime = null;
|
||||||
|
try {
|
||||||
|
localRefreshTime = (BigDecimal) configuration.get(REFRESH_TIME);
|
||||||
|
if (localRefreshTime.intValue() <= 0) {
|
||||||
throw new IllegalArgumentException("Refresh time must be positive number!");
|
throw new IllegalArgumentException("Refresh time must be positive number!");
|
||||||
}
|
}
|
||||||
|
refreshTime = localRefreshTime.longValue();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Refresh time [{}] is not valid. Falling back to default value: {}. {}", refreshTime,
|
logger.warn("Refresh time [{}] is not valid. Falling back to default value: {}. {}", localRefreshTime,
|
||||||
DEFAULT_REFRESH_TIME, e.getMessage());
|
DEFAULT_REFRESH_TIME, e.getMessage());
|
||||||
refreshTime = DEFAULT_REFRESH_TIME;
|
refreshTime = DEFAULT_REFRESH_TIME;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startAutomaticRefresh() {
|
private void startAutomaticRefresh() {
|
||||||
refreshTask = scheduler.scheduleWithFixedDelay(this::refreshFeedState, 0, refreshTime.intValue(),
|
refreshTask = scheduler.scheduleWithFixedDelay(this::refreshFeedState, 0, refreshTime, TimeUnit.MINUTES);
|
||||||
TimeUnit.MINUTES);
|
logger.debug("Start automatic refresh at {} minutes!", refreshTime);
|
||||||
logger.debug("Start automatic refresh at {} minutes", refreshTime.intValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshFeedState() {
|
private void refreshFeedState() {
|
||||||
SyndFeed feed = fetchFeedData(urlString);
|
SyndFeed feed = fetchFeedData();
|
||||||
boolean feedUpdated = updateFeedIfChanged(feed);
|
boolean feedUpdated = updateFeedIfChanged(feed);
|
||||||
|
|
||||||
if (feedUpdated) {
|
if (feedUpdated) {
|
||||||
List<Channel> channels = getThing().getChannels();
|
getThing().getChannels().forEach(channel -> publishChannelIfLinked(channel.getUID()));
|
||||||
for (Channel channel : channels) {
|
|
||||||
publishChannelIfLinked(channel.getUID());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishChannelIfLinked(ChannelUID channelUID) {
|
private void publishChannelIfLinked(ChannelUID channelUID) {
|
||||||
String channelID = channelUID.getId();
|
String channelID = channelUID.getId();
|
||||||
|
|
||||||
if (currentFeedState == null) {
|
SyndFeed feedState = currentFeedState;
|
||||||
|
if (feedState == null) {
|
||||||
// This will happen if the binding could not download data from the server
|
// This will happen if the binding could not download data from the server
|
||||||
logger.trace("Cannot update channel with ID {}; no data has been downloaded from the server!", channelID);
|
logger.trace("Cannot update channel with ID {}; no data has been downloaded from the server!", channelID);
|
||||||
return;
|
return;
|
||||||
|
@ -135,7 +143,7 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
State state = null;
|
State state = null;
|
||||||
SyndEntry latestEntry = getLatestEntry(currentFeedState);
|
SyndEntry latestEntry = getLatestEntry(feedState);
|
||||||
|
|
||||||
switch (channelID) {
|
switch (channelID) {
|
||||||
case CHANNEL_LATEST_TITLE:
|
case CHANNEL_LATEST_TITLE:
|
||||||
|
@ -166,19 +174,19 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CHANNEL_AUTHOR:
|
case CHANNEL_AUTHOR:
|
||||||
String author = currentFeedState.getAuthor();
|
String author = feedState.getAuthor();
|
||||||
state = new StringType(getValueSafely(author));
|
state = new StringType(getValueSafely(author));
|
||||||
break;
|
break;
|
||||||
case CHANNEL_DESCRIPTION:
|
case CHANNEL_DESCRIPTION:
|
||||||
String channelDescription = currentFeedState.getDescription();
|
String channelDescription = feedState.getDescription();
|
||||||
state = new StringType(getValueSafely(channelDescription));
|
state = new StringType(getValueSafely(channelDescription));
|
||||||
break;
|
break;
|
||||||
case CHANNEL_TITLE:
|
case CHANNEL_TITLE:
|
||||||
String channelTitle = currentFeedState.getTitle();
|
String channelTitle = feedState.getTitle();
|
||||||
state = new StringType(getValueSafely(channelTitle));
|
state = new StringType(getValueSafely(channelTitle));
|
||||||
break;
|
break;
|
||||||
case CHANNEL_NUMBER_OF_ENTRIES:
|
case CHANNEL_NUMBER_OF_ENTRIES:
|
||||||
int numberOfEntries = currentFeedState.getEntries().size();
|
int numberOfEntries = feedState.getEntries().size();
|
||||||
state = new DecimalType(numberOfEntries);
|
state = new DecimalType(numberOfEntries);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -200,7 +208,7 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
* @return <code>true</code> if new content is available on the server since the last update or <code>false</code>
|
* @return <code>true</code> if new content is available on the server since the last update or <code>false</code>
|
||||||
* otherwise
|
* otherwise
|
||||||
*/
|
*/
|
||||||
private synchronized boolean updateFeedIfChanged(SyndFeed newFeedState) {
|
private synchronized boolean updateFeedIfChanged(@Nullable SyndFeed newFeedState) {
|
||||||
// SyndFeed class has implementation of equals ()
|
// SyndFeed class has implementation of equals ()
|
||||||
if (newFeedState != null && !newFeedState.equals(currentFeedState)) {
|
if (newFeedState != null && !newFeedState.equals(currentFeedState)) {
|
||||||
currentFeedState = newFeedState;
|
currentFeedState = newFeedState;
|
||||||
|
@ -218,16 +226,18 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
* {@link ThingStatusDetail#CONFIGURATION_ERROR} or
|
* {@link ThingStatusDetail#CONFIGURATION_ERROR} or
|
||||||
* {@link ThingStatusDetail#COMMUNICATION_ERROR} and adequate message.
|
* {@link ThingStatusDetail#COMMUNICATION_ERROR} and adequate message.
|
||||||
*
|
*
|
||||||
* @param urlString URL of the Feed
|
|
||||||
* @return {@link SyndFeed} instance with the feed data, if the connection attempt was successful and
|
* @return {@link SyndFeed} instance with the feed data, if the connection attempt was successful and
|
||||||
* <code>null</code> otherwise
|
* <code>null</code> otherwise
|
||||||
*/
|
*/
|
||||||
private SyndFeed fetchFeedData(String urlString) {
|
private @Nullable SyndFeed fetchFeedData() {
|
||||||
SyndFeed feed = null;
|
URL localUrl = url;
|
||||||
try {
|
if (localUrl == null) {
|
||||||
URL url = new URL(urlString);
|
logger.trace("Url '{}' is not valid: ", localUrl);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
URLConnection connection = url.openConnection();
|
try {
|
||||||
|
URLConnection connection = localUrl.openConnection();
|
||||||
connection.setRequestProperty("Accept-Encoding", "gzip");
|
connection.setRequestProperty("Accept-Encoding", "gzip");
|
||||||
|
|
||||||
BufferedReader in = null;
|
BufferedReader in = null;
|
||||||
|
@ -238,50 +248,45 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
SyndFeedInput input = new SyndFeedInput();
|
SyndFeedInput input = new SyndFeedInput();
|
||||||
feed = input.build(in);
|
SyndFeed feed = input.build(in);
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
if (this.thing.getStatus() != ThingStatus.ONLINE) {
|
if (this.thing.getStatus() != ThingStatus.ONLINE) {
|
||||||
updateStatus(ThingStatus.ONLINE);
|
updateStatus(ThingStatus.ONLINE);
|
||||||
}
|
}
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
logger.warn("Url '{}' is not valid: ", urlString, e);
|
return feed;
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
|
||||||
return null;
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.warn("Error accessing feed: {}", urlString, e);
|
logger.warn("Error accessing feed: {}", localUrl, e);
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.warn("Feed URL is null ", e);
|
logger.warn("Feed URL is null: {} ", localUrl, e);
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
} catch (FeedException e) {
|
} catch (FeedException e) {
|
||||||
logger.warn("Feed content is not valid: {} ", urlString, e);
|
logger.warn("Feed content is not valid: {} ", localUrl, e);
|
||||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return feed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the most recent entry or null, if no entries are found.
|
* Returns the most recent entry or null, if no entries are found.
|
||||||
*/
|
*/
|
||||||
private SyndEntry getLatestEntry(SyndFeed feed) {
|
private @Nullable SyndEntry getLatestEntry(SyndFeed feed) {
|
||||||
List<SyndEntry> allEntries = feed.getEntries();
|
List<SyndEntry> allEntries = feed.getEntries();
|
||||||
SyndEntry lastEntry = null;
|
|
||||||
if (!allEntries.isEmpty()) {
|
if (!allEntries.isEmpty()) {
|
||||||
/*
|
/*
|
||||||
* The entries are stored in the SyndFeed object in the following order -
|
* The entries are stored in the SyndFeed object in the following order -
|
||||||
* the newest entry has index 0. The order is determined from the time the entry was posted, not the
|
* the newest entry has index 0. The order is determined from the time the entry was posted, not the
|
||||||
* published time of the entry.
|
* published time of the entry.
|
||||||
*/
|
*/
|
||||||
lastEntry = allEntries.get(0);
|
return allEntries.get(0);
|
||||||
} else {
|
} else {
|
||||||
logger.debug("No entries found");
|
logger.debug("No entries found");
|
||||||
}
|
}
|
||||||
return lastEntry;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -289,7 +294,7 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
if (command instanceof RefreshType) {
|
if (command instanceof RefreshType) {
|
||||||
// safeguard for multiple REFRESH commands for different channels in a row
|
// safeguard for multiple REFRESH commands for different channels in a row
|
||||||
if (isMinimumRefreshTimeExceeded()) {
|
if (isMinimumRefreshTimeExceeded()) {
|
||||||
SyndFeed feed = fetchFeedData(urlString);
|
SyndFeed feed = fetchFeedData();
|
||||||
updateFeedIfChanged(feed);
|
updateFeedIfChanged(feed);
|
||||||
}
|
}
|
||||||
publishChannelIfLinked(channelUID);
|
publishChannelIfLinked(channelUID);
|
||||||
|
@ -317,7 +322,7 @@ public class FeedHandler extends BaseThingHandler {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValueSafely(String value) {
|
public String getValueSafely(@Nullable String value) {
|
||||||
return value == null ? new String() : value;
|
return value == null ? "" : value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
|
|
||||||
<!-- DEFINITIONS of terms used in the binding: Feed is a XML document used for providing users with frequently updated content.
|
<!-- DEFINITIONS of terms used in the binding: Feed is a XML document used for providing users with frequently updated content.
|
||||||
The most popular feed formats are RSS and Atom. Entry is a single element in the Feed, that contains reference (link),
|
The most popular feed formats are RSS and Atom. Entry is a single element in the Feed, that contains reference (link),
|
||||||
short description and other information like images, comments and etc. Entry in this binding is abstraction for RSS item
|
short
|
||||||
element and Atom entry element. -->
|
description and other information like images, comments and etc. Entry in this binding is abstraction for RSS item element
|
||||||
|
and Atom entry element. -->
|
||||||
|
|
||||||
<!-- Feed Thing Type -->
|
<!-- Feed Thing Type -->
|
||||||
<thing-type id="feed">
|
<thing-type id="feed">
|
||||||
|
@ -28,18 +29,16 @@
|
||||||
<config-description>
|
<config-description>
|
||||||
<parameter name="URL" type="text" required="true">
|
<parameter name="URL" type="text" required="true">
|
||||||
<label>Feed URL</label>
|
<label>Feed URL</label>
|
||||||
<description>The URL of the feed</description>
|
<description>The URL of the feed.</description>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
|
||||||
<!--After the refresh time interval expires, the bindings checks for updates in the Feed, and if the information is not
|
<!-- After the refresh time interval expires, the bindings checks for updates in the Feed, and if the information is not
|
||||||
up to date, updates the feed content stored in the channel -->
|
up to date, updates the feed content stored in the channel -->
|
||||||
|
|
||||||
<parameter name="refresh" type="integer">
|
<parameter name="refresh" type="integer">
|
||||||
<label>Refresh Time Interval</label>
|
<label>Refresh Time Interval</label>
|
||||||
<description>Refresh time interval in minutes.</description>
|
<description>Refresh time interval in minutes.</description>
|
||||||
<default>20</default>
|
<default>20</default>
|
||||||
</parameter>
|
</parameter>
|
||||||
|
|
||||||
</config-description>
|
</config-description>
|
||||||
</thing-type>
|
</thing-type>
|
||||||
|
|
||||||
|
@ -67,35 +66,35 @@
|
||||||
<channel-type id="author" advanced="true">
|
<channel-type id="author" advanced="true">
|
||||||
<item-type>String</item-type>
|
<item-type>String</item-type>
|
||||||
<label>Author</label>
|
<label>Author</label>
|
||||||
<description>The name of the feed author, if author is present</description>
|
<description>The name of the feed author, if author is present.</description>
|
||||||
<state readOnly="true" pattern="%s"/>
|
<state readOnly="true" pattern="%s"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="title" advanced="true">
|
<channel-type id="title" advanced="true">
|
||||||
<item-type>String</item-type>
|
<item-type>String</item-type>
|
||||||
<label>Title</label>
|
<label>Title</label>
|
||||||
<description>The title of the feed</description>
|
<description>The title of the feed.</description>
|
||||||
<state readOnly="true" pattern="%s"/>
|
<state readOnly="true" pattern="%s"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="description" advanced="true">
|
<channel-type id="description" advanced="true">
|
||||||
<item-type>String</item-type>
|
<item-type>String</item-type>
|
||||||
<label>Description</label>
|
<label>Description</label>
|
||||||
<description>Description of the feed</description>
|
<description>Description of the feed.</description>
|
||||||
<state readOnly="true" pattern="%s"/>
|
<state readOnly="true" pattern="%s"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="last-update" advanced="true">
|
<channel-type id="last-update" advanced="true">
|
||||||
<item-type>DateTime</item-type>
|
<item-type>DateTime</item-type>
|
||||||
<label>Last Update</label>
|
<label>Last Update</label>
|
||||||
<description>The last update date of the feed</description>
|
<description>The last update date of the feed.</description>
|
||||||
<state readOnly="true" pattern="%tc %n"/>
|
<state readOnly="true" pattern="%tc %n"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
<channel-type id="number-of-entries" advanced="true">
|
<channel-type id="number-of-entries" advanced="true">
|
||||||
<item-type>Number</item-type>
|
<item-type>Number</item-type>
|
||||||
<label>Number of Entries</label>
|
<label>Number of Entries</label>
|
||||||
<description>Number of entries in the feed</description>
|
<description>Number of entries in the feed.</description>
|
||||||
<state readOnly="true" pattern="%d"/>
|
<state readOnly="true" pattern="%d"/>
|
||||||
</channel-type>
|
</channel-type>
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ public class FeedHandlerTest extends JavaOSGiTest {
|
||||||
/**
|
/**
|
||||||
* It is updated from mocked {@link StateChangeListener#stateUpdated() }
|
* It is updated from mocked {@link StateChangeListener#stateUpdated() }
|
||||||
*/
|
*/
|
||||||
private StringType currentItemState = null;
|
private StringType currentItemState;
|
||||||
|
|
||||||
// Required services for the test
|
// Required services for the test
|
||||||
private ManagedThingProvider managedThingProvider;
|
private ManagedThingProvider managedThingProvider;
|
||||||
|
|
|
@ -12,11 +12,13 @@
|
||||||
*/
|
*/
|
||||||
package org.openhab.binding.feed.test;
|
package org.openhab.binding.feed.test;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface is used to mark tests that take too much time
|
* This interface is used to mark tests that take too much time
|
||||||
*
|
*
|
||||||
* @author Svilen Valkanov
|
* @author Svilen Valkanov - Initial contribution
|
||||||
*/
|
*/
|
||||||
|
@NonNullByDefault
|
||||||
public interface SlowTests {
|
public interface SlowTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue