[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 <mail@holger-friedrich.de>
This commit is contained in:
Holger Friedrich 2024-01-22 22:37:46 +01:00 committed by GitHub
parent 3ea1451050
commit e62bcf5db4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 93 additions and 6 deletions

View File

@ -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.

View File

@ -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)), //

View File

@ -81,6 +81,28 @@ public class ValueDecoder {
public static final Pattern XYY_PATTERN = Pattern
.compile("(?:\\((?<x>\\d+(?:[,.]\\d+)?) (?<y>\\d+(?:[,.]\\d+)?)\\))?\\s*(?:(?<Y>\\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<? extends Type> 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);

View File

@ -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