]> git.basschouten.com Git - openhab-addons.git/blob
170499fa8db5f8cea69c78e0a557fec19c5abde5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.channel;
14
15 import static java.util.stream.Collectors.*;
16
17 import java.util.Arrays;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.LinkedList;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.Optional;
24 import java.util.Set;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import org.eclipse.jdt.annotation.NonNullByDefault;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.openhab.binding.knx.internal.KNXTypeMapper;
31 import org.openhab.binding.knx.internal.client.InboundSpec;
32 import org.openhab.binding.knx.internal.client.OutboundSpec;
33 import org.openhab.core.config.core.Configuration;
34 import org.openhab.core.library.types.DecimalType;
35 import org.openhab.core.library.types.QuantityType;
36 import org.openhab.core.types.Type;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import tuwien.auto.calimero.GroupAddress;
41 import tuwien.auto.calimero.KNXFormatException;
42
43 /**
44  * Meta-data abstraction for the KNX channel configurations.
45  *
46  * @author Simon Kaufmann - initial contribution and API.
47  *
48  */
49 @NonNullByDefault
50 public abstract class KNXChannelType {
51
52     private static final Pattern PATTERN = Pattern.compile(
53             "^((?<dpt>[0-9]{1,3}\\.[0-9]{3,4}):)?(?<read>\\<)?(?<mainGA>[0-9]{1,5}(/[0-9]{1,4}){0,2})(?<listenGAs>(\\+(\\<?[0-9]{1,5}(/[0-9]{1,4}){0,2}))*)$");
54
55     private static final Pattern PATTERN_LISTEN = Pattern
56             .compile("\\+((?<read>\\<)?(?<GA>[0-9]{1,5}(/[0-9]{1,4}){0,2}))");
57
58     private final Logger logger = LoggerFactory.getLogger(KNXChannelType.class);
59     private final Set<String> channelTypeIDs;
60
61     KNXChannelType(String... channelTypeIDs) {
62         this.channelTypeIDs = new HashSet<>(Arrays.asList(channelTypeIDs));
63     }
64
65     final Set<String> getChannelIDs() {
66         return channelTypeIDs;
67     }
68
69     @Nullable
70     protected final ChannelConfiguration parse(@Nullable String fancy) {
71         if (fancy == null) {
72             return null;
73         }
74         Matcher matcher = PATTERN.matcher(fancy.replace(" ", ""));
75
76         if (matcher.matches()) {
77             // Listen GAs
78             String input = matcher.group("listenGAs");
79             Matcher m2 = PATTERN_LISTEN.matcher(input);
80             List<GroupAddressConfiguration> listenGAs = new LinkedList<>();
81             while (m2.find()) {
82                 listenGAs.add(new GroupAddressConfiguration(m2.group("GA"), m2.group("read") != null));
83             }
84
85             // Main GA
86             GroupAddressConfiguration mainGA = new GroupAddressConfiguration(matcher.group("mainGA"),
87                     matcher.group("read") != null);
88
89             return new ChannelConfiguration(matcher.group("dpt"), mainGA, listenGAs);
90         }
91         return null;
92     }
93
94     protected abstract Set<String> getAllGAKeys();
95
96     public final Set<GroupAddress> getListenAddresses(Configuration channelConfiguration) {
97         Set<GroupAddress> ret = new HashSet<>();
98         for (String key : getAllGAKeys()) {
99             ChannelConfiguration conf = parse((String) channelConfiguration.get(key));
100             if (conf != null) {
101                 ret.addAll(conf.getListenGAs().stream().map(this::toGroupAddress).collect(toSet()));
102             }
103         }
104         return ret;
105     }
106
107     public final Set<GroupAddress> getReadAddresses(Configuration channelConfiguration) {
108         Set<GroupAddress> ret = new HashSet<>();
109         for (String key : getAllGAKeys()) {
110             ChannelConfiguration conf = parse((String) channelConfiguration.get(key));
111             if (conf != null) {
112                 ret.addAll(conf.getReadGAs().stream().map(this::toGroupAddress).collect(toSet()));
113             }
114         }
115         return ret;
116     }
117
118     public final Set<GroupAddress> getWriteAddresses(Configuration channelConfiguration) {
119         Set<GroupAddress> ret = new HashSet<>();
120         for (String key : getAllGAKeys()) {
121             ChannelConfiguration conf = parse((String) channelConfiguration.get(key));
122             if (conf != null) {
123                 GroupAddress ga = toGroupAddress(conf.getMainGA());
124                 if (ga != null) {
125                     ret.add(ga);
126                 }
127             }
128         }
129         return ret;
130     }
131
132     private @Nullable GroupAddress toGroupAddress(GroupAddressConfiguration ga) {
133         try {
134             return new GroupAddress(ga.getGA());
135         } catch (KNXFormatException e) {
136             logger.warn("Could not parse group address '{}'", ga.getGA());
137         }
138         return null;
139     }
140
141     protected final Set<GroupAddress> getAddresses(@Nullable Configuration configuration, Iterable<String> addresses)
142             throws KNXFormatException {
143         Set<GroupAddress> ret = new HashSet<>();
144         for (String address : addresses) {
145             if (configuration != null && configuration.get(address) != null) {
146                 ret.add(new GroupAddress((String) configuration.get(address)));
147             }
148         }
149         return ret;
150     }
151
152     protected final boolean isEquals(@Nullable Configuration configuration, String address, GroupAddress groupAddress)
153             throws KNXFormatException {
154         if (configuration != null && configuration.get(address) != null) {
155             return Objects.equals(new GroupAddress((String) configuration.get(address)), groupAddress);
156         }
157         return false;
158     }
159
160     protected final Set<String> asSet(String... values) {
161         return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(values)));
162     }
163
164     public final @Nullable OutboundSpec getCommandSpec(Configuration configuration, KNXTypeMapper typeHelper,
165             Type command) throws KNXFormatException {
166         logger.trace("getCommandSpec testing Keys '{}' for command '{}'", getAllGAKeys(), command);
167         for (String key : getAllGAKeys()) {
168             ChannelConfiguration config = parse((String) configuration.get(key));
169             if (config != null) {
170                 String dpt = config.getDPT();
171                 if (dpt == null) {
172                     dpt = getDefaultDPT(key);
173                 }
174                 Class<? extends Type> expectedTypeClass = typeHelper.toTypeClass(dpt);
175                 if (expectedTypeClass != null) {
176                     if (expectedTypeClass.isInstance(command)
177                             || ((expectedTypeClass == DecimalType.class) && (command instanceof QuantityType))) {
178                         logger.trace(
179                                 "getCommandSpec key '{}' uses expectedTypeClass '{}' which isInstance for command '{}' and dpt '{}'",
180                                 key, expectedTypeClass, command, dpt);
181                         return new WriteSpecImpl(config, dpt, command);
182                     }
183                 }
184             }
185         }
186         logger.trace("getCommandSpec no Spec found!");
187         return null;
188     }
189
190     public final List<InboundSpec> getReadSpec(Configuration configuration) throws KNXFormatException {
191         return getAllGAKeys().stream()
192                 .map(key -> new ReadRequestSpecImpl(parse((String) configuration.get(key)), getDefaultDPT(key)))
193                 .filter(spec -> !spec.getGroupAddresses().isEmpty()).collect(toList());
194     }
195
196     public final @Nullable InboundSpec getListenSpec(Configuration configuration, GroupAddress groupAddress) {
197         Optional<ListenSpecImpl> result = getAllGAKeys().stream()
198                 .map(key -> new ListenSpecImpl(parse((String) configuration.get(key)), getDefaultDPT(key)))
199                 .filter(spec -> !spec.getGroupAddresses().isEmpty())
200                 .filter(spec -> spec.getGroupAddresses().contains(groupAddress)).findFirst();
201         return result.isPresent() ? result.get() : null;
202     }
203
204     protected abstract String getDefaultDPT(String gaConfigKey);
205
206     public final @Nullable OutboundSpec getResponseSpec(Configuration configuration, GroupAddress groupAddress,
207             Type type) throws KNXFormatException {
208         Optional<ReadResponseSpecImpl> result = getAllGAKeys().stream()
209                 .map(key -> new ReadResponseSpecImpl(parse((String) configuration.get(key)), getDefaultDPT(key), type))
210                 .filter(spec -> groupAddress.equals(spec.getGroupAddress())).findFirst();
211         return result.isPresent() ? result.get() : null;
212     }
213
214     @Override
215     public String toString() {
216         return channelTypeIDs.toString();
217     }
218 }