From 7e678e55db7f6cecd6b4df3c1aa731363bfd3611 Mon Sep 17 00:00:00 2001 From: J-N-K Date: Tue, 22 Jun 2021 17:48:18 +0200 Subject: [PATCH] [knx] performance and resource improvements (#114) * improvements * performance improvements in DeviceThingHandler Signed-off-by: Jan N. Klug --- .../knx/internal/channel/AbstractSpec.java | 67 ----- .../channel/ChannelConfiguration.java | 59 ----- .../channel/GroupAddressConfiguration.java | 108 ++++++-- .../knx/internal/channel/KNXChannel.java | 140 ++++++++++ .../internal/channel/KNXChannelFactory.java | 64 +++++ .../knx/internal/channel/KNXChannelType.java | 170 ------------ .../knx/internal/channel/KNXChannelTypes.java | 59 ----- .../knx/internal/channel/ListenSpecImpl.java | 30 +-- .../internal/channel/ReadRequestSpecImpl.java | 28 +- .../channel/ReadResponseSpecImpl.java | 26 +- .../knx/internal/channel/TypeColor.java | 8 +- .../knx/internal/channel/TypeContact.java | 10 +- .../knx/internal/channel/TypeDateTime.java | 10 +- .../knx/internal/channel/TypeDimmer.java | 8 +- .../knx/internal/channel/TypeNumber.java | 10 +- .../internal/channel/TypeRollershutter.java | 9 +- .../knx/internal/channel/TypeString.java | 10 +- .../knx/internal/channel/TypeSwitch.java | 10 +- .../knx/internal/channel/WriteSpecImpl.java | 30 +-- .../internal/client/AbstractKNXClient.java | 11 +- .../knx/internal/client/InboundSpec.java | 4 +- .../knx/internal/client/OutboundSpec.java | 2 - .../internal/handler/DeviceThingHandler.java | 243 ++++++++---------- ...annelTypeTest.java => KNXChannelTest.java} | 77 +++--- 24 files changed, 560 insertions(+), 633 deletions(-) delete mode 100644 bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/AbstractSpec.java delete mode 100644 bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ChannelConfiguration.java create mode 100644 bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannel.java create mode 100644 bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelFactory.java delete mode 100644 bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelType.java delete mode 100644 bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypes.java rename bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/{KNXChannelTypeTest.java => KNXChannelTest.java} (52%) diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/AbstractSpec.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/AbstractSpec.java deleted file mode 100644 index 551a3e4135..0000000000 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/AbstractSpec.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Copyright (c) 2010-2021 Contributors to the openHAB project - * Copyright (c) 2021 Contributors to the SmartHome/J project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.smarthomej.binding.knx.internal.channel; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -import tuwien.auto.calimero.GroupAddress; -import tuwien.auto.calimero.KNXFormatException; - -/** - * Base class for telegram meta-data - * - * @author Simon Kaufmann - initial contribution and API. - * - */ -@NonNullByDefault -public abstract class AbstractSpec { - - private String dpt; - - protected AbstractSpec(@Nullable ChannelConfiguration channelConfiguration, String defaultDPT) { - if (channelConfiguration != null) { - String configuredDPT = channelConfiguration.getDPT(); - this.dpt = configuredDPT != null ? configuredDPT : defaultDPT; - } else { - this.dpt = defaultDPT; - } - } - - /** - * Helper method to convert a {@link GroupAddressConfiguration} into a {@link GroupAddress}. - * - * @param ga the group address configuration - * @return a group address object - */ - protected final GroupAddress toGroupAddress(GroupAddressConfiguration ga) { - try { - return new GroupAddress(ga.getGA()); - } catch (KNXFormatException e) { - throw new IllegalArgumentException(e); - } - } - - /** - * Return the data point type. - *

- * See {@link org.smarthomej.binding.knx.internal.client.InboundSpec#getDPT()} and - * {@link org.smarthomej.binding.knx.internal.client.OutboundSpec#getDPT()}. - * - * @return the data point type. - */ - public final String getDPT() { - return dpt; - } -} diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ChannelConfiguration.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ChannelConfiguration.java deleted file mode 100644 index ea0dfe01e6..0000000000 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ChannelConfiguration.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2010-2021 Contributors to the openHAB project - * Copyright (c) 2021 Contributors to the SmartHome/J project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.smarthomej.binding.knx.internal.channel; - -import static java.util.stream.Collectors.toList; - -import java.util.List; -import java.util.stream.Stream; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; - -/** - * Data structure representing the content of a channel's group address configuration. - * - * @author Simon Kaufmann - initial contribution and API. - * - */ -@NonNullByDefault -public class ChannelConfiguration { - - private final @Nullable String dpt; - private final GroupAddressConfiguration mainGA; - private final List listenGAs; - - public ChannelConfiguration(@Nullable String dpt, GroupAddressConfiguration mainGA, - List listenGAs) { - this.dpt = dpt; - this.mainGA = mainGA; - this.listenGAs = listenGAs; - } - - public @Nullable String getDPT() { - return dpt; - } - - public GroupAddressConfiguration getMainGA() { - return mainGA; - } - - public List getListenGAs() { - return Stream.concat(Stream.of(mainGA), listenGAs.stream()).collect(toList()); - } - - public List getReadGAs() { - return getListenGAs().stream().filter(ga -> ga.isRead()).collect(toList()); - } -} diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/GroupAddressConfiguration.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/GroupAddressConfiguration.java index 67653d35a8..055e7da676 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/GroupAddressConfiguration.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/GroupAddressConfiguration.java @@ -13,41 +13,107 @@ */ package org.smarthomej.binding.knx.internal.channel; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import tuwien.auto.calimero.GroupAddress; +import tuwien.auto.calimero.KNXFormatException; /** - * Data structure representing a single group address configuration within a channel configuration parameter. + * Data structure representing the content of a channel's group address configuration. * * @author Simon Kaufmann - initial contribution and API. * */ @NonNullByDefault public class GroupAddressConfiguration { + public static final Logger LOGGER = LoggerFactory.getLogger(GroupAddressConfiguration.class); + + private static final Pattern PATTERN_GA_CONFIGURATION = Pattern.compile( + "^((?[1-9][0-9]{0,2}\\.[0-9]{3,4}):)?(?\\<)?(?[0-9]{1,5}(/[0-9]{1,4}){0,2})(?(\\+(\\\\<)?(?[0-9]{1,5}(/[0-9]{1,4}){0,2}))"); + + private final @Nullable String dpt; + private final GroupAddress mainGA; + private final Set listenGAs; + private final Set readGAs; - private final String ga; - private final boolean read; + private GroupAddressConfiguration(@Nullable String dpt, GroupAddress mainGA, Set listenGAs, + Set readGAs) { + this.dpt = dpt; + this.mainGA = mainGA; + this.listenGAs = listenGAs; + this.readGAs = readGAs; + } + + public @Nullable String getDPT() { + return dpt; + } - public GroupAddressConfiguration(String ga, boolean read) { - super(); - this.ga = ga; - this.read = read; + public GroupAddress getMainGA() { + return mainGA; } - /** - * The group address. - * - * @return the group address. - */ - public String getGA() { - return ga; + public Set getListenGAs() { + return listenGAs; } - /** - * Denotes whether the group address is marked to be actively read from. - * - * @return {@code true} if read requests should be issued to this address - */ - public boolean isRead() { - return read; + public Set getReadGAs() { + return readGAs; + } + + public static @Nullable GroupAddressConfiguration parse(@Nullable Object configuration) { + if (!(configuration instanceof String)) { + return null; + } + + Matcher matcher = PATTERN_GA_CONFIGURATION.matcher(((String) configuration).replace(" ", "")); + if (matcher.matches()) { + // Listen GAs + String input = matcher.group("listenGAs"); + Matcher m2 = PATTERN_LISTEN_GA.matcher(input); + Set listenGAs = new HashSet<>(); + Set readGAs = new HashSet<>(); + while (m2.find()) { + String ga = m2.group("GA"); + try { + GroupAddress groupAddress = new GroupAddress(ga); + listenGAs.add(groupAddress); + if (m2.group("read") != null) { + readGAs.add(groupAddress); + } + } catch (KNXFormatException e) { + LOGGER.warn("Failed to create GroupAddress from {}", ga); + return null; + } + } + + // Main GA + String mainGA = matcher.group("mainGA"); + try { + GroupAddress groupAddress = new GroupAddress(mainGA); + listenGAs.add(groupAddress); // also listening to main GA + if (matcher.group("read") != null) { + readGAs.add(groupAddress); // also reading main GA + } + return new GroupAddressConfiguration(matcher.group("dpt"), groupAddress, listenGAs, readGAs); + } catch (KNXFormatException e) { + LOGGER.warn("Failed to create GroupAddress from {}", mainGA); + return null; + } + + } else { + LOGGER.warn("Failed parsing channel configuration '{}'.", configuration); + } + + return null; } } diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannel.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannel.java new file mode 100644 index 0000000000..6b54548d9d --- /dev/null +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannel.java @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2021 Contributors to the SmartHome/J project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.smarthomej.binding.knx.internal.channel; + +import static java.util.stream.Collectors.*; +import static org.smarthomej.binding.knx.internal.KNXBindingConstants.CONTROL_CHANNEL_TYPES; +import static org.smarthomej.binding.knx.internal.KNXBindingConstants.GA; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.Type; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.smarthomej.binding.knx.internal.client.InboundSpec; +import org.smarthomej.binding.knx.internal.client.OutboundSpec; +import org.smarthomej.binding.knx.internal.dpt.KNXCoreTypeMapper; + +import tuwien.auto.calimero.GroupAddress; + +/** + * Meta-data abstraction for the KNX channel configurations. + * + * @author Simon Kaufmann - initial contribution and API + * @author Jan N. Klug - refactored from type definition to channel instance + * + */ +@NonNullByDefault +public abstract class KNXChannel { + private final Logger logger = LoggerFactory.getLogger(KNXChannel.class); + private final Set gaKeys; + + private final Map groupAddressConfigurations = new HashMap<>(); + private final Set listenAddresses = new HashSet<>(); + private final Set writeAddresses = new HashSet<>(); + private final String channelType; + private final ChannelUID channelUID; + private final boolean isControl; + + KNXChannel(Channel channel) { + this(Set.of(GA), channel); + } + + KNXChannel(Set gaKeys, Channel channel) { + this.gaKeys = gaKeys; + + // this is safe because we already checked the presence of the ChannelTypeUID before + this.channelType = Objects.requireNonNull(channel.getChannelTypeUID()).getId(); + this.channelUID = channel.getUID(); + this.isControl = CONTROL_CHANNEL_TYPES.contains(channelType); + + // build map of ChannelConfigurations and GA lists + Configuration configuration = channel.getConfiguration(); + gaKeys.forEach(key -> { + GroupAddressConfiguration groupAddressConfiguration = GroupAddressConfiguration + .parse(configuration.get(key)); + if (groupAddressConfiguration != null) { + groupAddressConfigurations.put(key, groupAddressConfiguration); + // store address configuration for re-use + listenAddresses.addAll(groupAddressConfiguration.getListenGAs()); + writeAddresses.add(groupAddressConfiguration.getMainGA()); + } + }); + } + + public String getChannelType() { + return channelType; + } + + public ChannelUID getChannelUID() { + return channelUID; + } + + public boolean isControl() { + return isControl; + } + + public final Set getAllGroupAddresses() { + return listenAddresses; + } + + public final Set getWriteAddresses() { + return writeAddresses; + } + + public final @Nullable OutboundSpec getCommandSpec(Type command) { + logger.trace("getCommandSpec checking keys '{}' for command '{}' ({})", gaKeys, command, command.getClass()); + for (Map.Entry entry : groupAddressConfigurations.entrySet()) { + String dpt = Objects.requireNonNullElse(entry.getValue().getDPT(), getDefaultDPT(entry.getKey())); + Set> expectedTypeClass = KNXCoreTypeMapper.getAllowedTypes(dpt); + if (expectedTypeClass.contains(command.getClass())) { + logger.trace("getCommandSpec key '{}' has expectedTypeClass '{}', matching command '{}' and dpt '{}'", + entry.getKey(), expectedTypeClass, command, dpt); + return new WriteSpecImpl(entry.getValue(), dpt, command); + } + } + logger.trace("getCommandSpec no Spec found!"); + return null; + } + + public final List getReadSpec() { + return groupAddressConfigurations.entrySet().stream() + .map(entry -> new ReadRequestSpecImpl(entry.getValue(), getDefaultDPT(entry.getKey()))) + .filter(spec -> !spec.getGroupAddresses().isEmpty()).collect(toList()); + } + + public final @Nullable InboundSpec getListenSpec(GroupAddress groupAddress) { + return groupAddressConfigurations.entrySet().stream() + .map(entry -> new ListenSpecImpl(entry.getValue(), getDefaultDPT(entry.getKey()))) + .filter(spec -> spec.getGroupAddresses().contains(groupAddress)).findFirst().orElse(null); + } + + public final @Nullable OutboundSpec getResponseSpec(GroupAddress groupAddress, Type value) { + return groupAddressConfigurations.entrySet().stream() + .map(entry -> new ReadResponseSpecImpl(entry.getValue(), getDefaultDPT(entry.getKey()), value)) + .filter(spec -> spec.matchesDestination(groupAddress)).findFirst().orElse(null); + } + + protected abstract String getDefaultDPT(String gaConfigKey); +} diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelFactory.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelFactory.java new file mode 100644 index 0000000000..f57c0b12b9 --- /dev/null +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelFactory.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2010-2021 Contributors to the openHAB project + * Copyright (c) 2021 Contributors to the SmartHome/J project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.smarthomej.binding.knx.internal.channel; + +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * Helper class to find the matching {@link KNXChannel} for any given {@link ChannelTypeUID}. + * + * @author Simon Kaufmann - initial contribution and API + * @author Jan N. Klug - refactored ro factory class + * + */ +@NonNullByDefault +public final class KNXChannelFactory { + + private static final Map, Function> TYPES = Map.ofEntries( // + Map.entry(TypeColor.SUPPORTED_CHANNEL_TYPES, TypeColor::new), // + Map.entry(TypeContact.SUPPORTED_CHANNEL_TYPES, TypeContact::new), // + Map.entry(TypeDateTime.SUPPORTED_CHANNEL_TYPES, TypeDateTime::new), // + Map.entry(TypeDimmer.SUPPORTED_CHANNEL_TYPES, TypeDimmer::new), // + Map.entry(TypeNumber.SUPPORTED_CHANNEL_TYPES, TypeNumber::new), // + Map.entry(TypeRollershutter.SUPPORTED_CHANNEL_TYPES, TypeRollershutter::new), // + Map.entry(TypeString.SUPPORTED_CHANNEL_TYPES, TypeString::new), // + Map.entry(TypeSwitch.SUPPORTED_CHANNEL_TYPES, TypeSwitch::new)); + + private KNXChannelFactory() { + // prevent instantiation + } + + public static KNXChannel createKnxChannel(Channel channel) throws IllegalArgumentException { + ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); + if (channelTypeUID == null) { + throw new IllegalArgumentException("Could not determine ChannelTypeUID for channel " + channel.getUID()); + } + + String channelType = channelTypeUID.getId(); + + Function supplier = TYPES.entrySet().stream().filter(e -> e.getKey().contains(channelType)) + .map(Map.Entry::getValue).findFirst() + .orElseThrow(() -> new IllegalArgumentException(channelTypeUID + " is not a valid channel type ID")); + + KNXChannel knxChannel = supplier.apply(channel); + + return knxChannel; + } +} diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelType.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelType.java deleted file mode 100644 index 7484a80074..0000000000 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelType.java +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright (c) 2010-2021 Contributors to the openHAB project - * Copyright (c) 2021 Contributors to the SmartHome/J project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.smarthomej.binding.knx.internal.channel; - -import static java.util.stream.Collectors.*; -import static org.smarthomej.binding.knx.internal.KNXBindingConstants.GA; - -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.config.core.Configuration; -import org.openhab.core.types.Type; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.smarthomej.binding.knx.internal.client.InboundSpec; -import org.smarthomej.binding.knx.internal.client.OutboundSpec; -import org.smarthomej.binding.knx.internal.dpt.KNXCoreTypeMapper; - -import tuwien.auto.calimero.GroupAddress; -import tuwien.auto.calimero.KNXFormatException; - -/** - * Meta-data abstraction for the KNX channel configurations. - * - * @author Simon Kaufmann - initial contribution and API. - * - */ -@NonNullByDefault -public abstract class KNXChannelType { - - private static final Pattern PATTERN = Pattern.compile( - "^((?[1-9][0-9]{0,2}\\.[0-9]{3,4}):)?(?\\<)?(?[0-9]{1,5}(/[0-9]{1,4}){0,2})(?(\\+(\\\\<)?(?[0-9]{1,5}(/[0-9]{1,4}){0,2}))"); - - private final Logger logger = LoggerFactory.getLogger(KNXChannelType.class); - private final Set channelTypeIDs; - private final Set gaKeys; - - KNXChannelType(String... channelTypeIDs) { - this(Set.of(GA), channelTypeIDs); - } - - KNXChannelType(Set gaKeys, String... channelTypeIDs) { - this.gaKeys = gaKeys; - this.channelTypeIDs = Set.of(channelTypeIDs); - } - - final Set getChannelIDs() { - return channelTypeIDs; - } - - @Nullable - protected final ChannelConfiguration parse(@Nullable Object fancy) { - if (!(fancy instanceof String)) { - return null; - } - Matcher matcher = PATTERN.matcher(((String) fancy).replace(" ", "")); - - if (matcher.matches()) { - // Listen GAs - String input = matcher.group("listenGAs"); - Matcher m2 = PATTERN_LISTEN.matcher(input); - List listenGAs = new LinkedList<>(); - while (m2.find()) { - listenGAs.add(new GroupAddressConfiguration(m2.group("GA"), m2.group("read") != null)); - } - - // Main GA - GroupAddressConfiguration mainGA = new GroupAddressConfiguration(matcher.group("mainGA"), - matcher.group("read") != null); - - return new ChannelConfiguration(matcher.group("dpt"), mainGA, listenGAs); - } else { - logger.warn("Failed parsing channel configuration '{}'.", fancy); - } - - return null; - } - - private Set filterGroupAddresses(Configuration channelConfiguration, - Function> filter) { - return gaKeys.stream().map(channelConfiguration::get).map(this::parse).filter(Objects::nonNull) - .map(Objects::requireNonNull).map(filter).flatMap(List::stream).map(this::toGroupAddress) - .flatMap(Optional::stream).collect(Collectors.toSet()); - } - - public final Set getAllGroupAddresses(Configuration channelConfiguration) { - return filterGroupAddresses(channelConfiguration, ChannelConfiguration::getListenGAs); - } - - public final Set getWriteAddresses(Configuration channelConfiguration) { - return filterGroupAddresses(channelConfiguration, configuration -> List.of(configuration.getMainGA())); - } - - private Optional toGroupAddress(GroupAddressConfiguration ga) { - try { - return Optional.of(new GroupAddress(ga.getGA())); - } catch (KNXFormatException e) { - logger.warn("Could not parse group address '{}'", ga.getGA()); - } - return Optional.empty(); - } - - public final @Nullable OutboundSpec getCommandSpec(Configuration configuration, Type command) - throws KNXFormatException { - logger.trace("getCommandSpec checking keys '{}' for command '{}' ({})", gaKeys, command, command.getClass()); - for (String key : gaKeys) { - ChannelConfiguration config = parse(configuration.get(key)); - if (config != null) { - String dpt = Objects.requireNonNullElse(config.getDPT(), getDefaultDPT(key)); - Set> expectedTypeClass = KNXCoreTypeMapper.getAllowedTypes(dpt); - if (expectedTypeClass.contains(command.getClass())) { - logger.trace( - "getCommandSpec key '{}' has expectedTypeClass '{}', matching command '{}' and dpt '{}'", - key, expectedTypeClass, command, dpt); - return new WriteSpecImpl(config, dpt, command); - } - } - } - logger.trace("getCommandSpec no Spec found!"); - return null; - } - - public final List getReadSpec(Configuration configuration) throws KNXFormatException { - return gaKeys.stream().map(key -> new ReadRequestSpecImpl(parse(configuration.get(key)), getDefaultDPT(key))) - .filter(spec -> !spec.getGroupAddresses().isEmpty()).collect(toList()); - } - - public final @Nullable InboundSpec getListenSpec(Configuration configuration, GroupAddress groupAddress) { - return gaKeys.stream().map(key -> new ListenSpecImpl(parse(configuration.get(key)), getDefaultDPT(key))) - .filter(spec -> !spec.getGroupAddresses().isEmpty()) - .filter(spec -> spec.getGroupAddresses().contains(groupAddress)).findFirst().orElse(null); - } - - public final @Nullable OutboundSpec getResponseSpec(Configuration configuration, GroupAddress groupAddress, - Type value) { - return gaKeys.stream() - .map(key -> new ReadResponseSpecImpl(parse(configuration.get(key)), getDefaultDPT(key), value)) - .filter(spec -> spec.matchesDestination(groupAddress)).findFirst().orElse(null); - } - - protected abstract String getDefaultDPT(String gaConfigKey); - - @Override - public String toString() { - return channelTypeIDs.toString(); - } -} diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypes.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypes.java deleted file mode 100644 index f231172509..0000000000 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypes.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2010-2021 Contributors to the openHAB project - * Copyright (c) 2021 Contributors to the SmartHome/J project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.smarthomej.binding.knx.internal.channel; - -import java.util.Set; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.thing.Channel; -import org.openhab.core.thing.type.ChannelTypeUID; - -/** - * Helper class to find the matching {@link KNXChannelType} for any given {@link ChannelTypeUID}. - * - * @author Simon Kaufmann - initial contribution and API. - * - */ -@NonNullByDefault -public final class KNXChannelTypes { - - private static final Set TYPES = Set.of(// - new TypeColor(), // - new TypeContact(), // - new TypeDateTime(), // - new TypeDimmer(), // - new TypeNumber(), // - new TypeRollershutter(), // - new TypeString(), // - new TypeSwitch() // - ); - - private KNXChannelTypes() { - // prevent instantiation - } - - public static KNXChannelType getKnxChannelType(Channel channel) throws IllegalArgumentException { - ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); - if (channelTypeUID == null) { - throw new IllegalArgumentException("Could not determine ChannelTypeUID for channel " + channel.getUID()); - } - - for (KNXChannelType c : TYPES) { - if (c.getChannelIDs().contains(channelTypeUID.getId())) { - return c; - } - } - throw new IllegalArgumentException(channelTypeUID.getId() + " is not a valid value channel type ID"); - } -} diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ListenSpecImpl.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ListenSpecImpl.java index e4a36e9986..48a1e7bea5 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ListenSpecImpl.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ListenSpecImpl.java @@ -13,13 +13,10 @@ */ package org.smarthomej.binding.knx.internal.channel; -import static java.util.stream.Collectors.toList; - -import java.util.Collections; -import java.util.List; +import java.util.Objects; +import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.smarthomej.binding.knx.internal.client.InboundSpec; import tuwien.auto.calimero.GroupAddress; @@ -31,21 +28,22 @@ * */ @NonNullByDefault -public class ListenSpecImpl extends AbstractSpec implements InboundSpec { +public class ListenSpecImpl implements InboundSpec { + private final String dpt; + private final Set listenAddresses; - private final List listenAddresses; + public ListenSpecImpl(GroupAddressConfiguration groupAddressConfiguration, String defaultDPT) { + this.dpt = Objects.requireNonNullElse(groupAddressConfiguration.getDPT(), defaultDPT); + this.listenAddresses = groupAddressConfiguration.getListenGAs(); + } - public ListenSpecImpl(@Nullable ChannelConfiguration channelConfiguration, String defaultDPT) { - super(channelConfiguration, defaultDPT); - if (channelConfiguration != null) { - this.listenAddresses = channelConfiguration.getListenGAs().stream().map(this::toGroupAddress) - .collect(toList()); - } else { - this.listenAddresses = Collections.emptyList(); - } + @Override + public String getDPT() { + return dpt; } - public List getGroupAddresses() { + @Override + public Set getGroupAddresses() { return listenAddresses; } } diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadRequestSpecImpl.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadRequestSpecImpl.java index 20a0a6956e..103aa74982 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadRequestSpecImpl.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadRequestSpecImpl.java @@ -13,13 +13,10 @@ */ package org.smarthomej.binding.knx.internal.channel; -import static java.util.stream.Collectors.toList; - -import java.util.Collections; -import java.util.List; +import java.util.Objects; +import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.smarthomej.binding.knx.internal.client.InboundSpec; import tuwien.auto.calimero.GroupAddress; @@ -31,21 +28,22 @@ * */ @NonNullByDefault -public class ReadRequestSpecImpl extends AbstractSpec implements InboundSpec { +public class ReadRequestSpecImpl implements InboundSpec { + private final String dpt; + private final Set readAddresses; - private final List readAddresses; + public ReadRequestSpecImpl(GroupAddressConfiguration groupAddressConfiguration, String defaultDPT) { + this.dpt = Objects.requireNonNullElse(groupAddressConfiguration.getDPT(), defaultDPT); + this.readAddresses = groupAddressConfiguration.getReadGAs(); + } - public ReadRequestSpecImpl(@Nullable ChannelConfiguration channelConfiguration, String defaultDPT) { - super(channelConfiguration, defaultDPT); - if (channelConfiguration != null) { - this.readAddresses = channelConfiguration.getReadGAs().stream().map(this::toGroupAddress).collect(toList()); - } else { - this.readAddresses = Collections.emptyList(); - } + @Override + public String getDPT() { + return dpt; } @Override - public List getGroupAddresses() { + public Set getGroupAddresses() { return readAddresses; } } diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadResponseSpecImpl.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadResponseSpecImpl.java index 5f5aebca13..aefa0d100d 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadResponseSpecImpl.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/ReadResponseSpecImpl.java @@ -13,8 +13,9 @@ */ package org.smarthomej.binding.knx.internal.channel; +import java.util.Objects; + import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.types.Type; import org.smarthomej.binding.knx.internal.client.OutboundSpec; @@ -27,23 +28,24 @@ * */ @NonNullByDefault -public class ReadResponseSpecImpl extends AbstractSpec implements OutboundSpec { - - private final @Nullable GroupAddress groupAddress; +public class ReadResponseSpecImpl implements OutboundSpec { + private final String dpt; + private final GroupAddress groupAddress; private final Type value; - public ReadResponseSpecImpl(@Nullable ChannelConfiguration channelConfiguration, String defaultDPT, Type state) { - super(channelConfiguration, defaultDPT); - if (channelConfiguration != null) { - this.groupAddress = toGroupAddress(channelConfiguration.getMainGA()); - } else { - this.groupAddress = null; - } + public ReadResponseSpecImpl(GroupAddressConfiguration groupAddressConfiguration, String defaultDPT, Type state) { + this.dpt = Objects.requireNonNullElse(groupAddressConfiguration.getDPT(), defaultDPT); + this.groupAddress = groupAddressConfiguration.getMainGA(); this.value = state; } @Override - public @Nullable GroupAddress getGroupAddress() { + public String getDPT() { + return dpt; + } + + @Override + public GroupAddress getGroupAddress() { return groupAddress; } diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeColor.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeColor.java index 33d16f0548..8cb2777ac0 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeColor.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeColor.java @@ -18,6 +18,7 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlator3BitControlled; import tuwien.auto.calimero.dptxlator.DPTXlator8BitUnsigned; @@ -31,10 +32,11 @@ * */ @NonNullByDefault -class TypeColor extends KNXChannelType { +class TypeColor extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_COLOR, CHANNEL_COLOR_CONTROL); - TypeColor() { - super(Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA, HSB_GA), CHANNEL_COLOR, CHANNEL_COLOR_CONTROL); + TypeColor(Channel channel) { + super(Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA, HSB_GA), channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeContact.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeContact.java index 96caf014a3..d3360f1530 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeContact.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeContact.java @@ -15,7 +15,10 @@ import static org.smarthomej.binding.knx.internal.KNXBindingConstants.*; +import java.util.Set; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean; @@ -26,10 +29,11 @@ * */ @NonNullByDefault -class TypeContact extends KNXChannelType { +class TypeContact extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_CONTACT, CHANNEL_CONTACT_CONTROL); - TypeContact() { - super(CHANNEL_CONTACT, CHANNEL_CONTACT_CONTROL); + TypeContact(Channel channel) { + super(channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDateTime.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDateTime.java index baad286d7b..2896d774a9 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDateTime.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDateTime.java @@ -15,7 +15,10 @@ import static org.smarthomej.binding.knx.internal.KNXBindingConstants.*; +import java.util.Set; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlatorDateTime; @@ -26,10 +29,11 @@ * */ @NonNullByDefault -class TypeDateTime extends KNXChannelType { +class TypeDateTime extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_DATETIME, CHANNEL_DATETIME_CONTROL); - TypeDateTime() { - super(CHANNEL_DATETIME, CHANNEL_DATETIME_CONTROL); + TypeDateTime(Channel channel) { + super(channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDimmer.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDimmer.java index ec09834394..c3a124ebf0 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDimmer.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeDimmer.java @@ -19,6 +19,7 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlator3BitControlled; import tuwien.auto.calimero.dptxlator.DPTXlator8BitUnsigned; @@ -31,10 +32,11 @@ * */ @NonNullByDefault -class TypeDimmer extends KNXChannelType { +class TypeDimmer extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_DIMMER, CHANNEL_DIMMER_CONTROL); - TypeDimmer() { - super(Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA), CHANNEL_DIMMER, CHANNEL_DIMMER_CONTROL); + TypeDimmer(Channel channel) { + super(Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA), channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeNumber.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeNumber.java index 1b176b9a53..60b9b7de7c 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeNumber.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeNumber.java @@ -15,7 +15,10 @@ import static org.smarthomej.binding.knx.internal.KNXBindingConstants.*; +import java.util.Set; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; /** * number channel type description @@ -24,10 +27,11 @@ * */ @NonNullByDefault -class TypeNumber extends KNXChannelType { +class TypeNumber extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_NUMBER, CHANNEL_NUMBER_CONTROL); - TypeNumber() { - super(CHANNEL_NUMBER, CHANNEL_NUMBER_CONTROL); + TypeNumber(Channel channel) { + super(channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeRollershutter.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeRollershutter.java index bd43d5b966..3b16a895d9 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeRollershutter.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeRollershutter.java @@ -19,6 +19,7 @@ import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlator8BitUnsigned; import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean; @@ -30,10 +31,12 @@ * */ @NonNullByDefault -class TypeRollershutter extends KNXChannelType { +class TypeRollershutter extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_ROLLERSHUTTER, + CHANNEL_ROLLERSHUTTER_CONTROL); - TypeRollershutter() { - super(Set.of(UP_DOWN_GA, STOP_MOVE_GA, POSITION_GA), CHANNEL_ROLLERSHUTTER, CHANNEL_ROLLERSHUTTER_CONTROL); + TypeRollershutter(Channel channel) { + super(Set.of(UP_DOWN_GA, STOP_MOVE_GA, POSITION_GA), channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeString.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeString.java index 19f98cf833..873c56e1ac 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeString.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeString.java @@ -15,7 +15,10 @@ import static org.smarthomej.binding.knx.internal.KNXBindingConstants.*; +import java.util.Set; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlatorString; @@ -26,10 +29,11 @@ * */ @NonNullByDefault -class TypeString extends KNXChannelType { +class TypeString extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_STRING, CHANNEL_STRING_CONTROL); - TypeString() { - super(CHANNEL_STRING, CHANNEL_STRING_CONTROL); + TypeString(Channel channel) { + super(channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeSwitch.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeSwitch.java index 30aab99f51..b10932d7e7 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeSwitch.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/TypeSwitch.java @@ -15,7 +15,10 @@ import static org.smarthomej.binding.knx.internal.KNXBindingConstants.*; +import java.util.Set; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.Channel; import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean; @@ -26,10 +29,11 @@ * */ @NonNullByDefault -class TypeSwitch extends KNXChannelType { +class TypeSwitch extends KNXChannel { + public static final Set SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_SWITCH, CHANNEL_SWITCH_CONTROL); - TypeSwitch() { - super(CHANNEL_SWITCH, CHANNEL_SWITCH_CONTROL); + TypeSwitch(Channel channel) { + super(channel); } @Override diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/WriteSpecImpl.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/WriteSpecImpl.java index 3e01278f25..4eb193f845 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/WriteSpecImpl.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/channel/WriteSpecImpl.java @@ -13,13 +13,13 @@ */ package org.smarthomej.binding.knx.internal.channel; +import java.util.Objects; + import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.types.Type; import org.smarthomej.binding.knx.internal.client.OutboundSpec; import tuwien.auto.calimero.GroupAddress; -import tuwien.auto.calimero.KNXFormatException; /** * Command meta-data @@ -28,29 +28,29 @@ * */ @NonNullByDefault -public class WriteSpecImpl extends AbstractSpec implements OutboundSpec { - +public class WriteSpecImpl implements OutboundSpec { + private String dpt; private final Type value; - private final @Nullable GroupAddress groupAddress; - - public WriteSpecImpl(@Nullable ChannelConfiguration channelConfiguration, String defaultDPT, Type value) - throws KNXFormatException { - super(channelConfiguration, defaultDPT); - if (channelConfiguration != null) { - this.groupAddress = new GroupAddress(channelConfiguration.getMainGA().getGA()); - } else { - this.groupAddress = null; - } + private final GroupAddress groupAddress; + + public WriteSpecImpl(GroupAddressConfiguration groupAddressConfiguration, String defaultDPT, Type value) { + this.dpt = Objects.requireNonNullElse(groupAddressConfiguration.getDPT(), defaultDPT); + this.groupAddress = groupAddressConfiguration.getMainGA(); this.value = value; } + @Override + public String getDPT() { + return dpt; + } + @Override public Type getValue() { return value; } @Override - public @Nullable GroupAddress getGroupAddress() { + public GroupAddress getGroupAddress() { return groupAddress; } diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/AbstractKNXClient.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/AbstractKNXClient.java index 037b13b47e..46eaba2e09 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/AbstractKNXClient.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/AbstractKNXClient.java @@ -285,7 +285,6 @@ private void readNextQueuedDatapoint() { } } catch (InterruptedException e) { logger.debug("Interrupted sending KNX read request"); - return; } } } @@ -395,7 +394,7 @@ public void writeToKNX(OutboundSpec commandSpec) throws KNXException { ProcessCommunicator processCommunicator = this.processCommunicator; KNXNetworkLink link = this.link; if (processCommunicator == null || link == null) { - logger.debug("Cannot write to the KNX bus (processCommuicator: {}, link: {})", + logger.debug("Cannot write to the KNX bus (processCommunicator: {}, link: {})", processCommunicator == null ? "Not OK" : "OK", link == null ? "Not OK" : (link.isOpen() ? "Open" : "Closed")); return; @@ -404,9 +403,7 @@ public void writeToKNX(OutboundSpec commandSpec) throws KNXException { logger.trace("writeToKNX groupAddress '{}', commandSpec '{}'", groupAddress, commandSpec); - if (groupAddress != null) { - sendToKNX(processCommunicator, link, groupAddress, commandSpec.getDPT(), commandSpec.getValue()); - } + sendToKNX(processCommunicator, link, groupAddress, commandSpec.getDPT(), commandSpec.getValue()); } @Override @@ -423,9 +420,7 @@ public void respondToKNX(OutboundSpec responseSpec) throws KNXException { logger.trace("respondToKNX groupAddress '{}', responseSpec '{}'", groupAddress, responseSpec); - if (groupAddress != null) { - sendToKNX(responseCommunicator, link, groupAddress, responseSpec.getDPT(), responseSpec.getValue()); - } + sendToKNX(responseCommunicator, link, groupAddress, responseSpec.getDPT(), responseSpec.getValue()); } private void sendToKNX(ProcessCommunication communicator, KNXNetworkLink link, GroupAddress groupAddress, diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/InboundSpec.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/InboundSpec.java index da38f7bc1f..243eebbf6f 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/InboundSpec.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/InboundSpec.java @@ -13,7 +13,7 @@ */ package org.smarthomej.binding.knx.internal.client; -import java.util.List; +import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -40,5 +40,5 @@ public interface InboundSpec { * * @return a list of group addresses. */ - List getGroupAddresses(); + Set getGroupAddresses(); } diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/OutboundSpec.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/OutboundSpec.java index 83d5e920b1..02c947317e 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/OutboundSpec.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/client/OutboundSpec.java @@ -14,7 +14,6 @@ package org.smarthomej.binding.knx.internal.client; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.types.Type; import tuwien.auto.calimero.GroupAddress; @@ -40,7 +39,6 @@ public interface OutboundSpec { * * @return the group address */ - @Nullable GroupAddress getGroupAddress(); /** diff --git a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/handler/DeviceThingHandler.java b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/handler/DeviceThingHandler.java index e3ac67c793..a839a85b73 100644 --- a/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/handler/DeviceThingHandler.java +++ b/bundles/org.smarthomej.binding.knx/src/main/java/org/smarthomej/binding/knx/internal/handler/DeviceThingHandler.java @@ -26,12 +26,10 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.config.core.Configuration; import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.thing.Channel; import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.Thing; -import org.openhab.core.thing.type.ChannelTypeUID; import org.openhab.core.types.Command; import org.openhab.core.types.RefreshType; import org.openhab.core.types.State; @@ -40,8 +38,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smarthomej.binding.knx.internal.KNXBindingConstants; -import org.smarthomej.binding.knx.internal.channel.KNXChannelType; -import org.smarthomej.binding.knx.internal.channel.KNXChannelTypes; +import org.smarthomej.binding.knx.internal.channel.KNXChannel; +import org.smarthomej.binding.knx.internal.channel.KNXChannelFactory; import org.smarthomej.binding.knx.internal.client.AbstractKNXClient; import org.smarthomej.binding.knx.internal.client.InboundSpec; import org.smarthomej.binding.knx.internal.client.OutboundSpec; @@ -51,7 +49,6 @@ import tuwien.auto.calimero.GroupAddress; import tuwien.auto.calimero.IndividualAddress; import tuwien.auto.calimero.KNXException; -import tuwien.auto.calimero.KNXFormatException; import tuwien.auto.calimero.datapoint.CommandDP; import tuwien.auto.calimero.datapoint.Datapoint; @@ -60,6 +57,7 @@ * bus and updating the channels correspondingly. * * @author Simon Kaufmann - Initial contribution and API + * @author Jan N. Klug - Refactored for performance */ @NonNullByDefault public class DeviceThingHandler extends AbstractKNXThingHandler { @@ -71,6 +69,7 @@ public class DeviceThingHandler extends AbstractKNXThingHandler { private final Set groupAddressesRespondingSpec = ConcurrentHashMap.newKeySet(); private final Map> readFutures = new ConcurrentHashMap<>(); private final Map> channelFutures = new ConcurrentHashMap<>(); + private final Map knxChannels = new ConcurrentHashMap<>(); private int readInterval; public DeviceThingHandler(Thing thing) { @@ -83,10 +82,12 @@ public void initialize() { DeviceConfig config = getConfigAs(DeviceConfig.class); readInterval = config.getReadInterval(); - // gather all GAs from channel configurations - getThing().getChannels().forEach( - channel -> applyChannelFunction(channel, (knxChannelType, channelConfiguration) -> groupAddresses - .addAll(knxChannelType.getAllGroupAddresses(channelConfiguration)))); + // gather all GAs from channel configurations and create channels + getThing().getChannels().forEach(channel -> { + KNXChannel knxChannel = KNXChannelFactory.createKnxChannel(channel); + knxChannels.put(channel.getUID(), knxChannel); + groupAddresses.addAll(knxChannel.getAllGroupAddresses()); + }); } @Override @@ -101,6 +102,8 @@ public void dispose() { groupAddresses.clear(); groupAddressesWriteBlockedOnce.clear(); groupAddressesRespondingSpec.clear(); + knxChannels.clear(); + super.dispose(); } @@ -114,48 +117,30 @@ protected void cancelReadFutures() { } } - @FunctionalInterface - private interface ChannelFunction { - void apply(KNXChannelType knxChannelType, Configuration configuration) throws KNXException; - } - - private void applyChannelFunction(ChannelUID channelUID, ChannelFunction function) { - Channel channel = getThing().getChannel(channelUID.getId()); - if (channel == null) { - logger.warn("Channel '{}' does not exist", channelUID); - return; - } - applyChannelFunction(channel, function); - } - - private void applyChannelFunction(Channel channel, ChannelFunction function) { - try { - KNXChannelType knxChannelType = KNXChannelTypes.getKnxChannelType(channel); - function.apply(knxChannelType, channel.getConfiguration()); - } catch (KNXException e) { - logger.warn("An error occurred on channel {}: {}", channel.getUID(), e.getMessage(), e); - } - } - @Override public void channelLinked(ChannelUID channelUID) { - if (!isControl(channelUID)) { - applyChannelFunction(channelUID, this::scheduleRead); + KNXChannel knxChannel = knxChannels.get(channelUID); + if (knxChannel == null) { + logger.warn("Channel '{}' received a channel linked event, but no KNXChannel found", channelUID); + return; + } + if (!knxChannel.isControl()) { + scheduleRead(knxChannel); } } @Override protected void scheduleReadJobs() { cancelReadFutures(); - for (Channel channel : getThing().getChannels()) { - if (isLinked(channel.getUID().getId()) && !isControl(channel.getUID())) { - applyChannelFunction(channel, this::scheduleRead); + for (KNXChannel knxChannel : knxChannels.values()) { + if (isLinked(knxChannel.getChannelUID()) && knxChannel.isControl()) { + scheduleRead(knxChannel); } } } - private void scheduleRead(KNXChannelType knxChannelType, Configuration configuration) throws KNXFormatException { - List readSpecs = knxChannelType.getReadSpec(configuration); + private void scheduleRead(KNXChannel knxChannel) { + List readSpecs = knxChannel.getReadSpec(); for (InboundSpec readSpec : readSpecs) { readSpec.getGroupAddresses().forEach(ga -> scheduleReadJob(ga, readSpec.getDPT())); } @@ -193,9 +178,7 @@ public boolean listensTo(GroupAddress destination) { /** KNXIO remember controls, removeIf may be null */ private void rememberRespondingSpec(OutboundSpec commandSpec) { GroupAddress ga = commandSpec.getGroupAddress(); - if (ga != null) { - groupAddressesRespondingSpec.removeIf(spec -> spec.matchesDestination(ga)); - } + groupAddressesRespondingSpec.removeIf(spec -> spec.matchesDestination(ga)); groupAddressesRespondingSpec.add(commandSpec); logger.trace("rememberRespondingSpec handled commandSpec for '{}' size '{}'", ga, groupAddressesRespondingSpec.size()); @@ -205,21 +188,26 @@ private void rememberRespondingSpec(OutboundSpec commandSpec) { @Override public void handleCommand(ChannelUID channelUID, Command command) { logger.trace("Handling command '{}' for channel '{}'", command, channelUID); - if (command instanceof RefreshType && !isControl(channelUID)) { + KNXChannel knxChannel = knxChannels.get(channelUID); + if (knxChannel == null) { + logger.warn("Channel '{}' received command, but no KNXChannel found", channelUID); + return; + } + if (command instanceof RefreshType && !knxChannel.isControl()) { logger.debug("Refreshing channel '{}'", channelUID); - applyChannelFunction(channelUID, this::scheduleRead); + scheduleRead(knxChannel); } else { if (CHANNEL_RESET.equals(channelUID.getId())) { if (address != null) { restart(); } } else { - applyChannelFunction(channelUID, (knxChannelType, channelConfiguration) -> { - OutboundSpec commandSpec = knxChannelType.getCommandSpec(channelConfiguration, command); + try { + OutboundSpec commandSpec = knxChannel.getCommandSpec(command); // only send GroupValueWrite to KNX if GA is not blocked once if (commandSpec != null && !groupAddressesWriteBlockedOnce.remove(commandSpec.getGroupAddress())) { getClient().writeToKNX(commandSpec); - if (isControl(channelUID)) { + if (knxChannel.isControl()) { rememberRespondingSpec(commandSpec); } } else { @@ -227,39 +215,34 @@ public void handleCommand(ChannelUID channelUID, Command command) { "None of the configured GAs on channel '{}' could handle the command '{}' of type '{}'", channelUID, command, command.getClass().getSimpleName()); } - }); + } catch (KNXException e) { + logger.warn("An error occurred while handling command '{}' on channel '{}': {}", command, + channelUID, e.getMessage()); + } } } } - private boolean isControl(ChannelUID channelUID) { - ChannelTypeUID channelTypeUID = getChannelTypeUID(channelUID); - return CONTROL_CHANNEL_TYPES.contains(channelTypeUID.getId()); - } - - private ChannelTypeUID getChannelTypeUID(ChannelUID channelUID) { - Channel channel = getThing().getChannel(channelUID.getId()); - Objects.requireNonNull(channel); - ChannelTypeUID channelTypeUID = channel.getChannelTypeUID(); - Objects.requireNonNull(channelTypeUID); - return channelTypeUID; - } - /** KNXIO */ - private void sendGroupValueResponse(Channel channel, GroupAddress destination) { - Set rsa = KNXChannelTypes.getKnxChannelType(channel) - .getWriteAddresses(channel.getConfiguration()); + private void sendGroupValueResponse(ChannelUID channelUID, GroupAddress destination) { + KNXChannel knxChannel = knxChannels.get(channelUID); + if (knxChannel == null) { + return; + } + Set rsa = knxChannel.getWriteAddresses(); if (!rsa.isEmpty()) { logger.trace("onGroupRead size '{}'", rsa.size()); - applyChannelFunction(channel, (knxChannelType, configuration) -> { - Optional os = groupAddressesRespondingSpec.stream() - .filter(spec -> spec.matchesDestination(destination)).findFirst(); - if (os.isPresent()) { - logger.trace("onGroupRead respondToKNX '{}'", os.get().getGroupAddress()); - /* KNXIO: sending real "GroupValueResponse" to the KNX bus. */ + Optional os = groupAddressesRespondingSpec.stream() + .filter(spec -> spec.matchesDestination(destination)).findFirst(); + if (os.isPresent()) { + logger.trace("onGroupRead respondToKNX '{}'", os.get().getGroupAddress()); + /* KNXIO: sending real "GroupValueResponse" to the KNX bus. */ + try { getClient().respondToKNX(os.get()); + } catch (KNXException e) { + logger.warn("An error occurred on channel {}: {}", channelUID, e.getMessage(), e); } - }); + } } } @@ -270,22 +253,19 @@ private void sendGroupValueResponse(Channel channel, GroupAddress destination) { public void onGroupRead(AbstractKNXClient client, IndividualAddress source, GroupAddress destination, byte[] asdu) { logger.trace("onGroupRead Thing '{}' received a GroupValueRead telegram from '{}' for destination '{}'", getThing().getUID(), source, destination); - for (Channel channel : getThing().getChannels()) { - if (isControl(channel.getUID())) { - applyChannelFunction(channel, (knxChannelType, configuration) -> { - OutboundSpec responseSpec = knxChannelType.getResponseSpec(configuration, destination, - RefreshType.REFRESH); - if (responseSpec != null) { - logger.trace("onGroupRead isControl -> postCommand"); - // This event should be sent to KNX as GroupValueResponse immediately. - sendGroupValueResponse(channel, destination); - // Send REFRESH to openHAB to get this event for scripting with postCommand - // and remember to ignore/block this REFRESH to be sent back to KNX as GroupValueWrite after - // postCommand is done! - groupAddressesWriteBlockedOnce.add(destination); - postCommand(channel.getUID().getId(), RefreshType.REFRESH); - } - }); + for (KNXChannel knxChannel : knxChannels.values()) { + if (knxChannel.isControl()) { + OutboundSpec responseSpec = knxChannel.getResponseSpec(destination, RefreshType.REFRESH); + if (responseSpec != null) { + logger.trace("onGroupRead isControl -> postCommand"); + // This event should be sent to KNX as GroupValueResponse immediately. + sendGroupValueResponse(knxChannel.getChannelUID(), destination); + // Send REFRESH to openHAB to get this event for scripting with postCommand + // and remember to ignore/block this REFRESH to be sent back to KNX as GroupValueWrite after + // postCommand is done! + groupAddressesWriteBlockedOnce.add(destination); + postCommand(knxChannel.getChannelUID(), RefreshType.REFRESH); + } } } } @@ -308,34 +288,33 @@ public void onGroupWrite(AbstractKNXClient client, IndividualAddress source, Gro logger.debug("onGroupWrite Thing '{}' received a GroupValueWrite telegram from '{}' for destination '{}'", getThing().getUID(), source, destination); - for (Channel channel : getThing().getChannels()) { - applyChannelFunction(channel, (selector, configuration) -> { - InboundSpec listenSpec = selector.getListenSpec(configuration, destination); - if (listenSpec != null) { - logger.trace( - "onGroupWrite Thing '{}' processes a GroupValueWrite telegram for destination '{}' for channel '{}'", - getThing().getUID(), destination, channel.getUID()); - /** - * Remember current KNXIO outboundSpec only if it is a control channel. - */ - if (isControl(channel.getUID())) { - logger.trace("onGroupWrite isControl"); - Type value = KNXCoreTypeMapper.convertRawDataToType(listenSpec.getDPT(), asdu); - if (value != null) { - OutboundSpec commandSpec = selector.getCommandSpec(configuration, value); - if (commandSpec != null) { - rememberRespondingSpec(commandSpec); - } + for (KNXChannel knxChannel : knxChannels.values()) { + InboundSpec listenSpec = knxChannel.getListenSpec(destination); + if (listenSpec != null) { + logger.trace( + "onGroupWrite Thing '{}' processes a GroupValueWrite telegram for destination '{}' for channel '{}'", + getThing().getUID(), destination, knxChannel.getChannelUID()); + /** + * Remember current KNXIO outboundSpec only if it is a control channel. + */ + if (knxChannel.isControl()) { + logger.trace("onGroupWrite isControl"); + Type value = KNXCoreTypeMapper.convertRawDataToType(listenSpec.getDPT(), asdu); + if (value != null) { + OutboundSpec commandSpec = knxChannel.getCommandSpec(value); + if (commandSpec != null) { + rememberRespondingSpec(commandSpec); } } - processDataReceived(destination, asdu, listenSpec, channel.getUID()); } - }); + processDataReceived(destination, asdu, listenSpec, knxChannel); + } + } } private void processDataReceived(GroupAddress destination, byte[] asdu, InboundSpec listenSpec, - ChannelUID channelUID) { + KNXChannel knxChannel) { if (!isDPTSupported(listenSpec.getDPT())) { logger.warn("DPT '{}' is not supported by the KNX binding.", listenSpec.getDPT()); return; @@ -343,27 +322,33 @@ private void processDataReceived(GroupAddress destination, byte[] asdu, InboundS Type value = KNXCoreTypeMapper.convertRawDataToType(listenSpec.getDPT(), asdu); if (value != null) { - if (isControl(channelUID)) { - Channel channel = getThing().getChannel(channelUID.getId()); - Object repeat = channel != null ? channel.getConfiguration().get(KNXBindingConstants.REPEAT_FREQUENCY) - : null; - int frequency = repeat != null ? ((BigDecimal) repeat).intValue() : 0; - if (KNXBindingConstants.CHANNEL_DIMMER_CONTROL.equals(getChannelTypeUID(channelUID).getId()) - && (value instanceof UnDefType || value instanceof IncreaseDecreaseType) && frequency > 0) { + if (knxChannel.isControl()) { + ChannelUID channelUID = knxChannel.getChannelUID(); + int frequency; + if (KNXBindingConstants.CHANNEL_DIMMER_CONTROL.equals(knxChannel.getChannelType())) { + // if we have a dimmer control channel, check if a frequency is defined + Channel channel = getThing().getChannel(channelUID); + if (channel == null) { + logger.warn("Failed to find channel for ChannelUID '{}'", channelUID); + return; + } + frequency = ((BigDecimal) Objects.requireNonNullElse( + channel.getConfiguration().get(KNXBindingConstants.REPEAT_FREQUENCY), BigDecimal.ZERO)) + .intValue(); + } else { + // disable dimming by binding + frequency = 0; + } + if ((value instanceof UnDefType || value instanceof IncreaseDecreaseType) && frequency > 0) { // continuous dimming by the binding - if (UnDefType.UNDEF.equals(value)) { - channelFutures.computeIfPresent(channelUID, (k, v) -> { - v.cancel(false); - return null; - }); - } else if (value instanceof IncreaseDecreaseType) { - channelFutures.compute(channelUID, (k, v) -> { - if (v != null) { - v.cancel(true); - } - return scheduler.scheduleWithFixedDelay(() -> postCommand(channelUID, (Command) value), 0, - frequency, TimeUnit.MILLISECONDS); - }); + // cancel a running scheduler before adding a new (and only add if not UnDefType) + ScheduledFuture oldFuture = channelFutures.remove(channelUID); + if (oldFuture != null) { + oldFuture.cancel(true); + } + if (value instanceof IncreaseDecreaseType) { + channelFutures.put(channelUID, scheduler.scheduleWithFixedDelay( + () -> postCommand(channelUID, (Command) value), 0, frequency, TimeUnit.MILLISECONDS)); } } else { if (value instanceof Command) { @@ -373,7 +358,7 @@ private void processDataReceived(GroupAddress destination, byte[] asdu, InboundS } } else { if (value instanceof State) { - updateState(channelUID, (State) value); + updateState(knxChannel.getChannelUID(), (State) value); } } } else { diff --git a/bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypeTest.java b/bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTest.java similarity index 52% rename from bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypeTest.java rename to bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTest.java index 706fbc08d7..8d0f5940c8 100644 --- a/bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTypeTest.java +++ b/bundles/org.smarthomej.binding.knx/src/test/java/org/smarthomej/binding/knx/internal/channel/KNXChannelTest.java @@ -14,14 +14,17 @@ package org.smarthomej.binding.knx.internal.channel; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.Map; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openhab.core.config.core.Configuration; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.type.ChannelTypeUID; import tuwien.auto.calimero.GroupAddress; import tuwien.auto.calimero.KNXFormatException; @@ -32,18 +35,17 @@ * */ @NonNullByDefault -public class KNXChannelTypeTest { +public class KNXChannelTest { - private @NonNullByDefault({}) KNXChannelType ct; - - @BeforeEach - public void setup() { - ct = new MyKNXChannelType(""); + @Test + public void invalidFails() { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("5.001:<1/3/22+0/3/22+<0/8/15"); + assertNull(res); } @Test - public void testParseWithDPTMultipleWithRead() { - ChannelConfiguration res = ct.parse("5.001:<1/3/22+0/3/22+<0/8/15"); + public void testParseWithDPTMultipleWithRead() throws KNXFormatException { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("5.001:<1/3/22+0/3/22+<0/7/15"); if (res == null) { fail(); @@ -51,15 +53,15 @@ public void testParseWithDPTMultipleWithRead() { } assertEquals("5.001", res.getDPT()); - assertEquals("1/3/22", res.getMainGA().getGA()); - assertTrue(res.getMainGA().isRead()); + assertEquals(new GroupAddress("1/3/22"), res.getMainGA()); + assertTrue(res.getReadGAs().contains(res.getMainGA())); assertEquals(3, res.getListenGAs().size()); assertEquals(2, res.getReadGAs().size()); } @Test - public void testParseWithDPTMultipleWithoutRead() { - ChannelConfiguration res = ct.parse("5.001:1/3/22+0/3/22+0/8/15"); + public void testParseWithDPTMultipleWithoutRead() throws KNXFormatException { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("5.001:1/3/22+0/3/22+0/7/15"); if (res == null) { fail(); @@ -67,15 +69,15 @@ public void testParseWithDPTMultipleWithoutRead() { } assertEquals("5.001", res.getDPT()); - assertEquals("1/3/22", res.getMainGA().getGA()); - assertFalse(res.getMainGA().isRead()); + assertEquals(new GroupAddress("1/3/22"), res.getMainGA()); + assertFalse(res.getReadGAs().contains(res.getMainGA())); assertEquals(3, res.getListenGAs().size()); assertEquals(0, res.getReadGAs().size()); } @Test - public void testParseWithoutDPTSingleWithoutRead() { - ChannelConfiguration res = ct.parse("1/3/22"); + public void testParseWithoutDPTSingleWithoutRead() throws KNXFormatException { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("1/3/22"); if (res == null) { fail(); @@ -83,15 +85,15 @@ public void testParseWithoutDPTSingleWithoutRead() { } assertNull(res.getDPT()); - assertEquals("1/3/22", res.getMainGA().getGA()); - assertFalse(res.getMainGA().isRead()); + assertEquals(new GroupAddress("1/3/22"), res.getMainGA()); + assertFalse(res.getReadGAs().contains(res.getMainGA())); assertEquals(1, res.getListenGAs().size()); assertEquals(0, res.getReadGAs().size()); } @Test - public void testParseWithoutDPTSingleWithRead() { - ChannelConfiguration res = ct.parse("<1/3/22"); + public void testParseWithoutDPTSingleWithRead() throws KNXFormatException { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("<1/3/22"); if (res == null) { fail(); @@ -99,57 +101,64 @@ public void testParseWithoutDPTSingleWithRead() { } assertNull(res.getDPT()); - assertEquals("1/3/22", res.getMainGA().getGA()); - assertTrue(res.getMainGA().isRead()); + assertEquals(new GroupAddress("1/3/22"), res.getMainGA()); + assertTrue(res.getReadGAs().contains(res.getMainGA())); assertEquals(1, res.getListenGAs().size()); assertEquals(1, res.getReadGAs().size()); } @Test - public void testParseTwoLevel() { - ChannelConfiguration res = ct.parse("5.001:<3/1024+<4/1025"); + public void testParseTwoLevel() throws KNXFormatException { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("5.001:<3/1024+<4/1025"); if (res == null) { fail(); return; } - assertEquals("3/1024", res.getMainGA().getGA()); + assertEquals(new GroupAddress("3/1024"), res.getMainGA()); + assertTrue(res.getReadGAs().contains(res.getMainGA())); assertEquals(2, res.getListenGAs().size()); assertEquals(2, res.getReadGAs().size()); } @Test - public void testParseFreeLevel() { - ChannelConfiguration res = ct.parse("5.001:<4610+<4611"); + public void testParseFreeLevel() throws KNXFormatException { + GroupAddressConfiguration res = GroupAddressConfiguration.parse("5.001:<4610+<4611"); if (res == null) { fail(); return; } - assertEquals("4610", res.getMainGA().getGA()); + assertEquals(new GroupAddress("4610"), res.getMainGA()); assertEquals(2, res.getListenGAs().size()); assertEquals(2, res.getReadGAs().size()); } @Test public void testChannelGaParsing() throws KNXFormatException { + Channel channel = mock(Channel.class); Configuration configuration = new Configuration( Map.of("key1", "5.001:<1/2/3+4/5/6+1/5/6", "key2", "1.001:7/1/9+1/1/2")); - Set listenAddresses = ct.getAllGroupAddresses(configuration); + when(channel.getChannelTypeUID()).thenReturn(new ChannelTypeUID("a:b:c")); + when(channel.getConfiguration()).thenReturn(configuration); + + MyKNXChannel knxChannel = new MyKNXChannel(channel); + + Set listenAddresses = knxChannel.getAllGroupAddresses(); assertEquals(5, listenAddresses.size()); // we don't check the content since parsing has been checked before and the quantity is correct - Set writeAddresses = ct.getWriteAddresses(configuration); + Set writeAddresses = knxChannel.getWriteAddresses(); assertEquals(2, writeAddresses.size()); assertTrue(writeAddresses.contains(new GroupAddress("1/2/3"))); assertTrue(writeAddresses.contains(new GroupAddress("7/1/9"))); } - private static class MyKNXChannelType extends KNXChannelType { - public MyKNXChannelType(String channelTypeID) { - super(Set.of("key1", "key2"), channelTypeID); + private static class MyKNXChannel extends KNXChannel { + public MyKNXChannel(Channel channel) { + super(Set.of("key1", "key2"), channel); } @Override