From e62bcf5db4f3e3126f60ea467d31cbdd9755ee49 Mon Sep 17 00:00:00 2001 From: Holger Friedrich Date: Mon, 22 Jan 2024 22:37:46 +0100 Subject: [PATCH] [knx] Allow receiving DPT 235.001 (#16094) * [knx] Allow receiving DPT 235.001 Composed type not yet supported by Calimero, thus only receiving data is implemented. Configure DPT 235.001 to receive ActiveEnergy. Configure DPT 235.61001 to receive Tariff information. Fixes #15159 Signed-off-by: Holger Friedrich --- bundles/org.openhab.binding.knx/README.md | 12 ++++ .../binding/knx/internal/dpt/DPTUtil.java | 1 + .../knx/internal/dpt/ValueDecoder.java | 62 +++++++++++++++++-- .../binding/knx/internal/dpt/DPTTest.java | 24 +++++++ 4 files changed, 93 insertions(+), 6 deletions(-) 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