Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[XMLTV] Preparing for Crowdin and code refining #11594

Merged
merged 6 commits into from
Nov 20, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";

Expand All @@ -56,6 +52,6 @@ public class XmlTVBindingConstants {
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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@
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;
Expand All @@ -35,9 +40,9 @@
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
Expand All @@ -48,8 +53,17 @@
@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) {
Expand Down Expand Up @@ -79,15 +93,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());
}
XmlTVHandler bridgeHandler = new XmlTVHandler((Bridge) thing, xif, unmarshaller);
registerDeviceDiscoveryService(bridgeHandler);
return bridgeHandler;
} else if (XMLTV_CHANNEL_THING_TYPE.equals(thingTypeUID)) {
return new ChannelHandler(thing);
return new ChannelHandler(thing, timeZoneProvider.getTimeZone());
}

return null;
Expand All @@ -110,6 +120,8 @@ private synchronized void registerDeviceDiscoveryService(XmlTVHandler bridgeHand

private synchronized void unregisterDeviceDiscoveryService(Thing thing) {
lolodomo marked this conversation as resolved.
Show resolved Hide resolved
ServiceRegistration<?> serviceReg = discoveryServiceRegs.remove(thing.getUID());
serviceReg.unregister();
if (serviceReg != null) {
serviceReg.unregister();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
*/
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.openhab.binding.xmltv.internal.configuration.XmlChannelConfiguration;
import org.openhab.binding.xmltv.internal.handler.XmlTVHandler;
import org.openhab.binding.xmltv.internal.jaxb.Tv;
Expand All @@ -35,17 +34,16 @@
*/
@NonNullByDefault
public class XmlTVDiscoveryService extends AbstractDiscoveryService {
private final Logger logger = LoggerFactory.getLogger(XmlTVDiscoveryService.class);

private static final int SEARCH_TIME = 10;

private final Logger logger = LoggerFactory.getLogger(XmlTVDiscoveryService.class);
private XmlTVHandler bridgeHandler;

/**
* Creates a XmlTVDiscoveryService with background discovery disabled.
*/
public XmlTVDiscoveryService(XmlTVHandler bridgeHandler) {
super(XmlTVBindingConstants.SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME);
super(SUPPORTED_THING_TYPES_UIDS, SEARCH_TIME);
this.bridgeHandler = bridgeHandler;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,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<Programme> programmes = new ArrayList<>();
private final ZoneId zoneId;

public ChannelHandler(Thing thing) {
public ChannelHandler(Thing thing, ZoneId zoneId) {
super(thing);
this.zoneId = zoneId;
}

@Override
Expand All @@ -77,14 +79,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);
}
Expand Down Expand Up @@ -120,7 +122,7 @@ private void refreshProgramList() {

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);
Expand All @@ -132,8 +134,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;
}
}
Expand All @@ -153,6 +156,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;
Expand All @@ -161,29 +165,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<WithLangType> titles = programme.getTitles();
Expand Down Expand Up @@ -212,14 +209,11 @@ 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");
}
Expand All @@ -233,17 +227,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<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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
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;
Expand Down Expand Up @@ -51,16 +50,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
Expand All @@ -74,9 +73,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()));
Expand All @@ -88,7 +85,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) {
Expand Down
Loading