diff --git a/bom/compile/pom.xml b/bom/compile/pom.xml
index bea2b608278..9a1554bc90b 100644
--- a/bom/compile/pom.xml
+++ b/bom/compile/pom.xml
@@ -294,6 +294,12 @@
+
+ com.fazecast
+ jSerialComm
+ 2.10.4
+ compile
+
com.neuronrobotics
nrjavaserial
diff --git a/bom/openhab-core/pom.xml b/bom/openhab-core/pom.xml
index 905bfb7e0fd..ffaed9e53b3 100644
--- a/bom/openhab-core/pom.xml
+++ b/bom/openhab-core/pom.xml
@@ -262,6 +262,12 @@
${project.version}
compile
+
+ org.openhab.core.bundles
+ org.openhab.core.io.transport.serial.jserialcomm
+ ${project.version}
+ compile
+
org.openhab.core.bundles
org.openhab.core.io.transport.serial.rxtx
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/.classpath b/bundles/org.openhab.core.io.transport.serial.jserialcomm/.classpath
new file mode 100644
index 00000000000..e9d63b05acb
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/.classpath
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/.project b/bundles/org.openhab.core.io.transport.serial.jserialcomm/.project
new file mode 100644
index 00000000000..2d76f85d947
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/.project
@@ -0,0 +1,23 @@
+
+
+ org.openhab.core.io.transport.serial.rxtx
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/NOTICE b/bundles/org.openhab.core.io.transport.serial.jserialcomm/NOTICE
new file mode 100644
index 00000000000..6c17d0d8a45
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/NOTICE
@@ -0,0 +1,14 @@
+This content is produced and maintained by the openHAB project.
+
+* Project home: https://www.openhab.org
+
+== Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License 2.0 which is available at
+https://www.eclipse.org/legal/epl-2.0/.
+
+== Source Code
+
+https://github.com/openhab/openhab-core
+
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/bnd.bnd b/bundles/org.openhab.core.io.transport.serial.jserialcomm/bnd.bnd
new file mode 100644
index 00000000000..4fc04c311a5
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/bnd.bnd
@@ -0,0 +1,4 @@
+Bundle-SymbolicName: ${project.artifactId}
+Import-Package: \
+ com.fazecast.jSerialComm;version="[2.11,3)",\
+ *
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/pom.xml b/bundles/org.openhab.core.io.transport.serial.jserialcomm/pom.xml
new file mode 100644
index 00000000000..0caab1f56bb
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/pom.xml
@@ -0,0 +1,31 @@
+
+
+ 4.0.0
+
+
+ org.openhab.core.bundles
+ org.openhab.core.reactor.bundles
+ 4.2.0-SNAPSHOT
+
+
+ org.openhab.core.io.transport.serial.jserialcomm
+
+ openHAB Core :: Bundles :: Serial Transport for jSerialComm
+
+
+
+ org.openhab.core.bundles
+ org.openhab.core.io.transport.serial
+ ${project.version}
+
+
+
+ com.fazecast
+ jSerialComm
+ [2.11.0,3.0.0)
+
+
+
+
+
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPort.java b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPort.java
new file mode 100644
index 00000000000..5247bd7cb58
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPort.java
@@ -0,0 +1,400 @@
+/**
+ * Copyright (c) 2010-2024 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.core.io.transport.serial.jserialcomm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.TooManyListenersException;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.io.transport.serial.SerialPort;
+import org.openhab.core.io.transport.serial.SerialPortEventListener;
+import org.openhab.core.io.transport.serial.UnsupportedCommOperationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Specific OH serial transport SerialPort implementation using com.fazecast.jSerialComm.SerialPort.
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+@NonNullByDefault
+public class JSerialCommSerialPort implements SerialPort {
+
+ private final Logger logger = LoggerFactory.getLogger(JSerialCommSerialPort.class);
+
+ private final com.fazecast.jSerialComm.SerialPort sp;
+
+ private boolean notifyOnDataAvailable = false;
+ private boolean notifyOnBreakInterrupt = false;
+ private boolean notifyOnFramingError = false;
+ private boolean notifyOnOverrunError = false;
+ private boolean notifyOnParityError = false;
+ private boolean notifyOnOutputEmpty = false;
+ private boolean notifyOnCTS = false;
+ private boolean notifyOnDSR = false;
+ private boolean notifyOnRingIndicator = false;
+ private boolean notifyOnCarrierDetect = false;
+
+ private @Nullable SerialPortEventListener eventListener;
+
+ /**
+ * Constructor.
+ *
+ * @param sp the underlying serial port implementation
+ */
+ public JSerialCommSerialPort(final com.fazecast.jSerialComm.SerialPort sp) {
+ this.sp = sp;
+ }
+
+ @Override
+ public void close() {
+ sp.closePort();
+ }
+
+ @Override
+ public void setSerialPortParams(int baudrate, int dataBits, int stopBits, int parity)
+ throws UnsupportedCommOperationException {
+ if (!sp.setComPortParameters(baudrate, dataBits, stopBits, parity)) {
+ throw new UnsupportedCommOperationException();
+ }
+ }
+
+ @Override
+ public @Nullable InputStream getInputStream() throws IOException {
+ return sp.getInputStream();
+ }
+
+ @Override
+ public @Nullable OutputStream getOutputStream() throws IOException {
+ return sp.getOutputStream();
+ }
+
+ private int combineListeningEvents() {
+ // FIXME just use compact version below
+ int combined = 0;
+ if (notifyOnDataAvailable) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnDataAvailable({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_AVAILABLE, combined);
+ }
+ if (notifyOnBreakInterrupt) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_BREAK_INTERRUPT;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnBreakInterrupt({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_BREAK_INTERRUPT, combined);
+ }
+ if (notifyOnFramingError) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_FRAMING_ERROR;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnFramingError({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_FRAMING_ERROR, combined);
+ }
+ if (notifyOnOverrunError) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_FIRMWARE_OVERRUN_ERROR;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnOverrunError({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_FIRMWARE_OVERRUN_ERROR, combined);
+ }
+ if (notifyOnParityError) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_PARITY_ERROR;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnParityError({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_PARITY_ERROR, combined);
+ }
+ if (notifyOnOutputEmpty) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_WRITTEN;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnOutputEmpty({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_WRITTEN, combined);
+ }
+ if (notifyOnCTS) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_CTS;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnCTS({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_CTS, combined);
+ }
+ if (notifyOnDSR) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DSR;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnDSR({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DSR, combined);
+ }
+ if (notifyOnRingIndicator) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_RING_INDICATOR;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnRingIndicator({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_RING_INDICATOR, combined);
+ }
+ if (notifyOnCarrierDetect) {
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_CARRIER_DETECT;
+ logger.debug("--------TRANSPORT-jSerialComm--- subscribed notifyOnCarrierDetect({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_CARRIER_DETECT, combined);
+ }
+
+ combined = combined | com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_PORT_DISCONNECTED;
+ logger.debug(
+ "--------TRANSPORT-jSerialComm--- subscribed LISTENING_EVENT_PORT_DISCONNECTED({}) - combined now is: {}",
+ com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_PORT_DISCONNECTED, combined);
+
+ logger.debug("--------TRANSPORT-jSerialComm--- FINAL combined is: {}", combined);
+
+ return combined;
+
+ /*
+ * return com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_PORT_DISCONNECTED
+ * | (notifyOnDataAvailable ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_AVAILABLE : 0)
+ * | (notifyOnBreakInterrupt ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_BREAK_INTERRUPT : 0)
+ * | (notifyOnFramingError ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_FRAMING_ERROR : 0)
+ * | (notifyOnOverrunError ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_FIRMWARE_OVERRUN_ERROR
+ * : 0)
+ * | (notifyOnParityError ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_PARITY_ERROR : 0)
+ * | (notifyOnOutputEmpty ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DATA_WRITTEN : 0)
+ * | (notifyOnCTS ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_CTS : 0)
+ * | (notifyOnDSR ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_DSR : 0)
+ * | (notifyOnRingIndicator ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_RING_INDICATOR : 0)
+ * | (notifyOnCarrierDetect ? com.fazecast.jSerialComm.SerialPort.LISTENING_EVENT_CARRIER_DETECT : 0);
+ */
+ }
+
+ @Override
+ public void addEventListener(SerialPortEventListener listener) throws TooManyListenersException {
+ boolean success = sp.addDataListener(new com.fazecast.jSerialComm.SerialPortDataListener() {
+ @Override
+ public void serialEvent(final com.fazecast.jSerialComm.@Nullable SerialPortEvent event) {
+ if (event == null) {
+ return;
+ }
+ listener.serialEvent(new JSerialCommSerialPortEvent(event));
+ }
+
+ @Override
+ public int getListeningEvents() {
+ int subscribedEvents = combineListeningEvents();
+ logger.debug("--------TRANSPORT-jSerialComm--- {} is subscribed to events: {}", this.toString(),
+ subscribedEvents);
+ return subscribedEvents;
+ }
+ });
+ if (!success) {
+ logger.error("--------TRANSPORT-jSerialComm--- Could not add SerialPortDataListener to SerialPort {}",
+ sp.getSystemPortName());
+ throw new TooManyListenersException(
+ ("Could not add SerialPortDataListener to SerialPort " + sp.getSystemPortName()));
+ }
+ eventListener = listener;
+ }
+
+ // private method to refresh listener (if exists) in order to refresh subscriptions to lib
+ private synchronized void refreshListener() {
+ SerialPortEventListener eL = eventListener;
+ if (eL != null) {
+ sp.removeDataListener();
+ try {
+ this.addEventListener(eL);
+ logger.debug("--------TRANSPORT-jSerialComm--- LISTENER REFRESHED!");
+
+ } catch (TooManyListenersException e) {
+ logger.error("--------TRANSPORT-jSerialComm--- Could not add SerialPortDataListener to SerialPort {}",
+ sp.getSystemPortName());
+ }
+ }
+ }
+
+ @Override
+ public void removeEventListener() {
+ sp.removeDataListener();
+ }
+
+ @Override
+ public void notifyOnDataAvailable(boolean enable) {
+ this.notifyOnDataAvailable = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnBreakInterrupt(boolean enable) {
+ this.notifyOnBreakInterrupt = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnFramingError(boolean enable) {
+ this.notifyOnFramingError = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnOverrunError(boolean enable) {
+ this.notifyOnOverrunError = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnParityError(boolean enable) {
+ this.notifyOnParityError = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void setRTS(boolean enable) {
+ if (enable) {
+ sp.setRTS();
+ } else {
+ sp.clearRTS();
+ }
+ }
+
+ @Override
+ public void enableReceiveTimeout(int timeout) throws UnsupportedCommOperationException {
+ if (timeout < 0) {
+ throw new IllegalArgumentException(String.format("timeout must be non negative (is: %d)", timeout));
+ }
+ // FIXME !!!!! placeholder implementation
+ boolean success = sp.setComPortTimeouts(com.fazecast.jSerialComm.SerialPort.TIMEOUT_READ_BLOCKING, timeout, 0);
+ if (!success) {
+ throw new UnsupportedCommOperationException();
+ }
+ }
+
+ @Override
+ public void disableReceiveTimeout() {
+ // FIXME !!!!! placeholder implementation
+ sp.setComPortTimeouts(com.fazecast.jSerialComm.SerialPort.TIMEOUT_NONBLOCKING, 0, 0);
+ }
+
+ @Override
+ public String getName() {
+ String sysPortName = sp.getSystemPortName();
+ if (sysPortName != null && sysPortName.startsWith("COM")) {
+ return sysPortName;
+ } else {
+ return sp.getSystemPortPath();
+ }
+ }
+
+ @Override
+ public void setFlowControlMode(int flowcontrolRtsctsOut) throws UnsupportedCommOperationException {
+ // FIXME check mapping
+ if (!sp.setFlowControl(flowcontrolRtsctsOut)) {
+ throw new UnsupportedCommOperationException();
+ }
+ }
+
+ @Override
+ public void enableReceiveThreshold(int i) throws UnsupportedCommOperationException {
+ // FIXME !!!!! placeholder implementation
+ /*
+ * try {
+ * sp.enableReceiveThreshold(i);
+ * } catch (gnu.io.UnsupportedCommOperationException e) {
+ * throw new UnsupportedCommOperationException(e);
+ * }
+ */
+ }
+
+ @Override
+ public int getBaudRate() {
+ return sp.getBaudRate();
+ }
+
+ @Override
+ public int getDataBits() {
+ return sp.getNumDataBits();
+ }
+
+ @Override
+ public int getStopBits() {
+ return sp.getNumStopBits();
+ }
+
+ @Override
+ public int getParity() {
+ return sp.getParity();
+ }
+
+ @Override
+ public void notifyOnOutputEmpty(boolean enable) {
+ this.notifyOnOutputEmpty = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnCTS(boolean enable) {
+ this.notifyOnCTS = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnDSR(boolean enable) {
+ this.notifyOnDSR = enable;
+ refreshListener();
+ }
+
+ @Override
+ public void notifyOnRingIndicator(boolean enable) {
+ this.notifyOnRingIndicator = enable;
+ }
+
+ @Override
+ public void notifyOnCarrierDetect(boolean enable) {
+ this.notifyOnCarrierDetect = enable;
+ refreshListener();
+ }
+
+ @Override
+ public int getFlowControlMode() {
+ // FIXME check mapping
+ return sp.getFlowControlSettings();
+ }
+
+ @Override
+ public boolean isRTS() {
+ return sp.getRTS();
+ }
+
+ @Override
+ public void setDTR(boolean state) {
+ if (state) {
+ sp.setDTR();
+ } else {
+ sp.clearDTR();
+ }
+ }
+
+ @Override
+ public boolean isDTR() {
+ return sp.getDTR();
+ }
+
+ @Override
+ public boolean isCTS() {
+ return sp.getCTS();
+ }
+
+ @Override
+ public boolean isDSR() {
+ return sp.getDSR();
+ }
+
+ @Override
+ public boolean isCD() {
+ return sp.getDCD();
+ }
+
+ @Override
+ public boolean isRI() {
+ return sp.getRI();
+ }
+
+ @Override
+ public void sendBreak(int duration) {
+ // FIXME !!!!! placeholder implementation. Remove from OH serial transport since it's not used by any binding??
+ sp.setBreak();
+ // sp.sendBreak(duration);
+ }
+}
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortEvent.java b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortEvent.java
new file mode 100644
index 00000000000..fb9ec1434ba
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortEvent.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) 2010-2024 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.core.io.transport.serial.jserialcomm;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.core.io.transport.serial.SerialPortEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Specific OH serial transport SerialPortEvent implementation using com.fazecast.jSerialComm.SerialPortEvent
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+@NonNullByDefault
+public class JSerialCommSerialPortEvent implements SerialPortEvent {
+
+ private final Logger logger = LoggerFactory.getLogger(JSerialCommSerialPortEvent.class);
+
+ private final com.fazecast.jSerialComm.SerialPortEvent event;
+
+ /**
+ * Constructor.
+ *
+ * @param event the underlying event implementation
+ */
+ public JSerialCommSerialPortEvent(final com.fazecast.jSerialComm.SerialPortEvent event) {
+ logger.debug("--------TRANSPORT-jSerialComm--- New event of type: {}", event.getEventType());
+ this.event = event;
+ }
+
+ @Override
+ public int getEventType() {
+ // FIXME check event id mapping
+ return event.getEventType();
+ }
+
+ @Override
+ public boolean getNewValue() {
+ // FIXME !!!!! placeholder implementation
+ return false;
+ // return event.getNewValue();
+ }
+}
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortIdentifier.java b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortIdentifier.java
new file mode 100644
index 00000000000..3178ebbaaff
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortIdentifier.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2010-2024 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.core.io.transport.serial.jserialcomm;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Specific OH serial transport SerialPortIdentifier implementation using com.fazecast.jSerialComm.SerialPort
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+@NonNullByDefault
+public class JSerialCommSerialPortIdentifier implements SerialPortIdentifier {
+
+ private final Logger logger = LoggerFactory.getLogger(JSerialCommSerialPortIdentifier.class);
+
+ final com.fazecast.jSerialComm.SerialPort sp;
+
+ private @Nullable String owner;
+
+ /**
+ * Constructor.
+ *
+ * @param sp
+ */
+ public JSerialCommSerialPortIdentifier(final com.fazecast.jSerialComm.SerialPort sp) {
+ this.sp = sp;
+ }
+
+ @Override
+ public String getName() {
+ final String sysPortName = sp.getSystemPortName();
+ if (sysPortName != null && sysPortName.startsWith("COM")) {
+ return sysPortName;
+ } else {
+ return sp.getSystemPortPath();
+ }
+ // return name != null ? name : "";
+ }
+
+ @Override
+ public SerialPort open(String owner, int timeout) throws PortInUseException {
+ logger.debug("--------TRANSPORT-jSerialComm--- SerialPort.getReadTimeout() = {}", sp.getReadTimeout());
+ logger.debug("--------TRANSPORT-jSerialComm--- SerialPort.getPortDescription() = {}", sp.getPortDescription());
+ boolean success = sp.openPort();
+ if (success) {
+ this.owner = owner;
+ return new JSerialCommSerialPort(sp);
+ } else {
+ logger.error("--------TRANSPORT-jSerialComm--- Could not open port: {}", getName());
+ throw new PortInUseException(new Exception("Could not open port: " + getName()));
+ }
+ }
+
+ @Override
+ public boolean isCurrentlyOwned() {
+ return (owner != null);
+ }
+
+ @Override
+ public @Nullable String getCurrentOwner() {
+ return owner;
+ }
+}
diff --git a/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortProvider.java b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortProvider.java
new file mode 100644
index 00000000000..e50f06444fe
--- /dev/null
+++ b/bundles/org.openhab.core.io.transport.serial.jserialcomm/src/main/java/org/openhab/core/io/transport/serial/jserialcomm/JSerialCommSerialPortProvider.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2010-2024 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.core.io.transport.serial.jserialcomm;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.core.io.transport.serial.ProtocolType;
+import org.openhab.core.io.transport.serial.ProtocolType.PathType;
+import org.openhab.core.io.transport.serial.SerialPortIdentifier;
+import org.openhab.core.io.transport.serial.SerialPortProvider;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fazecast.jSerialComm.SerialPort;
+import com.fazecast.jSerialComm.SerialPortInvalidPortException;
+
+/**
+ * Specific OH serial transport SerialPortProvider implementation using com.fazecast.jSerialComm.SerialPort
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+@NonNullByDefault
+@Component(service = SerialPortProvider.class)
+public class JSerialCommSerialPortProvider implements SerialPortProvider {
+
+ private final Logger logger = LoggerFactory.getLogger(JSerialCommSerialPortProvider.class);
+
+ @Override
+ public @Nullable SerialPortIdentifier getPortIdentifier(URI port) {
+ String portPathAsString = port.getPath();
+ try {
+ SerialPort spFound = SerialPort.getCommPort(portPathAsString);
+ return new JSerialCommSerialPortIdentifier(spFound);
+ } catch (SerialPortInvalidPortException e) {
+ logger.debug(
+ "--------TRANSPORT-jSerialComm--- No SerialPort found for: {} (SerialPortInvalidPortException: {})",
+ portPathAsString, e.getMessage());
+ return null;
+ }
+ }
+
+ @Override
+ public Stream getAcceptedProtocols() {
+ return Stream.of(new ProtocolType(PathType.LOCAL, "jserialcomm"));
+ }
+
+ @Override
+ public Stream getSerialPortIdentifiers() {
+ com.fazecast.jSerialComm.SerialPort[] portsArray = com.fazecast.jSerialComm.SerialPort.getCommPorts();
+ logger.debug("--------TRANSPORT-jSerialComm--- getSerialPortIdentifiers() :: Listing found ports:");
+ for (com.fazecast.jSerialComm.SerialPort port : portsArray) {
+ logger.debug("--------TRANSPORT-jSerialComm--- port: {} ({} - {})", port.getSystemPortName(),
+ port.getSystemPortPath(), port.getPortDescription());
+ }
+ logger.debug("--------TRANSPORT-jSerialComm--- ...finished listing found ports.");
+
+ Stream ports = Arrays.stream(portsArray);
+
+ return ports.map(sid -> new JSerialCommSerialPortIdentifier(sid));
+ }
+}
diff --git a/bundles/pom.xml b/bundles/pom.xml
index 59da5879650..fa91d3aeb50 100644
--- a/bundles/pom.xml
+++ b/bundles/pom.xml
@@ -81,6 +81,7 @@
org.openhab.core.io.transport.mqtt
org.openhab.core.io.transport.serial
org.openhab.core.io.transport.serial.javacomm
+ org.openhab.core.io.transport.serial.jserialcomm
org.openhab.core.io.transport.serial.rxtx
org.openhab.core.io.transport.serial.rxtx.rfc2217
org.openhab.core.io.transport.upnp