]> git.basschouten.com Git - openhab-addons.git/commitdiff
Fix KNX dimmer channels (#16421)
authorJ-N-K <github@klug.nrw>
Sun, 18 Feb 2024 21:03:24 +0000 (22:03 +0100)
committerGitHub <noreply@github.com>
Sun, 18 Feb 2024 21:03:24 +0000 (22:03 +0100)
Signed-off-by: Jan N. Klug <github@klug.nrw>
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/channel/KNXChannel.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/channel/TypeColor.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/channel/TypeDimmer.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/channel/TypeRollershutter.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/handler/DeviceThingHandler.java
bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/channel/KNXChannelTest.java

index 923759b0e1d80415995c0020eff3b2d8f9cdb479..b08a309203c3913037e5829e2d7463b2b0988639 100644 (file)
@@ -15,8 +15,8 @@ package org.openhab.binding.knx.internal.channel;
 import static java.util.stream.Collectors.toList;
 import static org.openhab.binding.knx.internal.KNXBindingConstants.*;
 
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -47,21 +47,21 @@ import tuwien.auto.calimero.GroupAddress;
 @NonNullByDefault
 public abstract class KNXChannel {
     private final Logger logger = LoggerFactory.getLogger(KNXChannel.class);
-    private final Set<String> gaKeys;
+    private final List<String> gaKeys;
 
-    private final Map<String, GroupAddressConfiguration> groupAddressConfigurations = new HashMap<>();
-    private final Set<GroupAddress> listenAddresses = new HashSet<>();
-    private final Set<GroupAddress> writeAddresses = new HashSet<>();
+    private final Map<String, GroupAddressConfiguration> groupAddressConfigurations = new LinkedHashMap<>();
+    private final List<GroupAddress> listenAddresses = new ArrayList<>();
+    private final List<GroupAddress> writeAddresses = new ArrayList<>();
     private final String channelType;
     private final ChannelUID channelUID;
     private final boolean isControl;
     private final Class<? extends Type> preferredType;
 
     KNXChannel(List<Class<? extends Type>> acceptedTypes, Channel channel) {
-        this(Set.of(GA), acceptedTypes, channel);
+        this(List.of(GA), acceptedTypes, channel);
     }
 
-    KNXChannel(Set<String> gaKeys, List<Class<? extends Type>> acceptedTypes, Channel channel) {
+    KNXChannel(List<String> gaKeys, List<Class<? extends Type>> acceptedTypes, Channel channel) {
         this.gaKeys = gaKeys;
         this.preferredType = acceptedTypes.get(0);
 
@@ -109,16 +109,17 @@ public abstract class KNXChannel {
         return preferredType;
     }
 
-    public final Set<GroupAddress> getAllGroupAddresses() {
+    public final List<GroupAddress> getAllGroupAddresses() {
         return listenAddresses;
     }
 
-    public final Set<GroupAddress> getWriteAddresses() {
+    public final List<GroupAddress> getWriteAddresses() {
         return writeAddresses;
     }
 
     public final @Nullable OutboundSpec getCommandSpec(Type command) {
         logger.trace("getCommandSpec checking keys '{}' for command '{}' ({})", gaKeys, command, command.getClass());
+        // first check if there is a direct match for the provided command for all GAs
         for (Map.Entry<String, GroupAddressConfiguration> entry : groupAddressConfigurations.entrySet()) {
             String dpt = Objects.requireNonNullElse(entry.getValue().getDPT(), getDefaultDPT(entry.getKey()));
             Set<Class<? extends Type>> expectedTypeClasses = DPTUtil.getAllowedTypes(dpt);
@@ -128,19 +129,23 @@ public abstract class KNXChannel {
                         "getCommandSpec key '{}' has one of the expectedTypeClasses '{}', matching command '{}' and dpt '{}'",
                         entry.getKey(), expectedTypeClasses, command, dpt);
                 return new WriteSpecImpl(entry.getValue(), dpt, command);
-            } else {
-                for (Class<? extends Type> expectedTypeClass : expectedTypeClasses) {
-                    if (command instanceof State state && State.class.isAssignableFrom(expectedTypeClass)) {
-                        var subClass = expectedTypeClass.asSubclass(State.class);
-                        if (state.as(subClass) != null) {
-                            logger.trace(
-                                    "getCommandSpec command class '{}' is a sub-class of the expectedTypeClass '{}' for key '{}'",
-                                    command.getClass(), expectedTypeClass, entry.getKey());
-                            Class<? extends State> expectedTypeAsStateClass = expectedTypeClass.asSubclass(State.class);
-                            State convertedState = state.as(expectedTypeAsStateClass);
-                            if (convertedState != null) {
-                                return new WriteSpecImpl(entry.getValue(), dpt, convertedState);
-                            }
+            }
+        }
+        // if we didn't find a match, check if we find a sub-type match
+        for (Map.Entry<String, GroupAddressConfiguration> entry : groupAddressConfigurations.entrySet()) {
+            String dpt = Objects.requireNonNullElse(entry.getValue().getDPT(), getDefaultDPT(entry.getKey()));
+            Set<Class<? extends Type>> expectedTypeClasses = DPTUtil.getAllowedTypes(dpt);
+            for (Class<? extends Type> expectedTypeClass : expectedTypeClasses) {
+                if (command instanceof State state && State.class.isAssignableFrom(expectedTypeClass)) {
+                    var subClass = expectedTypeClass.asSubclass(State.class);
+                    if (state.as(subClass) != null) {
+                        logger.trace(
+                                "getCommandSpec command class '{}' is a sub-class of the expectedTypeClass '{}' for key '{}'",
+                                command.getClass(), expectedTypeClass, entry.getKey());
+                        Class<? extends State> expectedTypeAsStateClass = expectedTypeClass.asSubclass(State.class);
+                        State convertedState = state.as(expectedTypeAsStateClass);
+                        if (convertedState != null) {
+                            return new WriteSpecImpl(entry.getValue(), dpt, convertedState);
                         }
                     }
                 }
index a9580a76b195abcd7e46f37f0bcaf4dfa5d6897e..07f2599f6af13d50611683d7d04cc69f16f25f66 100644 (file)
@@ -40,7 +40,7 @@ class TypeColor extends KNXChannel {
     public static final Set<String> SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_COLOR, CHANNEL_COLOR_CONTROL);
 
     TypeColor(Channel channel) {
-        super(Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA, HSB_GA),
+        super(List.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA, HSB_GA),
                 List.of(HSBType.class, PercentType.class, OnOffType.class, IncreaseDecreaseType.class), channel);
     }
 
index e75243c5eee12c356146500fe5f7858711cdd8cc..edf20832708ede8597b3ca8ff7ae4a1c5dd7b9a1 100644 (file)
@@ -39,7 +39,7 @@ class TypeDimmer extends KNXChannel {
     public static final Set<String> SUPPORTED_CHANNEL_TYPES = Set.of(CHANNEL_DIMMER, CHANNEL_DIMMER_CONTROL);
 
     TypeDimmer(Channel channel) {
-        super(Set.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA),
+        super(List.of(SWITCH_GA, POSITION_GA, INCREASE_DECREASE_GA),
                 List.of(PercentType.class, OnOffType.class, IncreaseDecreaseType.class), channel);
     }
 
index b708db7ac52b2a5ef2956582eb442c00896e2db1..50fdfc9b03b0dace97f16a966912d8b942031f45 100644 (file)
@@ -39,7 +39,7 @@ class TypeRollershutter extends KNXChannel {
             CHANNEL_ROLLERSHUTTER_CONTROL);
 
     TypeRollershutter(Channel channel) {
-        super(Set.of(UP_DOWN_GA, STOP_MOVE_GA, POSITION_GA),
+        super(List.of(UP_DOWN_GA, STOP_MOVE_GA, POSITION_GA),
                 List.of(PercentType.class, UpDownType.class, StopMoveType.class), channel);
     }
 
index 00554934280779984c47d6d47dc42d74be6dce04..4c69b73e229aa02f23f8e853a86e20a362d8d72f 100644 (file)
@@ -300,7 +300,7 @@ public class DeviceThingHandler extends BaseThingHandler implements GroupAddress
         if (knxChannel == null) {
             return;
         }
-        Set<GroupAddress> rsa = knxChannel.getWriteAddresses();
+        List<GroupAddress> rsa = knxChannel.getWriteAddresses();
         if (!rsa.isEmpty()) {
             logger.trace("onGroupRead size '{}'", rsa.size());
             OutboundSpec os = groupAddressesRespondingSpec.get(destination);
index b3ca61b858488ad4ce6782b406713e672f1353ae..00f702c3adf095dab6cf59c252dba083a2735a58 100644 (file)
@@ -21,7 +21,6 @@ import static org.mockito.Mockito.when;
 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;
@@ -32,6 +31,7 @@ import org.openhab.binding.knx.internal.dpt.ValueEncoder;
 import org.openhab.core.config.core.Configuration;
 import org.openhab.core.library.items.ColorItem;
 import org.openhab.core.library.types.HSBType;
+import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.PercentType;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.type.ChannelTypeUID;
@@ -159,10 +159,10 @@ class KNXChannelTest {
 
         MyKNXChannel knxChannel = new MyKNXChannel(channel);
 
-        Set<GroupAddress> listenAddresses = knxChannel.getAllGroupAddresses();
+        List<GroupAddress> 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<GroupAddress> writeAddresses = knxChannel.getWriteAddresses();
+        List<GroupAddress> writeAddresses = knxChannel.getWriteAddresses();
         assertEquals(2, writeAddresses.size());
         assertTrue(writeAddresses.contains(new GroupAddress("1/2/3")));
         assertTrue(writeAddresses.contains(new GroupAddress("7/1/9")));
@@ -194,19 +194,41 @@ class KNXChannelTest {
         KNXChannel knxChannel = KNXChannelFactory.createKnxChannel(channel);
         assertThat(knxChannel, instanceOf(TypeDimmer.class));
 
-        Command command = new PercentType("100");
+        Command command = new PercentType("90");
         @Nullable
         OutboundSpec outboundSpec = knxChannel.getCommandSpec(command);
-        assertNotNull(outboundSpec);
+        assertThat(outboundSpec, is(notNullValue()));
+        assertThat(outboundSpec.getGroupAddress(), is(new GroupAddress("1/2/2")));
 
         String mappedValue = ValueEncoder.encode(outboundSpec.getValue(), outboundSpec.getDPT());
-        assertThat(mappedValue, is("100"));
+        assertThat(mappedValue, is("90"));
         assertThat(outboundSpec.getValue(), is(instanceOf(PercentType.class)));
     }
 
+    @Test
+    void test1001ToDimmerChannel() throws KNXFormatException {
+        Configuration configuration = new Configuration(Map.of("switch", "1.001:1/2/1", "position", "5.001:1/2/2"));
+        Channel channel = Objects.requireNonNull(mock(Channel.class));
+        when(channel.getChannelTypeUID())
+                .thenReturn(new ChannelTypeUID(KNXBindingConstants.BINDING_ID, KNXBindingConstants.CHANNEL_DIMMER));
+        when(channel.getConfiguration()).thenReturn(configuration);
+
+        KNXChannel knxChannel = KNXChannelFactory.createKnxChannel(channel);
+        assertThat(knxChannel, instanceOf(TypeDimmer.class));
+
+        Command command = OnOffType.ON;
+        OutboundSpec outboundSpec = knxChannel.getCommandSpec(command);
+        assertThat(outboundSpec, is(notNullValue()));
+        assertThat(outboundSpec.getGroupAddress(), is(new GroupAddress("1/2/1")));
+
+        String mappedValue = ValueEncoder.encode(outboundSpec.getValue(), outboundSpec.getDPT());
+        assertThat(mappedValue, is("on"));
+        assertThat(outboundSpec.getValue(), is(instanceOf(OnOffType.class)));
+    }
+
     private static class MyKNXChannel extends KNXChannel {
         public MyKNXChannel(Channel channel) {
-            super(Set.of("key1", "key2"), List.of(UnDefType.class), channel);
+            super(List.of("key1", "key2"), List.of(UnDefType.class), channel);
         }
 
         @Override