added migrated 2.x add-ons
Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<features name="org.openhab.binding.irtrans-${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-irtrans" description="IRTrans Binding" version="${project.version}">
|
||||
<feature>openhab-runtime-base</feature>
|
||||
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.irtrans/${project.version}</bundle>
|
||||
</feature>
|
||||
</features>
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.irtrans.internal;
|
||||
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link IRtransBindingConstants} contains constants used by the IRtrans
|
||||
* handler classes
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*
|
||||
**/
|
||||
public class IRtransBindingConstants {
|
||||
|
||||
public static final String BINDING_ID = "irtrans";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_ETHERNET_BRIDGE = new ThingTypeUID(BINDING_ID, "ethernet");
|
||||
public static final ThingTypeUID THING_TYPE_BLASTER = new ThingTypeUID(BINDING_ID, "blaster");
|
||||
|
||||
// List of all Channel ids
|
||||
public static final String CHANNEL_IO = "io";
|
||||
|
||||
// List of all Channel types
|
||||
public static final String BLASTER_CHANNEL_TYPE = "blaster";
|
||||
public static final String RECEIVER_CHANNEL_TYPE = "receiver";
|
||||
|
||||
// List of possible leds on a IRtrans transceiver
|
||||
public enum Led {
|
||||
DEFAULT("D"),
|
||||
INTERNAL("I"),
|
||||
EXTERNAL("E"),
|
||||
ALL("B"),
|
||||
ONE("1"),
|
||||
TWO("2"),
|
||||
THREE("3"),
|
||||
FOUR("4"),
|
||||
FIVE("5"),
|
||||
SIX("6"),
|
||||
SEVEN("7"),
|
||||
EIGHT("8");
|
||||
|
||||
private final String text;
|
||||
|
||||
private Led(final String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public static Led get(String valueSelectorText) throws IllegalArgumentException {
|
||||
for (Led c : Led.values()) {
|
||||
if (c.text.equals(valueSelectorText)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Not valid value selector");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.irtrans.internal;
|
||||
|
||||
import static org.openhab.binding.irtrans.internal.IRtransBindingConstants.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.openhab.binding.irtrans.internal.handler.BlasterHandler;
|
||||
import org.openhab.binding.irtrans.internal.handler.EthernetBridgeHandler;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
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.Component;
|
||||
|
||||
/**
|
||||
* The {@link IRtransHandlerFactory} is responsible for creating things and
|
||||
* thing handlers.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*
|
||||
*/
|
||||
@Component(service = ThingHandlerFactory.class, configurationPid = "binding.irtrans")
|
||||
public class IRtransHandlerFactory extends BaseThingHandlerFactory {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_BLASTER, THING_TYPE_ETHERNET_BRIDGE).collect(Collectors.toSet()));
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thing createThing(ThingTypeUID thingTypeUID, Configuration configuration, ThingUID thingUID,
|
||||
ThingUID bridgeUID) {
|
||||
if (IRtransBindingConstants.THING_TYPE_ETHERNET_BRIDGE.equals(thingTypeUID)) {
|
||||
ThingUID ethernetBridgeUID = getEthernetBridgeThingUID(thingTypeUID, thingUID, configuration);
|
||||
return super.createThing(thingTypeUID, configuration, ethernetBridgeUID, null);
|
||||
}
|
||||
if (IRtransBindingConstants.THING_TYPE_BLASTER.equals(thingTypeUID)) {
|
||||
ThingUID blasterUID = getBlasterUID(thingTypeUID, thingUID, configuration, bridgeUID);
|
||||
return super.createThing(thingTypeUID, configuration, blasterUID, bridgeUID);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"The thing type " + thingTypeUID + " is not supported by the IRtrans binding.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThingHandler createHandler(Thing thing) {
|
||||
if (thing.getThingTypeUID().equals(IRtransBindingConstants.THING_TYPE_ETHERNET_BRIDGE)) {
|
||||
return new EthernetBridgeHandler((Bridge) thing);
|
||||
} else if (thing.getThingTypeUID().equals(IRtransBindingConstants.THING_TYPE_BLASTER)) {
|
||||
return new BlasterHandler(thing);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ThingUID getEthernetBridgeThingUID(ThingTypeUID thingTypeUID, ThingUID thingUID,
|
||||
Configuration configuration) {
|
||||
if (thingUID == null) {
|
||||
String ipAddress = (String) configuration.get(EthernetBridgeHandler.IP_ADDRESS);
|
||||
return new ThingUID(thingTypeUID, ipAddress);
|
||||
}
|
||||
return thingUID;
|
||||
}
|
||||
|
||||
private ThingUID getBlasterUID(ThingTypeUID thingTypeUID, ThingUID thingUID, Configuration configuration,
|
||||
ThingUID bridgeUID) {
|
||||
String ledId = (String) configuration.get(BlasterHandler.LED);
|
||||
|
||||
if (thingUID == null) {
|
||||
return new ThingUID(thingTypeUID, "Led" + ledId, bridgeUID.getId());
|
||||
}
|
||||
return thingUID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.irtrans.internal;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link IrCommand} is a structure to store and manipulate infrared command
|
||||
* in various formats
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class IrCommand {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(IrCommand.class);
|
||||
|
||||
/**
|
||||
*
|
||||
* Each infrared command is in essence a sequence of characters/pointers
|
||||
* that refer to pulse/pause timing pairs. So, in order to build an infrared
|
||||
* command one has to collate the pulse/pause timings as defined by the
|
||||
* sequence
|
||||
*
|
||||
* PulsePair is a small datastructure to capture each pulse/pair timing pair
|
||||
*
|
||||
*/
|
||||
private class PulsePair {
|
||||
public int Pulse;
|
||||
public int Pause;
|
||||
}
|
||||
|
||||
private String remote;
|
||||
private String command;
|
||||
private String sequence;
|
||||
private List<PulsePair> pulsePairs;
|
||||
private int numberOfRepeats;
|
||||
private int frequency;
|
||||
private int frameLength;
|
||||
private int pause;
|
||||
private boolean startBit;
|
||||
private boolean repeatStartBit;
|
||||
private boolean noTog;
|
||||
private boolean rc5;
|
||||
private boolean rc6;
|
||||
|
||||
public String getRemote() {
|
||||
return remote;
|
||||
}
|
||||
|
||||
public void setRemote(String remote) {
|
||||
this.remote = remote;
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public String getSequence() {
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public void setSequence(String sequence) {
|
||||
this.sequence = sequence;
|
||||
}
|
||||
|
||||
public List<PulsePair> getPulsePairs() {
|
||||
return pulsePairs;
|
||||
}
|
||||
|
||||
public void setPulsePairs(ArrayList<PulsePair> pulsePairs) {
|
||||
this.pulsePairs = pulsePairs;
|
||||
}
|
||||
|
||||
public int getNumberOfRepeats() {
|
||||
return numberOfRepeats;
|
||||
}
|
||||
|
||||
public void setNumberOfRepeats(int numberOfRepeats) {
|
||||
this.numberOfRepeats = numberOfRepeats;
|
||||
}
|
||||
|
||||
public int getFrequency() {
|
||||
return frequency;
|
||||
}
|
||||
|
||||
public void setFrequency(int frequency) {
|
||||
this.frequency = frequency;
|
||||
}
|
||||
|
||||
public int getFrameLength() {
|
||||
return frameLength;
|
||||
}
|
||||
|
||||
public void setFrameLength(int frameLength) {
|
||||
this.frameLength = frameLength;
|
||||
}
|
||||
|
||||
public int getPause() {
|
||||
return pause;
|
||||
}
|
||||
|
||||
public void setPause(int pause) {
|
||||
this.pause = pause;
|
||||
}
|
||||
|
||||
public boolean isStartBit() {
|
||||
return startBit;
|
||||
}
|
||||
|
||||
public void setStartBit(boolean startBit) {
|
||||
this.startBit = startBit;
|
||||
}
|
||||
|
||||
public boolean isRepeatStartBit() {
|
||||
return repeatStartBit;
|
||||
}
|
||||
|
||||
public void setRepeatStartBit(boolean repeatStartBit) {
|
||||
this.repeatStartBit = repeatStartBit;
|
||||
}
|
||||
|
||||
public boolean isNoTog() {
|
||||
return noTog;
|
||||
}
|
||||
|
||||
public void setNoTog(boolean noTog) {
|
||||
this.noTog = noTog;
|
||||
}
|
||||
|
||||
public boolean isRc5() {
|
||||
return rc5;
|
||||
}
|
||||
|
||||
public void setRc5(boolean rc5) {
|
||||
this.rc5 = rc5;
|
||||
}
|
||||
|
||||
public boolean isRc6() {
|
||||
return rc6;
|
||||
}
|
||||
|
||||
public void setRc6(boolean rc6) {
|
||||
this.rc6 = rc6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches two IrCommands Commands match if they have the same remote and
|
||||
* the same command
|
||||
*
|
||||
* @param anotherCommand the another command
|
||||
* @return true, if successful
|
||||
*/
|
||||
public boolean matches(IrCommand anotherCommand) {
|
||||
return (matchRemote(anotherCommand) && matchCommand(anotherCommand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Match remote fields of two IrCommands In everything we do in the IRtrans
|
||||
* binding, the "*" stands for a wilcard character and will match anything
|
||||
*
|
||||
* @param S the infrared command
|
||||
* @return true, if successful
|
||||
*/
|
||||
private boolean matchRemote(IrCommand S) {
|
||||
if ("*".equals(getRemote()) || "*".equals(S.getRemote())) {
|
||||
return true;
|
||||
} else {
|
||||
return S.getRemote().equals(getRemote());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match command fields of two IrCommands
|
||||
*
|
||||
* @param S the infrared command
|
||||
* @return true, if successful
|
||||
*/
|
||||
private boolean matchCommand(IrCommand S) {
|
||||
if ("*".equals(command) || "*".equals(S.command)) {
|
||||
return true;
|
||||
} else {
|
||||
return S.command.equals(command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert/Parse the IRCommand into a ByteBuffer that is compatible with the
|
||||
* IRTrans devices
|
||||
*
|
||||
* @return the byte buffer
|
||||
*/
|
||||
public ByteBuffer toByteBuffer() {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(44 + 210 + 1);
|
||||
|
||||
// skip first byte for length - we will fill it in at the end
|
||||
byteBuffer.position(1);
|
||||
|
||||
// Checksum - 1 byte - not used in the ethernet version of the device
|
||||
byteBuffer.put((byte) 0);
|
||||
|
||||
// Command - 1 byte - not used
|
||||
byteBuffer.put((byte) 0);
|
||||
|
||||
// Address - 1 byte - not used
|
||||
byteBuffer.put((byte) 0);
|
||||
|
||||
// Mask - 2 bytes - not used
|
||||
byteBuffer.putShort((short) 0);
|
||||
|
||||
// Number of pulse pairs - 1 byte
|
||||
byte[] byteSequence = sequence.getBytes(StandardCharsets.US_ASCII);
|
||||
byteBuffer.put((byte) (byteSequence.length));
|
||||
|
||||
// Frequency - 1 byte
|
||||
byteBuffer.put((byte) frequency);
|
||||
|
||||
// Mode / Flags - 1 byte
|
||||
byte modeFlags = 0;
|
||||
if (startBit) {
|
||||
modeFlags = (byte) (modeFlags | 1);
|
||||
}
|
||||
if (repeatStartBit) {
|
||||
modeFlags = (byte) (modeFlags | 2);
|
||||
}
|
||||
if (rc5) {
|
||||
modeFlags = (byte) (modeFlags | 4);
|
||||
}
|
||||
if (rc6) {
|
||||
modeFlags = (byte) (modeFlags | 8);
|
||||
}
|
||||
byteBuffer.put(modeFlags);
|
||||
|
||||
// Pause timings - 8 Shorts = 16 bytes
|
||||
for (int i = 0; i < pulsePairs.size(); i++) {
|
||||
byteBuffer.putShort((short) Math.round(pulsePairs.get(i).Pause / 8));
|
||||
}
|
||||
for (int i = pulsePairs.size(); i <= 7; i++) {
|
||||
byteBuffer.putShort((short) 0);
|
||||
}
|
||||
|
||||
// Pulse timings - 8 Shorts = 16 bytes
|
||||
for (int i = 0; i < pulsePairs.size(); i++) {
|
||||
byteBuffer.putShort((short) Math.round(pulsePairs.get(i).Pulse / 8));
|
||||
}
|
||||
for (int i = pulsePairs.size(); i <= 7; i++) {
|
||||
byteBuffer.putShort((short) 0);
|
||||
}
|
||||
|
||||
// Time Counts - 1 Byte
|
||||
byteBuffer.put((byte) pulsePairs.size());
|
||||
|
||||
// Repeats - 1 Byte
|
||||
byte repeat = (byte) 0;
|
||||
repeat = (byte) numberOfRepeats;
|
||||
if (frameLength > 0) {
|
||||
repeat = (byte) (repeat | 128);
|
||||
}
|
||||
byteBuffer.put(repeat);
|
||||
|
||||
// Repeat Pause or Frame Length - 1 byte
|
||||
if ((repeat & 128) == 128) {
|
||||
byteBuffer.put((byte) frameLength);
|
||||
} else {
|
||||
byteBuffer.put((byte) pause);
|
||||
}
|
||||
|
||||
// IR pulse sequence
|
||||
try {
|
||||
byteBuffer.put(sequence.getBytes("ASCII"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.warn("An exception occurred while encoding the sequence : '{}'", e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Add <CR> (ASCII 13) at the end of the sequence
|
||||
byteBuffer.put((byte) ((char) 13));
|
||||
|
||||
// set the length of the byte sequence
|
||||
byteBuffer.flip();
|
||||
byteBuffer.position(0);
|
||||
byteBuffer.put((byte) (byteBuffer.limit() - 1));
|
||||
byteBuffer.position(0);
|
||||
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the the infrared command to a Hexadecimal notation/string that
|
||||
* can be interpreted by the IRTrans device
|
||||
*
|
||||
* Convert the first 44 bytes to hex notation, then copy the remainder (= IR
|
||||
* command piece) as ASCII string
|
||||
*
|
||||
* @return the byte buffer in Hex format
|
||||
*/
|
||||
public ByteBuffer toHEXByteBuffer() {
|
||||
byte hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
ByteBuffer byteBuffer = toByteBuffer();
|
||||
byte[] toConvert = new byte[byteBuffer.limit()];
|
||||
byteBuffer.get(toConvert, 0, byteBuffer.limit());
|
||||
|
||||
byte[] converted = new byte[toConvert.length * 2];
|
||||
|
||||
for (int k = 0; k < toConvert.length - 1; k++) {
|
||||
converted[2 * k] = hexDigit[(toConvert[k] >> 4) & 0x0f];
|
||||
converted[2 * k + 1] = hexDigit[toConvert[k] & 0x0f];
|
||||
|
||||
}
|
||||
|
||||
ByteBuffer convertedBuffer = ByteBuffer.allocate(converted.length);
|
||||
convertedBuffer.put(converted);
|
||||
convertedBuffer.flip();
|
||||
|
||||
return convertedBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert 'sequence' bit of the IRTrans compatible byte buffer to a
|
||||
* Hexidecimal string
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String sequenceToHEXString() {
|
||||
byte[] byteArray = toHEXByteArray();
|
||||
return new String(byteArray, 88, byteArray.length - 88 - 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the IRTrans compatible byte buffer to a string
|
||||
*
|
||||
* @return the string
|
||||
*/
|
||||
public String toHEXString() {
|
||||
return new String(toHEXByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the IRTrans compatible byte buffer to a byte array.
|
||||
*
|
||||
* @return the byte[]
|
||||
*/
|
||||
public byte[] toHEXByteArray() {
|
||||
return toHEXByteBuffer().array();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.irtrans.internal.handler;
|
||||
|
||||
import static org.openhab.binding.irtrans.internal.IRtransBindingConstants.CHANNEL_IO;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.openhab.binding.irtrans.internal.IRtransBindingConstants.Led;
|
||||
import org.openhab.binding.irtrans.internal.IrCommand;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.binding.BaseThingHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link BlasterHandler} is responsible for handling commands, which are
|
||||
* sent to one of the channels.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class BlasterHandler extends BaseThingHandler implements TransceiverStatusListener {
|
||||
|
||||
// List of Configuration constants
|
||||
public static final String COMMAND = "command";
|
||||
public static final String LED = "led";
|
||||
public static final String REMOTE = "remote";
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(BlasterHandler.class);
|
||||
|
||||
public BlasterHandler(Thing thing) {
|
||||
super(thing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
((EthernetBridgeHandler) getBridge().getHandler()).registerTransceiverStatusListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleRemoval() {
|
||||
((EthernetBridgeHandler) getBridge().getHandler()).unregisterTransceiverStatusListener(this);
|
||||
updateStatus(ThingStatus.REMOVED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
EthernetBridgeHandler ethernetBridge = (EthernetBridgeHandler) getBridge().getHandler();
|
||||
|
||||
if (ethernetBridge == null) {
|
||||
logger.warn("IRtrans Ethernet bridge handler not found. Cannot handle command without bridge.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(command instanceof RefreshType)) {
|
||||
if (channelUID.getId().equals(CHANNEL_IO)) {
|
||||
if (command instanceof StringType) {
|
||||
String remoteName = StringUtils.substringBefore(command.toString(), ",");
|
||||
String irCommandName = StringUtils.substringAfter(command.toString(), ",");
|
||||
|
||||
IrCommand ircommand = new IrCommand();
|
||||
ircommand.setRemote(remoteName);
|
||||
ircommand.setCommand(irCommandName);
|
||||
|
||||
IrCommand thingCompatibleCommand = new IrCommand();
|
||||
thingCompatibleCommand.setRemote((String) getConfig().get(REMOTE));
|
||||
thingCompatibleCommand.setCommand((String) getConfig().get(COMMAND));
|
||||
|
||||
if (ircommand.matches(thingCompatibleCommand)) {
|
||||
if (!ethernetBridge.sendIRcommand(ircommand, Led.get((String) getConfig().get(LED)))) {
|
||||
logger.warn("An error occured whilst sending the infrared command '{}' for Channel '{}'",
|
||||
ircommand, channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandReceived(EthernetBridgeHandler bridge, IrCommand command) {
|
||||
logger.debug("Received command {},{} for thing {}", command.getRemote(), command.getCommand(),
|
||||
this.getThing().getUID());
|
||||
|
||||
IrCommand thingCompatibleCommand = new IrCommand();
|
||||
thingCompatibleCommand.setRemote((String) getConfig().get(REMOTE));
|
||||
thingCompatibleCommand.setCommand((String) getConfig().get(COMMAND));
|
||||
|
||||
if (command.matches(thingCompatibleCommand)) {
|
||||
StringType stringType = new StringType(command.getRemote() + "," + command.getCommand());
|
||||
updateState(CHANNEL_IO, stringType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,948 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.irtrans.internal.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.nio.channels.NoConnectionPendingException;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.openhab.binding.irtrans.internal.IRtransBindingConstants;
|
||||
import org.openhab.binding.irtrans.internal.IRtransBindingConstants.Led;
|
||||
import org.openhab.binding.irtrans.internal.IrCommand;
|
||||
import org.openhab.core.config.core.Configuration;
|
||||
import org.openhab.core.library.types.StringType;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.openhab.core.thing.ThingStatusDetail;
|
||||
import org.openhab.core.thing.binding.BaseBridgeHandler;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openhab.core.types.RefreshType;
|
||||
import org.openhab.core.util.HexUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link EthernetBridgeHandler} is responsible for handling commands, which
|
||||
* are sent to one of the channels.
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*
|
||||
*/
|
||||
public class EthernetBridgeHandler extends BaseBridgeHandler implements TransceiverStatusListener {
|
||||
|
||||
// List of Configuration constants
|
||||
public static final String BUFFER_SIZE = "bufferSize";
|
||||
public static final String IP_ADDRESS = "ipAddress";
|
||||
public static final String IS_LISTENER = "isListener";
|
||||
public static final String FIRMWARE_VERSION = "firmwareVersion";
|
||||
public static final String LISTENER_PORT = "listenerPort";
|
||||
public static final String MODE = "mode";
|
||||
public static final String PING_TIME_OUT = "pingTimeOut";
|
||||
public static final String PORT_NUMBER = "portNumber";
|
||||
public static final String RECONNECT_INTERVAL = "reconnectInterval";
|
||||
public static final String REFRESH_INTERVAL = "refreshInterval";
|
||||
public static final String RESPONSE_TIME_OUT = "responseTimeOut";
|
||||
public static final String COMMAND = "command";
|
||||
public static final String LED = "led";
|
||||
public static final String REMOTE = "remote";
|
||||
public static final int LISTENING_INTERVAL = 100;
|
||||
|
||||
private static final Pattern RESPONSE_PATTERN = Pattern.compile("..(\\d{5}) (.*)", Pattern.DOTALL);
|
||||
private static final Pattern HEX_PATTERN = Pattern.compile("RCV_HEX (.*)");
|
||||
private static final Pattern IRDB_PATTERN = Pattern.compile("RCV_COM (.*),(.*),(.*),(.*)");
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(EthernetBridgeHandler.class);
|
||||
|
||||
private Selector selector;
|
||||
private Thread pollingThread;
|
||||
private SocketChannel socketChannel;
|
||||
protected SelectionKey socketChannelKey;
|
||||
protected ServerSocketChannel listenerChannel;
|
||||
protected SelectionKey listenerKey;
|
||||
protected boolean previousConnectionState;
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
private List<TransceiverStatusListener> transceiverStatusListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* Data structure to store the infrared commands that are 'loaded' from the
|
||||
* configuration files. Command loading from pre-defined configuration files is not supported
|
||||
* (anymore), but the code is maintained in case this functionality is re-added in the future
|
||||
**/
|
||||
protected final Collection<IrCommand> irCommands = new HashSet<>();
|
||||
|
||||
public EthernetBridgeHandler(Bridge bridge) {
|
||||
super(bridge);
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
// register ourselves as a Transceiver Status Listener
|
||||
registerTransceiverStatusListener(this);
|
||||
|
||||
try {
|
||||
selector = Selector.open();
|
||||
} catch (IOException e) {
|
||||
logger.debug("An exception occurred while registering the selector: '{}'", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.HANDLER_INITIALIZING_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
if (selector != null) {
|
||||
if (getConfig().get(IP_ADDRESS) != null && getConfig().get(PORT_NUMBER) != null) {
|
||||
if (pollingThread == null) {
|
||||
pollingThread = new Thread(pollingRunnable, "ESH-IRtrans-Polling " + getThing().getUID());
|
||||
pollingThread.start();
|
||||
}
|
||||
} else {
|
||||
logger.debug("Cannot connect to IRtrans Ethernet device. IP address or port number not set.");
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
|
||||
"IP address or port number not set.");
|
||||
}
|
||||
|
||||
if (getConfig().get(IS_LISTENER) != null) {
|
||||
configureListener((String) getConfig().get(LISTENER_PORT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
unregisterTransceiverStatusListener(this);
|
||||
|
||||
try {
|
||||
if (socketChannel != null) {
|
||||
socketChannel.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("An exception occurred while closing the channel '{}': {}", socketChannel, e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (listenerChannel != null) {
|
||||
listenerChannel.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("An exception occurred while closing the channel '{}': {}", listenerChannel, e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (selector != null) {
|
||||
selector.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("An exception occurred while closing the selector: '{}'", e.getMessage());
|
||||
}
|
||||
|
||||
logger.debug("Stopping the IRtrans polling Thread for {}", getThing().getUID());
|
||||
if (pollingThread != null) {
|
||||
pollingThread.interrupt();
|
||||
try {
|
||||
pollingThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
pollingThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean registerTransceiverStatusListener(@NonNull TransceiverStatusListener transceiverStatusListener) {
|
||||
return transceiverStatusListeners.add(transceiverStatusListener);
|
||||
}
|
||||
|
||||
public boolean unregisterTransceiverStatusListener(@NonNull TransceiverStatusListener transceiverStatusListener) {
|
||||
return transceiverStatusListeners.remove(transceiverStatusListener);
|
||||
}
|
||||
|
||||
public void onConnectionLost() {
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
|
||||
|
||||
try {
|
||||
if (socketChannel != null) {
|
||||
socketChannel.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("An exception occurred while closing the channel '{}': {}", socketChannel, e.getMessage());
|
||||
}
|
||||
|
||||
establishConnection();
|
||||
}
|
||||
|
||||
public void onConnectionResumed() {
|
||||
configureTransceiver();
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
|
||||
private void establishConnection() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (getConfig().get(IP_ADDRESS) != null && getConfig().get(PORT_NUMBER) != null) {
|
||||
try {
|
||||
socketChannel = SocketChannel.open();
|
||||
socketChannel.socket().setKeepAlive(true);
|
||||
socketChannel.configureBlocking(false);
|
||||
|
||||
synchronized (selector) {
|
||||
selector.wakeup();
|
||||
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT;
|
||||
socketChannelKey = socketChannel.register(selector, interestSet);
|
||||
}
|
||||
|
||||
InetSocketAddress remoteAddress = new InetSocketAddress((String) getConfig().get(IP_ADDRESS),
|
||||
((BigDecimal) getConfig().get(PORT_NUMBER)).intValue());
|
||||
socketChannel.connect(remoteAddress);
|
||||
} catch (IOException e) {
|
||||
logger.debug("An exception occurred while connecting to '{}:{}' : {}", getConfig().get(IP_ADDRESS),
|
||||
((BigDecimal) getConfig().get(PORT_NUMBER)).intValue(), e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(((BigDecimal) getConfig().get(RESPONSE_TIME_OUT)).intValue());
|
||||
} catch (NumberFormatException | InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.debug("An exception occurred while putting a thread to sleep: '{}'", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
onConnectable();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCommandReceived(EthernetBridgeHandler bridge, IrCommand command) {
|
||||
logger.debug("Received infrared command '{},{}' for thing '{}'", command.getRemote(), command.getCommand(),
|
||||
this.getThing().getUID());
|
||||
|
||||
for (Channel channel : getThing().getChannels()) {
|
||||
Configuration channelConfiguration = channel.getConfiguration();
|
||||
|
||||
if (channel.getChannelTypeUID() != null
|
||||
&& channel.getChannelTypeUID().getId().equals(IRtransBindingConstants.RECEIVER_CHANNEL_TYPE)) {
|
||||
IrCommand thingCompatibleCommand = new IrCommand();
|
||||
thingCompatibleCommand.setRemote((String) channelConfiguration.get(REMOTE));
|
||||
thingCompatibleCommand.setCommand((String) channelConfiguration.get(COMMAND));
|
||||
|
||||
if (command.matches(thingCompatibleCommand)) {
|
||||
StringType stringType = new StringType(command.getRemote() + "," + command.getCommand());
|
||||
logger.debug("Received a matching infrared command '{}' for channel '{}'", stringType,
|
||||
channel.getUID());
|
||||
updateState(channel.getUID(), stringType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
if (!(command instanceof RefreshType)) {
|
||||
Channel channel = this.getThing().getChannel(channelUID.getId());
|
||||
if (channel != null) {
|
||||
Configuration channelConfiguration = channel.getConfiguration();
|
||||
if (channel.getChannelTypeUID() != null
|
||||
&& channel.getChannelTypeUID().getId().equals(IRtransBindingConstants.BLASTER_CHANNEL_TYPE)) {
|
||||
if (command instanceof StringType) {
|
||||
String remoteName = StringUtils.substringBefore(command.toString(), ",");
|
||||
String irCommandName = StringUtils.substringAfter(command.toString(), ",");
|
||||
|
||||
IrCommand ircommand = new IrCommand();
|
||||
ircommand.setRemote(remoteName);
|
||||
ircommand.setCommand(irCommandName);
|
||||
|
||||
IrCommand thingCompatibleCommand = new IrCommand();
|
||||
thingCompatibleCommand.setRemote((String) channelConfiguration.get(REMOTE));
|
||||
thingCompatibleCommand.setCommand((String) channelConfiguration.get(COMMAND));
|
||||
|
||||
if (ircommand.matches(thingCompatibleCommand)) {
|
||||
if (sendIRcommand(ircommand, Led.get((String) channelConfiguration.get(LED)))) {
|
||||
logger.debug("Sent a matching infrared command '{}' for channel '{}'", command,
|
||||
channelUID);
|
||||
} else {
|
||||
logger.warn(
|
||||
"An error occured whilst sending the infrared command '{}' for Channel '{}'",
|
||||
command, channelUID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (channel.getAcceptedItemType() != null
|
||||
&& channel.getAcceptedItemType().equals(IRtransBindingConstants.RECEIVER_CHANNEL_TYPE)) {
|
||||
logger.warn("Receivers can only receive infrared commands, not send them");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void configureListener(String listenerPort) {
|
||||
try {
|
||||
listenerChannel = ServerSocketChannel.open();
|
||||
listenerChannel.socket().bind(new InetSocketAddress(Integer.parseInt(listenerPort)));
|
||||
listenerChannel.configureBlocking(false);
|
||||
|
||||
logger.info("Listening for incoming connections on {}", listenerChannel.getLocalAddress());
|
||||
|
||||
synchronized (selector) {
|
||||
selector.wakeup();
|
||||
try {
|
||||
listenerKey = listenerChannel.register(selector, SelectionKey.OP_ACCEPT);
|
||||
} catch (ClosedChannelException e1) {
|
||||
logger.debug("An exception occurred while registering a selector: '{}'", e1.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e1.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (IOException e3) {
|
||||
logger.error(
|
||||
"An exception occurred while creating configuring the listener channel on port number {}: '{}'",
|
||||
Integer.parseInt(listenerPort), e3.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
protected void configureTransceiver() {
|
||||
lock.lock();
|
||||
try {
|
||||
String putInASCIImode = "ASCI";
|
||||
ByteBuffer response = sendCommand(putInASCIImode);
|
||||
|
||||
String getFirmwareVersion = "Aver" + (char) 13;
|
||||
response = sendCommand(getFirmwareVersion);
|
||||
|
||||
if (response != null) {
|
||||
String message = stripByteCount(response).split("\0")[0];
|
||||
if (message != null) {
|
||||
if (message.contains("VERSION")) {
|
||||
logger.info("'{}' matches an IRtrans device with firmware {}", getThing().getUID(), message);
|
||||
getConfig().put(FIRMWARE_VERSION, message);
|
||||
} else {
|
||||
logger.debug("Received some non-compliant garbage ({})", message);
|
||||
onConnectionLost();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
logger.debug("Did not receive an answer from the IRtrans transceiver '{}' - Parsing is skipped",
|
||||
socketChannel.getRemoteAddress());
|
||||
onConnectionLost();
|
||||
} catch (IOException e1) {
|
||||
logger.debug("An exception occurred while getting a remote address: '{}'", e1.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e1.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
int numberOfRemotes = 0;
|
||||
int numberOfRemotesProcessed = 0;
|
||||
int numberOfRemotesInBatch = 0;
|
||||
String[] remoteList = getRemoteList(0);
|
||||
|
||||
if (remoteList.length > 0) {
|
||||
logger.debug("The IRtrans device for '{}' supports '{}' remotes", getThing().getUID(), remoteList[1]);
|
||||
numberOfRemotes = Integer.valueOf(remoteList[1]);
|
||||
numberOfRemotesInBatch = Integer.valueOf(remoteList[2]);
|
||||
}
|
||||
|
||||
while (numberOfRemotesProcessed < numberOfRemotes) {
|
||||
for (int i = 1; i <= numberOfRemotesInBatch; i++) {
|
||||
String remote = remoteList[2 + i];
|
||||
|
||||
// get remote commands
|
||||
String[] commands = getRemoteCommands(remote, 0);
|
||||
StringBuilder result = new StringBuilder();
|
||||
int numberOfCommands = 0;
|
||||
int numberOfCommandsInBatch = 0;
|
||||
int numberOfCommandsProcessed = 0;
|
||||
|
||||
if (commands.length > 0) {
|
||||
numberOfCommands = Integer.valueOf(commands[1]);
|
||||
numberOfCommandsInBatch = Integer.valueOf(commands[2]);
|
||||
numberOfCommandsProcessed = 0;
|
||||
}
|
||||
|
||||
while (numberOfCommandsProcessed < numberOfCommands) {
|
||||
for (int j = 1; j <= numberOfCommandsInBatch; j++) {
|
||||
String command = commands[2 + j];
|
||||
result.append(command);
|
||||
numberOfCommandsProcessed++;
|
||||
if (numberOfCommandsProcessed < numberOfCommands) {
|
||||
result.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
if (numberOfCommandsProcessed < numberOfCommands) {
|
||||
commands = getRemoteCommands(remote, numberOfCommandsProcessed);
|
||||
if (commands.length == 0) {
|
||||
break;
|
||||
}
|
||||
numberOfCommandsInBatch = Integer.valueOf(commands[2]);
|
||||
} else {
|
||||
numberOfCommandsInBatch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("The remote '{}' on '{}' supports '{}' commands: {}", remote, getThing().getUID(),
|
||||
numberOfCommands, result.toString());
|
||||
|
||||
numberOfRemotesProcessed++;
|
||||
}
|
||||
|
||||
if (numberOfRemotesProcessed < numberOfRemotes) {
|
||||
remoteList = getRemoteList(numberOfRemotesProcessed);
|
||||
if (remoteList.length == 0) {
|
||||
break;
|
||||
}
|
||||
numberOfRemotesInBatch = Integer.valueOf(remoteList[2]);
|
||||
} else {
|
||||
numberOfRemotesInBatch = 0;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getRemoteCommands(String remote, int index) {
|
||||
String getCommands = "Agetcommands " + remote + "," + index + (char) 13;
|
||||
ByteBuffer response = sendCommand(getCommands);
|
||||
String[] commandList = new String[0];
|
||||
|
||||
if (response != null) {
|
||||
String message = stripByteCount(response).split("\0")[0];
|
||||
logger.trace("commands returned {}", message);
|
||||
if (message != null) {
|
||||
if (message.contains("COMMANDLIST")) {
|
||||
commandList = message.split(",");
|
||||
} else {
|
||||
logger.debug("Received some non-compliant command ({})", message);
|
||||
onConnectionLost();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Did not receive an answer from the IRtrans transceiver for '{}' - Parsing is skipped",
|
||||
getThing().getUID());
|
||||
onConnectionLost();
|
||||
}
|
||||
|
||||
return commandList;
|
||||
}
|
||||
|
||||
private String[] getRemoteList(int index) {
|
||||
String getRemotes = "Agetremotes " + index + (char) 13;
|
||||
ByteBuffer response = sendCommand(getRemotes);
|
||||
String[] remoteList = new String[0];
|
||||
|
||||
if (response != null) {
|
||||
String message = stripByteCount(response).split("\0")[0];
|
||||
logger.trace("remotes returned {}", message);
|
||||
if (message != null) {
|
||||
if (message.contains("REMOTELIST")) {
|
||||
remoteList = message.split(",");
|
||||
} else {
|
||||
logger.debug("Received some non-compliant command ({})", message);
|
||||
onConnectionLost();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.debug("Did not receive an answer from the IRtrans transceiver for '{}' - Parsing is skipped",
|
||||
getThing().getUID());
|
||||
onConnectionLost();
|
||||
}
|
||||
|
||||
return remoteList;
|
||||
}
|
||||
|
||||
public boolean sendIRcommand(IrCommand command, Led led) {
|
||||
// construct the string we need to send to the IRtrans device
|
||||
String output = packIRDBCommand(led, command);
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
ByteBuffer response = sendCommand(output);
|
||||
|
||||
if (response != null) {
|
||||
String message = stripByteCount(response).split("\0")[0];
|
||||
if (message != null && message.contains("RESULT OK")) {
|
||||
return true;
|
||||
} else {
|
||||
logger.debug("Received an unexpected response from the IRtrans transceiver: '{}'", message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private ByteBuffer sendCommand(String command) {
|
||||
if (command != null) {
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(command.getBytes().length);
|
||||
try {
|
||||
byteBuffer.put(command.getBytes("ASCII"));
|
||||
onWritable(byteBuffer);
|
||||
Thread.sleep(((BigDecimal) getConfig().get(RESPONSE_TIME_OUT)).intValue());
|
||||
return onReadable(((BigDecimal) getConfig().get(BUFFER_SIZE)).intValue(), true);
|
||||
} catch (UnsupportedEncodingException | NumberFormatException | InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.debug("An exception occurred while sending a command to the IRtrans transceiver for '{}': {}",
|
||||
getThing().getUID(), e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Runnable pollingRunnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
if (socketChannel == null) {
|
||||
previousConnectionState = false;
|
||||
onConnectionLost();
|
||||
} else {
|
||||
if (!previousConnectionState && socketChannel.isConnected()) {
|
||||
previousConnectionState = true;
|
||||
onConnectionResumed();
|
||||
}
|
||||
|
||||
if (previousConnectionState && !socketChannel.isConnected()
|
||||
&& !socketChannel.isConnectionPending()) {
|
||||
previousConnectionState = false;
|
||||
onConnectionLost();
|
||||
}
|
||||
|
||||
if (!socketChannel.isConnectionPending() && !socketChannel.isConnected()) {
|
||||
previousConnectionState = false;
|
||||
logger.debug("Disconnecting '{}' because of a network error", getThing().getUID());
|
||||
socketChannel.close();
|
||||
Thread.sleep(1000 * ((BigDecimal) getConfig().get(RECONNECT_INTERVAL)).intValue());
|
||||
establishConnection();
|
||||
}
|
||||
|
||||
long stamp = System.currentTimeMillis();
|
||||
if (!InetAddress.getByName(((String) getConfig().get(IP_ADDRESS)))
|
||||
.isReachable(((BigDecimal) getConfig().get(PING_TIME_OUT)).intValue())) {
|
||||
logger.debug(
|
||||
"Ping timed out after '{}' milliseconds. Disconnecting '{}' because of a ping timeout",
|
||||
System.currentTimeMillis() - stamp, getThing().getUID());
|
||||
socketChannel.close();
|
||||
}
|
||||
|
||||
onConnectable();
|
||||
ByteBuffer buffer = onReadable(((BigDecimal) getConfig().get(BUFFER_SIZE)).intValue(), false);
|
||||
if (buffer != null && buffer.remaining() > 0) {
|
||||
onRead(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
onAcceptable();
|
||||
|
||||
if (!Thread.currentThread().isInterrupted()) {
|
||||
Thread.sleep(LISTENING_INTERVAL);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.trace("An exception occurred while polling the transceiver : '{}'", e.getMessage(), e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
protected void onAcceptable() {
|
||||
lock.lock();
|
||||
try {
|
||||
try {
|
||||
selector.selectNow();
|
||||
} catch (IOException e) {
|
||||
logger.debug("An exception occurred while selecting: {}", e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
|
||||
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
|
||||
while (it.hasNext()) {
|
||||
SelectionKey selKey = it.next();
|
||||
it.remove();
|
||||
if (selKey.isValid()) {
|
||||
if (selKey.isAcceptable() && selKey == listenerKey) {
|
||||
try {
|
||||
SocketChannel newChannel = listenerChannel.accept();
|
||||
newChannel.configureBlocking(false);
|
||||
logger.trace("Received a connection request from '{}'", newChannel.getRemoteAddress());
|
||||
|
||||
synchronized (selector) {
|
||||
selector.wakeup();
|
||||
newChannel.register(selector, newChannel.validOps());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.debug("An exception occurred while accepting a connection on channel '{}': {}",
|
||||
listenerChannel, e.getMessage());
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onConnectable() {
|
||||
lock.lock();
|
||||
SocketChannel aSocketChannel = null;
|
||||
try {
|
||||
synchronized (selector) {
|
||||
selector.selectNow();
|
||||
}
|
||||
|
||||
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
|
||||
while (it.hasNext()) {
|
||||
SelectionKey selKey = it.next();
|
||||
it.remove();
|
||||
if (selKey.isValid() && selKey.isConnectable()) {
|
||||
aSocketChannel = (SocketChannel) selKey.channel();
|
||||
aSocketChannel.finishConnect();
|
||||
logger.trace("The channel for '{}' is connected", aSocketChannel.getRemoteAddress());
|
||||
}
|
||||
}
|
||||
} catch (IOException | NoConnectionPendingException e) {
|
||||
if (aSocketChannel != null) {
|
||||
logger.debug("Disconnecting '{}' because of a socket error : '{}'", getThing().getUID(), e.getMessage(),
|
||||
e);
|
||||
try {
|
||||
aSocketChannel.close();
|
||||
} catch (IOException e1) {
|
||||
logger.debug("An exception occurred while closing the channel '{}': {}", socketChannel,
|
||||
e1.getMessage());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected ByteBuffer onReadable(int bufferSize, boolean isSelective) {
|
||||
lock.lock();
|
||||
try {
|
||||
synchronized (selector) {
|
||||
try {
|
||||
selector.selectNow();
|
||||
} catch (IOException e) {
|
||||
logger.error("An exception occurred while selecting: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
|
||||
while (it.hasNext()) {
|
||||
SelectionKey selKey = it.next();
|
||||
it.remove();
|
||||
if (selKey.isValid() && selKey.isReadable()) {
|
||||
SocketChannel aSocketChannel = (SocketChannel) selKey.channel();
|
||||
|
||||
if ((aSocketChannel.equals(socketChannel) && isSelective) || !isSelective) {
|
||||
ByteBuffer readBuffer = ByteBuffer.allocate(bufferSize);
|
||||
int numberBytesRead = 0;
|
||||
boolean error = false;
|
||||
|
||||
try {
|
||||
numberBytesRead = aSocketChannel.read(readBuffer);
|
||||
} catch (NotYetConnectedException e) {
|
||||
logger.warn("The channel '{}' is not yet connected: {}", aSocketChannel, e.getMessage());
|
||||
if (!aSocketChannel.isConnectionPending()) {
|
||||
error = true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// If some other I/O error occurs
|
||||
logger.warn("An IO exception occured on channel '{}': {}", aSocketChannel, e.getMessage());
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (numberBytesRead == -1) {
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
logger.debug("Disconnecting '{}' because of a socket error", getThing().getUID());
|
||||
try {
|
||||
aSocketChannel.close();
|
||||
} catch (IOException e1) {
|
||||
logger.debug("An exception occurred while closing the channel '{}': {}", socketChannel,
|
||||
e1.getMessage());
|
||||
}
|
||||
} else {
|
||||
readBuffer.flip();
|
||||
return readBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onWritable(ByteBuffer buffer) {
|
||||
lock.lock();
|
||||
try {
|
||||
synchronized (selector) {
|
||||
try {
|
||||
selector.selectNow();
|
||||
} catch (IOException e) {
|
||||
logger.error("An exception occurred while selecting: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
|
||||
while (it.hasNext()) {
|
||||
SelectionKey selKey = it.next();
|
||||
it.remove();
|
||||
if (selKey.isValid() && selKey.isWritable()) {
|
||||
SocketChannel aSocketChannel = (SocketChannel) selKey.channel();
|
||||
|
||||
if (aSocketChannel.equals(socketChannel)) {
|
||||
boolean error = false;
|
||||
|
||||
buffer.rewind();
|
||||
try {
|
||||
logger.trace("Sending '{}' on the channel '{}'->'{}'", new String(buffer.array()),
|
||||
aSocketChannel.getLocalAddress(), aSocketChannel.getRemoteAddress());
|
||||
aSocketChannel.write(buffer);
|
||||
} catch (NotYetConnectedException e) {
|
||||
logger.warn("The channel '{}' is not yet connected: {}", aSocketChannel, e.getMessage());
|
||||
if (!aSocketChannel.isConnectionPending()) {
|
||||
error = true;
|
||||
}
|
||||
} catch (ClosedChannelException e) {
|
||||
// If some other I/O error occurs
|
||||
logger.warn("The channel for '{}' is closed: {}", aSocketChannel, e.getMessage());
|
||||
error = true;
|
||||
} catch (IOException e) {
|
||||
// If some other I/O error occurs
|
||||
logger.warn("An IO exception occured on channel '{}': {}", aSocketChannel, e.getMessage());
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
try {
|
||||
aSocketChannel.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("An exception occurred while closing the channel '{}': {}", aSocketChannel,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onRead(ByteBuffer byteBuffer) {
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received bytebuffer : '{}'", HexUtils.bytesToHex(byteBuffer.array()));
|
||||
}
|
||||
int byteCount = getByteCount(byteBuffer);
|
||||
|
||||
while (byteCount > 0) {
|
||||
byte[] message = new byte[byteCount];
|
||||
byteBuffer.get(message, 0, byteCount);
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Received message : '{}'", HexUtils.bytesToHex(message));
|
||||
}
|
||||
|
||||
String strippedBuffer = stripByteCount(ByteBuffer.wrap(message));
|
||||
|
||||
if (strippedBuffer != null) {
|
||||
String strippedMessage = strippedBuffer.split("\0")[0];
|
||||
|
||||
// IRTrans devices return "RESULT OK" when it succeeds to emit an
|
||||
// infrared sequence
|
||||
if (strippedMessage.contains("RESULT OK")) {
|
||||
parseOKMessage(strippedMessage);
|
||||
}
|
||||
|
||||
// IRTrans devices return a string starting with RCV_HEX each time
|
||||
// it captures an infrared sequence from a remote control
|
||||
if (strippedMessage.contains("RCV_HEX")) {
|
||||
parseHexMessage(strippedMessage);
|
||||
}
|
||||
|
||||
// IRTrans devices return a string starting with RCV_COM each time
|
||||
// it captures an infrared sequence from a remote control that is stored in the device's internal dB
|
||||
if (strippedMessage.contains("RCV_COM")) {
|
||||
parseIRDBMessage(strippedMessage);
|
||||
}
|
||||
|
||||
byteCount = getByteCount(byteBuffer);
|
||||
} else {
|
||||
logger.warn("Received some non-compliant garbage '{}' - Parsing is skipped",
|
||||
new String(byteBuffer.array()));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("An exception occurred while reading bytebuffer '{}' : {}",
|
||||
HexUtils.bytesToHex(byteBuffer.array()), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getByteCount(ByteBuffer byteBuffer) {
|
||||
String response = new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
|
||||
response = StringUtils.chomp(response);
|
||||
|
||||
Matcher matcher = RESPONSE_PATTERN.matcher(response);
|
||||
if (matcher.matches()) {
|
||||
return Integer.parseInt(matcher.group(1));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected String stripByteCount(ByteBuffer byteBuffer) {
|
||||
String message = null;
|
||||
|
||||
String response = new String(byteBuffer.array(), 0, byteBuffer.limit());
|
||||
response = StringUtils.chomp(response);
|
||||
|
||||
Matcher matcher = RESPONSE_PATTERN.matcher(response);
|
||||
if (matcher.matches()) {
|
||||
String byteCountAsString = matcher.group(1);
|
||||
message = matcher.group(2);
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
protected void parseOKMessage(String message) {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
protected void parseHexMessage(String message) {
|
||||
Matcher matcher = HEX_PATTERN.matcher(message);
|
||||
|
||||
if (matcher.matches()) {
|
||||
String command = matcher.group(1);
|
||||
|
||||
IrCommand theCommand = null;
|
||||
for (IrCommand aCommand : irCommands) {
|
||||
if (aCommand.sequenceToHEXString().equals(command)) {
|
||||
theCommand = aCommand;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (theCommand != null) {
|
||||
for (TransceiverStatusListener listener : transceiverStatusListeners) {
|
||||
listener.onCommandReceived(this, theCommand);
|
||||
}
|
||||
} else {
|
||||
logger.error("{} does not match any know infrared command", command);
|
||||
}
|
||||
} else {
|
||||
logger.error("{} does not match the infrared message format '{}'", message, matcher.pattern());
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseIRDBMessage(String message) {
|
||||
Matcher matcher = IRDB_PATTERN.matcher(message);
|
||||
|
||||
if (matcher.matches()) {
|
||||
IrCommand command = new IrCommand();
|
||||
command.setRemote(matcher.group(1));
|
||||
command.setCommand(matcher.group(2));
|
||||
|
||||
for (TransceiverStatusListener listener : transceiverStatusListeners) {
|
||||
listener.onCommandReceived(this, command);
|
||||
}
|
||||
} else {
|
||||
logger.error("{} does not match the IRDB infrared message format '{}'", message, matcher.pattern());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* "Pack" the infrared command so that it can be sent to the IRTrans device
|
||||
*
|
||||
* @param led the led
|
||||
* @param command the the command
|
||||
* @return a string which is the full command to be sent to the device
|
||||
*/
|
||||
protected String packIRDBCommand(Led led, IrCommand command) {
|
||||
String output = new String();
|
||||
|
||||
output = "Asnd ";
|
||||
output += command.getRemote();
|
||||
output += ",";
|
||||
output += command.getCommand();
|
||||
output += ",l";
|
||||
output += led.toString();
|
||||
|
||||
output += "\r\n";
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Pack" the infrared command so that it can be sent to the IRTrans device
|
||||
*
|
||||
* @param led the led
|
||||
* @param command the the command
|
||||
* @return a string which is the full command to be sent to the device
|
||||
*/
|
||||
protected String packHexCommand(Led led, IrCommand command) {
|
||||
String output = new String();
|
||||
|
||||
output = "Asndhex ";
|
||||
output += "L";
|
||||
output += led.toString();
|
||||
|
||||
output += ",";
|
||||
output += "H" + command.toHEXString();
|
||||
|
||||
output += (char) 13;
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2020 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.irtrans.internal.handler;
|
||||
|
||||
import org.openhab.binding.irtrans.internal.IrCommand;
|
||||
|
||||
/**
|
||||
* The {@link TransceiverStatusListener} is interface that is to be implemented
|
||||
* by all classes that wish to be informed of events happening to a infrared
|
||||
* transceiver
|
||||
*
|
||||
* @author Karel Goderis - Initial contribution
|
||||
*
|
||||
*/
|
||||
public interface TransceiverStatusListener {
|
||||
|
||||
/**
|
||||
*
|
||||
* Called when the ethernet transceiver/bridge receives an infrared command
|
||||
*
|
||||
* @param bridge
|
||||
* @param command the infrared command
|
||||
*/
|
||||
public void onCommandReceived(EthernetBridgeHandler bridge, IrCommand command);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<binding:binding id="irtrans" 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>IRtrans Binding</name>
|
||||
<description>This is the binding for IRtrans (www.irtrans.de) Transceivers</description>
|
||||
<author>Karel Goderis</author>
|
||||
|
||||
</binding:binding>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="irtrans"
|
||||
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">
|
||||
|
||||
<!-- Blaster/Receiver diode connected to an IRtrans transceiver -->
|
||||
<thing-type id="blaster">
|
||||
<supported-bridge-type-refs>
|
||||
<bridge-type-ref id="ethernet"/>
|
||||
</supported-bridge-type-refs>
|
||||
|
||||
<label>Blaster</label>
|
||||
<description>This is an infrared transmitter that can send infrared commands</description>
|
||||
|
||||
<channels>
|
||||
<channel id="io" typeId="io"/>
|
||||
</channels>
|
||||
|
||||
<config-description>
|
||||
<parameter name="led" type="text" required="true">
|
||||
<label>Led</label>
|
||||
<description>The Led on which infrared commands will be emitted</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="remote" type="text" required="true">
|
||||
<label>Remote</label>
|
||||
<description>The remote or manufacturer name which's commands will be allowed, as defined in the IRtrans server
|
||||
database and flashed into the transceiver. Can be '*' for any remote</description>
|
||||
</parameter>
|
||||
|
||||
<parameter name="command" type="text" required="true">
|
||||
<label>Command</label>
|
||||
<description>The name of the command will be allowed, as defined in the IRtrans server database and flashed into the
|
||||
transceiver. Can be '*' for any command</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</thing-type>
|
||||
|
||||
<channel-type id="io">
|
||||
<item-type>String</item-type>
|
||||
<label>Input/Output</label>
|
||||
<description>Read commands received by the blaster, or write commands to be sent by the blaster</description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<thing:thing-descriptions bindingId="irtrans"
|
||||
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">
|
||||
|
||||
<!-- Bridge Thing Type -->
|
||||
<bridge-type id="ethernet">
|
||||
<label>IRtrans Ethernet Bridge</label>
|
||||
<description>This is an Ethernet (PoE) IRtrans transceiver equipped with an on-board IRDB database</description>
|
||||
|
||||
<config-description>
|
||||
<parameter name="ipAddress" type="text" required="true">
|
||||
<label>Network Address</label>
|
||||
<description>Network address of the ethernet transceiver</description>
|
||||
<context>network-address</context>
|
||||
</parameter>
|
||||
<parameter name="portNumber" type="integer" required="true">
|
||||
<label>Port Number</label>
|
||||
<description>TCP port number of the transceiver service</description>
|
||||
</parameter>
|
||||
<parameter name="bufferSize" type="integer" required="false">
|
||||
<label>Buffer Size</label>
|
||||
<description>Buffer size used by the TCP socket when sending and receiving commands to the transceiver</description>
|
||||
<default>1024</default>
|
||||
</parameter>
|
||||
<parameter name="responseTimeOut" type="integer" required="false">
|
||||
<label>Response Time Out</label>
|
||||
<description>Specifies the time milliseconds to wait for a response from the transceiver when sending a command.</description>
|
||||
<default>100</default>
|
||||
</parameter>
|
||||
<parameter name="pingTimeOut" type="integer" required="false">
|
||||
<label>Ping Time Out</label>
|
||||
<description>Specifies the time milliseconds to wait for a response from the transceiver when pinging the device</description>
|
||||
<default>1000</default>
|
||||
</parameter>
|
||||
<parameter name="reconnectInterval" type="integer" required="false">
|
||||
<label>Reconnect Interval</label>
|
||||
<description>Specifies the time seconds to wait before reconnecting to a transceiver after a communication failure</description>
|
||||
<default>10</default>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</bridge-type>
|
||||
|
||||
<channel-type id="blaster">
|
||||
<item-type>String</item-type>
|
||||
<label>Blaster Channel</label>
|
||||
<description>The Blaster Channel allows to send (filtered) infrared commands over the specified blaster led of the
|
||||
transceiver</description>
|
||||
<config-description>
|
||||
<parameter name="led" type="text" required="true">
|
||||
<label>Led</label>
|
||||
<description>The Led on which infrared commands will be emitted</description>
|
||||
</parameter>
|
||||
<parameter name="remote" type="text" required="true">
|
||||
<label>Remote</label>
|
||||
<description>The remote or manufacturer name which's commands will be allowed, as defined in the IRtrans server
|
||||
database and flashed into the transceiver. Can be '*' for any remote</description>
|
||||
</parameter>
|
||||
<parameter name="command" type="text" required="true">
|
||||
<label>Command</label>
|
||||
<description>The name of the command will be allowed, as defined in the IRtrans server database and flashed into the
|
||||
transceiver. Can be '*' for any command</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
<channel-type id="receiver">
|
||||
<item-type>String</item-type>
|
||||
<label>Receiver Channel</label>
|
||||
<description>The Receiver Channel allows to receive (filtered) infrared commands on the receiver led of the
|
||||
transceiver</description>
|
||||
<config-description>
|
||||
<parameter name="remote" type="text" required="true">
|
||||
<label>Remote</label>
|
||||
<description>The remote or manufacturer name which's commands will be allowed, as defined in the IRtrans server
|
||||
database and flashed into the transceiver. Can be '*' for any remote</description>
|
||||
</parameter>
|
||||
<parameter name="command" type="text" required="true">
|
||||
<label>Command</label>
|
||||
<description>The name of the command will be allowed, as defined in the IRtrans server database and flashed into the
|
||||
transceiver. Can be '*' for any command</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
</channel-type>
|
||||
|
||||
</thing:thing-descriptions>
|
||||
Reference in New Issue
Block a user