diff --git a/bundles/org.openhab.binding.xmltv/README.md b/bundles/org.openhab.binding.xmltv/README.md index b7e6d308a3ade..8c04268aed8d0 100644 --- a/bundles/org.openhab.binding.xmltv/README.md +++ b/bundles/org.openhab.binding.xmltv/README.md @@ -8,7 +8,7 @@ The building of the XMLTV file itself is taken in charge by so called "grabbers" 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. diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java index c9f60e8cfddea..ffe02e01be2be 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVBindingConstants.java @@ -12,10 +12,7 @@ */ 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; @@ -36,7 +33,6 @@ public class XmlTVBindingConstants { 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"; @@ -56,6 +52,6 @@ public class XmlTVBindingConstants { public static final String CHANNEL_PROGRAMME_TIMELEFT = "timeLeft"; // Supported Thing types - public static final Set SUPPORTED_THING_TYPES_UIDS = Collections - .unmodifiableSet(Stream.of(XMLTV_FILE_BRIDGE_TYPE, XMLTV_CHANNEL_THING_TYPE).collect(Collectors.toSet())); + public static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(XMLTV_FILE_BRIDGE_TYPE, + XMLTV_CHANNEL_THING_TYPE); } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java index d88bef01c9eae..e39f90879753c 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/XmlTVHandlerFactory.java @@ -14,19 +14,18 @@ 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; @@ -34,10 +33,9 @@ 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 @@ -48,8 +46,16 @@ @NonNullByDefault @Component(configurationPid = "binding.xmltv", service = ThingHandlerFactory.class) public class XmlTVHandlerFactory extends BaseThingHandlerFactory { - private final Logger logger = LoggerFactory.getLogger(XmlTVHandlerFactory.class); - private final Map> 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) { @@ -79,37 +85,11 @@ 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(); - } } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java index c22db5f0d334c..b58375dd8824f 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlChannelConfiguration.java @@ -12,16 +12,19 @@ */ 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; } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java index 9c5a2da0c9c0e..249c0c230beef 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/configuration/XmlTVConfiguration.java @@ -12,14 +12,17 @@ */ 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"; } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java index 194b85d1c001c..3a45e50665c52 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/discovery/XmlTVDiscoveryService.java @@ -12,18 +12,21 @@ */ 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; @@ -33,28 +36,33 @@ * * @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_]", "_"); @@ -67,7 +75,19 @@ protected void startScan() { thingDiscovered(discoveryResult); }); - } + }); } } + + @Override + public void setThingHandler(ThingHandler handler) { + if (handler instanceof XmlTVHandler) { + this.handler = (XmlTVHandler) handler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java index 53dfc6640b0c4..260ef19a8b4d5 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/ChannelHandler.java @@ -30,7 +30,6 @@ 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; @@ -61,14 +60,16 @@ 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 programmes = new ArrayList<>(); + private final ZoneId zoneId; - public ChannelHandler(Thing thing) { + public ChannelHandler(Thing thing, ZoneId zoneId) { super(thing); + this.zoneId = zoneId; } @Override @@ -77,14 +78,14 @@ public void initialize() { 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); } @@ -100,8 +101,7 @@ private void refreshProgramList() { 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) { @@ -119,9 +119,7 @@ private void refreshProgramList() { .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); } @@ -132,8 +130,9 @@ private void refreshProgramList() { @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; } } @@ -153,6 +152,7 @@ public void handleCommand(ChannelUID channelUID, Command command) { * */ 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; @@ -161,29 +161,22 @@ private void updateChannel(ChannelUID channelUID) { 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 titles = programme.getTitles(); @@ -212,18 +205,15 @@ private void updateChannel(ChannelUID channelUID) { 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; } @@ -233,17 +223,24 @@ private void updateChannel(ChannelUID channelUID) { } } + 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 icons) { + private State downloadIcon(List 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; } } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java index 85e7969462259..542994e244fe7 100644 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java +++ b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/handler/XmlTVHandler.java @@ -16,12 +16,14 @@ 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; @@ -31,6 +33,7 @@ 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; @@ -38,6 +41,7 @@ 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; @@ -51,16 +55,16 @@ @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 @@ -74,9 +78,7 @@ public void initialize() { 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())); @@ -88,7 +90,7 @@ public void initialize() { 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) { @@ -122,8 +124,12 @@ public void handleCommand(ChannelUID channelUID, Command command) { // nothing to do } - @Nullable - public Tv getXmlFile() { - return currentXmlFile; + public Optional getXmlFile() { + return Optional.ofNullable(currentXmlFile); + } + + @Override + public Collection> getServices() { + return Set.of(XmlTVDiscoveryService.class); } } diff --git a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/ObjectFactory.java b/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/ObjectFactory.java deleted file mode 100644 index 3fb6f97f5dff9..0000000000000 --- a/bundles/org.openhab.binding.xmltv/src/main/java/org/openhab/binding/xmltv/internal/jaxb/ObjectFactory.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * 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(); - } -} diff --git a/bundles/org.openhab.binding.xmltv/src/main/resources/OH-INF/i18n/xmltv.properties b/bundles/org.openhab.binding.xmltv/src/main/resources/OH-INF/i18n/xmltv.properties new file mode 100644 index 0000000000000..9b5f21afd24b8 --- /dev/null +++ b/bundles/org.openhab.binding.xmltv/src/main/resources/OH-INF/i18n/xmltv.properties @@ -0,0 +1,64 @@ +# 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