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;
@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);
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);
"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);
}
}
}
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.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;
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")));
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