]> git.basschouten.com Git - openhab-addons.git/blob
dc07731bdfce8d5b22a1880acf7826c5c8f6a730
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.knx.internal.handler;
14
15 import java.util.Map;
16 import java.util.TreeMap;
17 import java.util.concurrent.Executors;
18 import java.util.concurrent.ScheduledExecutorService;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.knx.internal.client.KNXClient;
23 import org.openhab.binding.knx.internal.client.StatusUpdateCallback;
24 import org.openhab.core.common.ThreadPoolManager;
25 import org.openhab.core.thing.Bridge;
26 import org.openhab.core.thing.ChannelUID;
27 import org.openhab.core.thing.ThingStatus;
28 import org.openhab.core.thing.ThingStatusDetail;
29 import org.openhab.core.thing.binding.BaseBridgeHandler;
30 import org.openhab.core.types.Command;
31
32 import tuwien.auto.calimero.knxnetip.SecureConnection;
33 import tuwien.auto.calimero.secure.KnxSecureException;
34
35 /**
36  * The {@link KNXBridgeBaseThingHandler} is responsible for handling commands, which are
37  * sent to one of the channels.
38  *
39  * @author Simon Kaufmann - Initial contribution and API
40  * @author Holger Friedrich - KNX Secure configuration
41  */
42 @NonNullByDefault
43 public abstract class KNXBridgeBaseThingHandler extends BaseBridgeHandler implements StatusUpdateCallback {
44
45     public static class SecureTunnelConfig {
46         public SecureTunnelConfig() {
47             devKey = new byte[0];
48             userKey = new byte[0];
49             user = 0;
50         }
51
52         public byte[] devKey;
53         public byte[] userKey;
54         public int user = 0;
55     }
56
57     public static class SecureRoutingConfig {
58         public SecureRoutingConfig() {
59             backboneGroupKey = new byte[0];
60             latencyToleranceMs = 0;
61         }
62
63         public byte[] backboneGroupKey;
64         public long latencyToleranceMs = 0;
65     }
66
67     /**
68      * Helper class to carry information which can be used by the
69      * command line extension (openHAB console).
70      */
71     public record CommandExtensionData(Map<String, Long> unknownGA) {
72     }
73
74     private final ScheduledExecutorService knxScheduler = ThreadPoolManager.getScheduledPool("knx");
75     private final ScheduledExecutorService backgroundScheduler = Executors.newSingleThreadScheduledExecutor();
76     protected SecureRoutingConfig secureRouting;
77     protected SecureTunnelConfig secureTunnel;
78     private CommandExtensionData commandExtensionData;
79
80     public KNXBridgeBaseThingHandler(Bridge bridge) {
81         super(bridge);
82         secureRouting = new SecureRoutingConfig();
83         secureTunnel = new SecureTunnelConfig();
84         commandExtensionData = new CommandExtensionData(new TreeMap<>());
85     }
86
87     protected abstract KNXClient getClient();
88
89     public CommandExtensionData getCommandExtensionData() {
90         return commandExtensionData;
91     }
92
93     /***
94      * Initialize KNX secure if configured (full interface)
95      *
96      * @param cRouterBackboneGroupKey shared key for secure router mode.
97      * @param cTunnelDevAuth device password for IP interface in tunnel mode.
98      * @param cTunnelUser user id for tunnel mode. Must be an integer >0.
99      * @param cTunnelPassword user password for tunnel mode.
100      * @return
101      */
102     protected boolean initializeSecurity(String cRouterBackboneGroupKey, String cTunnelDevAuth, String cTunnelUser,
103             String cTunnelPassword) throws KnxSecureException {
104         secureRouting = new SecureRoutingConfig();
105         secureTunnel = new SecureTunnelConfig();
106
107         boolean securityInitialized = false;
108
109         // step 1: secure routing, backbone group key manually specified in OH config
110         if (!cRouterBackboneGroupKey.isBlank()) {
111             // provided in config
112             String key = cRouterBackboneGroupKey.trim().replaceFirst("^0x", "").trim().replace(" ", "");
113             if (!key.isEmpty()) {
114                 // helper may throw KnxSecureException
115                 secureRouting.backboneGroupKey = secHelperParseBackboneKey(key);
116                 securityInitialized = true;
117             }
118         }
119
120         // step 2: check if valid tunnel parameters are specified in config
121         if (!cTunnelDevAuth.isBlank()) {
122             secureTunnel.devKey = SecureConnection.hashDeviceAuthenticationPassword(cTunnelDevAuth.toCharArray());
123             securityInitialized = true;
124         }
125         if (!cTunnelPassword.isBlank()) {
126             secureTunnel.userKey = SecureConnection.hashUserPassword(cTunnelPassword.toCharArray());
127             securityInitialized = true;
128         }
129         if (!cTunnelUser.isBlank()) {
130             String user = cTunnelUser.trim();
131             try {
132                 secureTunnel.user = Integer.decode(user);
133             } catch (NumberFormatException e) {
134                 throw new KnxSecureException("tunnelUser must be a number >0");
135             }
136             if (secureTunnel.user <= 0) {
137                 throw new KnxSecureException("tunnelUser must be a number >0");
138             }
139             securityInitialized = true;
140         }
141
142         // step 5: router: load latencyTolerance
143         // default to 2000ms
144         // this parameter is currently not exposed in config, it may later be set by using the keyring
145         secureRouting.latencyToleranceMs = 2000;
146
147         return securityInitialized;
148     }
149
150     /***
151      * converts hex string (32 characters) to byte[16]
152      *
153      * @param hexstring 32 characters hex
154      * @return key in byte array format
155      */
156     public static byte[] secHelperParseBackboneKey(String hexstring) throws KnxSecureException {
157         if (hexstring.length() != 32) {
158             throw new KnxSecureException("backbone key must be 32 characters (16 byte hex notation)");
159         }
160
161         byte[] parsed = new byte[16];
162         try {
163             for (byte i = 0; i < 16; i++) {
164                 parsed[i] = (byte) Integer.parseInt(hexstring.substring(2 * i, 2 * i + 2), 16);
165             }
166         } catch (NumberFormatException e) {
167             throw new KnxSecureException("backbone key configured, cannot parse hex string, illegal character", e);
168         }
169         return parsed;
170     }
171
172     @Override
173     public void handleCommand(ChannelUID channelUID, Command command) {
174         // Nothing to do here
175     }
176
177     public ScheduledExecutorService getScheduler() {
178         return knxScheduler;
179     }
180
181     public ScheduledExecutorService getBackgroundScheduler() {
182         return backgroundScheduler;
183     }
184
185     @Override
186     public void updateStatus(ThingStatus status) {
187         super.updateStatus(status);
188     }
189
190     @Override
191     public void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) {
192         super.updateStatus(status, statusDetail, description);
193     }
194 }