[vdr] Initial contribution (#9947)
Signed-off-by: Matthias Klocke <matthias.klocke@gmx.net>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.vdr-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
|
||||
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
|
||||
|
||||
<feature name="openhab-binding-vdr" description="VDR Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.vdr/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link VDRBindingConstants} class defines common constants, which are
|
||||
* used across the whole binding.
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VDRBindingConstants {
|
||||
|
||||
private static final String BINDING_ID = "vdr";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_VDR = new ThingTypeUID(BINDING_ID, "vdr");
|
||||
|
||||
public static final String CHANNEL_UID_POWER = "power";
|
||||
public static final String CHANNEL_UID_MESSAGE = "message";
|
||||
public static final String CHANNEL_UID_CHANNEL = "channel";
|
||||
public static final String CHANNEL_UID_CHANNEL_NAME = "channelName";
|
||||
public static final String CHANNEL_UID_VOLUME = "volume";
|
||||
public static final String CHANNEL_UID_KEYCODE = "keyCode";
|
||||
public static final String CHANNEL_UID_RECORDING = "recording";
|
||||
public static final String CHANNEL_UID_DISKUSAGE = "diskUsage";
|
||||
public static final String CHANNEL_UID_CURRENT_EVENT_TITLE = "currentEventTitle";
|
||||
public static final String CHANNEL_UID_CURRENT_EVENT_SUBTITLE = "currentEventSubTitle";
|
||||
public static final String CHANNEL_UID_CURRENT_EVENT_DURATION = "currentEventDuration";
|
||||
public static final String CHANNEL_UID_CURRENT_EVENT_BEGIN = "currentEventBegin";
|
||||
public static final String CHANNEL_UID_CURRENT_EVENT_END = "currentEventEnd";
|
||||
public static final String CHANNEL_UID_NEXT_EVENT_TITLE = "nextEventTitle";
|
||||
public static final String CHANNEL_UID_NEXT_EVENT_SUBTITLE = "nextEventSubTitle";
|
||||
public static final String CHANNEL_UID_NEXT_EVENT_DURATION = "nextEventDuration";
|
||||
public static final String CHANNEL_UID_NEXT_EVENT_BEGIN = "nextEventBegin";
|
||||
public static final String CHANNEL_UID_NEXT_EVENT_END = "nextEventEnd";
|
||||
|
||||
public static final String KEY_CODE_POWER = "Power";
|
||||
|
||||
public static final String PROPERTY_VERSION = "version";
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link VDRConfiguration} class contains fields mapping thing configuration parameters.
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VDRConfiguration {
|
||||
|
||||
private String host = "localhost";
|
||||
private int port = 6419;
|
||||
private Integer refresh = 60;
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Integer getRefresh() {
|
||||
return refresh;
|
||||
}
|
||||
|
||||
public void setRefresh(Integer refresh) {
|
||||
this.refresh = refresh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPChannel;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPClient;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPClientImpl;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPConnectionException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPDiskStatus;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPEpgEvent;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPParseResponseException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPVolume;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
import org.openhab.core.library.types.DateTimeType;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.PercentType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link VDRHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class VDRHandler extends BaseThingHandler {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(VDRHandler.class);
|
||||
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
private VDRConfiguration config = new VDRConfiguration();
|
||||
|
||||
private @Nullable ScheduledFuture<?> refreshThreadFuture = null;
|
||||
|
||||
public VDRHandler(Thing thing, TimeZoneProvider timeZoneProvider) {
|
||||
super(thing);
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* when disposing refresh thread has to be cancelled
|
||||
*/
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopRefreshThread(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of {@link VDRHandler}
|
||||
*/
|
||||
@Override
|
||||
public void initialize() {
|
||||
config = getConfigAs(VDRConfiguration.class);
|
||||
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
|
||||
// initialize and schedule refresh thread
|
||||
scheduleRefreshThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Thing's properties (e. g. VDR Version)
|
||||
*
|
||||
* @param client the {@link SVDRPClient} to be used for properties update
|
||||
*/
|
||||
public void updateProperties(SVDRPClient client) {
|
||||
Map<String, String> properties = editProperties();
|
||||
// set vdr version to properties of thing
|
||||
String version = client.getSVDRPVersion();
|
||||
properties.put(VDRBindingConstants.PROPERTY_VERSION, version.toString());
|
||||
|
||||
// persist changes only if there are any changes.
|
||||
if (!editProperties().equals(properties)) {
|
||||
updateProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handling Commands for {@link VDRHandler}
|
||||
*
|
||||
* @param channelUID channel command was executed for
|
||||
* @param command command to execute
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
SVDRPClient con = new SVDRPClientImpl(config.getHost(), config.getPort());
|
||||
try {
|
||||
con.openConnection();
|
||||
|
||||
if (command instanceof RefreshType) {
|
||||
this.onVDRRefresh();
|
||||
} else {
|
||||
State result = UnDefType.NULL;
|
||||
String cmd = command.toString();
|
||||
switch (channelUID.getId()) {
|
||||
case VDRBindingConstants.CHANNEL_UID_MESSAGE:
|
||||
con.sendSVDRPMessage(cmd);
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_POWER:
|
||||
con.sendSVDRPKey(VDRBindingConstants.KEY_CODE_POWER);
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CHANNEL:
|
||||
SVDRPChannel channel = con.setSVDRPChannel(Integer.parseInt(cmd));
|
||||
result = new DecimalType(channel.getNumber());
|
||||
updateState(channelUID, result);
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_VOLUME:
|
||||
SVDRPVolume volume = con.setSVDRPVolume(Integer.parseInt(cmd));
|
||||
result = new PercentType(volume.getVolume());
|
||||
updateState(channelUID, result);
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_KEYCODE:
|
||||
con.sendSVDRPKey(cmd.trim());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (SVDRPParseResponseException e) {
|
||||
logger.trace("VDR handleCommand for Thing {}, ChannelUID {}, Parse Response failed. Message: {}",
|
||||
this.getThing().getUID(), channelUID, e.getMessage());
|
||||
} catch (SVDRPConnectionException e) {
|
||||
logger.debug("VDR handleCommand for Thing {}, ChannelUID {}, Connection failed. Message: {}",
|
||||
this.getThing().getUID(), channelUID, e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
con.closeConnection();
|
||||
} catch (SVDRPException ex) {
|
||||
logger.trace("Error on VDR handleCommand while closing SVDRP Connection for Thing : {} with message {}",
|
||||
this.getThing().getUID(), ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh data from SVDRPClient (Polling)
|
||||
*/
|
||||
private void onVDRRefresh() {
|
||||
SVDRPClient con = new SVDRPClientImpl(config.getHost(), config.getPort());
|
||||
Thing thing = getThing();
|
||||
try {
|
||||
con.openConnection();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
updateProperties(con);
|
||||
|
||||
thing.getChannels().stream().map(c -> c.getUID()).filter(this::isLinked).forEach(channelUID -> {
|
||||
try {
|
||||
logger.trace("updateChannel: {}", channelUID);
|
||||
|
||||
SVDRPEpgEvent entry;
|
||||
State result = UnDefType.NULL;
|
||||
|
||||
switch (channelUID.getId()) {
|
||||
case VDRBindingConstants.CHANNEL_UID_RECORDING:
|
||||
boolean isRecording = con.isRecordingActive();
|
||||
if (isRecording) {
|
||||
result = OnOffType.ON;
|
||||
} else {
|
||||
result = OnOffType.OFF;
|
||||
}
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_VOLUME:
|
||||
SVDRPVolume volume = con.getSVDRPVolume();
|
||||
result = new PercentType(volume.getVolume());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CHANNEL:
|
||||
SVDRPChannel channel = con.getCurrentSVDRPChannel();
|
||||
result = new DecimalType(channel.getNumber());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CHANNEL_NAME:
|
||||
SVDRPChannel svdrpChannel = con.getCurrentSVDRPChannel();
|
||||
result = new StringType(svdrpChannel.getName());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_POWER:
|
||||
SVDRPDiskStatus status = con.getDiskStatus();
|
||||
if (status.getPercentUsed() >= 0) {
|
||||
result = OnOffType.ON;
|
||||
} else {
|
||||
result = OnOffType.OFF;
|
||||
}
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_DISKUSAGE:
|
||||
SVDRPDiskStatus diskStatus = con.getDiskStatus();
|
||||
result = new DecimalType(diskStatus.getPercentUsed());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CURRENT_EVENT_TITLE:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NOW);
|
||||
result = new StringType(entry.getTitle());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CURRENT_EVENT_SUBTITLE:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NOW);
|
||||
result = new StringType(entry.getSubtitle());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CURRENT_EVENT_DURATION:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NOW);
|
||||
result = new QuantityType<>(entry.getDuration(), Units.MINUTE);
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CURRENT_EVENT_BEGIN:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NOW);
|
||||
result = new DateTimeType(LocalDateTime.ofInstant(entry.getBegin(), ZoneId.systemDefault())
|
||||
.atZone(timeZoneProvider.getTimeZone()));
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_CURRENT_EVENT_END:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NOW);
|
||||
result = new DateTimeType(LocalDateTime.ofInstant(entry.getEnd(), ZoneId.systemDefault())
|
||||
.atZone(timeZoneProvider.getTimeZone()));
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_NEXT_EVENT_TITLE:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NEXT);
|
||||
result = new StringType(entry.getTitle());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_NEXT_EVENT_SUBTITLE:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NEXT);
|
||||
result = new StringType(entry.getSubtitle());
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_NEXT_EVENT_DURATION:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NEXT);
|
||||
result = new QuantityType<>(entry.getDuration(), Units.MINUTE);
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_NEXT_EVENT_BEGIN:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NEXT);
|
||||
result = new DateTimeType(LocalDateTime.ofInstant(entry.getBegin(), ZoneId.systemDefault())
|
||||
.atZone(timeZoneProvider.getTimeZone()));
|
||||
break;
|
||||
case VDRBindingConstants.CHANNEL_UID_NEXT_EVENT_END:
|
||||
entry = con.getEpgEvent(SVDRPEpgEvent.TYPE.NEXT);
|
||||
result = new DateTimeType(LocalDateTime.ofInstant(entry.getEnd(), ZoneId.systemDefault())
|
||||
.atZone(timeZoneProvider.getTimeZone()));
|
||||
break;
|
||||
|
||||
}
|
||||
updateState(channelUID, result);
|
||||
|
||||
} catch (SVDRPParseResponseException e) {
|
||||
logger.trace("VDR Refresh for Thing {}, ChannelUID {}, Parse Response failed. Message: {}",
|
||||
this.getThing().getUID(), channelUID, e.getMessage());
|
||||
updateState(channelUID, UnDefType.UNDEF);
|
||||
} catch (SVDRPConnectionException e) {
|
||||
logger.debug("VDR Refresh for Thing {}, ChannelUID {}, Connection failed. Message: {}",
|
||||
this.getThing().getUID(), channelUID, e.getMessage());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
} catch (SVDRPConnectionException ce) {
|
||||
if (thing.getStatus() == ThingStatus.ONLINE) {
|
||||
// also update power channel when thing is offline before setting it offline
|
||||
thing.getChannels().stream().map(c -> c.getUID()).filter(this::isLinked).forEach(channelUID -> {
|
||||
if (VDRBindingConstants.CHANNEL_UID_POWER.equals(channelUID.getIdWithoutGroup())) {
|
||||
updateState(channelUID, OnOffType.OFF);
|
||||
}
|
||||
});
|
||||
}
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ce.getMessage());
|
||||
} catch (SVDRPParseResponseException se) {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, se.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
con.closeConnection();
|
||||
} catch (SVDRPException e) {
|
||||
logger.trace("Error on VDR Refresh while closing SVDRP Connection for Thing : {} with message {}",
|
||||
this.getThing().getUID(), e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the refresh thread
|
||||
*/
|
||||
private void scheduleRefreshThread() {
|
||||
refreshThreadFuture = scheduler.scheduleWithFixedDelay(this::onVDRRefresh, 3, config.getRefresh(),
|
||||
TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the refresh thread.
|
||||
*
|
||||
* @param force if set to true thread cancellation will be forced
|
||||
*/
|
||||
private void stopRefreshThread(boolean force) {
|
||||
ScheduledFuture<?> refreshThread = refreshThreadFuture;
|
||||
if (refreshThread != null)
|
||||
refreshThread.cancel(force);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.openhab.binding.vdr.internal.VDRBindingConstants.THING_TYPE_VDR;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.core.i18n.TimeZoneProvider;
|
||||
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.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link VDRHandlerFactory} is responsible for creating things and thing
|
||||
* handlers.
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(configurationPid = "binding.vdr", service = ThingHandlerFactory.class)
|
||||
public class VDRHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.singleton(THING_TYPE_VDR);
|
||||
|
||||
private final TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public VDRHandlerFactory(final @Reference TimeZoneProvider timeZoneProvider) {
|
||||
this.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable ThingHandler createHandler(Thing thing) {
|
||||
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
|
||||
|
||||
if (THING_TYPE_VDR.equals(thingTypeUID)) {
|
||||
return new VDRHandler(thing, timeZoneProvider);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPChannel} contains SVDRP Response Data for Channels
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPChannel {
|
||||
private int number;
|
||||
private String name = "";
|
||||
|
||||
private SVDRPChannel() {
|
||||
}
|
||||
|
||||
/**
|
||||
* parse object from SVDRP Client Response
|
||||
*
|
||||
* @param message SVDRP Client Response
|
||||
* @return Channel Object
|
||||
* @throws SVDRPParseResponseException thrown if response data is not parseable
|
||||
*/
|
||||
public static SVDRPChannel parse(String message) throws SVDRPParseResponseException {
|
||||
String number = message.substring(0, message.indexOf(" "));
|
||||
String name = message.substring(message.indexOf(" ") + 1, message.length());
|
||||
SVDRPChannel channel = new SVDRPChannel();
|
||||
try {
|
||||
channel.setNumber(Integer.parseInt(number));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new SVDRPParseResponseException(e.getMessage(), e);
|
||||
}
|
||||
channel.setName(name);
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Channel Number
|
||||
*
|
||||
* @return Channel Number
|
||||
*/
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Channel Number
|
||||
*
|
||||
* @param number Channel Number
|
||||
*/
|
||||
public void setNumber(int number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Channel Name
|
||||
*
|
||||
* @return Channel Name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Channel Name
|
||||
*
|
||||
* @param name Channel Name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* String Representation of SVDRPChannel Object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (number >= 0) {
|
||||
sb.append("Number: " + String.valueOf(number) + System.lineSeparator());
|
||||
}
|
||||
sb.append("Name: " + name + System.lineSeparator());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPClient} encapsulates all calls to the SVDRP interface of a VDR
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface SVDRPClient {
|
||||
|
||||
/**
|
||||
*
|
||||
* Open VDR Socket Connection
|
||||
*
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public void openConnection() throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Close VDR Socket Connection
|
||||
*
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public void closeConnection() throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Retrieve Disk Status from SVDRP Client
|
||||
*
|
||||
* @return SVDRP Disk Status
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public SVDRPDiskStatus getDiskStatus() throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Retrieve EPG Event from SVDRPClient
|
||||
*
|
||||
* @param type Type of EPG Event (now, next)
|
||||
* @return SVDRP EPG Event
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public SVDRPEpgEvent getEpgEvent(SVDRPEpgEvent.TYPE type)
|
||||
throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Retrieve current volume from SVDRP Client
|
||||
*
|
||||
* @return SVDRP Volume Object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public SVDRPVolume getSVDRPVolume() throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Set volume on SVDRP Client
|
||||
*
|
||||
* @param newVolume Volume in Percent
|
||||
* @return SVDRP Volume Object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public SVDRPVolume setSVDRPVolume(int newVolume) throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Send Key command to SVDRP Client
|
||||
*
|
||||
* @param key Key Command to send
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public void sendSVDRPKey(String key) throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Send Message to SVDRP Client
|
||||
*
|
||||
* @param message Message to send
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public void sendSVDRPMessage(String message) throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Retrieve current Channel from SVDRP Client
|
||||
*
|
||||
* @return SVDRPChannel object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public SVDRPChannel getCurrentSVDRPChannel() throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Change current Channel on SVDRP Client
|
||||
*
|
||||
* @param number Channel to be set
|
||||
* @return SVDRPChannel object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public SVDRPChannel setSVDRPChannel(int number) throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Retrieve from SVDRP Client if a recording is currently active
|
||||
*
|
||||
* @return is currently a recording active
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public boolean isRecordingActive() throws SVDRPConnectionException, SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Retrieve VDR Version from SVDRP Client
|
||||
*
|
||||
* @return VDR Version
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
public String getSVDRPVersion();
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPClientImpl} encapsulates all calls to the SVDRP interface of a VDR
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPClientImpl implements SVDRPClient {
|
||||
|
||||
private String host;
|
||||
private int port = 6419;
|
||||
private String charset = "UTF-8";
|
||||
private String version = "";
|
||||
|
||||
private static final String WELCOME_MESSAGE = "([0-9]{3})([ -])(.*)";
|
||||
private static final Pattern PATTERN_WELCOME = Pattern.compile(WELCOME_MESSAGE);
|
||||
|
||||
private static final int TIMEOUT_MS = 3000;
|
||||
|
||||
private @Nullable Socket socket = null;
|
||||
private @Nullable BufferedWriter out = null;
|
||||
private @Nullable BufferedReader in = null;
|
||||
|
||||
public SVDRPClientImpl(String host, int port) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public SVDRPClientImpl(String host, int port, String charset) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Open VDR Socket Connection
|
||||
*
|
||||
* @throws IOException if an IO Error occurs
|
||||
*/
|
||||
@Override
|
||||
public void openConnection() throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
Socket localSocket = socket;
|
||||
BufferedWriter localOut = out;
|
||||
BufferedReader localIn = in;
|
||||
|
||||
if (localSocket == null || localSocket.isClosed()) {
|
||||
localSocket = new Socket();
|
||||
socket = localSocket;
|
||||
}
|
||||
try {
|
||||
InetSocketAddress isa = new InetSocketAddress(host, port);
|
||||
localSocket.connect(isa, TIMEOUT_MS);
|
||||
localSocket.setSoTimeout(TIMEOUT_MS);
|
||||
|
||||
localOut = new BufferedWriter(new OutputStreamWriter(localSocket.getOutputStream(), charset), 8192);
|
||||
out = localOut;
|
||||
localIn = new BufferedReader(new InputStreamReader(localSocket.getInputStream(), charset), 8192);
|
||||
in = localIn;
|
||||
|
||||
// read welcome message and init version & charset
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute(null);
|
||||
|
||||
if (res.getCode() == 220) {
|
||||
SVDRPWelcome welcome = SVDRPWelcome.parse(res.getMessage());
|
||||
this.charset = welcome.getCharset();
|
||||
this.version = welcome.getVersion();
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// cleanup after timeout
|
||||
try {
|
||||
if (localOut != null)
|
||||
localOut.close();
|
||||
if (localIn != null)
|
||||
localIn.close();
|
||||
localSocket.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
throw new SVDRPConnectionException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close VDR Socket Connection
|
||||
*
|
||||
* @throws IOException if an IO Error occurs
|
||||
*/
|
||||
@Override
|
||||
public void closeConnection() throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
Socket localSocket = socket;
|
||||
BufferedWriter localOut = out;
|
||||
BufferedReader localIn = in;
|
||||
/*
|
||||
* socket on vdr stays in FIN_WAIT2 without closing connection
|
||||
*/
|
||||
try {
|
||||
if (localOut != null) {
|
||||
localOut.write("QUIT");
|
||||
localOut.newLine();
|
||||
localOut.flush();
|
||||
localOut.close();
|
||||
}
|
||||
if (localIn != null) {
|
||||
localIn.close();
|
||||
}
|
||||
if (localSocket != null) {
|
||||
localSocket.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SVDRPConnectionException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* execute SVDRP Call
|
||||
*
|
||||
* @param command SVDRP command to execute
|
||||
* @return response of SVDRPCall
|
||||
* @throws SVDRPException exception from SVDRP call
|
||||
*/
|
||||
private SVDRPResponse execute(@Nullable String command)
|
||||
throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
BufferedWriter localOut = out;
|
||||
BufferedReader localIn = in;
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
Matcher matcher = null;
|
||||
|
||||
int code;
|
||||
try {
|
||||
if (command != null) {
|
||||
if (localOut == null) {
|
||||
throw new SVDRPConnectionException("OutputStream is null!");
|
||||
} else {
|
||||
localOut.write(command);
|
||||
localOut.newLine();
|
||||
localOut.flush();
|
||||
}
|
||||
}
|
||||
|
||||
if (localIn != null) {
|
||||
code = -1;
|
||||
String line = null;
|
||||
boolean cont = true;
|
||||
while (cont && (line = localIn.readLine()) != null) {
|
||||
matcher = PATTERN_WELCOME.matcher(line);
|
||||
if (matcher.matches() && matcher.groupCount() > 2) {
|
||||
if (code < 0) {
|
||||
code = Integer.parseInt(matcher.group(1));
|
||||
}
|
||||
if (" ".equals(matcher.group(2))) {
|
||||
cont = false;
|
||||
}
|
||||
message.append(matcher.group(3));
|
||||
if (cont) {
|
||||
message.append(System.lineSeparator());
|
||||
}
|
||||
} else {
|
||||
cont = false;
|
||||
}
|
||||
}
|
||||
return new SVDRPResponse(code, message.toString());
|
||||
} else {
|
||||
throw new SVDRPConnectionException("SVDRP Input Stream is Null");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new SVDRPConnectionException(ioe.getMessage(), ioe);
|
||||
} catch (NumberFormatException ne) {
|
||||
throw new SVDRPParseResponseException(ne.getMessage(), ne);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Disk Status from SVDRP Client
|
||||
*
|
||||
* @return SVDRP Disk Status
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public SVDRPDiskStatus getDiskStatus() throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute("STAT disk");
|
||||
|
||||
if (res.getCode() == 250) {
|
||||
SVDRPDiskStatus status = SVDRPDiskStatus.parse(res.getMessage());
|
||||
return status;
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve EPG Event from SVDRPClient
|
||||
*
|
||||
* @param type Type of EPG Event (now, next)
|
||||
* @return SVDRP EPG Event
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public SVDRPEpgEvent getEpgEvent(SVDRPEpgEvent.TYPE type)
|
||||
throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
SVDRPChannel channel = this.getCurrentSVDRPChannel();
|
||||
switch (type) {
|
||||
case NOW:
|
||||
res = execute(String.format("LSTE %s %s", channel.getNumber(), "now"));
|
||||
break;
|
||||
case NEXT:
|
||||
res = execute(String.format("LSTE %s %s", channel.getNumber(), "next"));
|
||||
break;
|
||||
}
|
||||
|
||||
if (res != null && res.getCode() == 215) {
|
||||
SVDRPEpgEvent entry = SVDRPEpgEvent.parse(res.getMessage());
|
||||
return entry;
|
||||
} else if (res != null) {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
} else {
|
||||
throw new SVDRPConnectionException("SVDRPResponse is Null");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve current volume from SVDRP Client
|
||||
*
|
||||
* @return SVDRP Volume Object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public SVDRPVolume getSVDRPVolume() throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute("VOLU");
|
||||
|
||||
if (res.getCode() == 250) {
|
||||
SVDRPVolume volume = SVDRPVolume.parse(res.getMessage());
|
||||
return volume;
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set volume on SVDRP Client
|
||||
*
|
||||
* @param newVolume Volume in Percent
|
||||
* @return SVDRP Volume Object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public SVDRPVolume setSVDRPVolume(int newVolume) throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
double newVolumeDouble = newVolume * 255 / 100;
|
||||
res = execute(String.format("VOLU %s", String.valueOf(Math.round(newVolumeDouble))));
|
||||
|
||||
if (res.getCode() == 250) {
|
||||
SVDRPVolume volume = SVDRPVolume.parse(res.getMessage());
|
||||
return volume;
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Key command to SVDRP Client
|
||||
*
|
||||
* @param key Key Command to send
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public void sendSVDRPKey(String key) throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute(String.format("HITK %s", key));
|
||||
|
||||
if (res.getCode() != 250) {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Message to SVDRP Client
|
||||
*
|
||||
* @param message Message to send
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public void sendSVDRPMessage(String message) throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute(String.format("MESG %s", message));
|
||||
|
||||
if (res.getCode() != 250) {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve current Channel from SVDRP Client
|
||||
*
|
||||
* @return SVDRPChannel object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public SVDRPChannel getCurrentSVDRPChannel() throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute("CHAN");
|
||||
|
||||
if (res.getCode() == 250) {
|
||||
SVDRPChannel channel = SVDRPChannel.parse(res.getMessage());
|
||||
return channel;
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change current Channel on SVDRP Client
|
||||
*
|
||||
* @param number Channel to be set
|
||||
* @return SVDRPChannel object
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public SVDRPChannel setSVDRPChannel(int number) throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute(String.format("CHAN %s", number));
|
||||
|
||||
if (res.getCode() == 250) {
|
||||
SVDRPChannel channel = SVDRPChannel.parse(res.getMessage());
|
||||
return channel;
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve from SVDRP Client if a recording is currently active
|
||||
*
|
||||
* @return is currently a recording active
|
||||
* @throws SVDRPConnectionException thrown if connection to VDR failed or was not possible
|
||||
* @throws SVDRPParseResponseException thrown if something's not OK with SVDRP response
|
||||
*/
|
||||
@Override
|
||||
public boolean isRecordingActive() throws SVDRPConnectionException, SVDRPParseResponseException {
|
||||
SVDRPResponse res = null;
|
||||
|
||||
res = execute("LSTT");
|
||||
|
||||
if (res.getCode() == 250) {
|
||||
SVDRPTimerList timers = SVDRPTimerList.parse(res.getMessage());
|
||||
return timers.isRecordingActive();
|
||||
} else if (res.getCode() == 550) {
|
||||
// Error 550 is "No timers defined". Therefore there cannot be an active recording
|
||||
return false;
|
||||
} else {
|
||||
throw new SVDRPParseResponseException(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve VDR Version from SVDRP Client
|
||||
*
|
||||
* @return VDR Version
|
||||
* @throws SVDRPException thrown if something's not OK with SVDRP call
|
||||
*/
|
||||
@Override
|
||||
public String getSVDRPVersion() {
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPConnectionException} is thrown when SVDRP Connection cannot be established
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPConnectionException extends SVDRPException {
|
||||
|
||||
private static final long serialVersionUID = 2825596676109860370L;
|
||||
|
||||
public SVDRPConnectionException(@Nullable String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SVDRPConnectionException(@Nullable String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPDiskStatus} contains SVDRP Response Data for DiskStatus queries
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPDiskStatus {
|
||||
private static final Pattern PATTERN_DISK_STATUS = Pattern.compile("([\\d]*)MB ([\\d]*)MB ([\\d]*)%");
|
||||
|
||||
private long megaBytesFree = -1;
|
||||
private long megaBytesTotal = -1;
|
||||
private int percentUsed = -1;
|
||||
|
||||
private SVDRPDiskStatus() {
|
||||
}
|
||||
|
||||
/**
|
||||
* parse object from SVDRP Client Response
|
||||
*
|
||||
* @param message SVDRP Client Response
|
||||
* @return Disk Status Object
|
||||
* @throws SVDRPParseResponseException thrown if response data is not parseable
|
||||
*/
|
||||
public static SVDRPDiskStatus parse(String message) throws SVDRPParseResponseException {
|
||||
SVDRPDiskStatus status = new SVDRPDiskStatus();
|
||||
Matcher matcher = PATTERN_DISK_STATUS.matcher(message);
|
||||
if (matcher.find() && matcher.groupCount() == 3) {
|
||||
status.setMegaBytesTotal(Long.parseLong(matcher.group(1)));
|
||||
status.setMegaBytesFree(Long.parseLong(matcher.group(2)));
|
||||
status.setPercentUsed(Integer.parseInt(matcher.group(3)));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Megabytes Free on Disk
|
||||
*
|
||||
* @return megabytes free
|
||||
*/
|
||||
public long getMegaBytesFree() {
|
||||
return megaBytesFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Megabytes Free on Disk
|
||||
*
|
||||
* @param megaBytesFree megabytes free
|
||||
*/
|
||||
public void setMegaBytesFree(long megaBytesFree) {
|
||||
this.megaBytesFree = megaBytesFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Megabytes Total on Disk
|
||||
*
|
||||
* @return megabytes total
|
||||
*/
|
||||
public long getMegaBytesTotal() {
|
||||
return megaBytesTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Megabytes Total on Disk
|
||||
*
|
||||
* @param megaBytesTotal megabytes total
|
||||
*/
|
||||
public void setMegaBytesTotal(long megaBytesTotal) {
|
||||
this.megaBytesTotal = megaBytesTotal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Percentage Used on Disk
|
||||
*
|
||||
* @return percentage used
|
||||
*/
|
||||
public int getPercentUsed() {
|
||||
return percentUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Percentage Used on Disk
|
||||
*
|
||||
* @param percentUsed percentage used
|
||||
*/
|
||||
public void setPercentUsed(int percentUsed) {
|
||||
this.percentUsed = percentUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* String Representation of SVDRPDiskStatus Object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (megaBytesTotal >= 0) {
|
||||
sb.append("Total: " + megaBytesTotal + "MB" + System.lineSeparator());
|
||||
}
|
||||
if (megaBytesFree >= 0) {
|
||||
sb.append("Free: " + megaBytesFree + "MB" + System.lineSeparator());
|
||||
}
|
||||
if (percentUsed >= 0) {
|
||||
sb.append("Free: " + percentUsed + "%" + System.lineSeparator());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPEpgEvent} contains SVDRP Response Data for an EPG Event
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPEpgEvent {
|
||||
|
||||
public enum TYPE {
|
||||
NOW,
|
||||
NEXT
|
||||
}
|
||||
|
||||
private String title = "";
|
||||
private String subtitle = "";
|
||||
private Instant begin = Instant.now();
|
||||
private Instant end = Instant.now();
|
||||
private int duration;
|
||||
|
||||
private SVDRPEpgEvent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* parse object from SVDRP Client Response
|
||||
*
|
||||
* @param message SVDRP Client Response
|
||||
* @return SVDRPEpgEvent Object
|
||||
* @throws SVDRPParseResponseException thrown if response data is not parseable
|
||||
*/
|
||||
public static SVDRPEpgEvent parse(String message) throws SVDRPParseResponseException {
|
||||
SVDRPEpgEvent entry = new SVDRPEpgEvent();
|
||||
StringTokenizer st = new StringTokenizer(message, System.lineSeparator());
|
||||
|
||||
while (st.hasMoreTokens()) {
|
||||
String line = st.nextToken();
|
||||
if (line.length() >= 1 && !line.startsWith("End")) {
|
||||
switch (line.charAt(0)) {
|
||||
case 'T':
|
||||
entry.setTitle(line.substring(1).trim());
|
||||
break;
|
||||
case 'S':
|
||||
entry.setSubtitle(line.substring(1).trim());
|
||||
break;
|
||||
case 'E':
|
||||
StringTokenizer lt = new StringTokenizer(line.substring(1).trim(), " ");
|
||||
lt.nextToken(); // event id
|
||||
try {
|
||||
long begin = Long.parseLong(lt.nextToken());
|
||||
entry.setBegin(Instant.ofEpochSecond(begin));
|
||||
} catch (NumberFormatException | NoSuchElementException e) {
|
||||
throw new SVDRPParseResponseException("Begin: " + e.getMessage(), e);
|
||||
}
|
||||
try {
|
||||
entry.setDuration(Integer.parseInt(lt.nextToken()) / 60);
|
||||
} catch (NumberFormatException | NoSuchElementException e) {
|
||||
throw new SVDRPParseResponseException("Duration: " + e.getMessage(), e);
|
||||
}
|
||||
entry.setEnd(entry.getBegin().plus(entry.getDuration(), ChronoUnit.MINUTES));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (!line.startsWith("End")) {
|
||||
throw new SVDRPParseResponseException("EPG Event Line corrupt: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Title of EPG Event
|
||||
*
|
||||
* @return Event Title
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Title of EPG Event
|
||||
*
|
||||
* @param title Event Title
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Subtitle of EPG Event
|
||||
*
|
||||
* @return Event Subtitle
|
||||
*/
|
||||
public String getSubtitle() {
|
||||
return subtitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Subtitle of EPG Event
|
||||
*
|
||||
* @param subtitle Event Subtitle
|
||||
*/
|
||||
public void setSubtitle(String subtitle) {
|
||||
this.subtitle = subtitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Begin of EPG Event
|
||||
*
|
||||
* @return Event Begin
|
||||
*/
|
||||
public Instant getBegin() {
|
||||
return begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Begin of EPG Event
|
||||
*
|
||||
* @param begin Event Begin
|
||||
*/
|
||||
public void setBegin(Instant begin) {
|
||||
this.begin = begin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get End of EPG Event
|
||||
*
|
||||
* @return Event End
|
||||
*/
|
||||
public Instant getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set End of EPG Event
|
||||
*
|
||||
* @param end Event End
|
||||
*/
|
||||
public void setEnd(Instant end) {
|
||||
this.end = end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Duration of EPG Event in Minutes
|
||||
*
|
||||
* @return Event Duration in Minutes
|
||||
*/
|
||||
public int getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Duration of EPG Event in Minutes
|
||||
*
|
||||
* @param duration Event Duration in Minutes
|
||||
*/
|
||||
public void setDuration(int duration) {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* String Representation of SVDRPDiskStatus Object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("Title: ");
|
||||
sb.append(title);
|
||||
sb.append(System.lineSeparator());
|
||||
|
||||
sb.append("Subtitle: ");
|
||||
sb.append(subtitle);
|
||||
sb.append(System.lineSeparator());
|
||||
|
||||
sb.append("Begin: ");
|
||||
sb.append(begin);
|
||||
sb.append(System.lineSeparator());
|
||||
|
||||
sb.append("End: ");
|
||||
sb.append(end);
|
||||
sb.append(System.lineSeparator());
|
||||
|
||||
if (duration > -1) {
|
||||
sb.append("Duration: ");
|
||||
sb.append(duration);
|
||||
sb.append(System.lineSeparator());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPException} is thrown in case of Failure of SVDRP Handling
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public abstract class SVDRPException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 3816136415994156427L;
|
||||
|
||||
public SVDRPException(@Nullable String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public SVDRPException(@Nullable String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPParseResponseException} is thrown if a SVDRP Response cannot be parsed as expected
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPParseResponseException extends SVDRPException {
|
||||
|
||||
private static final long serialVersionUID = 631229205838438373L;
|
||||
|
||||
public SVDRPParseResponseException(SVDRPResponse response) {
|
||||
super(response.getMessage());
|
||||
}
|
||||
|
||||
public SVDRPParseResponseException(SVDRPResponse response, Throwable cause) {
|
||||
super(response.getMessage(), cause);
|
||||
}
|
||||
|
||||
public SVDRPParseResponseException(@Nullable String message) {
|
||||
super(message);
|
||||
String newMessage = message;
|
||||
if (newMessage == null) {
|
||||
newMessage = "Null Value on Exception Message";
|
||||
}
|
||||
}
|
||||
|
||||
public SVDRPParseResponseException(@Nullable String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
String newMessage = message;
|
||||
if (newMessage == null) {
|
||||
newMessage = "Null Value on Exception Message";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPResponse} represents a general Object returned by an SVDRP Client Call
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPResponse {
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
public SVDRPResponse(int code, String response) {
|
||||
this.code = code;
|
||||
this.message = response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Status Code of SVDRP Response
|
||||
*
|
||||
* @return Status Code of SVDRP Response
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Message of SVDRP Response
|
||||
*
|
||||
* @return Message of SVDRP Response
|
||||
*/
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPTimerList} contains SVDRP Response Data for a Timer list
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPTimerList {
|
||||
|
||||
private List<String> timers = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* parse object from SVDRP Client Response
|
||||
*
|
||||
* @param message SVDRP Client Response
|
||||
* @return Timer List Object
|
||||
* @throws SVDRPParseResponseException thrown if response data is not parseable
|
||||
*/
|
||||
public static SVDRPTimerList parse(String message) {
|
||||
SVDRPTimerList timers = new SVDRPTimerList();
|
||||
List<String> lines = new ArrayList<String>();
|
||||
|
||||
StringTokenizer st = new StringTokenizer(message, System.lineSeparator());
|
||||
while (st.hasMoreTokens()) {
|
||||
String timer = st.nextToken();
|
||||
lines.add(timer);
|
||||
}
|
||||
timers.setTimers(lines);
|
||||
return timers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is there currently an active Recording on SVDRP Client
|
||||
*
|
||||
* @return returns true if there is an active recording
|
||||
*/
|
||||
public boolean isRecordingActive() {
|
||||
for (String line : timers) {
|
||||
String timerContent = line.substring(line.indexOf(" ") + 1);
|
||||
String timerStatus = timerContent.substring(0, timerContent.indexOf(":"));
|
||||
byte b = Byte.parseByte(timerStatus);
|
||||
if (((b >> 3) & 0x0001) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set timers object of SVDRPTimerList
|
||||
*
|
||||
* @param timers timers to set
|
||||
*/
|
||||
private void setTimers(List<String> timers) {
|
||||
this.timers = timers;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPVolume} contains SVDRP Response Data for Volume Object
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPVolume {
|
||||
|
||||
private int volume = -1;
|
||||
|
||||
private SVDRPVolume() {
|
||||
}
|
||||
|
||||
/**
|
||||
* parse object from SVDRP Client Response
|
||||
*
|
||||
* @param message SVDRP Client Response
|
||||
* @return Volume Object
|
||||
* @throws SVDRPParseResponseException thrown if response data is not parseable
|
||||
*/
|
||||
public static SVDRPVolume parse(String message) throws SVDRPParseResponseException {
|
||||
SVDRPVolume volume = new SVDRPVolume();
|
||||
try {
|
||||
String vol = message.substring(message.lastIndexOf(" ") + 1, message.length());
|
||||
if ("mute".equals(vol)) {
|
||||
volume.setVolume(0);
|
||||
} else {
|
||||
int val = Integer.parseInt(vol);
|
||||
val = val * 100 / 255;
|
||||
volume.setVolume(val);
|
||||
}
|
||||
} catch (NumberFormatException nex) {
|
||||
throw new SVDRPParseResponseException(nex.getMessage(), nex);
|
||||
} catch (IndexOutOfBoundsException ie) {
|
||||
throw new SVDRPParseResponseException(ie.getMessage(), ie);
|
||||
}
|
||||
return volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Volume in Percent
|
||||
*
|
||||
* @param volume Volume in Percent
|
||||
*/
|
||||
private void setVolume(int volume) {
|
||||
this.volume = volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Volume in Percent
|
||||
*
|
||||
* @return Volume in Percent
|
||||
*/
|
||||
public int getVolume() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* String Representation of SVDRPDiskStatus Object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (volume > -1) {
|
||||
sb.append("Volume: ");
|
||||
sb.append(String.valueOf(volume));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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.vdr.internal.svdrp;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
/**
|
||||
* The {@link SVDRPWelcome} contains SVDRP Response Data that is sent after Connection has been established
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPWelcome {
|
||||
|
||||
private String version = "";
|
||||
private String charset = "";
|
||||
private String dateAndTime = "";
|
||||
|
||||
private SVDRPWelcome() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse SVDRPResponse into SVDRPWelcome Object
|
||||
*
|
||||
* @param message SVDRP Client Response
|
||||
* Example: VDRHOST SVDRP VideoDiskRecorder 2.4.5; Sat Jan 9 22:28:11 2021; UTF-8
|
||||
* @return Welcome Object
|
||||
* @throws SVDRPParseResponseException thrown if response data is not parseable
|
||||
*/
|
||||
public static SVDRPWelcome parse(String message) throws SVDRPParseResponseException {
|
||||
SVDRPWelcome welcome = new SVDRPWelcome();
|
||||
StringTokenizer st = new StringTokenizer(message, ";");
|
||||
try {
|
||||
String hostAndVersion = st.nextToken();
|
||||
String dateAndTime = st.nextToken();
|
||||
String charset = st.nextToken();
|
||||
welcome.setCharset(charset.trim());
|
||||
welcome.setVersion(hostAndVersion.substring(hostAndVersion.lastIndexOf(" ")).trim());
|
||||
welcome.setDateAndTime(dateAndTime.trim());
|
||||
} catch (NoSuchElementException nex) {
|
||||
throw new SVDRPParseResponseException(nex.getMessage(), nex);
|
||||
}
|
||||
return welcome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get VDR version
|
||||
*
|
||||
* @return VDR version String
|
||||
*/
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set VDR version
|
||||
*
|
||||
* @param version VDR version String
|
||||
*/
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get VDR Charset
|
||||
*
|
||||
* @return VDR charset
|
||||
*/
|
||||
public String getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set VDR Charset
|
||||
*
|
||||
* @param charset VDR charset
|
||||
*/
|
||||
public void setCharset(String charset) {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get VDR Date and Time String
|
||||
*
|
||||
* @return VDR Date and Time String
|
||||
*/
|
||||
public String getDateAndTime() {
|
||||
return dateAndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set VDR Date and Time String
|
||||
*
|
||||
* @param dateAndTime VDR Date and Time String
|
||||
*/
|
||||
public void setDateAndTime(String dateAndTime) {
|
||||
this.dateAndTime = dateAndTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="vdr" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
|
||||
|
||||
<name>VDR Binding</name>
|
||||
<description>The Video Disk Recorder (VDR) binding allows to control your own Video Disk Recorder
|
||||
(https://www.tvdr.de).</description>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="vdr" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
|
||||
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
|
||||
|
||||
<thing-type id="vdr">
|
||||
<label>VDR</label>
|
||||
<description>VDR - The Video Disk Recorder (https://tvdr.de)</description>
|
||||
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="channel" typeId="vdrChannel"/>
|
||||
<channel id="channelName" typeId="vdrChannelName"/>
|
||||
<channel id="volume" typeId="system.volume"/>
|
||||
<channel id="recording" typeId="vdrRecordingActive"/>
|
||||
<channel id="diskUsage" typeId="vdrDiskUsage"/>
|
||||
<channel id="message" typeId="vdrMessage"/>
|
||||
<channel id="keyCode" typeId="vdrKeyCode"/>
|
||||
<channel id="currentEventTitle" typeId="vdrEventTitle">
|
||||
<label>Current EPG Event Title</label>
|
||||
</channel>
|
||||
<channel id="currentEventSubTitle" typeId="vdrEventSubTitle">
|
||||
<label>Current EPG Event Sub Title</label>
|
||||
</channel>
|
||||
<channel id="currentEventBegin" typeId="vdrEventBegin">
|
||||
<label>Current EPG Event Begin</label>
|
||||
</channel>
|
||||
<channel id="currentEventEnd" typeId="vdrEventEnd">
|
||||
<label>Current EPG Event End</label>
|
||||
</channel>
|
||||
<channel id="currentEventDuration" typeId="vdrEventDuration">
|
||||
<label>Current EPG Event Duration in Minutes</label>
|
||||
</channel>
|
||||
<channel id="nextEventTitle" typeId="vdrEventTitle">
|
||||
<label>Next EPG Event Title</label>
|
||||
</channel>
|
||||
<channel id="nextEventSubTitle" typeId="vdrEventSubTitle">
|
||||
<label>Next EPG Event Sub Title</label>
|
||||
</channel>
|
||||
<channel id="nextEventBegin" typeId="vdrEventBegin">
|
||||
<label>Next EPG Event Begin</label>
|
||||
</channel>
|
||||
<channel id="nextEventEnd" typeId="vdrEventEnd">
|
||||
<label>Next EPG Event End</label>
|
||||
</channel>
|
||||
<channel id="nextEventDuration" typeId="vdrEventDuration">
|
||||
<label>Next EPG Event Duration in Minutes</label>
|
||||
</channel>
|
||||
</channels>
|
||||
|
||||
<properties>
|
||||
<property name="version">VDR Version</property>
|
||||
</properties>
|
||||
|
||||
<config-description>
|
||||
<parameter name="host" type="text" required="true">
|
||||
<context>network-address</context>
|
||||
<label>Hostname</label>
|
||||
<description>Hostname or IP Address of VDR instance</description>
|
||||
</parameter>
|
||||
<parameter name="port" type="integer" required="false">
|
||||
<label>SVDRP Port</label>
|
||||
<description>SVDRP Port of VDR instance</description>
|
||||
<default>6419</default>
|
||||
</parameter>
|
||||
<parameter name="refresh" type="integer" min="0" unit="s">
|
||||
<label>Refresh Interval</label>
|
||||
<description>Interval in seconds the data from VDR instance is refreshed</description>
|
||||
<advanced>true</advanced>
|
||||
<default>30</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="vdrMessage">
|
||||
<item-type>String</item-type>
|
||||
<label>Display Message</label>
|
||||
<description>Send Message to be displayed on VDR</description>
|
||||
<state readOnly="false"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrChannel">
|
||||
<item-type>Number</item-type>
|
||||
<label>Channel Number</label>
|
||||
<description>Current Channel Number</description>
|
||||
<state readOnly="false" pattern="%d"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrChannelName">
|
||||
<item-type>String</item-type>
|
||||
<label>Channel Name</label>
|
||||
<description>Current Channel Name</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrRecordingActive">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Recording Active</label>
|
||||
<description>ON if a recording is active</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrDiskUsage">
|
||||
<item-type>Number</item-type>
|
||||
<label>Disk Usage</label>
|
||||
<description>Current Disk Usage in %</description>
|
||||
<state readOnly="true" min="0" max="100" step="1" pattern="%d %%"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrKeyCode">
|
||||
<item-type>String</item-type>
|
||||
<label>VDR Key Code</label>
|
||||
<description>Send Key Code of Remote Control to VDR</description>
|
||||
<command>
|
||||
<options>
|
||||
<option value="Up">Up</option>
|
||||
<option value="Down">Down</option>
|
||||
<option value="Menu">Menu</option>
|
||||
<option value="Ok">Ok</option>
|
||||
<option value="Back">Back</option>
|
||||
<option value="Left">Left</option>
|
||||
<option value="Right">Right</option>
|
||||
<option value="Red">Red</option>
|
||||
<option value="Green">Green</option>
|
||||
<option value="Yellow">Yellow</option>
|
||||
<option value="Blue">Blue</option>
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="Info">Info</option>
|
||||
<option value="Play/Pause">Play/Pause</option>
|
||||
<option value="Play">Play</option>
|
||||
<option value="Pause">Pause</option>
|
||||
<option value="Stop">Stop</option>
|
||||
<option value="Record">Record</option>
|
||||
<option value="FastFwd">FastFwd</option>
|
||||
<option value="FastRew">FastRew</option>
|
||||
<option value="Next">Next</option>
|
||||
<option value="Prev">Prev</option>
|
||||
<option value="Power">Power</option>
|
||||
<option value="Channel+">Channel+</option>
|
||||
<option value="Channel-">Channel-</option>
|
||||
<option value="PrevChannel">PrevChannel</option>
|
||||
<option value="Volume+">Volume+</option>
|
||||
<option value="Volume-">Volume-</option>
|
||||
<option value="Mute">Mute</option>
|
||||
<option value="Audio">Audio</option>
|
||||
<option value="Subtitles">Subtitles</option>
|
||||
<option value="Schedule">Schedule</option>
|
||||
<option value="Channels">Channels</option>
|
||||
<option value="Timers">Timers</option>
|
||||
<option value="Recordings">Recordings</option>
|
||||
<option value="Setup">Setup</option>
|
||||
<option value="Commands">Commands</option>
|
||||
<option value="User0">User0</option>
|
||||
<option value="User1">User1</option>
|
||||
<option value="User2">User2</option>
|
||||
<option value="User3">User3</option>
|
||||
<option value="User4">User4</option>
|
||||
<option value="User5">User5</option>
|
||||
<option value="User6">User6</option>
|
||||
<option value="User7">User7</option>
|
||||
<option value="User8">User8</option>
|
||||
<option value="User9">User9</option>
|
||||
</options>
|
||||
</command>
|
||||
</channel-type>
|
||||
<channel-type id="vdrEventTitle">
|
||||
<item-type>String</item-type>
|
||||
<label>Event Title</label>
|
||||
<description>Title of EPG Event</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrEventSubTitle">
|
||||
<item-type>String</item-type>
|
||||
<label>Event Sub Title</label>
|
||||
<description>Sub Title of EPG Event</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrEventBegin">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Event Start Time</label>
|
||||
<description>Start Time of EPG Event</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrEventEnd">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Event End Time</label>
|
||||
<description>End Time of EPG Event</description>
|
||||
<state readOnly="true"/>
|
||||
</channel-type>
|
||||
<channel-type id="vdrEventDuration">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Event Duration</label>
|
||||
<description>Duration of EPG Event in Minutes</description>
|
||||
<state readOnly="true" pattern="%d %unit%"/>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPChannel;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Specific unit tests to check if {@link SVDRPChannel} parses SVDRP responses correctly
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPChannelTest {
|
||||
|
||||
private final String channelResponseOk = "3 WDR HD Bielefeld";
|
||||
private final String channelResponseParseError = "250WDR HD Bielefeld";
|
||||
|
||||
@Test
|
||||
public void testParseChannelData() throws SVDRPException {
|
||||
SVDRPChannel channel = SVDRPChannel.parse(channelResponseOk);
|
||||
assertEquals("WDR HD Bielefeld", channel.getName());
|
||||
assertEquals(3, channel.getNumber());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExceptionChannelData() {
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPChannel.parse(channelResponseParseError);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPChannel;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPDiskStatus;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Specific unit tests to check if {@link SVDRPDiskStatus} parses SVDRP responses correctly
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPDiskStatusTest {
|
||||
|
||||
private final String diskStatusResponseOk = "411266MB 30092MB 92%";
|
||||
private final String diskStatusResponseParseError1 = "411266MB 30092MB 92%";
|
||||
private final String diskStatusResponseParseError2 = "411266MB 30092 92%";
|
||||
private final String diskStatusResponseParseError3 = "42b3MB 30092MB 92%";
|
||||
|
||||
@Test
|
||||
public void testParseDiskStatus() throws SVDRPException {
|
||||
SVDRPDiskStatus diskStatus = SVDRPDiskStatus.parse(diskStatusResponseOk);
|
||||
assertEquals(411266, diskStatus.getMegaBytesTotal());
|
||||
assertEquals(30092, diskStatus.getMegaBytesFree());
|
||||
assertEquals(92, diskStatus.getPercentUsed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExceptionDiskStatus() {
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPChannel.parse(diskStatusResponseParseError1);
|
||||
});
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPChannel.parse(diskStatusResponseParseError2);
|
||||
});
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPChannel.parse(diskStatusResponseParseError3);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPEpgEvent;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPParseResponseException;
|
||||
|
||||
/**
|
||||
* Specific unit tests to check if {@link SVDRPEpgEvent} parses SVDRP responses correctly
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPEpgEventTest {
|
||||
private final String epgResponseComplete = "C S19.2E-1-1201-28326 WDR HD Bielefeld\n"
|
||||
+ "E 9886 1610391600 900 4E F\n" + "T Tagesschau\n" + "S Aktuelle Nachrichten aus der Welt\n"
|
||||
+ "D Themen u.a.:|* Corona-Pandemie in Deutschland: Verschärfter Lockdown bundesweit in Kraft|* Entmachtung des US-Präsidenten: Demokraten planen Schritte gegen Trump|* Wintereinbruch in Bosnien-Herzegowina: Dramatische Lage der Flüchtlinge an der Grenze zu Kroatien\n"
|
||||
+ "G 20 80\n" + "X 2 03 deu stereo\n" + "X 2 03 deu ohne Audiodeskription\n"
|
||||
+ "X 3 01 deu Teletext-Untertitel\n" + "X 3 20 deu mit DVB-Untertitel\n" + "X 5 0B deu HD-Video\n"
|
||||
+ "V 1610391600\n" + "e\n" + "c\n" + "End of EPG data";
|
||||
private final String epgMissingSubtitle = "C S19.2E-1-1201-28326 WDR HD Bielefeld\n"
|
||||
+ "E 9886 1610391600 900 4E F\n" + "T Tagesschau\n"
|
||||
+ "D Themen u.a.:|* Corona-Pandemie in Deutschland: Verschärfter Lockdown bundesweit in Kraft|* Entmachtung des US-Präsidenten: Demokraten planen Schritte gegen Trump|* Wintereinbruch in Bosnien-Herzegowina: Dramatische Lage der Flüchtlinge an der Grenze zu Kroatien\n"
|
||||
+ "G 20 80\n" + "X 2 03 deu stereo\n" + "X 2 03 deu ohne Audiodeskription\n"
|
||||
+ "X 3 01 deu Teletext-Untertitel\n" + "X 3 20 deu mit DVB-Untertitel\n" + "X 5 0B deu HD-Video\n"
|
||||
+ "V 1610391600\n" + "e\n" + "c\n" + "End of EPG data";
|
||||
private final String epgParseError = "E 9999999999999999999999999";
|
||||
private final String epgCorruptDate = "C S19.2E-1-1201-28326 WDR HD Bielefeld\n" + "E 9886 2a10391600 900 4E F\n"
|
||||
+ "T Tagesschau\n"
|
||||
+ "D Themen u.a.:|* Corona-Pandemie in Deutschland: Verschärfter Lockdown bundesweit in Kraft|* Entmachtung des US-Präsidenten: Demokraten planen Schritte gegen Trump|* Wintereinbruch in Bosnien-Herzegowina: Dramatische Lage der Flüchtlinge an der Grenze zu Kroatien\n"
|
||||
+ "G 20 80\n" + "X 2 03 deu stereo\n" + "X 2 03 deu ohne Audiodeskription\n"
|
||||
+ "X 3 01 deu Teletext-Untertitel\n" + "X 3 20 deu mit DVB-Untertitel\n" + "X 5 0B deu HD-Video\n"
|
||||
+ "V 1610391600\n" + "e\n" + "c\n" + "End of EPG data";
|
||||
|
||||
private final String epgMissingEnd = "C S19.2E-1-1201-28326 WDR HD Bielefeld\n" + "E 9886 1610391600 900 4E F\n"
|
||||
+ "T Tagesschau\n"
|
||||
+ "D Themen u.a.:|* Corona-Pandemie in Deutschland: Verschärfter Lockdown bundesweit in Kraft|* Entmachtung des US-Präsidenten: Demokraten planen Schritte gegen Trump|* Wintereinbruch in Bosnien-Herzegowina: Dramatische Lage der Flüchtlinge an der Grenze zu Kroatien\n"
|
||||
+ "G 20 80\n" + "X 2 03 deu stereo\n" + "X 2 03 deu ohne Audiodeskription\n"
|
||||
+ "X 3 01 deu Teletext-Untertitel\n" + "X 3 20 deu mit DVB-Untertitel\n" + "X 5 0B deu HD-Video\n"
|
||||
+ "V 1610391600\n" + "e\n" + "c\n";
|
||||
|
||||
@Test
|
||||
public void testParseEpgEventComplete() throws SVDRPException, ParseException {
|
||||
SVDRPEpgEvent event = SVDRPEpgEvent.parse(epgResponseComplete);
|
||||
assertEquals("Tagesschau", event.getTitle());
|
||||
assertEquals("Aktuelle Nachrichten aus der Welt", event.getSubtitle());
|
||||
assertEquals(15, event.getDuration());
|
||||
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
|
||||
assertEquals(ZonedDateTime.parse("2021-01-11 19:00:00 UTC", dtf).toInstant(), event.getBegin());
|
||||
assertEquals(ZonedDateTime.parse("2021-01-11 19:15:00 UTC", dtf).toInstant(), event.getEnd());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseEpgEventMissingSubtitle() throws SVDRPException {
|
||||
SVDRPEpgEvent event = SVDRPEpgEvent.parse(epgMissingSubtitle);
|
||||
assertEquals("Tagesschau", event.getTitle());
|
||||
assertEquals("", event.getSubtitle());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseEpgEventCorruptDate() {
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPEpgEvent.parse(epgCorruptDate);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseEpgEventMissingEnd() throws SVDRPException, ParseException {
|
||||
SVDRPEpgEvent event = SVDRPEpgEvent.parse(epgMissingEnd);
|
||||
assertEquals("Tagesschau", event.getTitle());
|
||||
assertEquals("", event.getSubtitle());
|
||||
assertEquals(15, event.getDuration());
|
||||
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
|
||||
assertEquals(ZonedDateTime.parse("2021-01-11 19:00:00 UTC", dtf).toInstant(), event.getBegin());
|
||||
assertEquals(ZonedDateTime.parse("2021-01-11 19:15:00 UTC", dtf).toInstant(), event.getEnd());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExceptionVolumeData() {
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPEpgEvent.parse(epgParseError);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPTimerList;
|
||||
|
||||
/**
|
||||
* Specific unit tests to check if {@link SVDRPTimerList} parses SVDRP responses correctly
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPTimerListTest {
|
||||
private final String timerListResponseTimerActive = "1 1:1:2021-01-12:2013:2110:50:99:Charité (1/6)~Eiserne Lunge:Test\n"
|
||||
+ "2 9:1:2021-01-12:2058:2200:50:99:Charité (2/6)~Blutsauger:Test";
|
||||
private final String timerListResponseTimerNotActive = "1 1:1:2021-01-12:2013:2110:50:99:Charité (1/6)~Eiserne Lunge:Test\n"
|
||||
+ "2 1:1:2021-01-12:2058:2200:50:99:Charité (2/6)~Blutsauger:Test";
|
||||
|
||||
@Test
|
||||
public void testParseTimerList() throws SVDRPException {
|
||||
SVDRPTimerList list = SVDRPTimerList.parse(timerListResponseTimerActive);
|
||||
assertEquals(true, list.isRecordingActive());
|
||||
list = SVDRPTimerList.parse(timerListResponseTimerNotActive);
|
||||
assertEquals(false, list.isRecordingActive());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPParseResponseException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPVolume;
|
||||
|
||||
/**
|
||||
* Specific unit tests to check if {@link SVDRPVolume} parses SVDRP responses correctly
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPVolumeTest {
|
||||
private final String volumeResponseOk = "Audio volume is 255";
|
||||
private final String volumeResponseMute = "Audio is mute";
|
||||
private final String volumeResponseParseError1 = "Audiovolumeis255";
|
||||
private final String volumeResponseParseError2 = "Audio volume is 255x";
|
||||
|
||||
@Test
|
||||
public void testParseVolumeData() throws SVDRPException {
|
||||
SVDRPVolume volume = SVDRPVolume.parse(volumeResponseOk);
|
||||
assertEquals(100, volume.getVolume());
|
||||
volume = SVDRPVolume.parse(volumeResponseMute);
|
||||
assertEquals(0, volume.getVolume());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExceptionVolumeData() {
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPVolume.parse(volumeResponseParseError1);
|
||||
});
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPVolume.parse(volumeResponseParseError2);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.vdr.internal;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPParseResponseException;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPVolume;
|
||||
import org.openhab.binding.vdr.internal.svdrp.SVDRPWelcome;
|
||||
|
||||
/**
|
||||
* Specific unit tests to check if {@link SVDRPWelcome} parses SVDRP responses correctly
|
||||
*
|
||||
* @author Matthias Klocke - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class SVDRPWelcomeTest {
|
||||
private final String welcomeResponseOk = "srv SVDRP VideoDiskRecorder 2.5.1; Mon Jan 11 19:46:54 2021; UTF-8";
|
||||
private final String welcomeResponseParseError1 = "srv SVDRP VideoDiskRecorder 2.5.1; Mon Jan 11 19:46:54 2021 UTF-8";
|
||||
private final String welcomeResponseParseError2 = "srv SVDRP VideoDiskRecorder2.5.1; Mon Jan 11 19:46:54 2021 UTF-8";
|
||||
|
||||
@Test
|
||||
public void testParseWelcomeData() throws SVDRPException {
|
||||
SVDRPWelcome welcome = SVDRPWelcome.parse(welcomeResponseOk);
|
||||
assertEquals("UTF-8", welcome.getCharset());
|
||||
assertEquals("2.5.1", welcome.getVersion());
|
||||
assertEquals("Mon Jan 11 19:46:54 2021", welcome.getDateAndTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseExceptionVolumeData() {
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPVolume.parse(welcomeResponseParseError1);
|
||||
});
|
||||
assertThrows(SVDRPParseResponseException.class, () -> {
|
||||
SVDRPVolume.parse(welcomeResponseParseError2);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user