Skip to content

Commit

Permalink
[knx] Add support for RGBW represented by HSBType (openhab#16078)
Browse files Browse the repository at this point in the history
Allow lossy conversion from RGBW to HSBType and back instead
of using separate items for RGB and W.
Select via DPT 251.60600.

Signed-off-by: Holger Friedrich <mail@holger-friedrich.de>
Signed-off-by: Jørgen Austvik <jaustvik@acm.org>
  • Loading branch information
holgerfriedrich authored and austvik committed Mar 27, 2024
1 parent e0a543c commit b8627bb
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 18 deletions.
12 changes: 9 additions & 3 deletions bundles/org.openhab.binding.knx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Switching lights on and off, activating your roller shutters, or changing room t
To access your KNX bus, you either need a gateway device which is connected to the KNX bus and allows computers to access the bus communication.
This can be either an Ethernet (as a Router or a Tunnel type) or a serial gateway.
The KNX binding then can communicate directly with this gateway.
Alternatively, a PC running [KNXD](https://github.com/knxd/knxd) (free open source component software) can be put in between which then acts as a broker allowing multiple client to connect to the same gateway.
Alternatively, a PC running [KNXD](https://github.com/knxd/knxd) (free open source component software) can be put in between which then acts as a broker allowing multiple clients to connect to the same gateway.
Since the protocol is identical, the KNX binding can also communicate with it transparently.

***Attention:*** With the introduction of Unit of Measurement (UoM) support, some data types have changed (see `number` channel below):
Expand Down Expand Up @@ -121,11 +121,17 @@ When a `GroupValueRead` telegram is sent from the KNX bus to a *-control Channel
| position | Group address brightness | 5.001 |
| increaseDecrease | Group address for relative brightness | 3.007 |

The `hsb` address supports DPT 242.600 and 251.600.
The `hsb` address supports DPT 232.600 (RGB), 242.600 (xyY), and 251.600 (RGBW).

Some RGB/RGBW products (e.g. MDT) support HSB values for DPT 232.600 instead of RGB.
Some RGB/RGBW products (e.g. MDT) use HSB values for DPT 232.600 instead of RGB.
This is supported as "vendor-specific DPT" with a value of 232.60000.

RGBW (DPT 251.600) can either be converted to HSBType, or be represented two items: a HSBType for RGB and an additional PercentType for W channel.
Default handling for RGBW is to use separate items.
Note that this also requires two frames being sent out separately when these elements are sent to the bus, as the binary representation uses a partially populated KNX frame.
Alternatively, a single HSB item can be used. Conversion to a single HSBType will loose the exact setting for W, and will reconstruct it when a conversion to RGBW is required.
This option can be selected using the special DPT 251.60600.

##### Channel Type `contact`, `contact-control`

| Parameter | Description | Default DPT |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class DPTUtil {

// used to map vendor-specific data to standard DPT
public static final Map<String, String> NORMALIZED_DPT = Map.of(//
"232.60000", "232.600");
"232.60000", "232.600", "251.60600", "251.600");

// fall back if no specific type is defined in DPT_TYPE_MAP
private static final Map<String, Set<Class<? extends Type>>> DPT_MAIN_TYPE_MAP = Map.ofEntries( //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ private static boolean check23561001(byte[] data) throws KNXException {
case "242":
return handleDpt242(value);
case "251":
return handleDpt251(value, preferredType);
return handleDpt251(value, subType, preferredType);
default:
return handleNumericDpt(id, translator, preferredType);
// TODO 6.001 is mapped to PercentType, which can only cover 0-100%, not -128..127%
Expand Down Expand Up @@ -418,27 +418,47 @@ private static Type handleDpt10(String value) throws ParseException {
return null;
}

private static @Nullable Type handleDpt251(String value, Class<? extends Type> preferredType) {
private static @Nullable Type handleDpt251(String value, String subType, Class<? extends Type> preferredType) {
Matcher rgbw = RGBW_PATTERN.matcher(value);
if (rgbw.matches()) {
String rString = rgbw.group("r");
String gString = rgbw.group("g");
String bString = rgbw.group("b");
String wString = rgbw.group("w");

if (rString != null && gString != null && bString != null && HSBType.class.equals(preferredType)) {
// does not support PercentType and r,g,b valid -> HSBType
int r = coerceToRange((int) (Double.parseDouble(rString.replace(",", ".")) * 2.55), 0, 255);
int g = coerceToRange((int) (Double.parseDouble(gString.replace(",", ".")) * 2.55), 0, 255);
int b = coerceToRange((int) (Double.parseDouble(bString.replace(",", ".")) * 2.55), 0, 255);

return HSBType.fromRGB(r, g, b);
} else if (wString != null && PercentType.class.equals(preferredType)) {
// does support PercentType and w valid -> PercentType
BigDecimal w = new BigDecimal(wString.replace(",", "."));

return new PercentType(w);
switch (subType) {
case "600":
if (rString != null && gString != null && bString != null && HSBType.class.equals(preferredType)) {
// does not support PercentType and r,g,b valid -> HSBType
int r = coerceToRange((int) (Double.parseDouble(rString.replace(",", ".")) * 2.55), 0, 255);
int g = coerceToRange((int) (Double.parseDouble(gString.replace(",", ".")) * 2.55), 0, 255);
int b = coerceToRange((int) (Double.parseDouble(bString.replace(",", ".")) * 2.55), 0, 255);

return HSBType.fromRGB(r, g, b);
} else if (wString != null && PercentType.class.equals(preferredType)) {
// does support PercentType and w valid -> PercentType
BigDecimal w = new BigDecimal(wString.replace(",", "."));

return new PercentType(w);
}
case "60600":
// special type used by OH for .600 indicating that RGBW should be handled with a single HSBType,
// typically we use HSBType for RGB and PercentType for W.
if (rString != null && gString != null && bString != null && wString != null
&& HSBType.class.equals(preferredType)) {
// does support PercentType and w valid -> PercentType
int r = coerceToRange((int) (Double.parseDouble(rString.replace(",", ".")) * 2.55), 0, 255);
int g = coerceToRange((int) (Double.parseDouble(gString.replace(",", ".")) * 2.55), 0, 255);
int b = coerceToRange((int) (Double.parseDouble(bString.replace(",", ".")) * 2.55), 0, 255);
int w = coerceToRange((int) (Double.parseDouble(wString.replace(",", ".")) * 2.55), 0, 255);

return ColorUtil.rgbToHsb(new int[] { r, g, b, w });
}
default:
LOGGER.warn("Unknown subtype '251.{}', no conversion possible.", subType);
return null;
}

}
LOGGER.warn("Failed to convert '{}' (DPT 251): Pattern does not match or invalid content", value);
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ private static String handleHSBType(String dptId, HSBType hsb) {
PercentType[] rgbw = ColorUtil.hsbToRgbPercent(hsb);
return String.format("%,.1f %,.1f %,.1f - %%", rgbw[0].doubleValue(), rgbw[1].doubleValue(),
rgbw[2].doubleValue());
case "251.60600":
PercentType[] rgbw2 = ColorUtil.hsbToRgbwPercent(hsb);
return String.format("%,.1f %,.1f %,.1f %,.1f %%", rgbw2[0].doubleValue(), rgbw2[1].doubleValue(),
rgbw2[2].doubleValue(), rgbw2[3].doubleValue());
case "5.003":
return hsb.getHue().toString();
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,16 @@ void testColorDpts() {
// RGBW, only RGB part
helper("251.600", new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x0e },
new HSBType("0, 0, 100"), new byte[] { 1, 1, 1, 0, 0, 0 }, new byte[0]);
// RGBW, only W part
helper("251.600", new byte[] { 0x0, 0x0, 0x0, 0x1A, 0x00, 0x01 }, new PercentType("10.2"));
// RGBW, all
helper("251.60600", new byte[] { (byte) 0x0, (byte) 0x0, (byte) 0x0, (byte) 0xff, 0x00, 0x0f },
new HSBType("0, 0, 100"), new byte[] { 1, 1, 1, 2, 0, 0 }, new byte[0]);
// RGBW, mixed
int[] rgbw = new int[] { 240, 0x0, 0x0, 0x0f };
HSBType hsb = ColorUtil.rgbToHsb(rgbw);
helper("251.60600", new byte[] { (byte) rgbw[0], (byte) rgbw[1], (byte) rgbw[2], (byte) rgbw[3], 0x00, 0x0f },
hsb, new byte[] { 2, 2, 2, 2, 0, 0 }, new byte[0]);
}

@Test
Expand Down

0 comments on commit b8627bb

Please sign in to comment.