diff --git a/bundles/org.openhab.binding.knx/pom.xml b/bundles/org.openhab.binding.knx/pom.xml
index 65d508c0e..acab25a95 100644
--- a/bundles/org.openhab.binding.knx/pom.xml
+++ b/bundles/org.openhab.binding.knx/pom.xml
@@ -15,10 +15,16 @@
openHAB Add-ons :: Bundles :: KNX Binding
- gnu.io;version="[3.12,6)",javax.microedition.io.*;resolution:="optional",javax.usb.*;resolution:="optional",org.usb4java.*;resolution:="optional"
+ javax.microedition.io.*;resolution:="optional",javax.usb.*;resolution:="optional",org.usb4java.*;resolution:="optional"
+
+ biz.aQute.bnd
+ biz.aQute.bnd.annotation
+ ${bnd.version}
+ provided
+
com.github.calimero
calimero-core
@@ -43,23 +49,28 @@
-
- com.github.calimero
- calimero-rxtx
- 2.5.1
- compile
-
-
- org.slf4j
- slf4j-api
-
-
-
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+
+
+
+
+
org.apache.maven.plugins
maven-javadoc-plugin
diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java
index 6e20fa676..24a5d6f5f 100644
--- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java
+++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialClient.java
@@ -12,16 +12,16 @@
*/
package org.openhab.binding.knx.internal.client;
-import java.util.Enumeration;
import java.util.concurrent.ScheduledExecutorService;
+import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.transport.serial.SerialPortIdentifier;
+import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.ThingUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import gnu.io.CommPortIdentifier;
-import gnu.io.RXTXVersion;
import tuwien.auto.calimero.KNXException;
import tuwien.auto.calimero.link.KNXNetworkLink;
import tuwien.auto.calimero.link.KNXNetworkLinkFT12;
@@ -40,14 +40,16 @@ public class SerialClient extends AbstractKNXClient {
private final Logger logger = LoggerFactory.getLogger(SerialClient.class);
+ private final SerialPortManager serialPortManager;
private final String serialPort;
private final boolean useCemi;
public SerialClient(int autoReconnectPeriod, ThingUID thingUID, int responseTimeout, int readingPause,
int readRetriesLimit, ScheduledExecutorService knxScheduler, String serialPort, boolean useCemi,
- StatusUpdateCallback statusUpdateCallback) {
+ SerialPortManager serialPortManager, StatusUpdateCallback statusUpdateCallback) {
super(autoReconnectPeriod, thingUID, responseTimeout, readingPause, readRetriesLimit, knxScheduler,
statusUpdateCallback);
+ this.serialPortManager = serialPortManager;
this.serialPort = serialPort;
this.useCemi = useCemi;
}
@@ -55,7 +57,6 @@ public class SerialClient extends AbstractKNXClient {
@Override
protected KNXNetworkLink establishConnection() throws KNXException, InterruptedException {
try {
- RXTXVersion.getVersion();
logger.debug("Establishing connection to KNX bus through FT1.2 on serial port {}{}.", serialPort,
(useCemi ? " using CEMI" : ""));
// CEMI support by Calimero library, userful for newer serial devices like KNX RF sticks, kBerry,
@@ -73,17 +74,12 @@ public class SerialClient extends AbstractKNXClient {
final String msg = e.getMessage();
// TODO add a test for this string match; error message might change in later version of Calimero library
if ((msg != null) && (msg.startsWith(CALIMERO_ERROR_CANNOT_OPEN_PORT))) {
- StringBuilder sb = new StringBuilder("Available ports are:\n");
- Enumeration> portList = CommPortIdentifier.getPortIdentifiers();
- while (portList.hasMoreElements()) {
- CommPortIdentifier id = (CommPortIdentifier) portList.nextElement();
- if ((id != null) && (id.getPortType() == CommPortIdentifier.PORT_SERIAL)) {
- sb.append(id.getName());
- sb.append("\n");
- }
+ String availablePorts = serialPortManager.getIdentifiers().map(SerialPortIdentifier::getName)
+ .collect(Collectors.joining("\n"));
+ if (!availablePorts.isEmpty()) {
+ availablePorts = " Available ports are:\n" + availablePorts;
}
- sb.deleteCharAt(sb.length() - 1);
- throw new KNXException("Serial port '" + serialPort + "' could not be opened. " + sb.toString());
+ throw new KNXException("Serial port '" + serialPort + "' could not be opened." + availablePorts);
} else {
throw e;
}
diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java
new file mode 100644
index 000000000..125c2e2ea
--- /dev/null
+++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/client/SerialTransportAdapter.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2010-2023 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.knx.internal.client;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.io.transport.serial.PortInUseException;
+import org.openhab.core.io.transport.serial.SerialPort;
+import org.openhab.core.io.transport.serial.SerialPortIdentifier;
+import org.openhab.core.io.transport.serial.SerialPortManager;
+import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import aQute.bnd.annotation.spi.ServiceProvider;
+import tuwien.auto.calimero.KNXException;
+import tuwien.auto.calimero.serial.spi.SerialCom;
+
+/**
+ * The {@link SerialTransportAdapter} provides org.openhab.core.io.transport.serial
+ * services to the Calimero library.
+ *
+ * @ServiceProvider annotation (biz.aQute.bnd.annotation) automatically creates the file
+ * /META-INF/services/tuwien.auto.calimero.serial.spi.SerialCom
+ * to register SerialTransportAdapter to the service loader.
+ * Additional attributes for SerialTansportAdapter can be specified as well, e.g.
+ * attribute = { "position=1" }
+ * and will be part of MANIFEST.MF
+ *
+ * @author Holger Friedrich - Initial contribution
+ */
+@ServiceProvider(value = SerialCom.class)
+@NonNullByDefault
+public class SerialTransportAdapter implements SerialCom {
+ private static final int OPEN_TIMEOUT_MS = 200;
+ private static final int RECEIVE_TIMEOUT_MS = 5;
+ private static final int RECEIVE_THRESHOLD = 1024;
+ private static final int BAUDRATE = 19200;
+
+ private Logger logger = LoggerFactory.getLogger(SerialTransportAdapter.class);
+ @Nullable
+ private static SerialPortManager serialPortManager = null;
+ @Nullable
+ private SerialPort serialPort = null;
+
+ public static void setSerialPortManager(SerialPortManager serialPortManager) {
+ SerialTransportAdapter.serialPortManager = serialPortManager;
+ }
+
+ public SerialTransportAdapter() {
+ }
+
+ @Override
+ public void open(@Nullable String portId) throws IOException, KNXException {
+ if (portId == null) {
+ throw new IOException("Port not available");
+ }
+ logger = LoggerFactory.getLogger("SerialTransportAdapter:" + portId);
+
+ final @Nullable SerialPortManager tmpSerialPortManager = serialPortManager;
+ if (tmpSerialPortManager == null) {
+ throw new IOException("PortManager not available");
+ }
+ try {
+ SerialPortIdentifier portIdentifier = tmpSerialPortManager.getIdentifier(portId);
+ if (portIdentifier != null) {
+ logger.trace("Trying to open port {}", portId);
+ SerialPort serialPort = portIdentifier.open(this.getClass().getName(), OPEN_TIMEOUT_MS);
+ // apply default settings for com port, may be overwritten by caller
+ serialPort.setSerialPortParams(BAUDRATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
+ SerialPort.PARITY_EVEN);
+ serialPort.enableReceiveThreshold(RECEIVE_THRESHOLD);
+ serialPort.enableReceiveTimeout(RECEIVE_TIMEOUT_MS);
+ this.serialPort = serialPort;
+
+ // Notification / event listeners are available and may be used to log/trace com failures
+ // serialPort.notifyOnDataAvailable(true);
+ logger.trace("Port opened successfully");
+ } else {
+ logger.info("Port {} not available", portId);
+ throw new IOException("Port " + portId + " not available");
+ }
+ } catch (PortInUseException e) {
+ logger.info("Port {} already in use", portId);
+ throw new IOException("Port " + portId + " already in use", e);
+ } catch (UnsupportedCommOperationException e) {
+ logger.info("Port {} unsupported com operation", portId);
+ throw new IOException("Port " + portId + " unsupported com operation", e);
+ }
+ }
+
+ // SerialCom extends AutoCloseable, close() throws Exception
+ @Override
+ public void close() {
+ logger.trace("Closing serial port");
+ final @Nullable SerialPort tmpSerialPort = serialPort;
+ if (tmpSerialPort != null) {
+ tmpSerialPort.close();
+ serialPort = null;
+ }
+ }
+
+ @Override
+ public @Nullable InputStream inputStream() {
+ final @Nullable SerialPort tmpSerialPort = serialPort;
+ if (tmpSerialPort != null) {
+ try {
+ return tmpSerialPort.getInputStream();
+ } catch (IOException e) {
+ logger.info("Cannot open input stream");
+ }
+ }
+ // should not throw, create a dummy return value
+ byte buf[] = {};
+ return new ByteArrayInputStream(buf);
+ }
+
+ @Override
+ public @Nullable OutputStream outputStream() {
+ final @Nullable SerialPort tmpSerialPort = serialPort;
+ if (tmpSerialPort != null) {
+ try {
+ return tmpSerialPort.getOutputStream();
+ } catch (IOException e) {
+ logger.info("Cannot open output stream");
+ }
+ }
+ // should not throw, create a dummy return value
+ return new ByteArrayOutputStream(0);
+ }
+
+ // disable NonNullByDefault for this function, legacy interface List
+ @NonNullByDefault({})
+ @Override
+ public List portIdentifiers() {
+ final @Nullable SerialPortManager tmpSerialPortManager = serialPortManager;
+ if (tmpSerialPortManager == null) {
+ return Collections.emptyList();
+ }
+ return tmpSerialPortManager.getIdentifiers().map(SerialPortIdentifier::getName).collect(Collectors.toList());
+ }
+
+ @Override
+ public int baudRate() throws IOException {
+ final @Nullable SerialPort tmpSerialPort = serialPort;
+ if (tmpSerialPort == null) {
+ throw new IOException("Port not available");
+ }
+ return tmpSerialPort.getBaudRate();
+ }
+
+ @Override
+ public void setSerialPortParams(final int baudrate, final int databits, @Nullable StopBits stopbits,
+ @Nullable Parity parity) throws IOException {
+ final @Nullable SerialPort tmpSerialPort = serialPort;
+ if (tmpSerialPort == null) {
+ throw new IOException("Port not available");
+ }
+ if ((stopbits == null) || (parity == null)) {
+ throw new IOException("Invalid parameters");
+ }
+ try {
+ tmpSerialPort.setSerialPortParams(baudrate, databits, stopbits.value(), parity.value());
+ } catch (final UnsupportedCommOperationException e) {
+ throw new IOException("Setting serial port parameters for " + tmpSerialPort.getName() + " failed", e);
+ }
+ }
+
+ @Override
+ public void setFlowControlMode(@Nullable FlowControl mode) throws IOException {
+ final @Nullable SerialPort tmpSerialPort = serialPort;
+ if (tmpSerialPort == null) {
+ throw new IOException("Port not available");
+ }
+ if (mode == null) {
+ throw new IOException("Invalid parameters");
+ }
+ if (mode == FlowControl.None) {
+ try {
+ tmpSerialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
+ } catch (final UnsupportedCommOperationException e) {
+ throw new IOException("Setting flow control parameters for " + tmpSerialPort.getName() + " failed", e);
+ }
+ } else {
+ logger.warn("Unknown FlowControl mode {}", mode);
+ throw new IOException("Invalid flow mode");
+ }
+ }
+}
diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java
index a510355f2..460d23620 100644
--- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java
+++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/factory/KNXHandlerFactory.java
@@ -19,6 +19,7 @@ import java.util.Collection;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.knx.internal.client.SerialTransportAdapter;
import org.openhab.binding.knx.internal.handler.DeviceThingHandler;
import org.openhab.binding.knx.internal.handler.IPBridgeThingHandler;
import org.openhab.binding.knx.internal.handler.SerialBridgeThingHandler;
@@ -26,6 +27,7 @@ import org.openhab.binding.knx.internal.i18n.KNXTranslationProvider;
import org.openhab.core.config.core.Configuration;
import org.openhab.core.i18n.LocaleProvider;
import org.openhab.core.i18n.TranslationProvider;
+import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.net.NetworkAddressService;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
@@ -53,11 +55,14 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory {
@Nullable
private NetworkAddressService networkAddressService;
+ private final SerialPortManager serialPortManager;
@Activate
public KNXHandlerFactory(final @Reference TranslationProvider translationProvider,
- final @Reference LocaleProvider localeProvider) {
+ final @Reference LocaleProvider localeProvider, final @Reference SerialPortManager serialPortManager) {
KNXTranslationProvider.I18N.setProvider(localeProvider, translationProvider);
+ this.serialPortManager = serialPortManager;
+ SerialTransportAdapter.setSerialPortManager(serialPortManager);
}
@Override
@@ -87,7 +92,7 @@ public class KNXHandlerFactory extends BaseThingHandlerFactory {
if (thing.getThingTypeUID().equals(THING_TYPE_IP_BRIDGE)) {
return new IPBridgeThingHandler((Bridge) thing, networkAddressService);
} else if (thing.getThingTypeUID().equals(THING_TYPE_SERIAL_BRIDGE)) {
- return new SerialBridgeThingHandler((Bridge) thing);
+ return new SerialBridgeThingHandler((Bridge) thing, serialPortManager);
} else if (thing.getThingTypeUID().equals(THING_TYPE_DEVICE)) {
return new DeviceThingHandler(thing);
}
diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java
index 4c598f1fe..a97538165 100644
--- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java
+++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/SerialBridgeThingHandler.java
@@ -20,6 +20,7 @@ import org.openhab.binding.knx.internal.client.KNXClient;
import org.openhab.binding.knx.internal.client.NoOpClient;
import org.openhab.binding.knx.internal.client.SerialClient;
import org.openhab.binding.knx.internal.config.SerialBridgeConfiguration;
+import org.openhab.core.io.transport.serial.SerialPortManager;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
import org.slf4j.Logger;
@@ -42,8 +43,11 @@ public class SerialBridgeThingHandler extends KNXBridgeBaseThingHandler {
private final Logger logger = LoggerFactory.getLogger(SerialBridgeThingHandler.class);
- public SerialBridgeThingHandler(Bridge bridge) {
+ private final SerialPortManager serialPortManager;
+
+ public SerialBridgeThingHandler(Bridge bridge, final SerialPortManager serialPortManager) {
super(bridge);
+ this.serialPortManager = serialPortManager;
}
@Override
@@ -53,7 +57,7 @@ public class SerialBridgeThingHandler extends KNXBridgeBaseThingHandler {
SerialBridgeConfiguration config = getConfigAs(SerialBridgeConfiguration.class);
client = new SerialClient(config.getAutoReconnectPeriod(), thing.getUID(), config.getResponseTimeout(),
config.getReadingPause(), config.getReadRetriesLimit(), getScheduler(), config.getSerialPort(),
- config.useCemi(), this);
+ config.useCemi(), serialPortManager, this);
updateStatus(ThingStatus.UNKNOWN);
// delay actual initialization, allow for longer runtime of actual initialization