2 * Copyright (c) 2010-2022 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.knx.internal.handler;
15 import java.net.InetSocketAddress;
16 import java.text.MessageFormat;
17 import java.util.concurrent.Future;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.knx.internal.KNXBindingConstants;
22 import org.openhab.binding.knx.internal.client.IPClient;
23 import org.openhab.binding.knx.internal.client.KNXClient;
24 import org.openhab.binding.knx.internal.client.NoOpClient;
25 import org.openhab.binding.knx.internal.config.IPBridgeConfiguration;
26 import org.openhab.core.net.NetworkAddressService;
27 import org.openhab.core.thing.Bridge;
28 import org.openhab.core.thing.ThingStatus;
29 import org.openhab.core.thing.ThingStatusDetail;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 import tuwien.auto.calimero.secure.KnxSecureException;
36 * The {@link IPBridgeThingHandler} is responsible for handling commands, which are
37 * sent to one of the channels. It implements a KNX/IP Gateway, that either acts a a
38 * conduit for other {@link DeviceThingHandler}s, or for Channels that are
39 * directly defined on the bridge
41 * @author Karel Goderis - Initial contribution
42 * @author Simon Kaufmann - Refactoring & cleanup
45 public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
46 private static final String MODE_ROUTER = "ROUTER";
47 private static final String MODE_TUNNEL = "TUNNEL";
48 private static final String MODE_SECURE_ROUTER = "SECUREROUTER";
49 private static final String MODE_SECURE_TUNNEL = "SECURETUNNEL";
50 private @Nullable Future<?> initJob = null;
52 private final Logger logger = LoggerFactory.getLogger(IPBridgeThingHandler.class);
54 private @Nullable IPClient client = null;
55 private final NetworkAddressService networkAddressService;
57 public IPBridgeThingHandler(Bridge bridge, NetworkAddressService networkAddressService) {
59 this.networkAddressService = networkAddressService;
63 public void initialize() {
64 // initialisation would take too long and show a warning during binding startup
65 // KNX secure is adding serious delay
66 updateStatus(ThingStatus.UNKNOWN);
67 initJob = scheduler.submit(() -> {
72 public void initializeLater() {
73 IPBridgeConfiguration config = getConfigAs(IPBridgeConfiguration.class);
74 boolean securityAvailable = false;
76 securityAvailable = initializeSecurity(config.getRouterBackboneKey(),
77 config.getTunnelDeviceAuthentication(), config.getTunnelUserId(), config.getTunnelUserPassword());
78 if (securityAvailable) {
79 logger.debug("KNX secure: router backboneGroupKey is {} set",
80 ((secureRouting.backboneGroupKey.length == 16) ? "properly" : "not"));
81 boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
82 && (secureTunnel.userKey.length == 16));
83 logger.debug("KNX secure: tunnel keys are {} set", (tunnelOk ? "properly" : "not"));
85 logger.debug("KNX security not configured");
87 } catch (KnxSecureException e) {
88 logger.debug("{}, {}", thing.getUID(), e.toString());
90 String message = e.getLocalizedMessage();
91 if (message == null) {
92 message = e.getClass().getSimpleName();
94 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "KNX security: " + message);
98 int autoReconnectPeriod = config.getAutoReconnectPeriod();
99 if (autoReconnectPeriod != 0 && autoReconnectPeriod < 30) {
100 logger.info("autoReconnectPeriod for {} set to {}s, allowed range is 0 (never) or >30", thing.getUID(),
101 autoReconnectPeriod);
102 autoReconnectPeriod = 30;
103 config.setAutoReconnectPeriod(autoReconnectPeriod);
105 String localSource = config.getLocalSourceAddr();
106 String connectionTypeString = config.getType();
107 int port = config.getPortNumber().intValue();
108 String ip = config.getIpAddress();
109 InetSocketAddress localEndPoint = null;
110 boolean useNAT = false;
112 IPClient.IpConnectionType ipConnectionType;
113 if (MODE_TUNNEL.equalsIgnoreCase(connectionTypeString)) {
114 useNAT = config.getUseNAT();
115 ipConnectionType = IPClient.IpConnectionType.TUNNEL;
116 } else if (MODE_SECURE_TUNNEL.equalsIgnoreCase(connectionTypeString)) {
117 useNAT = config.getUseNAT();
118 ipConnectionType = IPClient.IpConnectionType.SECURE_TUNNEL;
120 if (!securityAvailable) {
121 logger.warn("Bridge {} missing security configuration for secure tunnel", thing.getUID());
122 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
123 "Security configuration missing for secure tunnel");
126 boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
127 && (secureTunnel.userKey.length == 16));
129 logger.warn("Bridge {} incomplete security configuration for secure tunnel", thing.getUID());
130 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
131 "Security configuration for secure tunnel is incomplete");
135 logger.debug("KNX secure tunneling needs a few seconds to establish connection");
136 // user id, key, devAuth are already stored
137 } else if (MODE_ROUTER.equalsIgnoreCase(connectionTypeString)) {
140 ip = KNXBindingConstants.DEFAULT_MULTICAST_IP;
142 ipConnectionType = IPClient.IpConnectionType.ROUTER;
143 } else if (MODE_SECURE_ROUTER.equalsIgnoreCase(connectionTypeString)) {
146 ip = KNXBindingConstants.DEFAULT_MULTICAST_IP;
148 ipConnectionType = IPClient.IpConnectionType.SECURE_ROUTER;
150 if (!securityAvailable) {
151 logger.warn("Bridge {} missing security configuration for secure routing", thing.getUID());
152 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
153 "Security configuration missing for secure routing");
156 if (secureRouting.backboneGroupKey.length != 16) {
157 // failed to read shared backbone group key from config
158 logger.warn("Bridge {} missing security configuration for secure routing", thing.getUID());
159 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
160 "backboneGroupKey required for secure routing; please configure");
163 logger.debug("KNX secure routing needs a few seconds to establish connection");
165 logger.debug("Bridge {} unknown connection type", thing.getUID());
166 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, MessageFormat.format(
167 "Unknown IP connection type {0}. Known types are either 'TUNNEL', 'ROUTER', 'SECURETUNNEL', or 'SECUREROUTER'",
168 connectionTypeString));
172 if (!config.getLocalIp().isEmpty()) {
173 localEndPoint = new InetSocketAddress(config.getLocalIp(), 0);
175 localEndPoint = new InetSocketAddress(networkAddressService.getPrimaryIpv4HostAddress(), 0);
178 updateStatus(ThingStatus.UNKNOWN);
179 client = new IPClient(ipConnectionType, ip, localSource, port, localEndPoint, useNAT, autoReconnectPeriod,
180 secureRouting.backboneGroupKey, secureRouting.latencyToleranceMs, secureTunnel.devKey,
181 secureTunnel.user, secureTunnel.userKey, thing.getUID(), config.getResponseTimeout().intValue(),
182 config.getReadingPause().intValue(), config.getReadRetriesLimit().intValue(), getScheduler(), this);
184 final var tmpClient = client;
185 if (tmpClient != null) {
186 tmpClient.initialize();
189 logger.trace("Bridge {} completed KNX scheduled initialization", thing.getUID());
193 public void dispose() {
194 final var tmpInitJob = initJob;
195 if (tmpInitJob != null) {
196 while (!tmpInitJob.isDone()) {
197 logger.trace("Bridge {}, shutdown during init, trying to cancel", thing.getUID());
198 tmpInitJob.cancel(true);
201 } catch (InterruptedException e) {
202 logger.trace("Bridge {}, cancellation interrupted", thing.getUID());
207 final var tmpClient = client;
208 if (tmpClient != null) {
216 protected KNXClient getClient() {
217 KNXClient ret = client;
219 return new NoOpClient();