2 * Copyright (c) 2010-2024 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 as 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 and 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 @Nullable final NetworkAddressService networkAddressService;
58 public IPBridgeThingHandler(Bridge bridge, @Nullable NetworkAddressService networkAddressService) {
60 this.networkAddressService = networkAddressService;
64 public void initialize() {
65 // initialization 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(this::initializeLater);
71 public void initializeLater() {
72 IPBridgeConfiguration config = getConfigAs(IPBridgeConfiguration.class);
73 boolean securityAvailable = false;
75 securityAvailable = initializeSecurity(config.getRouterBackboneKey(),
76 config.getTunnelDeviceAuthentication(), config.getTunnelUserId(), config.getTunnelUserPassword());
77 if (securityAvailable) {
78 logger.debug("KNX secure: router backboneGroupKey is {} set",
79 ((secureRouting.backboneGroupKey.length == 16) ? "properly" : "not"));
80 boolean tunnelOk = ((secureTunnel.user > 0) && (secureTunnel.devKey.length == 16)
81 && (secureTunnel.userKey.length == 16));
82 logger.debug("KNX secure: tunnel keys are {} set", (tunnelOk ? "properly" : "not"));
84 logger.debug("KNX security not configured");
86 } catch (KnxSecureException e) {
87 logger.debug("{}, {}", thing.getUID(), e.toString());
89 Throwable cause = e.getCause();
93 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
94 KNXTranslationProvider.I18N.getLocalizedException(cause));
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();
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 "@text/error.knx-secure-tunnel-config-missing");
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 "@text/error.knx-secure-tunnel-config-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 "@text/error.knx-secure-routing-config-missing");
156 if (secureRouting.backboneGroupKey.length != 16) {
157 // failed to read shared backbone group key from config
158 logger.warn("Bridge {} invalid security configuration for secure routing", thing.getUID());
159 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
160 "@text/error.knx-secure-routing-backbonegroupkey-invalid");
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,
167 MessageFormat.format("@text/knx-unknown-ip-connection-type", connectionTypeString));
171 if (!config.getLocalIp().isEmpty()) {
172 localEndPoint = new InetSocketAddress(config.getLocalIp(), 0);
174 NetworkAddressService localNetworkAddressService = networkAddressService;
175 if (localNetworkAddressService == null) {
176 logger.debug("NetworkAddressService not available, cannot create bridge {}", thing.getUID());
177 updateStatus(ThingStatus.OFFLINE);
180 localEndPoint = new InetSocketAddress(localNetworkAddressService.getPrimaryIpv4HostAddress(), 0);
184 updateStatus(ThingStatus.UNKNOWN);
185 client = new IPClient(ipConnectionType, ip, localSource, port, localEndPoint, useNAT, autoReconnectPeriod,
186 secureRouting.backboneGroupKey, secureRouting.latencyToleranceMs, secureTunnel.devKey,
187 secureTunnel.user, secureTunnel.userKey, thing.getUID(), config.getResponseTimeout(),
188 config.getReadingPause(), config.getReadRetriesLimit(), getScheduler(), getCommandExtensionData(),
191 IPClient tmpClient = client;
192 if (tmpClient != null) {
193 tmpClient.initialize();
196 logger.trace("Bridge {} completed KNX scheduled initialization", thing.getUID());
200 public void dispose() {
201 Future<?> tmpInitJob = initJob;
202 if (tmpInitJob != null) {
203 while (!tmpInitJob.isDone()) {
204 logger.trace("Bridge {}, shutdown during init, trying to cancel", thing.getUID());
205 tmpInitJob.cancel(true);
208 } catch (InterruptedException e) {
209 logger.trace("Bridge {}, cancellation interrupted", thing.getUID());
214 IPClient tmpClient = client;
215 if (tmpClient != null) {
223 protected KNXClient getClient() {
224 KNXClient ret = client;
226 return new NoOpClient();