diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueDecoder.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueDecoder.java index 1d3109b8a50..757d48ec6d5 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueDecoder.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueDecoder.java @@ -231,7 +231,8 @@ public class ValueDecoder { } catch (NumberFormatException | KNXFormatException | KNXIllegalArgumentException | ParseException e) { LOGGER.info("Translator couldn't parse data '{}' for datapoint type '{}' ({}).", data, dptId, e.getClass()); } catch (KNXException e) { - LOGGER.warn("Failed creating a translator for datapoint type '{}'.", dptId, e); + // should never happen unless Calimero changes + LOGGER.warn("Failed creating a translator for datapoint type '{}'. Please open an issue.", dptId, e); } return null; @@ -280,7 +281,8 @@ public class ValueDecoder { case "008": return translator3BitControlled.getControlBit() ? UpDownType.DOWN : UpDownType.UP; default: - LOGGER.warn("DPT3, subtype '{}' is unknown.", subType); + // should never happen unless Calimero introduces new subtypes + LOGGER.warn("DPT3, subtype '{}' is unknown. Please open an issue.", subType); return null; } } @@ -324,7 +326,12 @@ public class ValueDecoder { if (translatorDateTime.isValidField(DPTXlatorDateTime.YEAR) && !translatorDateTime.isValidField(DPTXlatorDateTime.TIME)) { // Pure date format, no time information - cal.setTimeInMillis(translatorDateTime.getValueMilliseconds()); + try { + cal.setTimeInMillis(translatorDateTime.getValueMilliseconds()); + } catch (KNXFormatException e) { + LOGGER.debug("KNX clock msg ignored: {}", e.getMessage()); + throw e; + } String value = new SimpleDateFormat(DateTimeType.DATE_PATTERN).format(cal.getTime()); return DateTimeType.valueOf(value); } else if (!translatorDateTime.isValidField(DPTXlatorDateTime.YEAR) diff --git a/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/dpt/DPTTest.java b/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/dpt/DPTTest.java index d20b5504724..8c1fbde69be 100644 --- a/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/dpt/DPTTest.java +++ b/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/dpt/DPTTest.java @@ -26,14 +26,18 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.openhab.binding.knx.internal.itests.Back2BackTest; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.IncreaseDecreaseType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.Units; import org.openhab.core.util.ColorUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tuwien.auto.calimero.dptxlator.DPTXlator2ByteUnsigned; import tuwien.auto.calimero.dptxlator.DPTXlator4ByteFloat; @@ -50,6 +54,13 @@ import tuwien.auto.calimero.dptxlator.DptXlator2ByteSigned; */ @NonNullByDefault class DPTTest { + public static final Logger LOGGER = LoggerFactory.getLogger(Back2BackTest.class); + + @Test + void testDptBroken() { + assertNull(ValueEncoder.encode(new DecimalType(), "9.042.1")); + assertNotNull(DPTUtil.getAllowedTypes("9.042.1")); + } @Test void testToDPTValueTrailingZeroesStrippedOff() { @@ -62,6 +73,21 @@ class DPTTest { assertEquals("23.1", ValueEncoder.encode(new DecimalType("23.1"), "9.001")); } + @Test + public void dpt1Value() { + // unknown subtype + assertNull(ValueDecoder.decode("1.091", new byte[] { 0 }, DecimalType.class)); + assertNotNull(ValueEncoder.encode(new DecimalType(), "1.001")); + } + + @Test + public void dpt3Value() { + // unknown subtype + assertNull(ValueDecoder.decode("3.042", new byte[] { 0 }, IncreaseDecreaseType.class)); + assertNotNull(ValueEncoder.encode(new HSBType(), "5.003")); + assertNotNull(ValueEncoder.encode(new HSBType(), "5.001")); + } + @Test void testToDPT5ValueFromQuantityType() { assertEquals("80", ValueEncoder.encode(new QuantityType<>("80 %"), "5.001")); @@ -72,6 +98,22 @@ class DPTTest { assertEquals("80", ValueEncoder.encode(new QuantityType<>("80 %"), "5.004")); } + @Test + public void dpt6Value() { + assertEquals("42", ValueEncoder.encode(new DecimalType(42), "6.001")); + + assertEquals("42", ValueEncoder.encode(new DecimalType(42), "6.010")); + + assertEquals("0/0/0/0/1 0", Objects.toString(ValueDecoder.decode("6.020", new byte[] { 9 }, StringType.class))); + assertEquals("0/0/0/0/0 1", Objects.toString(ValueDecoder.decode("6.020", new byte[] { 2 }, StringType.class))); + assertEquals("1/1/1/1/1 2", + Objects.toString(ValueDecoder.decode("6.020", new byte[] { (byte) 0xfc }, StringType.class))); + assertEquals("0/0/0/0/1 0", ValueEncoder.encode(StringType.valueOf("0/0/0/0/1 0"), "6.020")); + + // unknown subtype + assertNull(ValueDecoder.decode("6.200", new byte[] { 0 }, IncreaseDecreaseType.class)); + } + @Test void testToDPT7ValueFromQuantityType() { assertEquals("1000", ValueEncoder.encode(new QuantityType<>("1000 ms"), "7.002")); @@ -155,6 +197,11 @@ class DPTTest { assertEquals("10", ValueEncoder.encode(new QuantityType<>("10 km/h"), "9.028")); assertEquals("1", ValueEncoder.encode(new QuantityType<>("1 g/m³"), "9.029")); assertEquals("1", ValueEncoder.encode(new QuantityType<>("1 µg/m³"), "9.030")); + + // w/o unit + ValueEncoder.encode(new QuantityType<>("1"), "9.030"); + // wrong unit + ValueEncoder.encode(new QuantityType<>("1 kg"), "9.030"); } @Test @@ -297,6 +344,30 @@ class DPTTest { void testToDPT19ValueFromQuantityType() { // DateTimeType, not QuantityType assertEquals("2019-06-12 17:30:00", ValueEncoder.encode(new DateTimeType("2019-06-12T17:30:00Z"), "19.001")); + // special: clock fault + assertNull(ValueDecoder.decode("19.001", new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x80, 0 }, + DateTimeType.class)); + // special: no year, but month/day + assertNull(ValueDecoder.decode("19.001", new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x10, 0 }, + DateTimeType.class)); + // special: no day, but year + assertNull(ValueDecoder.decode("19.001", new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x08, 0 }, + DateTimeType.class)); + // special: no date, no time, no year + assertNull(ValueDecoder.decode("19.001", new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x1A, 0 }, + DateTimeType.class)); + // special: no time, but year etc. -> works if weekday is matching + assertNotNull(ValueDecoder.decode("19.001", + new byte[] { (byte) (2019 - 1900), 1, 15, 0x51, 30, 0, (byte) 0x02, 0 }, DateTimeType.class)); + // special: no time, but year etc. -> weekday is not matching + assertNull(ValueDecoder.decode("19.001", new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x02, 0 }, + DateTimeType.class)); + // special: no time, no year + assertNull(ValueDecoder.decode("19.001", new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x12, 0 }, + DateTimeType.class)); + // special: no date, no year + assertNotNull(ValueDecoder.decode("19.001", + new byte[] { (byte) (2019 - 1900), 1, 15, 17, 30, 0, (byte) 0x18, 0 }, DateTimeType.class)); } @Test @@ -306,19 +377,6 @@ class DPTTest { assertEquals("42", ValueEncoder.encode(new QuantityType<>("42 varh"), "29.012")); } - @Test - public void dpt6Value() { - assertEquals("42", ValueEncoder.encode(new DecimalType(42), "6.001")); - - assertEquals("42", ValueEncoder.encode(new DecimalType(42), "6.010")); - - assertEquals("0/0/0/0/1 0", Objects.toString(ValueDecoder.decode("6.020", new byte[] { 9 }, StringType.class))); - assertEquals("0/0/0/0/0 1", Objects.toString(ValueDecoder.decode("6.020", new byte[] { 2 }, StringType.class))); - assertEquals("1/1/1/1/1 2", - Objects.toString(ValueDecoder.decode("6.020", new byte[] { (byte) 0xfc }, StringType.class))); - assertEquals("0/0/0/0/1 0", ValueEncoder.encode(StringType.valueOf("0/0/0/0/1 0"), "6.020")); - } - @Test public void dpt232RgbValue() { // input data @@ -374,6 +432,18 @@ class DPTTest { byte[] negativeEnergy = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x02 }; assertEquals(new QuantityType<>("-1 Wh"), ValueDecoder.decode("235.001", negativeEnergy, QuantityType.class)); + + // invalid frame size + byte[] frameSizeTooSmall = new byte[] {}; + assertNull(ValueDecoder.decode("235.001", frameSizeTooSmall, DecimalType.class)); + assertNull(ValueDecoder.decode("235.001", frameSizeTooSmall, QuantityType.class)); + assertNull(ValueDecoder.decode("235.61001", frameSizeTooSmall, DecimalType.class)); + assertNull(ValueDecoder.decode("235.61001", frameSizeTooSmall, QuantityType.class)); + byte[] frameSizeTooLong = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + assertNull(ValueDecoder.decode("235.001", frameSizeTooLong, DecimalType.class)); + assertNull(ValueDecoder.decode("235.001", frameSizeTooLong, QuantityType.class)); + assertNull(ValueDecoder.decode("235.61001", frameSizeTooLong, DecimalType.class)); + assertNull(ValueDecoder.decode("235.61001", frameSizeTooLong, QuantityType.class)); } @Test diff --git a/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/i18n/KNXTranslationProviderTest.java b/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/i18n/KNXTranslationProviderTest.java index 836a84e213a..a24b3bae863 100644 --- a/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/i18n/KNXTranslationProviderTest.java +++ b/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/i18n/KNXTranslationProviderTest.java @@ -97,6 +97,7 @@ class KNXTranslationProviderTest { final Exception se = new KNXLinkClosedException("connection closed", e); assertNotNull(KNXTranslationProvider.I18N.getLocalizedException(e)); assertNotNull(KNXTranslationProvider.I18N.getLocalizedException(se)); + assertNotNull(KNXTranslationProvider.I18N.getLocalizedException(new Exception())); assertEquals("KNXException, error 1", KNXTranslationProvider.I18N.getLocalizedException(e)); // use mockup classes with known dictionary diff --git a/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/itests/Back2BackTest.java b/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/itests/Back2BackTest.java index 3d96e7bcfa1..8b3cd044606 100644 --- a/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/itests/Back2BackTest.java +++ b/bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/itests/Back2BackTest.java @@ -301,7 +301,8 @@ public class Back2BackTest { @Test void testDpt2() { for (int subType = 1; subType <= 12; subType++) { - helper("2." + String.format("%03d", subType), new byte[] { 3 }, new DecimalType(3)); + helper("2." + String.format("%03d", subType), new byte[] { (byte) (subType % 4) }, + new DecimalType(subType % 4)); } }