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.channel;
15 import static java.util.stream.Collectors.*;
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;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
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.types.Type;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import tuwien.auto.calimero.GroupAddress;
39 import tuwien.auto.calimero.KNXFormatException;
42 * Meta-data abstraction for the KNX channel configurations.
44 * @author Simon Kaufmann - initial contribution and API.
48 public abstract class KNXChannelType {
50 private static final Pattern PATTERN = Pattern.compile(
51 "^((?<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}))*)$");
53 private static final Pattern PATTERN_LISTEN = Pattern
54 .compile("\\+((?<read>\\<)?(?<GA>[0-9]{1,5}(/[0-9]{1,4}){0,2}))");
56 private final Logger logger = LoggerFactory.getLogger(KNXChannelType.class);
57 private final Set<String> channelTypeIDs;
59 KNXChannelType(String... channelTypeIDs) {
60 this.channelTypeIDs = new HashSet<>(Arrays.asList(channelTypeIDs));
63 final Set<String> getChannelIDs() {
64 return channelTypeIDs;
68 protected final ChannelConfiguration parse(@Nullable String fancy) {
72 Matcher matcher = PATTERN.matcher(fancy.replace(" ", ""));
74 if (matcher.matches()) {
76 String input = matcher.group("listenGAs");
77 Matcher m2 = PATTERN_LISTEN.matcher(input);
78 List<GroupAddressConfiguration> listenGAs = new LinkedList<>();
80 listenGAs.add(new GroupAddressConfiguration(m2.group("GA"), m2.group("read") != null));
84 GroupAddressConfiguration mainGA = new GroupAddressConfiguration(matcher.group("mainGA"),
85 matcher.group("read") != null);
87 return new ChannelConfiguration(matcher.group("dpt"), mainGA, listenGAs);
92 protected abstract Set<String> getAllGAKeys();
94 public final Set<GroupAddress> getListenAddresses(Configuration channelConfiguration) {
95 Set<GroupAddress> ret = new HashSet<>();
96 for (String key : getAllGAKeys()) {
97 ChannelConfiguration conf = parse((String) channelConfiguration.get(key));
99 ret.addAll(conf.getListenGAs().stream().map(this::toGroupAddress).collect(toSet()));
105 public final Set<GroupAddress> getReadAddresses(Configuration channelConfiguration) {
106 Set<GroupAddress> ret = new HashSet<>();
107 for (String key : getAllGAKeys()) {
108 ChannelConfiguration conf = parse((String) channelConfiguration.get(key));
110 ret.addAll(conf.getReadGAs().stream().map(this::toGroupAddress).collect(toSet()));
116 public final Set<GroupAddress> getWriteAddresses(Configuration channelConfiguration) {
117 Set<GroupAddress> ret = new HashSet<>();
118 for (String key : getAllGAKeys()) {
119 ChannelConfiguration conf = parse((String) channelConfiguration.get(key));
121 GroupAddress ga = toGroupAddress(conf.getMainGA());
130 private @Nullable GroupAddress toGroupAddress(GroupAddressConfiguration ga) {
132 return new GroupAddress(ga.getGA());
133 } catch (KNXFormatException e) {
134 logger.warn("Could not parse group address '{}'", ga.getGA());
139 protected final Set<GroupAddress> getAddresses(@Nullable Configuration configuration, Iterable<String> addresses)
140 throws KNXFormatException {
141 Set<GroupAddress> ret = new HashSet<>();
142 for (String address : addresses) {
143 if (configuration != null && configuration.get(address) != null) {
144 ret.add(new GroupAddress((String) configuration.get(address)));
150 protected final boolean isEquals(@Nullable Configuration configuration, String address, GroupAddress groupAddress)
151 throws KNXFormatException {
152 if (configuration != null && configuration.get(address) != null) {
153 return Objects.equals(new GroupAddress((String) configuration.get(address)), groupAddress);
158 protected final Set<String> asSet(String... values) {
159 return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(values)));
162 public final @Nullable OutboundSpec getCommandSpec(Configuration configuration, KNXTypeMapper typeHelper,
163 Type command) throws KNXFormatException {
164 logger.trace("getCommandSpec testing Keys '{}' for command '{}'", getAllGAKeys(), command);
165 for (String key : getAllGAKeys()) {
166 ChannelConfiguration config = parse((String) configuration.get(key));
167 if (config != null) {
168 String dpt = config.getDPT();
170 dpt = getDefaultDPT(key);
172 Class<? extends Type> expectedTypeClass = typeHelper.toTypeClass(dpt);
173 if (expectedTypeClass != null) {
174 if (expectedTypeClass.isInstance(command)) {
176 "getCommandSpec key '{}' uses expectedTypeClass '{}' witch isInstance for command '{}' and dpt '{}'",
177 key, expectedTypeClass, command, dpt);
178 return new WriteSpecImpl(config, dpt, command);
183 logger.trace("getCommandSpec no Spec found!");
187 public final List<InboundSpec> getReadSpec(Configuration configuration) throws KNXFormatException {
188 return getAllGAKeys().stream()
189 .map(key -> new ReadRequestSpecImpl(parse((String) configuration.get(key)), getDefaultDPT(key)))
190 .filter(spec -> !spec.getGroupAddresses().isEmpty()).collect(toList());
193 public final @Nullable InboundSpec getListenSpec(Configuration configuration, GroupAddress groupAddress) {
194 Optional<ListenSpecImpl> result = getAllGAKeys().stream()
195 .map(key -> new ListenSpecImpl(parse((String) configuration.get(key)), getDefaultDPT(key)))
196 .filter(spec -> !spec.getGroupAddresses().isEmpty())
197 .filter(spec -> spec.getGroupAddresses().contains(groupAddress)).findFirst();
198 return result.isPresent() ? result.get() : null;
201 protected abstract String getDefaultDPT(String gaConfigKey);
203 public final @Nullable OutboundSpec getResponseSpec(Configuration configuration, GroupAddress groupAddress,
204 Type type) throws KNXFormatException {
205 Optional<ReadResponseSpecImpl> result = getAllGAKeys().stream()
206 .map(key -> new ReadResponseSpecImpl(parse((String) configuration.get(key)), getDefaultDPT(key), type))
207 .filter(spec -> groupAddress.equals(spec.getGroupAddress())).findFirst();
208 return result.isPresent() ? result.get() : null;
212 public String toString() {
213 return channelTypeIDs.toString();