[knx] DPT 1 to support Switch and Contact (#16238)

* Allow assigning Switch or Contact to most of the DPT 1 subtypes
(exceptions 1.008 UpDownType, 1.010 StopMoveType, 1.022 DecimalType).
* Tests for all published subtypes for DPT 1.
* Fix sending of DPT 1.022.

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
This commit is contained in:
Holger Friedrich 2024-01-21 21:43:21 +01:00 committed by GitHub
parent 73402f691c
commit ba35e8d84a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 95 additions and 7 deletions

View File

@ -116,7 +116,7 @@ public class ValueDecoder {
switch (mainType) { switch (mainType) {
case "1": case "1":
return handleDpt1(subType, translator); return handleDpt1(subType, translator, preferredType);
case "2": case "2":
DPTXlator1BitControlled translator1BitControlled = (DPTXlator1BitControlled) translator; DPTXlator1BitControlled translator1BitControlled = (DPTXlator1BitControlled) translator;
int decValue = (translator1BitControlled.getControlBit() ? 2 : 0) int decValue = (translator1BitControlled.getControlBit() ? 2 : 0)
@ -172,13 +172,18 @@ public class ValueDecoder {
return null; return null;
} }
private static Type handleDpt1(String subType, DPTXlator translator) { private static Type handleDpt1(String subType, DPTXlator translator, Class<? extends Type> preferredType) {
DPTXlatorBoolean translatorBoolean = (DPTXlatorBoolean) translator; DPTXlatorBoolean translatorBoolean = (DPTXlatorBoolean) translator;
switch (subType) { switch (subType) {
case "008": case "008":
return translatorBoolean.getValueBoolean() ? UpDownType.DOWN : UpDownType.UP; return translatorBoolean.getValueBoolean() ? UpDownType.DOWN : UpDownType.UP;
case "009": case "009":
case "019": case "019":
// default is OpenClosedType (Contact), but it may be mapped to OnOffType as well
if (OnOffType.class.equals(preferredType)) {
return OnOffType.from(translatorBoolean.getValueBoolean());
}
// This is wrong for DPT 1.009. It should be true -> CLOSE, false -> OPEN, but unfortunately // This is wrong for DPT 1.009. It should be true -> CLOSE, false -> OPEN, but unfortunately
// can't be fixed without breaking a lot of working installations. // can't be fixed without breaking a lot of working installations.
// The documentation has been updated to reflect that. / @J-N-K // The documentation has been updated to reflect that. / @J-N-K
@ -188,6 +193,11 @@ public class ValueDecoder {
case "022": case "022":
return DecimalType.valueOf(translatorBoolean.getValueBoolean() ? "1" : "0"); return DecimalType.valueOf(translatorBoolean.getValueBoolean() ? "1" : "0");
default: default:
// default is OnOffType (Switch), but it may be mapped to OpenClosedType as well
if (OpenClosedType.class.equals(preferredType)) {
return translatorBoolean.getValueBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED;
}
return OnOffType.from(translatorBoolean.getValueBoolean()); return OnOffType.from(translatorBoolean.getValueBoolean());
} }
} }

View File

@ -47,6 +47,7 @@ import tuwien.auto.calimero.dptxlator.DPTXlator1BitControlled;
import tuwien.auto.calimero.dptxlator.DPTXlator2ByteFloat; import tuwien.auto.calimero.dptxlator.DPTXlator2ByteFloat;
import tuwien.auto.calimero.dptxlator.DPTXlator3BitControlled; import tuwien.auto.calimero.dptxlator.DPTXlator3BitControlled;
import tuwien.auto.calimero.dptxlator.DPTXlator4ByteFloat; import tuwien.auto.calimero.dptxlator.DPTXlator4ByteFloat;
import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean;
import tuwien.auto.calimero.dptxlator.DPTXlatorDate; import tuwien.auto.calimero.dptxlator.DPTXlatorDate;
import tuwien.auto.calimero.dptxlator.DPTXlatorDateTime; import tuwien.auto.calimero.dptxlator.DPTXlatorDateTime;
import tuwien.auto.calimero.dptxlator.DPTXlatorTime; import tuwien.auto.calimero.dptxlator.DPTXlatorTime;
@ -236,6 +237,11 @@ public class ValueEncoder {
} }
} }
switch (mainNumber) { switch (mainNumber) {
case "1":
if (DPTXlatorBoolean.DPT_SCENE_AB.getID().equals(dptId)) {
return (bigDecimal.intValue() == 0) ? dpt.getLowerValue() : dpt.getUpperValue();
}
return bigDecimal.stripTrailingZeros().toPlainString();
case "2": case "2":
DPT valueDPT = ((DPTXlator1BitControlled.DPT1BitControlled) dpt).getValueDPT(); DPT valueDPT = ((DPTXlator1BitControlled.DPT1BitControlled) dpt).getValueDPT();
switch (bigDecimal.intValue()) { switch (bigDecimal.intValue()) {

View File

@ -190,40 +190,112 @@ public class Back2BackTest {
@Test @Test
void testDpt1() { void testDpt1() {
// for now only the DPTs for general use, others omitted
// TODO add tests for more subtypes
helper("1.001", new byte[] { 0 }, OnOffType.OFF); helper("1.001", new byte[] { 0 }, OnOffType.OFF);
helper("1.001", new byte[] { 1 }, OnOffType.ON); helper("1.001", new byte[] { 1 }, OnOffType.ON);
helper("1.001", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.001", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.002", new byte[] { 0 }, OnOffType.OFF); helper("1.002", new byte[] { 0 }, OnOffType.OFF);
helper("1.002", new byte[] { 1 }, OnOffType.ON); helper("1.002", new byte[] { 1 }, OnOffType.ON);
helper("1.002", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.002", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.003", new byte[] { 0 }, OnOffType.OFF); helper("1.003", new byte[] { 0 }, OnOffType.OFF);
helper("1.003", new byte[] { 1 }, OnOffType.ON); helper("1.003", new byte[] { 1 }, OnOffType.ON);
helper("1.003", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.003", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.004", new byte[] { 0 }, OnOffType.OFF);
helper("1.004", new byte[] { 1 }, OnOffType.ON);
helper("1.004", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.004", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.005", new byte[] { 0 }, OnOffType.OFF);
helper("1.005", new byte[] { 1 }, OnOffType.ON);
helper("1.005", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.005", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.006", new byte[] { 0 }, OnOffType.OFF);
helper("1.006", new byte[] { 1 }, OnOffType.ON);
helper("1.006", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.006", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.007", new byte[] { 0 }, OnOffType.OFF);
helper("1.007", new byte[] { 1 }, OnOffType.ON);
helper("1.007", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.007", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.008", new byte[] { 0 }, UpDownType.UP); helper("1.008", new byte[] { 0 }, UpDownType.UP);
helper("1.008", new byte[] { 1 }, UpDownType.DOWN); helper("1.008", new byte[] { 1 }, UpDownType.DOWN);
// NOTE: This is how DPT 1.009 is defined: 0: open, 1: closed // NOTE: This is how DPT 1.009 is defined: 0: open, 1: closed
// For historical reasons it is defined the other way on OH // For historical reasons it is defined the other way on OH
helper("1.009", new byte[] { 0 }, OnOffType.OFF);
helper("1.009", new byte[] { 1 }, OnOffType.ON);
helper("1.009", new byte[] { 0 }, OpenClosedType.CLOSED); helper("1.009", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.009", new byte[] { 1 }, OpenClosedType.OPEN); helper("1.009", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.010", new byte[] { 0 }, StopMoveType.STOP); helper("1.010", new byte[] { 0 }, StopMoveType.STOP);
helper("1.010", new byte[] { 1 }, StopMoveType.MOVE); helper("1.010", new byte[] { 1 }, StopMoveType.MOVE);
helper("1.011", new byte[] { 0 }, OnOffType.OFF);
helper("1.011", new byte[] { 1 }, OnOffType.ON);
helper("1.011", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.011", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.012", new byte[] { 0 }, OnOffType.OFF);
helper("1.012", new byte[] { 1 }, OnOffType.ON);
helper("1.012", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.012", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.013", new byte[] { 0 }, OnOffType.OFF);
helper("1.013", new byte[] { 1 }, OnOffType.ON);
helper("1.013", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.013", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.014", new byte[] { 0 }, OnOffType.OFF);
helper("1.014", new byte[] { 1 }, OnOffType.ON);
helper("1.014", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.014", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.015", new byte[] { 0 }, OnOffType.OFF); helper("1.015", new byte[] { 0 }, OnOffType.OFF);
helper("1.015", new byte[] { 1 }, OnOffType.ON); helper("1.015", new byte[] { 1 }, OnOffType.ON);
helper("1.015", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.015", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.016", new byte[] { 0 }, OnOffType.OFF); helper("1.016", new byte[] { 0 }, OnOffType.OFF);
helper("1.016", new byte[] { 1 }, OnOffType.ON); helper("1.016", new byte[] { 1 }, OnOffType.ON);
helper("1.016", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.016", new byte[] { 1 }, OpenClosedType.OPEN);
// DPT 1.017 is a special case, "trigger" has no "value", both 0 and 1 shall trigger // DPT 1.017 is a special case, "trigger" has no "value", both 0 and 1 shall trigger
helper("1.017", new byte[] { 0 }, OnOffType.OFF); helper("1.017", new byte[] { 0 }, OnOffType.OFF);
helper("1.017", new byte[] { 0 }, OpenClosedType.CLOSED);
// Calimero maps it always to 0 // Calimero maps it always to 0
// helper("1.017", new byte[] { 1 }, OnOffType.ON); // helper("1.017", new byte[] { 1 }, OnOffType.ON);
helper("1.018", new byte[] { 0 }, OnOffType.OFF); helper("1.018", new byte[] { 0 }, OnOffType.OFF);
helper("1.018", new byte[] { 1 }, OnOffType.ON); helper("1.018", new byte[] { 1 }, OnOffType.ON);
helper("1.018", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.018", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.019", new byte[] { 0 }, OnOffType.OFF);
helper("1.019", new byte[] { 1 }, OnOffType.ON);
helper("1.019", new byte[] { 0 }, OpenClosedType.CLOSED); helper("1.019", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.019", new byte[] { 1 }, OpenClosedType.OPEN); helper("1.019", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.021", new byte[] { 0 }, OnOffType.OFF);
helper("1.021", new byte[] { 1 }, OnOffType.ON);
helper("1.021", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.021", new byte[] { 1 }, OpenClosedType.OPEN);
// DPT 1.022 is mapped to decimal, Calimero does not follow the recommendation
// from KNX spec to add offset 1
helper("1.022", new byte[] { 0 }, DecimalType.valueOf("0"));
helper("1.022", new byte[] { 1 }, DecimalType.valueOf("1"));
helper("1.023", new byte[] { 0 }, OnOffType.OFF);
helper("1.023", new byte[] { 1 }, OnOffType.ON);
helper("1.023", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.023", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.024", new byte[] { 0 }, OnOffType.OFF); helper("1.024", new byte[] { 0 }, OnOffType.OFF);
helper("1.024", new byte[] { 1 }, OnOffType.ON); helper("1.024", new byte[] { 1 }, OnOffType.ON);
helper("1.024", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.024", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.100", new byte[] { 0 }, OnOffType.OFF);
helper("1.100", new byte[] { 1 }, OnOffType.ON);
helper("1.100", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.100", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.1200", new byte[] { 0 }, OnOffType.OFF);
helper("1.1200", new byte[] { 1 }, OnOffType.ON);
helper("1.1200", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.1200", new byte[] { 1 }, OpenClosedType.OPEN);
helper("1.1201", new byte[] { 0 }, OnOffType.OFF);
helper("1.1201", new byte[] { 1 }, OnOffType.ON);
helper("1.1201", new byte[] { 0 }, OpenClosedType.CLOSED);
helper("1.1201", new byte[] { 1 }, OpenClosedType.OPEN);
} }
@Test @Test