diff --git a/bundles/org.openhab.core/src/main/java/org/openhab/core/util/ColorUtil.java b/bundles/org.openhab.core/src/main/java/org/openhab/core/util/ColorUtil.java
index 23295f0e38a..fb85974e684 100644
--- a/bundles/org.openhab.core/src/main/java/org/openhab/core/util/ColorUtil.java
+++ b/bundles/org.openhab.core/src/main/java/org/openhab/core/util/ColorUtil.java
@@ -46,7 +46,10 @@ public class ColorUtil {
private static final BigDecimal BIG_DECIMAL_60 = BigDecimal.valueOf(60);
private static final BigDecimal BIG_DECIMAL_5 = BigDecimal.valueOf(5);
private static final BigDecimal BIG_DECIMAL_3 = BigDecimal.valueOf(3);
+ private static final BigDecimal BIG_DECIMAL_2 = BigDecimal.valueOf(2);
private static final BigDecimal BIG_DECIMAL_2_POINT_55 = new BigDecimal("2.55");
+ private static final BigDecimal BIG_DECIMAL_0 = BigDecimal.valueOf(0);
+ private static final BigDecimal BIG_DECIMAL_127_POINT_5 = BigDecimal.valueOf(127.5);
public static final Gamut DEFAULT_GAMUT = new Gamut(new double[] { 0.9961, 0.0001 }, new double[] { 0, 0.9961 },
new double[] { 0, 0.0001 });
@@ -79,7 +82,7 @@ public static int[] hsbToRgb(HSBType hsb) {
* This function does not round the components. For conversion to integer values in the range 0 to 255 use
* {@link #hsbToRgb(HSBType)}.
*
- * See also: {@link #hsbToRgb(HSBType)}, {@link #hsbTosRgb(HSBType)}
+ * See also: {@link #hsbToRgb(HSBType)}, {@link #hsbTosRgb(HSBType)}, {@link #hsbToRgbwPercent(HSBType)}
*
* @param hsb an {@link HSBType} value.
* @return array of three {@link PercentType} with the RGB values in the range 0 to 100 percent.
@@ -140,6 +143,102 @@ public static PercentType[] hsbToRgbPercent(HSBType hsb) {
return new PercentType[] { red, green, blue };
}
+ /**
+ * Transform HSV based {@link HSBType} to RGBW.
+ *
+ * See Converting RGB to RGBW.
+ *
+ * This function does not round the components. For conversion to integer values in the range 0 to 255 use
+ * {@link #hsbToRgb(HSBType)}.
+ *
+ * See also: {@link #hsbToRgb(HSBType)}, {@link #hsbTosRgb(HSBType)}, {@link #hsbToRgbPercent(HSBType)}
+ *
+ * @param hsb an {@link HSBType} value.
+ * @return array of four {@link PercentType} with the RGBW values in the range 0 to 100 percent.
+ */
+ public static PercentType[] hsbToRgbwPercent(HSBType hsb) {
+ int[] rgb = hsbToRgb(hsb);
+ final BigDecimal Ri = new BigDecimal(rgb[0]);
+ final BigDecimal Gi = new BigDecimal(rgb[1]);
+ final BigDecimal Bi = new BigDecimal(rgb[2]);
+ // Get the maximum between R, G, and B
+ final BigDecimal tM = Ri.max(Gi.max(Bi));
+
+ // If the maximum value is 0, immediately return pure black.
+ if (tM.floatValue() == 0) {
+ return new PercentType[] { PercentType.ZERO, PercentType.ZERO, PercentType.ZERO, PercentType.ZERO };
+ }
+
+ // This section serves to figure out what the color with 100% hue is
+ final BigDecimal multiplier = BIG_DECIMAL_255.divide(tM, 0, RoundingMode.DOWN);
+ final BigDecimal hR = Ri.multiply(multiplier);
+ final BigDecimal hG = Gi.multiply(multiplier);
+ final BigDecimal hB = Bi.multiply(multiplier);
+
+ // This calculates the Whiteness (not strictly speaking Luminance) of the color
+ final BigDecimal M = hR.max(hG.max(hB));
+ final BigDecimal m = hR.min(hG.min(hB));
+ final BigDecimal Luminance = ((M.add(m).divide(BIG_DECIMAL_2).subtract(BIG_DECIMAL_127_POINT_5))
+ .multiply(BIG_DECIMAL_255.divide(BIG_DECIMAL_127_POINT_5))).divide(multiplier);
+
+ // Calculate the output values
+ BigDecimal Ro = (Ri.subtract(Luminance).multiply(BIG_DECIMAL_100).divide(BIG_DECIMAL_255, 0,
+ RoundingMode.DOWN));
+ BigDecimal Go = (Gi.subtract(Luminance).multiply(BIG_DECIMAL_100).divide(BIG_DECIMAL_255, 0,
+ RoundingMode.DOWN));
+ BigDecimal Bo = (Bi.subtract(Luminance).multiply(BIG_DECIMAL_100).divide(BIG_DECIMAL_255, 0,
+ RoundingMode.DOWN));
+ BigDecimal Wo = Luminance.multiply(BIG_DECIMAL_100).divide(BIG_DECIMAL_255, 0, RoundingMode.DOWN);
+
+ // check range
+ Ro = BIG_DECIMAL_100.min(Ro.max(BIG_DECIMAL_0));
+ Go = BIG_DECIMAL_100.min(Go.max(BIG_DECIMAL_0));
+ Bo = BIG_DECIMAL_100.min(Bo.max(BIG_DECIMAL_0));
+ Wo = BIG_DECIMAL_100.min(Wo.max(BIG_DECIMAL_0));
+
+ return new PercentType[] { new PercentType(Ro), new PercentType(Go), new PercentType(Bo), new PercentType(Wo) };
+ }
+
+ /**
+ * Transform HSV based {@link HSBType} to RGBW.
+ *
+ * See Converting RGB to RGBW.
+ *
+ * This function does not round the components. For conversion to integer values in the range 0 to 255 use
+ * {@link #hsbToRgb(HSBType)}.
+ *
+ * See also: {@link #hsbToRgb(HSBType)}, {@link #hsbTosRgb(HSBType)}, {@link #hsbToRgbPercent(HSBType)}
+ *
+ * @param rgb array of three int with the RGB values in the range 0 to 255.
+ * @return hsb an {@link HSBType} value.
+ *
+ */
+ public static HSBType rgbwTohsb(double r, double g, double b, double w) {
+
+ BigDecimal Luminance = BigDecimal.valueOf(w);
+ BigDecimal Ri = BigDecimal.valueOf(r).add(Luminance);
+ BigDecimal Gi = BigDecimal.valueOf(g).add(Luminance);
+ BigDecimal Bi = BigDecimal.valueOf(b).add(Luminance);
+
+ // Get the maximum between R, G, and B
+ final BigDecimal tM = BIG_DECIMAL_255.min(Ri.max(Gi.max(Bi)).max(BIG_DECIMAL_0));
+
+ // If the maximum value is 0, immediately return pure black.
+ if (tM.floatValue() == 0) {
+ return HSBType.BLACK;
+ }
+
+ final BigDecimal multiplier = BIG_DECIMAL_255.divide(tM, 0, RoundingMode.DOWN);
+
+ BigDecimal Ro = Ri.divide(multiplier).min(BIG_DECIMAL_255).max(BIG_DECIMAL_0);
+ BigDecimal Go = Gi.divide(multiplier).min(BIG_DECIMAL_255).max(BIG_DECIMAL_0);
+ BigDecimal Bo = Bi.divide(multiplier).min(BIG_DECIMAL_255).max(BIG_DECIMAL_0);
+
+ return HSBType.fromRGB(Ro.intValue(), Go.intValue(), Bo.intValue());
+ }
+
/**
* Transform HSV based {@link HSBType}
* to the RGB value representing the color in the default
diff --git a/bundles/org.openhab.core/src/test/java/org/openhab/core/util/ColorUtilTest.java b/bundles/org.openhab.core/src/test/java/org/openhab/core/util/ColorUtilTest.java
index 21acc54d127..e1577389fc8 100644
--- a/bundles/org.openhab.core/src/test/java/org/openhab/core/util/ColorUtilTest.java
+++ b/bundles/org.openhab.core/src/test/java/org/openhab/core/util/ColorUtilTest.java
@@ -59,6 +59,71 @@ public void inversionTest(HSBType hsb) {
assertThat(deltaBri, is(lessThanOrEqualTo(1.0)));
}
+ @Test
+ public void hsbtorgbwTest() {
+ HSBType hsb = HSBType.WHITE;
+ PercentType[] rgbw = ColorUtil.hsbToRgbwPercent(hsb);
+ assertEquals(0.0, rgbw[0].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[1].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[2].doubleValue(), 0.01);
+ assertEquals(100.0, rgbw[3].doubleValue(), 0.01);
+
+ hsb = HSBType.BLACK;
+ rgbw = ColorUtil.hsbToRgbwPercent(hsb);
+ assertEquals(0.0, rgbw[0].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[1].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[2].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[3].doubleValue(), 0.01);
+
+ hsb = HSBType.RED;
+ rgbw = ColorUtil.hsbToRgbwPercent(hsb);
+ assertEquals(100.0, rgbw[0].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[1].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[2].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[3].doubleValue(), 0.01);
+
+ hsb = HSBType.GREEN;
+ rgbw = ColorUtil.hsbToRgbwPercent(hsb);
+ assertEquals(0.0, rgbw[0].doubleValue(), 0.01);
+ assertEquals(100.0, rgbw[1].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[2].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[3].doubleValue(), 0.01);
+
+ hsb = HSBType.BLUE;
+ rgbw = ColorUtil.hsbToRgbwPercent(hsb);
+ assertEquals(0.0, rgbw[0].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[1].doubleValue(), 0.01);
+ assertEquals(100.0, rgbw[2].doubleValue(), 0.01);
+ assertEquals(0.0, rgbw[3].doubleValue(), 0.01);
+ }
+
+ public void rgbwtohsbTest() {
+ // Test Red
+ HSBType hsb = ColorUtil.rgbwTohsb(255, 0, 0, 0);
+ int[] convertedRgb = ColorUtil.hsbToRgb(hsb);
+ assertRgbEquals(new int[] { 255, 0, 0 }, convertedRgb);
+
+ // Test Green
+ hsb = ColorUtil.rgbwTohsb(0, 255, 0, 0);
+ convertedRgb = ColorUtil.hsbToRgb(hsb);
+ assertRgbEquals(new int[] { 0, 255, 0 }, convertedRgb);
+
+ // Test Blue
+ hsb = ColorUtil.rgbwTohsb(0, 0, 255, 0);
+ convertedRgb = ColorUtil.hsbToRgb(hsb);
+ assertRgbEquals(new int[] { 0, 0, 255 }, convertedRgb);
+
+ // Test White
+ hsb = ColorUtil.rgbwTohsb(0, 0, 0, 255);
+ convertedRgb = ColorUtil.hsbToRgb(hsb);
+ assertRgbEquals(new int[] { 255, 255, 255 }, convertedRgb);
+
+ // Test Black
+ hsb = ColorUtil.rgbwTohsb(0, 0, 0, 0);
+ convertedRgb = ColorUtil.hsbToRgb(hsb);
+ assertRgbEquals(new int[] { 0, 0, 0 }, convertedRgb);
+ }
+
@ParameterizedTest
@MethodSource("invalids")
public void invalidXyValues(double[] xy) {