Light Color RGB/RGBW/RGBCW to and from HSB+Dimmer Conversion

color
hsb
rgbw
Tags: #<Tag:0x00007fd3120bb098> #<Tag:0x00007fd3120baf58> #<Tag:0x00007fd3120bae18>

( ) #1

Important! This article is a wiki article. Everyone can edit it. Please do!

The goal of this set of Items and Rules is to convert a proprietary given color information by a light bulb into a further processable Color Item and back.

The solution addresses:

  • An Item representing the color information about a Light Bulb or similar in the form of a String Item
  • An Item representing the color information about a Light Bulb or similar in the form of a Color Item
  • Optionally: Often Light Bulbs offer RBG LEDs and additionally control channels for cold/warm white LEDs. These are represented in the String Item and will be translated to Dimmer Item(s)

Example applications:

  • Sonoff B1 with Tasmota firmware (RGBCW)
  • AiLight with Tasmota firmware (RGBW)

The following rules assume that your lamp publishes and understands color commands in a hex-based concatenated format:

  • Tasmota firmware RGBCW: rrggbbccww, e.g., “FFCCAA7733”
  • Tasmota firmware RGBW: rrggbbww, e.g., “FFCCAA77”

Of course other formats are possible if you adapt the rules accordingly.

Solution

The following Shows the Items and Rules needed for light bulbs with five LED types: Red, Green, Blue, Cold White, Warm White (RGBCW). For Light bulbs with only one white channel (RGBW) or without dedicated white LEDs (RGB), remove the “Dimmer” lines accordingly.

Items File

Switch Lamp_Power            "Lamp General On/Off"            (Lights) {mqtt="..."}
String Lamp_ColorRGBCWString "Color RGBCW hex [0x%s]" <light> (Lights) {mqtt="..."}
Color  Lamp_ColorHSB         "Color HSB"              <light> (Lights)
Dimmer Lamp_CWDimmer         "Dimmer Cold White"      <light> (Lights)
Dimmer Lamp_WWDimmer         "Dimmer Warm White"      <light> (Lights)

Rules File

Unrelated, a good practice I can recommend:

val String filename = "rgbcw-light.rules"

Rule Nr. 1

Receive, convert and store updates by the light bulb / Reacting on an update to the String Item.

rule "RGBCW -> HSB+CW"
when
    Item Lamp_ColorRGBCWString received update
then
    var rgbcw = Lamp_ColorRGBCWString.state.toString
    
    var r = Integer::parseInt(rgbcw.substring(0, 2), 16)
    var g = Integer::parseInt(rgbcw.substring(2, 4), 16)
    var b = Integer::parseInt(rgbcw.substring(4, 6), 16)
    //logInfo(filename, "Input Conversion: r" + r + " g" + g + " b" + b)
    var HSBType hsb = HSBType.fromRGB(r, g, b)
    //logInfo(filename, "Input Conversion: hsb" + hsb)
    
    var cw_dimm = Integer::parseInt(rgbcw.substring(6, 8), 16) * 100 / 255
    var ww_dimm = Integer::parseInt(rgbcw.substring(8, 10), 16) * 100 / 255
    //logInfo(filename, "Input Conversion: cw_dimm" + cw_dimm)
    //logInfo(filename, "Input Conversion: ww_dimm" + ww_dimm)

    Lamp_ColorHSB.postUpdate(hsb)
    Lamp_CWDimmer.postUpdate(cw_dimm)
    Lamp_WWDimmer.postUpdate(ww_dimm)
end

Rule Nr.2

Send Commands to the Light Bulb / Process changes to the Color Item and/or the Dimmer Item(s).

rule "HSB+CW -> RGBCW"
when
    Item Lamp_ColorHSB received command or
    Item Lamp_CWDimmer received command or
    Item Lamp_WWDimmer received command
then
    //logInfo(filename, "Command received: " + receivedCommand)
    if (receivedCommand instanceof OnOffType) {
        if (receivedCommand == ON) {
            Lamp_Power.sendCommand(ON)
        } else {
            Lamp_Power.sendCommand(OFF)
        }
    return;
    }
    var r = ((Lamp_ColorHSB.state as HSBType).getRed * 255 / 100).intValue
    var g = ((Lamp_ColorHSB.state as HSBType).getGreen * 255 / 100).intValue
    var b = ((Lamp_ColorHSB.state as HSBType).getBlue * 255 / 100).intValue
    var cw = ((Lamp_CWDimmer.state as Number) * 255 / 100).intValue
    var ww = ((Lamp_WWDimmer.state as Number) * 255 / 100).intValue
    //logInfo(filename, "Output Conversion: r" + r + " g" + g + " b" + b)
    //logInfo(filename, "Output Conversion: cw" + cw + " ww" + ww)
    var rgbcw = String.format("%02x%02x%02x%02x%02x", r, g, b, cw, ww)
    //logInfo(filename, "Output Conversion: rgbcw" + rgbcw)
    Lamp_ColorRGBCWString.sendCommand(rgbcw)
end

Update on Tasmota Software since Build 5.12.0.h HSB is supported.
So conversion HSB <-> RGB is no longer needed

5.12.0h


