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.binding.knx.internal.i18n.KNXTranslationProvider;
27 import org.openhab.core.net.NetworkAddressService;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import tuwien.auto.calimero.secure.KnxSecureException;
37 * The {@link IPBridgeThingHandler} is responsible for handling commands, which are
38 * sent to one of the channels. It implements a KNX/IP Gateway, that either acts a a
39 * conduit for other {@link DeviceThingHandler}s, or for Channels that are
40 * directly defined on the bridge
42 * @author Karel Goderis - Initial contribution
43 * @author Simon Kaufmann - Refactoring & cleanup
46 public class IPBridgeThingHandler extends KNXBridgeBaseThingHandler {
47 private static final String MODE_ROUTER = "ROUTER";
48 private static final String MODE_TUNNEL = "TUNNEL";
49 private static final String MODE_SECURE_ROUTER = "SECUREROUTER";
50 private static final String MODE_SECURE_TUNNEL = "SECURETUNNEL";
51 private @Nullable Future<?> initJob = null;
53 private final Logger logger = LoggerFactory.getLogger(IPBridgeThingHandler.class);
55 private @Nullable IPClient client = null;
56 private final NetworkAddressService networkAddressService;
58 public IPBridgeThingHandler(Bridge bridge, NetworkAddressService networkAddressService) {
60 this.networkAddressService = networkAddressService;
64 public void initialize() {
65 // initialisation would take too long and show a warning during binding startup
66 // KNX secure is adding serious delay
67 updateStatus(ThingStatus.UNKNOWN);
68 initJob = scheduler.submit(() -> {
73 public void initializeLater() {
74 IPBridgeConfiguration config = getConfigAs(IPBridgeConfiguration.class);
75 boolean securityAvailable = false;
77 securityAvailable = initializeSecurity(config.getRouterBackboneKey(),
78 config.getTunnelDeviceAuthentication(), config.getTunnelUserId(), config.getTunnelUserPassword());
79 if (securityAvailable) {
80 logger.debug("KNX secure: router backboneGroupKey is {} set",
81 ((secureRouting.backboneGroupKey.length == 16) ? "properly" : "not"));
82 boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
83 && (secureTunnel.userKey.length == 16));
84 logger.debug("KNX secure: tunnel keys are {} set", (tunnelOk ? "properly" : "not"));
86 logger.debug("KNX security not configured");
88 } catch (KnxSecureException e) {
89 logger.debug("{}, {}", thing.getUID(), e.toString());
91 Throwable cause = e.getCause();
95 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
96 KNXTranslationProvider.I18N.getLocalizedException(cause));
100 int autoReconnectPeriod = config.getAutoReconnectPeriod();
101 if (autoReconnectPeriod != 0 && autoReconnectPeriod < 30) {
102 logger.info("autoReconnectPeriod for {} set to {}s, allowed range is 0 (never) or >30", thing.getUID(),
103 autoReconnectPeriod);
104 autoReconnectPeriod = 30;
105 config.setAutoReconnectPeriod(autoReconnectPeriod);
107 String localSource = config.getLocalSourceAddr();
108 String connectionTypeString = config.getType();
109 int port = config.getPortNumber().intValue();
110 String ip = config.getIpAddress();
111 InetSocketAddress localEndPoint = null;
112 boolean useNAT = false;
114 IPClient.IpConnectionType ipConnectionType;
115 if (MODE_TUNNEL.equalsIgnoreCase(connectionTypeString)) {
116 useNAT = config.getUseNAT();
117 ipConnectionType = IPClient.IpConnectionType.TUNNEL;
118 } else if (MODE_SECURE_TUNNEL.equalsIgnoreCase(connectionTypeString)) {
119 useNAT = config.getUseNAT();
120 ipConnectionType = IPClient.IpConnectionType.SECURE_TUNNEL;
122 if (!securityAvailable) {
123 logger.warn("Bridge {} missing security configuration for secure tunnel", thing.getUID());
124 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
125 "@text/error.knx-secure-tunnel-config-missing");
128 boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
129 && (secureTunnel.userKey.length == 16));
131 logger.warn("Bridge {} incomplete security configuration for secure tunnel", thing.getUID());
132 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
133 "@text/error.knx-secure-tunnel-config-incomplete");
137 logger.debug("KNX secure tunneling needs a few seconds to establish connection");
138 // user id, key, devAuth are already stored
139 } else if (MODE_ROUTER.equalsIgnoreCase(connectionTypeString)) {
142 ip = KNXBindingConstants.DEFAULT_MULTICAST_IP;
144 ipConnectionType = IPClient.IpConnectionType.ROUTER;
145 } else if (MODE_SECURE_ROUTER.equalsIgnoreCase(connectionTypeString)) {
148 ip = KNXBindingConstants.DEFAULT_MULTICAST_IP;
150 ipConnectionType = IPClient.IpConnectionType.SECURE_ROUTER;
152 if (!securityAvailable) {
153 logger.warn("Bridge {} missing security configuration for secure routing", thing.getUID());
154 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
155 "@text/error.knx-secure-routing-config-missing");
158 if (secureRouting.backboneGroupKey.length != 16) {
159 // failed to read shared backbone group key from config
160 logger.warn("Bridge {} invalid security configuration for secure routing", thing.getUID());
161 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
162 "@text/error.knx-secure-routing-backbonegroupkey-invalid");
165 logger.debug("KNX secure routing needs a few seconds to establish connection");
167 logger.debug("Bridge {} unknown connection type", thing.getUID());
168 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
169 MessageFormat.format("@text/knx-unknown-ip-connection-type", connectionTypeString));
173 if (!config.getLocalIp().isEmpty()) {
174 localEndPoint = new InetSocketAddress(config.getLocalIp(), 0);
176 localEndPoint = new InetSocketAddress(networkAddressService.getPrimaryIpv4HostAddress(), 0);
179 updateStatus(ThingStatus.UNKNOWN);
180 client = new IPClient(ipConnectionType, ip, localSource, port, localEndPoint, useNAT, autoReconnectPeriod,
181 secureRouting.backboneGroupKey, secureRouting.latencyToleranceMs, secureTunnel.devKey,
182 secureTunnel.user, secureTunnel.userKey, thing.getUID(), config.getResponseTimeout().intValue(),
183 config.getReadingPause().intValue(), config.getReadRetriesLimit().intValue(), getScheduler(), this);
185 final var tmpClient = client;
186 if (tmpClient != null) {
187 tmpClient.initialize();
190 logger.trace("Bridge {} completed KNX scheduled initialization", thing.getUID());
194 public void dispose() {
195 final var tmpInitJob = initJob;
196 if (tmpInitJob != null) {
197 while (!tmpInitJob.isDone()) {
198 logger.trace("Bridge {}, shutdown during init, trying to cancel", thing.getUID());
199 tmpInitJob.cancel(true);
202 } catch (InterruptedException e) {
203 logger.trace("Bridge {}, cancellation interrupted", thing.getUID());
208 final var tmpClient = client;
209 if (tmpClient != null) {
217 protected KNXClient getClient() {
218 KNXClient ret = client;
220 return new NoOpClient();