From 4f1dd5792f7d7ce69d3ece73d746c41858dd21d0 Mon Sep 17 00:00:00 2001 From: Holger Friedrich Date: Tue, 21 Feb 2023 19:36:52 +0100 Subject: [PATCH] [knx] FT12: Autodetect cEMI on Weinzierl devices (#14454) Signed-off-by: Holger Friedrich --- .../knx/internal/client/SerialClient.java | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) 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 24a5d6f5f..20e269f83 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,7 +12,11 @@ */ package org.openhab.binding.knx.internal.client; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -22,10 +26,12 @@ import org.openhab.core.thing.ThingUID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tuwien.auto.calimero.Connection.BlockingMode; import tuwien.auto.calimero.KNXException; import tuwien.auto.calimero.link.KNXNetworkLink; import tuwien.auto.calimero.link.KNXNetworkLinkFT12; import tuwien.auto.calimero.link.medium.TPSettings; +import tuwien.auto.calimero.serial.FT12Connection; /** * Serial specific {@link AbstractKNXClient} implementation. @@ -54,21 +60,73 @@ public class SerialClient extends AbstractKNXClient { this.useCemi = useCemi; } + /** + * try autodetection of cEMI devices via the PEI identification frame + * + * @implNote This is based on an vendor specific extension and may not work for other devices. + */ + protected boolean detectCemi() throws InterruptedException { + final byte[] peiIdentifyReqFrame = { (byte) 0xa7 }; + final byte peiIdentifyCon = (byte) 0xa8; + final byte peiWzIdentFrameLength = 11; + + logger.trace("Checking for cEMI support"); + + try (FT12Connection serialConnection = new FT12Connection(serialPort)) { + final CompletableFuture frameListener = new CompletableFuture(); + serialConnection.addConnectionListener(frameReceived -> { + final byte[] content = frameReceived.getFrameBytes(); + if ((content.length > 0) && (content[0] == peiIdentifyCon)) { + logger.trace("Received PEI confirmation of {} bytes", content.length); + frameListener.complete(content); + } + }); + + serialConnection.send(peiIdentifyReqFrame, BlockingMode.NonBlocking); + byte[] content = frameListener.get(1, TimeUnit.SECONDS); + + if (peiWzIdentFrameLength == content.length) { + // standard emi2 frame contain 9 bytes, + // content[1..2] physical address + // content[3..8] serial no + // + // Weinzierl adds 2 extra bytes, 0x0004 for capablity cEMI, + // see "Weinzierl KNX BAOS Starter Kit, User Guide" + if (0 == content[9] && 4 == content[10]) { + logger.debug("Detected device with cEMI support"); + return true; + } + } + } catch (final ExecutionException | TimeoutException | KNXException na) { + if (logger.isTraceEnabled()) { + logger.trace("Exception detecting cEMI: ", na); + } + } + + logger.trace("Did not detect device with cEMI support"); + return false; + } + @Override protected KNXNetworkLink establishConnection() throws KNXException, InterruptedException { try { - logger.debug("Establishing connection to KNX bus through FT1.2 on serial port {}{}.", serialPort, - (useCemi ? " using CEMI" : "")); + boolean useCemiL = useCemi; + if (!useCemiL) { + useCemiL = detectCemi(); + } + logger.debug("Establishing connection to KNX bus through FT1.2 on serial port {}{}{}", serialPort, + (useCemiL ? " using cEMI" : ""), ((useCemiL != useCemi) ? " (autodetected)" : "")); // CEMI support by Calimero library, userful for newer serial devices like KNX RF sticks, kBerry, // etc.; default is still old EMI frame format - if (useCemi) { + if (useCemiL) { return KNXNetworkLinkFT12.newCemiLink(serialPort, new TPSettings()); } + return new KNXNetworkLinkFT12(serialPort, new TPSettings()); } catch (NoClassDefFoundError e) { throw new KNXException( - "The serial FT1.2 KNX connection requires the RXTX libraries to be available, but they could not be found!", + "The serial FT1.2 KNX connection requires the serial libraries to be available, but they could not be found!", e); } catch (KNXException e) { final String msg = e.getMessage();