]> git.basschouten.com Git - openhab-addons.git/commitdiff
[knx] Add support for RGBW represented by HSBType (#16078)
authorHolger Friedrich <mail@holger-friedrich.de>
Fri, 26 Jan 2024 20:57:17 +0000 (21:57 +0100)
committerGitHub <noreply@github.com>
Fri, 26 Jan 2024 20:57:17 +0000 (21:57 +0100)
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>
bundles/org.openhab.binding.knx/README.md
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/DPTUtil.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueDecoder.java
bundles/org.openhab.binding.knx/src/main/java/org/openhab/binding/knx/internal/dpt/ValueEncoder.java
bundles/org.openhab.binding.knx/src/test/java/org/openhab/binding/knx/internal/itests/Back2BackTest.java

index fc0b764521a66d9fbfea8e5f1f788e4c804784c0..8012f3d2784c964d358541f59d68156be6610441 100644 (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 |
index 96fb13676a6163dccd7af2b6b49ad574aaf2c51d..c3c7479e377c9199981758e391cb4f8f1771603e 100644 (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( //
index 85c7d4337a96e45685743933e5ab1bedd350654f..8d6b33550cec47122fccfd25beb128d49ab80740 100644 (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,19 +426,39 @@ public class ValueDecoder {
             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;
index cbf5de4bab04d9eb77e34a4674588b4375393b2f..f2768a7b83a325c5b75e7c54a4aa0663ea90995e 100644 (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:
index 80499040032e626829eca36438501131d2d72ea9..36a57a51697d8bb4d24e5f28975ccfde9749a656 100644 (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