Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[knx] Add support for RGBW represented by HSBType #16078

Merged
merged 1 commit into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -158,7 +158,7 @@ public class ValueDecoder {
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 @@ -358,27 +358,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 @@ -170,6 +170,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 @@ -510,6 +510,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