diff --git a/bundles/org.openhab.binding.knx/README.md b/bundles/org.openhab.binding.knx/README.md index c76196e42a2..fc0b764521a 100644 --- a/bundles/org.openhab.binding.knx/README.md +++ b/bundles/org.openhab.binding.knx/README.md @@ -267,6 +267,18 @@ With `*-control` channels, the state is not owned by any device on the KNX bus, The element `dpt` is highly recommended and may change to a mandatory element in future versions. If omitted, the corresponding default value will be used (see the channel descriptions above). +## Special DPTs + +OpenHAB supports all DPTs supported by the corresponding release of Calimero library. + +Additional DPTs have been introduced to add functionality: + +| DPT | Description | Remark | +|---------------|-------------------------------------------------------------|------------| +| DPT 232.60000 | DPT 232.600 with HSB instead of RGB data (see below) | read/write | +| DPT 235.001 | Composed DPT 235.001, first element ActiveEnergy (Wh) | read only | +| DPT 235.61001 | Composed DPT 235.001, second element Tariff (plain number) | read only | + ## KNX Secure > NOTE: Support for KNX Secure is partly implemented for openHAB and should be considered as experimental. diff --git a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/DPTUtil.java b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/DPTUtil.java index 7df4abf4054..96fb13676a6 100644 --- a/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/DPTUtil.java +++ b/bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/DPTUtil.java @@ -84,6 +84,7 @@ public class DPTUtil { Map.entry("29", Set.of(QuantityType.class, DecimalType.class)), // Map.entry("229", Set.of(DecimalType.class)), // Map.entry("232", Set.of(HSBType.class)), // + Map.entry("235", Set.of(QuantityType.class, DecimalType.class)), // Map.entry("242", Set.of(HSBType.class)), // Map.entry("243", Set.of(StringType.class)), // Map.entry("249", Set.of(StringType.class)), // 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 1fc19b0ce42..85c7d4337a9 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 @@ -81,6 +81,28 @@ public class ValueDecoder { public static final Pattern XYY_PATTERN = Pattern .compile("(?:\\((?\\d+(?:[,.]\\d+)?) (?\\d+(?:[,.]\\d+)?)\\))?\\s*(?:(?\\d+(?:[,.]\\d+)?)\\s%)?"); + private static boolean check235001(byte[] data) throws KNXException { + if (data.length != 6) { + throw new KNXFormatException("DPT235 broken frame"); + } + if ((data[5] & 2) == 0) { + LOGGER.trace("DPT235.001 w/o ActiveEnergy ignored"); + return false; + } + return true; + } + + private static boolean check23561001(byte[] data) throws KNXException { + if (data.length != 6) { + throw new KNXFormatException("DPT235 broken frame"); + } + if ((data[5] & 1) == 0) { + LOGGER.trace("DPT235.61001 w/o Tariff ignored"); + return false; + } + return true; + } + /** * convert the raw value received to the corresponding openHAB value * @@ -91,18 +113,46 @@ public class ValueDecoder { */ public static @Nullable Type decode(String dptId, byte[] data, Class preferredType) { try { - DPTXlator translator = TranslatorTypes.createTranslator(0, - DPTUtil.NORMALIZED_DPT.getOrDefault(dptId, dptId)); - translator.setData(data); - String value = translator.getValue(); - + String value = ""; + String translatorDptId = dptId; + DPTXlator translator; + try { + translator = TranslatorTypes.createTranslator(0, DPTUtil.NORMALIZED_DPT.getOrDefault(dptId, dptId)); + translator.setData(data); + value = translator.getValue(); + translatorDptId = translator.getType().getID(); + } catch (KNXException e) { + // special handling for decoding DPTs not yet supported by Calimero + if ("235.001".equals(dptId)) { + if (!check235001(data)) { + return null; + } + translator = TranslatorTypes.createTranslator(0, "13.010"); + translator.setData(data); + value = translator.getValue(); + dptId = "13.010"; + translatorDptId = dptId; + } else if ("235.61001".equals(dptId)) { + if (!check23561001(data)) { + return null; + } + translator = TranslatorTypes.createTranslator(0, "5.006"); + translator.setData(new byte[] { data[4] }); + value = translator.getValue(); + dptId = "5.006"; + translatorDptId = dptId; + } else { + // no known special case, handle unknown translator outer try block + throw e; + } + } String id = dptId; // prefer using the user-supplied DPT Matcher m = DPTUtil.DPT_PATTERN.matcher(id); if (!m.matches() || m.groupCount() != 2) { LOGGER.trace("User-Supplied DPT '{}' did not match for sub-type, using DPT returned from Translator", id); - id = translator.getType().getID(); + id = translatorDptId; m = DPTUtil.DPT_PATTERN.matcher(id); if (!m.matches() || m.groupCount() != 2) { LOGGER.warn("Couldn't identify main/sub number in dptID '{}'", id); 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 430a74edc97..1775261087a 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 @@ -330,6 +330,30 @@ class DPTTest { assertEquals(encoded, "r:" + data[0] + " g:" + data[1] + " b:" + data[2]); } + @Test + public void dpt235Decoder() { + byte[] noActiveEnergy = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xfd }; + assertNull(ValueDecoder.decode("235.001", noActiveEnergy, QuantityType.class)); + + byte[] noTariff = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe }; + assertNull(ValueDecoder.decode("235.61001", noTariff, DecimalType.class)); + + byte[] activeEnergy = new byte[] { (byte) 0x0, (byte) 0x0, (byte) 0x03, (byte) 0xff, (byte) 0x0a, (byte) 0x02 }; + assertEquals(new QuantityType<>("1023 Wh"), ValueDecoder.decode("235.001", activeEnergy, QuantityType.class)); + + byte[] activeTariff = new byte[] { (byte) 0x0, (byte) 0x0, (byte) 0x03, (byte) 0xff, (byte) 0x0a, (byte) 0x01 }; + assertEquals(new DecimalType("10"), ValueDecoder.decode("235.61001", activeTariff, DecimalType.class)); + + byte[] activeAll = new byte[] { (byte) 0x0, (byte) 0x0, (byte) 0x03, (byte) 0xff, (byte) 0x0a, (byte) 0x03 }; + assertEquals(new QuantityType<>("1023 Wh"), ValueDecoder.decode("235.001", activeAll, QuantityType.class)); + assertEquals(new DecimalType("10"), ValueDecoder.decode("235.61001", activeAll, DecimalType.class)); + + 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)); + } + @Test public void dpt251White() { // input data: color white