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

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: Ciprian Pascu <contact@ciprianpascu.ro>
This commit is contained in:
Holger Friedrich 2024-01-26 21:57:17 +01:00 committed by Ciprian Pascu
parent 543721aa1b
commit 1e9d41442e
5 changed files with 56 additions and 16 deletions

View File

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

View File

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

View File

@ -208,7 +208,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%
@ -418,7 +418,7 @@ public class ValueDecoder {
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");
@ -426,6 +426,8 @@ public class ValueDecoder {
String bString = rgbw.group("b");
String wString = rgbw.group("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);
@ -439,6 +441,24 @@ public class ValueDecoder {
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;

View File

@ -171,6 +171,10 @@ public class ValueEncoder {
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:

View File

@ -582,6 +582,16 @@ public class Back2BackTest {
// 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