* Preparing for Crowdin and code refining.
Signed-off-by: Gaël L'hopital <gael@lhopital.org>
* Satisfying SAT
Signed-off-by: Gaël L'hopital <gael@lhopital.org>
* Preventing two potential NPE
Signed-off-by: Gaël L'hopital <gael@lhopital.org>
* Code review comments taken in account
Signed-off-by: clinique <gael@lhopital.org>
* Reverting description removal
Signed-off-by: clinique <gael@lhopital.org>
* Forgot spotless apply
Signed-off-by: clinique <gael@lhopital.org>
Some websites provides updated XMLTV files than can be directly downloaded.
-Here is a sample for France : https://www.xmltv.fr/
+Here is a sample for France and Switzerland : https://xmltv.ch/
This binding takes an XMLTV file as input and creates a thing for each channel contained in it.
XmlTV channels are called Media Channels in this binding in order to avoid messing with openHAB Channels.
*/
package org.openhab.binding.xmltv.internal;
-import java.util.Collections;
import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
public static final ThingTypeUID XMLTV_CHANNEL_THING_TYPE = new ThingTypeUID(BINDING_ID, "channel");
// Channel groups
- public static final String GROUP_CURRENT_PROGRAMME = "currentprog";
public static final String GROUP_NEXT_PROGRAMME = "nextprog";
public static final String GROUP_CHANNEL_PROPERTIES = "channelprops";
public static final String CHANNEL_PROGRAMME_TIMELEFT = "timeLeft";
// Supported Thing types
- public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
- .unmodifiableSet(Stream.of(XMLTV_FILE_BRIDGE_TYPE, XMLTV_CHANNEL_THING_TYPE).collect(Collectors.toSet()));
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(XMLTV_FILE_BRIDGE_TYPE,
+ XMLTV_CHANNEL_THING_TYPE);
}
import static org.openhab.binding.xmltv.internal.XmlTVBindingConstants.*;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-
+import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.XMLInputFactory;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.xmltv.internal.discovery.XmlTVDiscoveryService;
import org.openhab.binding.xmltv.internal.handler.ChannelHandler;
import org.openhab.binding.xmltv.internal.handler.XmlTVHandler;
+import org.openhab.binding.xmltv.internal.jaxb.Tv;
import org.openhab.core.config.core.Configuration;
-import org.openhab.core.config.discovery.DiscoveryService;
+import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
-import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.osgi.service.component.annotations.Reference;
/**
* The {@link XmlTVHandlerFactory} is responsible for creating things and thing
@NonNullByDefault
@Component(configurationPid = "binding.xmltv", service = ThingHandlerFactory.class)
public class XmlTVHandlerFactory extends BaseThingHandlerFactory {
- private final Logger logger = LoggerFactory.getLogger(XmlTVHandlerFactory.class);
- private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceRegs = new HashMap<>();
+ private final XMLInputFactory xif = XMLInputFactory.newFactory();
+ private final TimeZoneProvider timeZoneProvider;
+ private final Unmarshaller unmarshaller;
+
+ @Activate
+ public XmlTVHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) throws JAXBException {
+ this.timeZoneProvider = timeZoneProvider;
+ this.unmarshaller = JAXBContext.newInstance(Tv.class).createUnmarshaller();
+ xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ }
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (XMLTV_FILE_BRIDGE_TYPE.equals(thingTypeUID)) {
- try {
- XmlTVHandler bridgeHandler = new XmlTVHandler((Bridge) thing);
- registerDeviceDiscoveryService(bridgeHandler);
- return bridgeHandler;
- } catch (JAXBException e) {
- logger.error("Unable to create XmlTVHandler : {}", e.getMessage());
- }
+ return new XmlTVHandler((Bridge) thing, xif, unmarshaller);
} else if (XMLTV_CHANNEL_THING_TYPE.equals(thingTypeUID)) {
- return new ChannelHandler(thing);
+ return new ChannelHandler(thing, timeZoneProvider.getTimeZone());
}
return null;
}
-
- @Override
- protected void removeHandler(ThingHandler thingHandler) {
- if (thingHandler instanceof XmlTVHandler) {
- Thing thing = thingHandler.getThing();
- unregisterDeviceDiscoveryService(thing);
- }
- super.removeHandler(thingHandler);
- }
-
- private synchronized void registerDeviceDiscoveryService(XmlTVHandler bridgeHandler) {
- XmlTVDiscoveryService discoveryService = new XmlTVDiscoveryService(bridgeHandler);
- discoveryServiceRegs.put(bridgeHandler.getThing().getUID(),
- bundleContext.registerService(DiscoveryService.class.getName(), discoveryService, new Hashtable<>()));
- }
-
- private synchronized void unregisterDeviceDiscoveryService(Thing thing) {
- ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thing.getUID());
- serviceReg.unregister();
- }
}
*/
package org.openhab.binding.xmltv.internal.configuration;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link XmlChannelConfiguration} class contains fields mapping
* Channel thing configuration parameters.
*
* @author Gaël L'hopital - Initial contribution
*/
+@NonNullByDefault
public class XmlChannelConfiguration {
public static final String CHANNEL_ID = "channelId";
- public String channelId;
- public Integer offset;
- public Integer refresh;
+ public String channelId = "";
+ public int offset = 0;
+ public int refresh = 60;
}
*/
package org.openhab.binding.xmltv.internal.configuration;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* The {@link XmlTVConfiguration} class contains fields mapping TV bridge
* configuration parameters.
*
* @author Gaël L'hopital - Initial contribution
*/
+@NonNullByDefault
public class XmlTVConfiguration {
- public String filePath;
- public Integer refresh;
- public String encoding;
+ public String filePath = "";
+ public int refresh = 24;
+ public String encoding = "UTF8";
}
*/
package org.openhab.binding.xmltv.internal.discovery;
-import static org.openhab.binding.xmltv.internal.XmlTVBindingConstants.XMLTV_CHANNEL_THING_TYPE;
+import static org.openhab.binding.xmltv.internal.XmlTVBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.xmltv.internal.XmlTVBindingConstants;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.xmltv.internal.configuration.XmlChannelConfiguration;
import org.openhab.binding.xmltv.internal.handler.XmlTVHandler;
-import org.openhab.binding.xmltv.internal.jaxb.Tv;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingUID;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
*
* @author Gaël L'hopital - Initial contribution
*/
+@Component(service = ThingHandlerService.class)
@NonNullByDefault
-public class XmlTVDiscoveryService extends AbstractDiscoveryService {
- private final Logger logger = LoggerFactory.getLogger(XmlTVDiscoveryService.class);
-
- private static final int SEARCH_TIME = 10;
+public class XmlTVDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService {
+ private static final int SEARCH_TIME = 5;
- private XmlTVHandler bridgeHandler;
+ private final Logger logger = LoggerFactory.getLogger(XmlTVDiscoveryService.class);
+ private @Nullable XmlTVHandler handler;
/**
* Creates a XmlTVDiscoveryService with background discovery disabled.
*/
- public XmlTVDiscoveryService(XmlTVHandler bridgeHandler) {
- super(XmlTVBindingConstants.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME);
- this.bridgeHandler = bridgeHandler;
+ @Activate
+ public XmlTVDiscoveryService() {
+ super(SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME);
+ }
+
+ @Override
+ public void deactivate() {
+ super.deactivate();
}
@Override
protected void startScan() {
logger.debug("Starting XmlTV discovery scan");
- if (bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) {
- Tv tv = bridgeHandler.getXmlFile();
- if (tv != null) {
+ XmlTVHandler bridgeHandler = handler;
+ if (bridgeHandler != null && bridgeHandler.getThing().getStatus() == ThingStatus.ONLINE) {
+ bridgeHandler.getXmlFile().ifPresent(tv -> {
tv.getMediaChannels().stream().forEach(channel -> {
String channelId = channel.getId();
String uid = channelId.replaceAll("[^A-Za-z0-9_]", "_");
thingDiscovered(discoveryResult);
});
- }
+ });
}
}
+
+ @Override
+ public void setThingHandler(ThingHandler handler) {
+ if (handler instanceof XmlTVHandler) {
+ this.handler = (XmlTVHandler) handler;
+ }
+ }
+
+ @Override
+ public @Nullable ThingHandler getThingHandler() {
+ return handler;
+ }
}
import org.openhab.binding.xmltv.internal.jaxb.Icon;
import org.openhab.binding.xmltv.internal.jaxb.MediaChannel;
import org.openhab.binding.xmltv.internal.jaxb.Programme;
-import org.openhab.binding.xmltv.internal.jaxb.Tv;
import org.openhab.binding.xmltv.internal.jaxb.WithLangType;
import org.openhab.core.io.net.http.HttpUtil;
import org.openhab.core.library.types.DateTimeType;
public class ChannelHandler extends BaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(ChannelHandler.class);
- private @NonNullByDefault({}) ScheduledFuture<?> globalJob;
+ private @Nullable ScheduledFuture<?> globalJob;
private @Nullable MediaChannel mediaChannel;
- private @Nullable RawType mediaIcon = new RawType(new byte[0], RawType.DEFAULT_MIME_TYPE);
+ private State mediaIcon = UnDefType.UNDEF;
public final List<Programme> programmes = new ArrayList<>();
+ private final ZoneId zoneId;
- public ChannelHandler(Thing thing) {
+ public ChannelHandler(Thing thing, ZoneId zoneId) {
super(thing);
+ this.zoneId = zoneId;
}
@Override
logger.debug("Initializing Broadcast Channel handler for uid '{}'", getThing().getUID());
- if (globalJob == null || globalJob.isCancelled()) {
+ ScheduledFuture<?> job = globalJob;
+ if (job == null || job.isCancelled()) {
globalJob = scheduler.scheduleWithFixedDelay(() -> {
if (programmes.size() < 2) {
refreshProgramList();
}
if (programmes.isEmpty()) {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE,
- "No programmes to come in the current XML file for this channel");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/no-more-programs");
} else if (Instant.now().isAfter(programmes.get(0).getProgrammeStop())) {
programmes.remove(0);
}
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
XmlTVHandler handler = (XmlTVHandler) bridge.getHandler();
if (handler != null) {
- Tv tv = handler.getXmlFile();
- if (tv != null) {
+ handler.getXmlFile().ifPresentOrElse(tv -> {
String channelId = (String) getConfig().get(XmlChannelConfiguration.CHANNEL_ID);
if (mediaChannel == null) {
.forEach(p -> programmes.add(p));
updateStatus(ThingStatus.ONLINE);
- } else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "No file available");
- }
+ }, () -> updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.NONE, "@text/no-file-available"));
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
@Override
public void dispose() {
- if (globalJob != null && !globalJob.isCancelled()) {
- globalJob.cancel(true);
+ ScheduledFuture<?> job = globalJob;
+ if (job != null && !job.isCancelled()) {
+ job.cancel(true);
globalJob = null;
}
}
*
*/
private void updateChannel(ChannelUID channelUID) {
+ // TODO : usage extraction of groupname
String[] uidElements = channelUID.getId().split("#");
if (uidElements.length == 2) {
int target = GROUP_NEXT_PROGRAMME.equals(uidElements[0]) ? 1 : 0;
switch (uidElements[1]) {
case CHANNEL_ICON:
- State icon = null;
- if (GROUP_CHANNEL_PROPERTIES.equals(uidElements[0])) {
- icon = mediaIcon;
- } else {
- icon = downloadIcon(programme.getIcons());
- }
- updateState(channelUID, icon != null ? icon : UnDefType.UNDEF);
+ State icon = GROUP_CHANNEL_PROPERTIES.equals(uidElements[0]) ? mediaIcon
+ : downloadIcon(programme.getIcons());
+ updateState(channelUID, icon);
break;
case CHANNEL_CHANNEL_URL:
+ MediaChannel channel = mediaChannel;
updateState(channelUID,
- mediaChannel != null ? !mediaChannel.getIcons().isEmpty()
- ? new StringType(mediaChannel.getIcons().get(0).getSrc())
+ channel != null ? !channel.getIcons().isEmpty()
+ ? new StringType(channel.getIcons().get(0).getSrc())
: UnDefType.UNDEF : UnDefType.UNDEF);
break;
case CHANNEL_PROGRAMME_START:
- Instant is = programme.getProgrammeStart();
- ZonedDateTime zds = ZonedDateTime.ofInstant(is, ZoneId.systemDefault());
- updateState(channelUID, new DateTimeType(zds));
+ updateDateTimeChannel(channelUID, programme.getProgrammeStart());
break;
case CHANNEL_PROGRAMME_END:
- ZonedDateTime zde = ZonedDateTime.ofInstant(programme.getProgrammeStop(),
- ZoneId.systemDefault());
- updateState(channelUID, new DateTimeType(zde));
+ updateDateTimeChannel(channelUID, programme.getProgrammeStop());
break;
case CHANNEL_PROGRAMME_TITLE:
List<WithLangType> titles = programme.getTitles();
updateState(channelUID, getDurationInSeconds(Instant.now(), programme.getProgrammeStart()));
break;
case CHANNEL_PROGRAMME_PROGRESS:
- Duration totalLength = Duration.between(programme.getProgrammeStart(),
- programme.getProgrammeStop());
- Duration elapsed1 = Duration.between(programme.getProgrammeStart(), Instant.now());
-
- long secondsElapsed1 = elapsed1.toMillis() / 1000;
- long secondsLength = totalLength.toMillis() / 1000;
+ long totalLength = Duration.between(programme.getProgrammeStart(), programme.getProgrammeStop())
+ .toSeconds();
+ long elapsed1 = Duration.between(programme.getProgrammeStart(), Instant.now()).toSeconds();
- double progress = 100.0 * secondsElapsed1 / secondsLength;
+ double progress = 100.0 * elapsed1 / totalLength;
if (progress > 100 || progress < 0) {
logger.debug("Outstanding process");
}
- updateState(channelUID, new QuantityType<>(progress, Units.PERCENT));
+ updateState(channelUID, new QuantityType<>((int) progress, Units.PERCENT));
break;
}
}
}
+ private void updateDateTimeChannel(ChannelUID channelUID, Instant instant) {
+ ZonedDateTime zds = ZonedDateTime.ofInstant(instant, zoneId);
+ updateState(channelUID, new DateTimeType(zds));
+ }
+
private QuantityType<?> getDurationInSeconds(Instant from, Instant to) {
- Duration elapsed = Duration.between(from, to);
- long secondsElapsed = TimeUnit.MILLISECONDS.toSeconds(elapsed.toMillis());
- return new QuantityType<>(secondsElapsed, Units.SECOND);
+ long elapsed = Duration.between(from, to).toSeconds();
+ return new QuantityType<>(elapsed, Units.SECOND);
}
- private @Nullable RawType downloadIcon(List<Icon> icons) {
+ private State downloadIcon(List<Icon> icons) {
if (!icons.isEmpty()) {
String url = icons.get(0).getSrc();
- return HttpUtil.downloadImage(url);
+ RawType result = HttpUtil.downloadImage(url);
+ if (result != null) {
+ return result;
+ }
}
- return null;
+ return UnDefType.NULL;
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.time.Instant;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
-import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.xmltv.internal.configuration.XmlTVConfiguration;
+import org.openhab.binding.xmltv.internal.discovery.XmlTVDiscoveryService;
import org.openhab.binding.xmltv.internal.jaxb.Programme;
import org.openhab.binding.xmltv.internal.jaxb.Tv;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@NonNullByDefault
public class XmlTVHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(XmlTVHandler.class);
- private final XMLInputFactory xif = XMLInputFactory.newFactory();
- private final JAXBContext jc;
+ private final XMLInputFactory xif;
+ private final Unmarshaller unmarshaller;
private @Nullable Tv currentXmlFile;
private @NonNullByDefault({}) ScheduledFuture<?> reloadJob;
- public XmlTVHandler(Bridge thing) throws JAXBException {
+ public XmlTVHandler(Bridge thing, XMLInputFactory xif, Unmarshaller unmarshaller) {
super(thing);
- xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
- jc = JAXBContext.newInstance(Tv.class);
+ this.xif = xif;
+ this.unmarshaller = unmarshaller;
}
@Override
try {
// This can take some seconds depending upon weight of the XmlTV source file
xsr = xif.createXMLStreamReader(new FileInputStream(new File(config.filePath)), config.encoding);
-
try {
- Unmarshaller unmarshaller = jc.createUnmarshaller();
Tv xmlFile = (Tv) unmarshaller.unmarshal(xsr);
// Remove all finished programmes
xmlFile.getProgrammes().removeIf(programme -> Instant.now().isAfter(programme.getProgrammeStop()));
currentXmlFile = xmlFile;
updateStatus(ThingStatus.ONLINE);
} else {
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DISABLED, "XMLTV file seems outdated");
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.DISABLED, "@text/file-outdated");
}
xsr.close();
} catch (JAXBException e) {
// nothing to do
}
- @Nullable
- public Tv getXmlFile() {
- return currentXmlFile;
+ public Optional<Tv> getXmlFile() {
+ return Optional.ofNullable(currentXmlFile);
+ }
+
+ @Override
+ public Collection<Class<? extends ThingHandlerService>> getServices() {
+ return Set.of(XmlTVDiscoveryService.class);
}
}
+++ /dev/null
-/**
- * Copyright (c) 2010-2021 Contributors to the openHAB project
- *
- * See the NOTICE file(s) distributed with this work for additional
- * information.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0
- *
- * SPDX-License-Identifier: EPL-2.0
- */
-package org.openhab.binding.xmltv.internal.jaxb;
-
-import javax.xml.bind.annotation.XmlRegistry;
-
-import org.eclipse.jdt.annotation.NonNullByDefault;
-
-/**
- * This object contains factory methods for each Java content
- * interface and Java element interface generated in the
- * org.openhab.binding.xmltv.internal.jaxb package.
- *
- * @author Gaël L'hopital - Initial contribution
- */
-@XmlRegistry
-@NonNullByDefault
-public class ObjectFactory {
-
- /**
- * Create an instance of {@link Tv }
- *
- */
- public Tv createTv() {
- return new Tv();
- }
-
- /**
- * Create an instance of {@link Programme }
- *
- */
- public Programme createProgramme() {
- return new Programme();
- }
-
- /**
- * Create an instance of {@link MediaChannel }
- *
- */
- public MediaChannel createChannel() {
- return new MediaChannel();
- }
-
- /**
- * Create an instance of {@link Icon }
- *
- */
- public Icon createIcon() {
- return new Icon();
- }
-
- /**
- * Create an instance of {@link WithLangType }
- *
- */
- public WithLangType createWithLangType() {
- return new WithLangType();
- }
-}
--- /dev/null
+# binding
+
+binding.xmltv.name = XmlTV Binding
+binding.xmltv.description = This is the binding for reading and parsing XmlTV files
+
+# bridge types
+
+thing-type.xmltv.xmltvfile.label = XmlTVFile
+thing-type.xmltv.xmltvfile.description = This is the interface to a XmlTV file
+
+thing-type.config.xmltv.xmltvfile.filePath.label = XmlTV File Path
+thing-type.config.xmltv.xmltvfile.filePath.description = Path to an XmlTV file.
+thing-type.config.xmltv.xmltvfile.refresh.label = Refresh Interval
+thing-type.config.xmltv.xmltvfile.refresh.description = Specifies the XMLTV file reload interval in hours.
+thing-type.config.xmltv.xmltvfile.encoding.label = File encoding
+thing-type.config.xmltv.xmltvfile.encoding.description = Specifies the XMLTV file encoding.
+
+# thing types
+
+thing-type.xmltv.channel.label = Channel
+thing-type.xmltv.channel.description = This represent a channel on a given TV file
+
+thing-type.config.xmltv.channel.channelId.label = Channel Id
+thing-type.config.xmltv.channel.channelId.description = Id of the channel as presented in the XmlTV file.
+thing-type.config.xmltv.channel.offset.label = Offset
+thing-type.config.xmltv.channel.offset.description = Moves an event or datetime value forward or backward (in minutes)
+thing-type.config.xmltv.channel.refresh.label = Refresh Interval
+thing-type.config.xmltv.channel.refresh.description = Specifies the refresh interval in seconds.
+
+# channel group types
+
+channel-group-type.xmltv.channelprops.label = Channel Properties
+channel-group-type.xmltv.currentprog.label = Current Program
+channel-group-type.xmltv.nextprog.label = Next Program
+
+# channel type
+
+channel-type.xmltv.iconUrl.label = Channel Icon URL
+channel-type.xmltv.iconUrl.description = Icon URL of the TV channel.
+channel-type.xmltv.progIconUrl.label = Program URL
+channel-type.xmltv.progIconUrl.description = URL to an image of the program.
+channel-type.xmltv.progTitle.label = Title
+channel-type.xmltv.progTitle.description = Program Title.
+channel-type.xmltv.progCategory.label = Category
+channel-type.xmltv.progCategory.description = Program Category.
+channel-type.xmltv.progStart.label = Start Time
+channel-type.xmltv.progStart.description = Program Start Time
+channel-type.xmltv.progEnd.label = End Time
+channel-type.xmltv.progEnd.description = Program End Time
+channel-type.xmltv.elapsedTime.label = Current Time
+channel-type.xmltv.elapsedTime.description = Current time of currently playing program.
+channel-type.xmltv.remainingTime.label = Remaining Time
+channel-type.xmltv.remainingTime.description = Time remaining until end of the program.
+channel-type.xmltv.timeLeft.label = Time Left
+channel-type.xmltv.timeLeft.description = Time left before program start
+channel-type.xmltv.progress.label = Progress
+channel-type.xmltv.progress.description = Relative progression of the current program.
+channel-type.xmltv.icon.label = Icon
+channel-type.xmltv.icon.description = Icon of the channel / program.
+
+# messages
+no-more-programs = No programmes to come in the current XML file for this channel
+no-file-available = No file available
+file-outdated = XMLTV file seems outdated