Add command HSBColor Hue,Sat,Bri (#1642, #2203)

Details here:


Sonoff B1 RGBCW WiFi Bulb w/ Sonoff-Tasmota
Sonoff B1 RGBCW WiFi Bulb w/ Sonoff-Tasmota
Sonoff B1 RGBCW WiFi Bulb w/ Sonoff-Tasmota
( ) #2

@rlkoshak @lipp_markus Maybe some aspects from here could go into the Rules DSL -> Type Conversion documentation. Wdyt?

@Kai may I ask shortly, why is there no RGBType as an alternative to HSBType? Are there real reasons or did simply no one write it? I couldn’t find issues on the matter.


(Kai Kreuzer) #3

Argh, my eyes now hurt :persevere:

This is exactly what bindings are supposed to do - to encapsulate the logic of bringing specific devices to the ESH functional abstraction.

String Lamp_ColorRGBCWString “Color RGBCW hex [0x%s]” (Lights) {mqtt="…"}

This is really something that should not be exposed to the user, it is some internal value for which there is no suitable UI widget that would make any sense from it.

Because HSB is the ESH abstraction of a color value. We generally do not have alternatives (There is also only an ON/OFF and not a TRUE/FALSE and a location expects lat,long,alt and nothing else.

I understand where your tutorial comes from (low-level integration through MQTT/TCP/HTTP/etc), but please be aware that this is really not recommended and rather for tinkerers.

For the average user, it might be nicer to offer that transformation as a profile on a channel (e.g. of an MQTT thing), but ok, this infrastructure isn’t ready yet…


( ) #4

Oh, Interesting, this topic provoked more feedback than I expected ^^

I do get your point regarding a binding should handle this kind of thing! Every sane technology-specific binding does. The MQTT/TCP/HTTP binding will never be able to cover all of those cases though. That’s a fact, right?

Not sure what you want to say with that. The level of these rules is pretty normal for a rule that does not implement a simple if-this-then-that logic and could have a real usage scenario. Even the less tinkery user might be in need of such an option.

There will always be data we need to further process. Ultimately this is what rules logic is for. Yes this should be avoided, yes we don’t want users to be forced to do so, and yes, many bindings or the profile idea help with that. Still I believe one of the strong suits of openHAB is that the end user can always resort to writing a super-specific logic for his personal need.

Back to Bindings. Actually, a Binding specific to the “Tasmota Firmware MQTT Convention” is indeed something I was thinking about developing (1) when the MQTT base is finished and (2) if I ever find time to do so! :wink:

After some thinking I come to the same conclusion. From a neutral standpoint it is simply not as intuitive to be forced to “misuse” an HSBType object when you actually want to work with RGB. Yes, the type offers getter and setter methods for RGB but still.
It’s pointless to start the discussion now but it would probably have been better to name it ColorType and handle multiple color spaces equally inside with HSV being the default for all I care…


(Rich Koshak) #5

I’m a bit ambivalent about this one. If we did add it to the Type Conversion doc does that start us down a slippery slope of needing to document other types of conversions? Celsius to fahrenheit? Watts and Time to KwH? KM to Miles?

I think all of this information is very useful to many users but I’m concerned it would add length and complexity to that section of the docs, making it harder for the average user to use.

I guess I could go either way.


(lipp_markus) #6

Interesting question, thanks for bringing it up; interesting thread too.
I certainly can see some value in this and even that it may be helpful to users. However, the concern of how much fragmentation/granularity would be introduced came also to my mind. I think I am leaning that this would easily get out of hand and cause confusion, especially as some bindings will (as they should) handle this and others may not.

Given that I regularly have to scour the forum and internet for conversions of “this” in “that” (poor practical programming skills on my part), I am wondering whether that would not fit rather in a new chapter “Practical problems and some solutions” or similar, with some examples on how to handle transformation through transforms and/or rules to show more of the power of multiple approaches. But again, finding the right boundaries may be difficult, for example I could see this easily get out of hand with times and dates and how to convert these (although it remains a recurrent topic). Disclaimer: sorry, my skills are too inadequate to contribute though.


(trumee) #7

I modified the rules for the AiLight bulb flashed with Tasmota. My config is as follows,

Items

Switch Lamp_Power            "Lamp General On/Off"            (gLights,gBedRoom) {mqtt=">[mosquitto:cmnd/sonoff-7895/POWER:command:*:default], <[mosquitto:stat/sonoff-7895/POWER:state:default]" }
String Lamp_ColorRGBWString "Color RGBCW hex [0x%s]" <colorlight> (gLights,gBedRoom)   {mqtt=">[mosquitto:cmnd/sonoff-7895/COLOR:command:*:default],<[mosquitto:stat/sonoff-7895/RESULT:state:default]",autoupdate="false"}
Color  Lamp_ColorHSB         "Color HSB"              <colorlight> (gLights,gBedRoom)
Dimmer Lamp_WWDimmer         "Dimmer Warm White"      <colorlight> (gLights,gBedRoom)

Rules

val String filename = "rgbw-light.rules"
var Number ww

rule "System startup"
        when
                System started
        then
/*Initial value of dimmer*/
                ww=50
end


rule "RGBW -> HSB+CW"
when
    Item Lamp_ColorRGBWString received update
then
    var rgbw = Lamp_ColorRGBWString.state.toString


    var r = Integer::parseInt(rgbw.substring(0, 2), 16)
    var g = Integer::parseInt(rgbw.substring(2, 4), 16)
    var b = Integer::parseInt(rgbw.substring(4, 6), 16)
    logInfo(filename, "Input Conversion: r" + r + " g" + g + " b" + b)
    var HSBType hsb = HSBType.fromRGB(r, g, b)
    logInfo(filename, "Input Conversion: hsb" + hsb)

    var ww_dimm = Integer::parseInt(rgbw.substring(8, 10), 16) * 100 / 255
    logInfo(filename, "Input Conversion: ww_dimm" + ww_dimm)

    Lamp_ColorHSB.postUpdate(hsb)
    Lamp_WWDimmer.postUpdate(ww_dimm)
end


rule "HSB+CW -> RGBW"
when
    Item Lamp_ColorHSB received command or
    Item Lamp_WWDimmer received command
then
    logInfo(filename, "Command received: " + receivedCommand)
    if (receivedCommand instanceof OnOffType) {
        if (receivedCommand == ON) {
            Lamp_Power.sendCommand(ON)
        } else {
            Lamp_Power.sendCommand(OFF)
        }
    return;
    }
    var r = ((Lamp_ColorHSB.state as HSBType).getRed * 255 / 100).intValue
    var g = ((Lamp_ColorHSB.state as HSBType).getGreen * 255 / 100).intValue
    var b = ((Lamp_ColorHSB.state as HSBType).getBlue * 255 / 100).intValue
    ww = ((Lamp_WWDimmer.state as Number) * 255 / 100).intValue
    logInfo(filename, "Output Conversion: r" + r + " g" + g + " b" + b)
    logInfo(filename, "Output Conversion: ww" + ww)
    var rgbw = String.format("%02x%02x%02x%02x", r, g, b, ww)
    logInfo(filename, "Output Conversion: rgbw" + rgbw)
    Lamp_ColorRGBWString.sendCommand(rgbw)
end

With this I get error on the ww variable,

[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'HSB+CW -> RGBW': Could not cast NULL to java.lang.Number; line 52, column 12, length 29

How can i fix the rules?


(Johann Obermeier) #8

it think this should be ww_dimm = Integer::parseInt(rgbw.substring(6, 8), 16) * 100 / 255

if there are no CW Led in your Lamp


(Rich Koshak) #9

One of your Items is NULL (i.e. does not have a value).This is probably the line that is failing:

 ww = ((Lamp_WWDimmer.state as Number) * 255 / 100).intValue

You need to test that Lamp_WWDimmer.state is not NULL before blindly trying to cast it to Number. And if it is NULL, which you should treat as an unknown state, you will have to decide what to do.


(trumee) #10

The following rule seems to work,

val String filename = "rgbw-light.rules"

rule "System startup"
        when
                System started
        then
/*Initial value of dimmer, full brightness*/
         if (Lamp_WWDimmer.state == NULL) Lamp_WWDimmer.postUpdate(100)
end


rule "RGBW -> HSB+CW"
when
    Item Lamp_ColorRGBWString received update
then
    var rgbw = Lamp_ColorRGBWString.state.toString


    var r = Integer::parseInt(rgbw.substring(0, 2), 16)
    var g = Integer::parseInt(rgbw.substring(2, 4), 16)
    var b = Integer::parseInt(rgbw.substring(4, 6), 16)
    logInfo(filename, "Input Conversion: r" + r + " g" + g + " b" + b)
    var HSBType hsb = HSBType.fromRGB(r, g, b)
    logInfo(filename, "Input Conversion: hsb" + hsb)

    var ww_dimm = Integer::parseInt(rgbw.substring(6, 8), 16) * 100 / 255
    logInfo(filename, "Input Conversion: ww_dimm" + ww_dimm)

    Lamp_ColorHSB.postUpdate(hsb)
    Lamp_WWDimmer.postUpdate(ww_dimm)
end


rule "HSB+CW -> RGBW"
when
    Item Lamp_ColorHSB received command or
    Item Lamp_WWDimmer received command
then
    logInfo(filename, "Command received: " + receivedCommand)
    if (receivedCommand instanceof OnOffType) {
        if (receivedCommand == ON) {
            Lamp_Power.sendCommand(ON)
        } else {
            Lamp_Power.sendCommand(OFF)
        }
    return;
    }
    logInfo(filename,"HSB Received:"+ Lamp_ColorHSB.state.toString)
    logInfo(filename,"Dimmer Received:"+ Lamp_WWDimmer.state.toString)
    var r = ((Lamp_ColorHSB.state as HSBType).getRed * 255 / 100).intValue
    var g = ((Lamp_ColorHSB.state as HSBType).getGreen * 255 / 100).intValue
    var b = ((Lamp_ColorHSB.state as HSBType).getBlue * 255 / 100).intValue
    var ww = ((Lamp_WWDimmer.state as Number) * 255 / 100).intValue
    logInfo(filename, "Output Conversion: r" + r + " g" + g + " b" + b)
    logInfo(filename, "Output Conversion: ww" + ww)
    var rgbw = String.format("%02x%02x%02x%02x", r, g, b, ww)
    logInfo(filename, "Output Conversion: rgbw" + rgbw)
    Lamp_ColorRGBWString.sendCommand(rgbw)
end

Changing the color from the webui shows,

2018-03-12 17:37:38.420 [INFO ] [rthome.model.script.rgbw-light.rules] - Command received: 15.438597,91.44385,73.333336
2018-03-12 17:37:38.431 [INFO ] [rthome.model.script.rgbw-light.rules] - HSB Received:15.438597,91.44385,73.333336
2018-03-12 17:37:38.440 [INFO ] [rthome.model.script.rgbw-light.rules] - Dimmer Received:0
2018-03-12 17:37:38.469 [INFO ] [rthome.model.script.rgbw-light.rules] - Output Conversion: r187 g60 b16
2018-03-12 17:37:38.473 [INFO ] [rthome.model.script.rgbw-light.rules] - Output Conversion: ww0
2018-03-12 17:37:38.480 [INFO ] [rthome.model.script.rgbw-light.rules] - Output Conversion: rgbwbb3c1000
2018-03-12 17:37:38.537 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'RGBW -> HSB+CW': For input string: "{""

From the Sonoff, i can see the MQTT state,

00:09:28 MQT: stat/sonoff-7895/RESULT = {"POWER":"ON","Dimmer":73,"Color":"BA3B0F00"}

(trumee) #11

I have four of these 4 bulbs now. I am able to switch them OFF and ON as a group. How can i make them change color as a group?

The item file is like so,

Switch Lamp_Power_1            "Lamp On/Off"            (gLights,gBedRoomLights) {mqtt="blah blah}
String Lamp_ColorRGBWString_1 "Color RGBCW hex [0x%s]"     {mqtt="blah blah}
Color  Lamp_ColorHSB_1         "Color HSB"              <colorlight> (gLights,gBedRoom)
Dimmer Lamp_WWDimmer_1         "Whiteness"      <colorlight> (gLights,gBedRoom)

Switch Lamp_Power_2            "Lamp On/Off"            (gLights,gBedRoomLights) {{mqtt="blah blah}
String Lamp_ColorRGBWString_2 "Color RGBCW hex [0x%s]"     {mqtt="blah blah}
Color  Lamp_ColorHSB_2         "Color HSB"              <colorlight> (gLights,gBedRoom)
Dimmer Lamp_WWDimmer_2         "Whiteness"      <colorlight> (gLights,gBedRoom)

Switch Lamp_Power_3            "Lamp On/Off"            (gLights,gBedRoomLights) {mqtt="blah blah}
String Lamp_ColorRGBWString_3 "Color RGBCW hex [0x%s]"     {mqtt="blah blah}
Color  Lamp_ColorHSB_3         "Color HSB"              <colorlight> (gLights,gBedRoom)
Dimmer Lamp_WWDimmer_3         "Whiteness"      <colorlight> (gLights,gBedRoom)

Switch Lamp_Power_4            "Lamp On/Off"            (gLights,gBedRoomLights){mqtt="blah blah}
String Lamp_ColorRGBWString_4 "Color RGBCW hex [0x%s]"     {mqtt="blah blah}
Color  Lamp_ColorHSB_4         "Color HSB"              <colorlight> (gLights,gBedRoom)
Dimmer Lamp_WWDimmer_4         "Whiteness"      <colorlight> (gLights,gBedRoom)

(Johann Obermeier) #12

Newer Tasmota Builds supports the HSB model.
So in many cases no conversion needed anymore


(Thomas Bail) #13

Could you give some more information on that?


(Christoph Wempe) #14

Create a virtual item (or “proxy item”).

And then create rules like:
“When Color of Group changes, sendCommand of new color to each bulb”


( ) #15

Great development! Could you please add a note to the first posting?


(Johann Obermeier) #16

Update on Tasmota Software since Build 5.12.0.h HSB is supported.
So conversion HSB <-> RGB is no longer needed

5.12.0h

  • Add command HSBColor Hue,Sat,Bri (#1642, #2203)

Command:
HsbColor ,, Set Hue, Saturation and Brightness


( ) #17

I actually meant the first posting at the top. Down here soon no one will find this important detail! Thanks!


(Kai Stevens) #18

Hey there,
I have succesfully connected my tasmota b1 with openhab with mqtt. Color changing and the other stuff like on /off switching and dimming are working.
Is there a way to keep the dimming setting when changing the color.

For example
color red dimming 20%,
after changing color to blue light will glow to 100%.

With my experimatal rule there is no way for chaning the dimming anymore :frowning:

Color  ColorLed1 "Farbe" (gLED1) [ "Lighting" ] { mqtt=">[broker:cmnd/sonoff-led1/HsbColor:command:*:default]"}

Thank you for helping!