@@ -54,6 +54,7 @@ abstract class LightModelAbstractLogicImpl {
5454 private boolean supportsColor = false ; // true if the light supports color
5555 private boolean rgbLinkedToBrightness = false ; // true if RGB(W) values are linked to the brightness
5656 private boolean supportsRgbWhite = false ; // true if the light supports RGB with White
57+ private boolean supportsRgbCoolWarmWhite = false ; // true if the light supports RGB with Cool+Warm White
5758 private boolean supportsBrightness = false ; // true if the light supports brightness
5859 private boolean supportsColorTemperature = false ; // true if the light supports color temperature
5960
@@ -79,18 +80,21 @@ abstract class LightModelAbstractLogicImpl {
7980 * @param supportsColor true if the light supports color control
8081 * @param rgbLinkedToBrightness true if RGB values are linked with the 'B' part of the {@link HSBType}
8182 * @param supportsRgbWhite true if the light supports RGBW rather than RGB color control
83+ * @param supportsRgbCoolWarmWhite true if the light supports RGBCW rather than RGB or RGBW color control
8284 * @param minimumOnBrightness the minimum brightness percent to consider as light "ON"
8385 * @param warmestMired the 'warmest' white color temperature in Mired
8486 * @param coolestMired the 'coolest' white color temperature in Mired
8587 * @param stepSize the step size for IncreaseDecreaseType commands
8688 */
8789 LightModelAbstractLogicImpl (boolean supportsBrightness , boolean supportsColorTemperature , boolean supportsColor ,
88- boolean rgbLinkedToBrightness , boolean supportsRgbWhite , @ Nullable Double minimumOnBrightness ,
89- @ Nullable Double warmestMired , @ Nullable Double coolestMired , @ Nullable Double stepSize ) {
90+ boolean rgbLinkedToBrightness , boolean supportsRgbWhite , boolean supportsRgbCoolWarmWhite ,
91+ @ Nullable Double minimumOnBrightness , @ Nullable Double warmestMired , @ Nullable Double coolestMired ,
92+ @ Nullable Double stepSize ) {
9093 this .supportsColor = supportsColor || supportsRgbWhite || rgbLinkedToBrightness ;
9194 this .supportsBrightness = supportsBrightness || supportsColor || supportsRgbWhite || rgbLinkedToBrightness ;
9295 this .supportsColorTemperature = supportsColorTemperature ;
9396 this .supportsRgbWhite = supportsRgbWhite ;
97+ this .supportsRgbCoolWarmWhite = supportsRgbCoolWarmWhite ;
9498 this .rgbLinkedToBrightness = rgbLinkedToBrightness ;
9599 this .minimumOnBrightness = minimumOnBrightness != null ? minimumOnBrightness : this .minimumOnBrightness ;
96100 this .warmestMired = warmestMired != null ? warmestMired : this .warmestMired ;
@@ -167,6 +171,13 @@ boolean configGetSupportsColorTemperature() {
167171 return supportsColorTemperature ;
168172 }
169173
174+ /**
175+ * Configuration: check if RGBCW color control is supported
176+ */
177+ boolean configGetSupportsRgbCoolWarmWhite () {
178+ return supportsRgbCoolWarmWhite ;
179+ }
180+
170181 /**
171182 * Configuration: check if RGBW color control is supported versus RGB only
172183 */
@@ -283,6 +294,15 @@ void configSetSupportsColorTemperature(boolean supportsColorTemperature) {
283294 this .supportsColorTemperature = supportsColorTemperature ;
284295 }
285296
297+ /**
298+ * Configuration: set whether RGBCW color control is supported
299+ *
300+ * @param supportsRgbCoolWarmWhite true if RGBW color control is supported
301+ */
302+ void configSetSupportsRgbCoolWarmWhite (boolean supportsRgbCoolWarmWhite ) {
303+ this .supportsRgbCoolWarmWhite = supportsRgbCoolWarmWhite ;
304+ }
305+
286306 /**
287307 * Configuration: set whether RGBW color control is supported versus RGB only
288308 *
@@ -380,10 +400,11 @@ OnOffType getOnOff(boolean... forceChannelVisible) {
380400 }
381401
382402 /**
383- * Runtime State: get the RGB(W) values as an array of doubles in range [0..255]. Depending on the value of '{@link
384- * supportsRgbWhite}', the array length is either 3 (RGB) or 4 for (RGBW). The array is in the order [red, green,
385- * blue, (white)]. Depending on the value of '{@link supportsRgbDimming}', the brightness may or may not be used
386- * follows:
403+ * Runtime State: get the RGB(C)(W) values as an array of doubles in range [0..255]. Depending on the value of
404+ * '{@link supportsRgbWhite}' and {@link supportsRgbColdWarmWhite}, the array length is either 3 (RGB), 4 (RGBW),
405+ * or 5 (RGBCW). The array is in the order [red, green, blue, (cold-)(white), (warm-white)].
406+ *
407+ * Depending on the value of '{@link supportsRgbDimming}', the brightness may or may not be used as follows:
387408 *
388409 * <ul>
389410 * <li>{@code supportsRgbDimming == false}: The return result does not depend on the current brightness. In other
@@ -395,13 +416,29 @@ OnOffType getOnOff(boolean... forceChannelVisible) {
395416 * values relate to all the 'HSB' parts of the {@link HSBType} state.</li>
396417 * <ul>
397418 *
398- * @return double[] representing the RGB(W) components in range [0..255.0]
419+ * @return double[] representing the RGB(C)( W) components in range [0..255.0]
399420 */
400421 double [] getRGBx () {
401422 HSBType hsb = rgbLinkedToBrightness ? cachedColor
402423 : new HSBType (cachedColor .getHue (), cachedColor .getSaturation (), PercentType .HUNDRED );
403- PercentType [] rgbw = supportsRgbWhite ? ColorUtil .hsbToRgbwPercent (hsb ) : ColorUtil .hsbToRgbPercent (hsb );
404- return Arrays .stream (rgbw ).mapToDouble (p -> p .doubleValue () * 255.0 / 100.0 ).toArray ();
424+ if (!supportsRgbCoolWarmWhite ) {
425+ // use ColorUtils to get either RGB or RGBW
426+ PercentType [] rgbx = supportsRgbWhite ? ColorUtil .hsbToRgbwPercent (hsb ) : ColorUtil .hsbToRgbPercent (hsb );
427+ return Arrays .stream (rgbx ).mapToDouble (p -> p .doubleValue () * 255.0 / 100.0 ).toArray ();
428+ } else {
429+ // use own code to get RGBCW (consider moving this to ColorUtils later in a second step)
430+ PercentType [] rgbPct = ColorUtil .hsbToRgbPercent (hsb );
431+ double [] rgb = Arrays .stream (rgbPct ).mapToDouble (p -> p .doubleValue () * 255.0 / 100.0 ).toArray ();
432+ double [] rgbcw = new double [5 ];
433+ if (Arrays .stream (rgb ).max ().orElse (0.0 ) > 0.0 ) {
434+ double white = Arrays .stream (rgb ).min ().orElse (0.0 );
435+ System .arraycopy (Arrays .stream (rgb ).map (c -> c - white ).toArray (), 0 , rgbcw , 0 , 3 );
436+ double cool = (warmestMired - cachedMired ) / (warmestMired - coolestMired );
437+ rgbcw [3 ] = cool * white ;
438+ rgbcw [4 ] = (1.0 - cool ) * white ;
439+ }
440+ return rgbcw ;
441+ }
405442 }
406443
407444 /**
@@ -512,10 +549,10 @@ void setMired(double mired) throws IllegalArgumentException {
512549 }
513550
514551 /**
515- * Runtime State: update the color with RGB(W) fields from the remote light, and update the cached HSB color
516- * accordingly. The array must be in the order [red, green, blue, (white)]. If white is present but the light does
517- * not support white channel then IllegalArgumentException is thrown. Depending on the value of
518- * '{@link supportsRgbDimming}', the brightness may or may not change as follows:
552+ * Runtime State: update the color with RGB(C)( W) fields from the remote light, and update the cached HSB color
553+ * accordingly. The array must be in the order [red, green, blue, (cold)( white), (warm-white) ]. If white is
554+ * present but the light does not support white channelS then IllegalArgumentException is thrown. Depending on
555+ * the value of '{@link supportsRgbDimming}', the brightness may or may not change as follows:
519556 *
520557 * <ul>
521558 * <li>{@code supportsRgbDimming == false} both [255,0,0] and [127.5,0,0] change the color to RED without a change
@@ -531,8 +568,24 @@ void setMired(double mired) throws IllegalArgumentException {
531568 * @param rgbx an array of double representing RGB or RGBW values in range [0..255]
532569 */
533570 void setRGBx (double [] rgbx ) throws IllegalArgumentException {
534- if (rgbx .length > 3 && !supportsRgbWhite ) {
535- throw new IllegalArgumentException ("Light does not support white channel" );
571+ if (rgbx .length > 5 ) {
572+ throw new IllegalArgumentException ("Too many arguments in RGBx array" );
573+ }
574+ if (rgbx .length == 5 ) {
575+ if (!supportsRgbCoolWarmWhite ) {
576+ throw new IllegalArgumentException ("Light does not support RGBCW" );
577+ }
578+ /*
579+ * TODO implement RGBCW setting..
580+ * This requires a conversion from RGBCW to RGBW and then to HS(B).
581+ * This conversion is not entirely straightforward because it affects color temperature too.
582+ * Also it is not clear how to handle the 'B' part of HSB in the case of !rgbLinkedToBrightness.
583+ * For now, just ignore the cold and warm white channels..
584+ */
585+ return ;
586+ }
587+ if (rgbx .length == 4 && !supportsRgbWhite ) {
588+ throw new IllegalArgumentException ("Light does not support RGBW" );
536589 }
537590 HSBType dimmedHSB = ColorUtil .rgbToHsb (Arrays .stream (rgbx ).map (d -> d * 100.0 / 255.0 )
538591 .mapToObj (d -> zInternalPercentTypeOf (d )).toArray (PercentType []::new ));
0 commit comments