OH3: How to define the CW channel for a RGBCW LED; does it need a JS transformation?

I managed to create an OH Thing for a RGBCW LED paired with zigbee2mqtt, which sends and receives MQTT commands.

zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001 {"brightness":80,"color":{"x":0.6719,"y":0.3143},"color_mode":"xy","color_temp":223,"do_not_disturb":true,"linkquality":153,"state":"OFF"}
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/state OFF
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color_mode xy
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color_temp 223
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/brightness 80
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color-x 0.6719
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color-y 0.3143
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/linkquality 153
zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/do_not_disturb true

What I came to like about this ‘chain’ (zigbee2maqtt > OH), is the extra configuration for transitions, steps, and other values the LED can ‘consume’.

Well, to the problem…

I am using a dimmer; its range is 0…100. Yet the LED expects values from 153…500. How can I achieve this mapping?

I suspect it needs an equivalent JS transformation like this C code:

// x = value to transform; rest self-explanatory)
int map(int x, int in_min, int in_max, int out_min, int out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Actually, I don’t really know how to pass multiple values into a JS transformation.

Or is there a built-in or more elegant way to achieve the desired outcome?


OK, I created a dimmer channel for the light temperature and the JS versions of the above C code:

  - id: color_temperature
    channelTypeUID: mqtt:dimmer
    label: Colour temperature
    description: null
    configuration:
      commandTopic: zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color_temp/set
      transformationPatternOut: JS(light_temp_out.js):%s
      stateTopic: zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color_temp
      transformationPattern: JS(light_temp_in.js):%s

light_temp_out.js

(function(i){
    var in_min  =   0;
    var in_max  = 100;
    var out_min = 153;
    var out_max = 500;

    return (i - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

})(input)

light_temp_in.js

(function(i){
    var in_min  = 153;
    var in_max  = 500;
    var out_min =   0;
    var out_max = 100;

    return (i - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

})(input)

However, the transformation does not happen.
The JS Scripting add-on is installed (has been for yonks).

[edit] Later added parseInt(i) in the Js transformation, as outlined here, which made no difference, neither to the error message, nor the result.

Where did I bugger up?


Well, OH can’t find the transformations. Not sure why!

2024-03-27 16:54:45.974 [WARN ] [t.generic.ChannelStateTransformation] - Transformation service JS(LIGHT_TEMP_OUT.JS) for pattern %s not found!
2024-03-27 16:54:45.983 [WARN ] [t.generic.ChannelStateTransformation] - Transformation service JS(LIGHT_TEMP_IN.JS) for pattern %s not found!

But these files are there:

# [2024-03-27 17:00] openhabian@openhabian /etc/openhab/transform $ 
ls -la light*
-rw-rw-r-- 1 openhab openhab 194 Mar 27 15:40 light_temp_in.js
-rw-rw-r-- 1 openhab openhab 194 Mar 27 15:40 light_temp_out.js

Also, why does the error message show the filename in all uppercase; the filename is all lowercase?

In general I think the answer to your question is ‘no’. The OH core does contain a code module called ‘ColorUtil’ which does contain code for handling color transformations. However this code is probably NOT accessible from a rule or a transform.

Just for info, this range of 153 … 500 is a type of temperature unit called ‘Mirek’ (or sometimes ‘Mired’) which stands for “micro-reciprocal-kelvin” which is often used for measuring the color temperature of lamps e.g. as shown below. If you use the OH ‘Quantity’ resp. ‘QuantityType’ class in your transform, you can convert directly between Mirek and Kelvin using the OH code…

  • 153 mk => 1000000/153 => 6500 K
  • 500 mk => 1000000/500 => 2000 K
val mirek = Quantity("2000 K").toUnit(Units.MIREK);

I’m fiddling with this for six hours now; all variations of the transform command I can think off… to no avail.

The dimmer type has a range from 0…100, which needs ot eb MQTT-ed as 153…500.
Hence, I do not need to convert from K to mk, given the dimmer range.

What I have figured is: the transformation is not working, when called like so:

 - id: color_temperature
    channelTypeUID: mqtt:dimmer
    label: Colour temperature
    description: null
    configuration:
      commandTopic: zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/set
      formatBeforePublish: '{"color_temp":%s}'
      transformationPatternOut: JS(light_temp_out.js):%s
      stateTopic: zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color_temp
      transformationPattern: JS(light_temp_in.js):%s

transformationPatternOut will be executed before putting the MQTT message together.
formatBeforePublish puts the (suposedly converted) number into the json.

I have tried these ‘commands’ in transformationPatternOut:
light_temp_out.js
light_temp_out.js:%s
JS:light_temp_out.js
JS:light_temp_out.js:%s
JS(light_temp_out.js):%s

The latter I deem correct, but could be wrong.

Any hints appreciated.

Well, at least I learned something :smiley:

This is the correct call:

  - id: color_temperature
    channelTypeUID: mqtt:dimmer
    label: Colour temperature
    description: null
    configuration:
      commandTopic: zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/set
      formatBeforePublish: '{"color_temp":%s}'
      transformationPatternOut: JS:light_temp_out.js
      stateTopic: zigbee2mqtt/ArgyleCourt/House/EaveLights/RGBCW_001/color_temp
      transformationPattern: JS:light_temp_in.js

However, I had to remove `toInt() in my JS return statements, and parseInt() the whole return statement.

(function(i)
{
    var in_min  =   0;
    var in_max  = 100;
    var out_min = 153;
    var out_max = 500;

    return parseInt(((parseInt(i) - in_min) * (out_max - out_min) / (in_max - in_min) + out_min));
})(input)

DANG!

Apparently you already solved you problem but maybe helpful for you:
For outgoing there is the feature to send brightness_percent so that zigbee2mqtt does the calculation. For incoming you need a transformation.
You can find it here: