From 3c53e1f08f82bacf9842ed9b353f79ae7c30586a Mon Sep 17 00:00:00 2001 From: M Valla <12682715+mvalla@users.noreply.github.com> Date: Mon, 5 Feb 2024 18:16:03 +0100 Subject: [PATCH] [openwebnet] Replace gnu.io dependency with serial transport (#16376) Signed-off-by: Massimo Valla --- .../org.openhab.binding.openwebnet/pom.xml | 6 +- .../discovery/UsbGatewayDiscoveryService.java | 34 ++-- .../handler/OpenWebNetBridgeHandler.java | 7 +- .../handler/OpenWebNetLightingHandler.java | 5 +- .../internal/serial/SerialPortAdapter.java | 145 ++++++++++++++++++ .../serial/SerialPortEventAdapter.java | 48 ++++++ .../serial/SerialPortProviderAdapter.java | 69 +++++++++ 7 files changed, 295 insertions(+), 19 deletions(-) create mode 100644 bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortAdapter.java create mode 100644 bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortEventAdapter.java create mode 100644 bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortProviderAdapter.java diff --git a/bundles/org.openhab.binding.openwebnet/pom.xml b/bundles/org.openhab.binding.openwebnet/pom.xml index 151d57dbcb4d7..392345816ea68 100644 --- a/bundles/org.openhab.binding.openwebnet/pom.xml +++ b/bundles/org.openhab.binding.openwebnet/pom.xml @@ -14,16 +14,12 @@ openHAB Add-ons :: Bundles :: OpenWebNet (BTicino/Legrand) Binding - - gnu.io;version="[3.12,6)" - - io.github.openwebnet4j openwebnet4j - 0.10.1 + 0.12.0 compile diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/UsbGatewayDiscoveryService.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/UsbGatewayDiscoveryService.java index 6e6662879f4bf..baf841c03ac9c 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/UsbGatewayDiscoveryService.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/UsbGatewayDiscoveryService.java @@ -18,11 +18,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants; +import org.openhab.binding.openwebnet.internal.serial.SerialPortProviderAdapter; import org.openhab.core.config.discovery.AbstractDiscoveryService; import org.openhab.core.config.discovery.DiscoveryResult; import org.openhab.core.config.discovery.DiscoveryResultBuilder; @@ -44,11 +44,14 @@ import org.slf4j.LoggerFactory; /** - * The {@link UsbGatewayDiscoveryService} extends {@link AbstractDiscoveryService} to detect Zigbee USB gateways - * connected via serial port. The service will iterate over the available serial ports and open each one to test if a - * OpenWebNet Zigbee USB gateway is connected. On successful connection, a new DiscoveryResult is created. + * The {@link UsbGatewayDiscoveryService} extends + * {@link AbstractDiscoveryService} to detect Zigbee USB gateways connected via + * serial port. The service will iterate over the available serial ports and + * open each one to test if a OpenWebNet Zigbee USB gateway is connected. On + * successful connection, a new DiscoveryResult is created. * - * @author Massimo Valla - Initial contribution + * @author Massimo Valla - Initial contribution. Inject SerialPortManager to + * openwebnet4j lib. */ @NonNullByDefault @Component(service = DiscoveryService.class, configurationPid = "discovery.openwebnet") @@ -64,6 +67,8 @@ public class UsbGatewayDiscoveryService extends AbstractDiscoveryService impleme private @Nullable ScheduledFuture connectTimeout; private final SerialPortManager serialPortManager; + private final SerialPortProviderAdapter transportAdapter; + private @Nullable USBGateway zbGateway; private String currentScannedPortName = ""; @@ -74,27 +79,35 @@ public class UsbGatewayDiscoveryService extends AbstractDiscoveryService impleme private boolean scanning; /** - * Constructs a new UsbGatewayDiscoveryService with the specified Zigbee USB Bridge ThingTypeUID + * Constructs a new UsbGatewayDiscoveryService with the specified Zigbee USB + * Bridge ThingTypeUID */ @Activate public UsbGatewayDiscoveryService(final @Reference SerialPortManager spm) { super(Set.of(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY), DISCOVERY_TIMEOUT_SECONDS, false); - // Obtain the serial port manager service using an OSGi reference + // Inject the SerialPortManager passed via @Reference into the adapter serialPortManager = spm; + SerialPortProviderAdapter.setSerialPortManager(spm); + this.transportAdapter = new SerialPortProviderAdapter(); + logger.debug("**** -SPI- **** Set SerialPortManager to: {}", SerialPortProviderAdapter.serialPortManager); } /** - * Starts a new discovery scan. All available Serial Ports are scanned. + * Starts a new discovery scan. All available SerialPortsIdentifiers returned by + * SerialPortManager are scanned. */ @Override protected void startScan() { logger.debug("Started OpenWebNet Zigbee USB Gateway discovery scan"); removeOlderResults(getTimestampOfLastScan()); scanning = true; - Stream portEnum = serialPortManager.getIdentifiers(); + + SerialPortIdentifier[] foundSerialPortIds = serialPortManager.getIdentifiers() + .toArray(SerialPortIdentifier[]::new); + // Check each available serial port try { - for (SerialPortIdentifier portIdentifier : portEnum.toArray(SerialPortIdentifier[]::new)) { + for (SerialPortIdentifier portIdentifier : foundSerialPortIds) { if (scanning) { currentScannedPortName = portIdentifier.getName(); logger.debug("[{}] == checking serial port", currentScannedPortName); @@ -104,6 +117,7 @@ protected void startScan() { } else { logger.debug("[{}] trying to connect to a Zigbee USB Gateway...", currentScannedPortName); USBGateway gw = new USBGateway(currentScannedPortName); + gw.setSerialPortProvider(transportAdapter); zbGateway = gw; gw.subscribe(this); portCheckLatch = new CountDownLatch(1); diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java index 1e18259abe1c3..7bf064e163bc1 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java @@ -32,6 +32,7 @@ import org.openhab.binding.openwebnet.internal.discovery.OpenWebNetDeviceDiscoveryService; import org.openhab.binding.openwebnet.internal.handler.config.OpenWebNetBusBridgeConfig; import org.openhab.binding.openwebnet.internal.handler.config.OpenWebNetZigBeeBridgeConfig; +import org.openhab.binding.openwebnet.internal.serial.SerialPortProviderAdapter; import org.openhab.core.config.core.status.ConfigStatusMessage; import org.openhab.core.thing.Bridge; import org.openhab.core.thing.ChannelUID; @@ -179,7 +180,11 @@ public void initialize() { "@text/offline.conf-error-no-serial-port"); return null; } else { - return new USBGateway(serialPort); + USBGateway tmpUSBGateway = new USBGateway(serialPort); + tmpUSBGateway.setSerialPortProvider(new SerialPortProviderAdapter()); + logger.debug("**** -SPI- **** OpenWebNetBridgeHandler :: setSerialPortProvider to: {}", + tmpUSBGateway.getSerialPortProvider()); + return tmpUSBGateway; } } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java index bb3df121fb7ca..97e3374e788be 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java @@ -385,8 +385,7 @@ protected Where buildBusWhere(String wStr) throws IllegalArgumentException { * * @param channelId the channelId string **/ - @Nullable - private String toWhere(String channelId) { + private String toWhere(String channelId) throws OWNException { Where w = deviceWhere; if (w != null) { OpenWebNetBridgeHandler brH = bridgeHandler; @@ -400,6 +399,6 @@ private String toWhere(String channelId) { } } } - return null; + throw new OWNException("Cannot select channel from WHERE " + w); } } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortAdapter.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortAdapter.java new file mode 100644 index 0000000000000..46bd4904c49ad --- /dev/null +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortAdapter.java @@ -0,0 +1,145 @@ +/** + * 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.binding.openwebnet.internal.serial; + +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.PortInUseException; +import org.openhab.core.io.transport.serial.SerialPort; +import org.openhab.core.io.transport.serial.SerialPortEvent; +import org.openhab.core.io.transport.serial.SerialPortEventListener; +import org.openhab.core.io.transport.serial.SerialPortIdentifier; +import org.openhab.core.io.transport.serial.UnsupportedCommOperationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * openwebnet4j SerialPort implementation based on OH serial transport + * + * @author M. Valla - Initial contribution + */ + +@NonNullByDefault +public class SerialPortAdapter implements org.openwebnet4j.communication.serial.spi.SerialPort { + + private static final Logger logger = LoggerFactory.getLogger(SerialPortAdapter.class); + + private static final int OPEN_TIMEOUT_MS = 200; + + private final SerialPortIdentifier spid; + + private @Nullable SerialPort sp = null; + + public SerialPortAdapter(final SerialPortIdentifier spid) { + this.spid = spid; + } + + @Override + public boolean setSerialPortParams(int baudrate, int dataBits, int stopBits, int parity) { + @Nullable + SerialPort lsp = sp; + if (lsp != null) { + try { + lsp.setSerialPortParams(baudrate, dataBits, stopBits, parity); + return true; + } catch (UnsupportedCommOperationException e) { + logger.error("UnsupportedCommOperationException while setting port params in setSerialPortParams: {}", + e.getMessage()); + return false; + } + } + return false; + } + + @Override + public boolean addEventListener(org.openwebnet4j.communication.serial.spi.SerialPortEventListener listener) { + @Nullable + SerialPort lsp = sp; + if (lsp != null) { + try { + lsp.addEventListener(new SerialPortEventListener() { + + @Override + public void serialEvent(SerialPortEvent event) { + if (event != null) { + listener.serialEvent(new SerialPortEventAdapter(event)); + } + } + }); + lsp.notifyOnDataAvailable(true); + return true; + } catch (TooManyListenersException e) { + logger.error("TooManyListenersException while adding event listener: {}", e.getMessage()); + } + } + return false; + } + + @Override + public boolean open() { + try { + sp = spid.open(this.getClass().getName(), OPEN_TIMEOUT_MS); + } catch (PortInUseException e) { + logger.error("PortInUseException while opening serial port {}: {}", spid.getName(), e.getMessage()); + return false; + } + return true; + } + + @Override + public @Nullable String getName() { + @Nullable + SerialPort lsp = sp; + if (lsp != null) { + return lsp.getName(); + } else { + return null; + } + } + + @Override + public @Nullable InputStream getInputStream() throws IOException { + @Nullable + SerialPort lsp = sp; + if (lsp != null) { + return lsp.getInputStream(); + } else { + return null; + } + } + + @Override + public @Nullable OutputStream getOutputStream() throws IOException { + @Nullable + SerialPort lsp = sp; + if (lsp != null) { + return lsp.getOutputStream(); + } else { + return null; + } + } + + @Override + public void close() { + @Nullable + SerialPort lsp = sp; + if (lsp != null) { + lsp.close(); + } + } +} diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortEventAdapter.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortEventAdapter.java new file mode 100644 index 0000000000000..4d5646d49cc5d --- /dev/null +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortEventAdapter.java @@ -0,0 +1,48 @@ +/** + * 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.binding.openwebnet.internal.serial; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openwebnet4j.communication.serial.spi.SerialPortEvent; + +/** + * openwebnet4j SerialPortEvent implementation based on OH serial transport + * + * @author M. Valla - Initial contribution + */ + +@NonNullByDefault +public class SerialPortEventAdapter implements SerialPortEvent { + + private final org.openhab.core.io.transport.serial.SerialPortEvent event; + + /** + * Constructor. + * + * @param event the underlying event implementation + */ + public SerialPortEventAdapter(org.openhab.core.io.transport.serial.SerialPortEvent event) { + this.event = event; + } + + @Override + public int getEventType() { + if (event.getEventType() == org.openhab.core.io.transport.serial.SerialPortEvent.PORT_DISCONNECTED) { + return SerialPortEvent.EVENT_PORT_DISCONNECTED; + } else if (event.getEventType() == org.openhab.core.io.transport.serial.SerialPortEvent.DATA_AVAILABLE) { + return SerialPortEvent.EVENT_DATA_AVAILABLE; + } else { + return event.getEventType(); + } + } +} diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortProviderAdapter.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortProviderAdapter.java new file mode 100644 index 0000000000000..e7db89f59d5d6 --- /dev/null +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/serial/SerialPortProviderAdapter.java @@ -0,0 +1,69 @@ +/** + * 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.binding.openwebnet.internal.serial; + +import java.util.stream.Stream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.io.transport.serial.SerialPortIdentifier; +import org.openhab.core.io.transport.serial.SerialPortManager; +import org.openwebnet4j.communication.serial.spi.SerialPort; +import org.openwebnet4j.communication.serial.spi.SerialPortProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import aQute.bnd.annotation.spi.ServiceProvider; + +/** + * openwebnet4j SerialPortProvider implementation based on OH serial transport + * + * @author M. Valla - Initial contribution + */ +@ServiceProvider(value = SerialPortProvider.class) +@NonNullByDefault +public class SerialPortProviderAdapter implements SerialPortProvider { + + private Logger logger = LoggerFactory.getLogger(SerialPortProviderAdapter.class); + @Nullable + public static SerialPortManager serialPortManager = null; + + public static void setSerialPortManager(SerialPortManager serialPortManager) { + SerialPortProviderAdapter.serialPortManager = serialPortManager; + } + + @Override + public @Nullable SerialPort getSerialPort(String portName) { + final @Nullable SerialPortManager spm = serialPortManager; + if (spm == null) { + return null; + } + SerialPortIdentifier spid = spm.getIdentifier(portName); + if (spid == null) { + logger.debug("No SerialPort {} found", portName); + return null; + } else { + return new SerialPortAdapter(spid); + } + } + + @Override + public Stream getSerialPorts() { + final @Nullable SerialPortManager spm = serialPortManager; + if (spm == null) { + return Stream.empty(); + } + return spm.getIdentifiers().map(sid -> new SerialPortAdapter(sid)); + } +}