Foreword
If you want easy setup, don’t copy me - use the binding from here WLed: A binding for controlling LED strips and strings from an opensource esp8266 project and forget you ever saw this post.
This post (#2) shows my first attempt in all its glory, for posterity. The first post (#1) simplifies the things file somewhat, and adds features thanks to @sujitrp.
Introduction
I have a NodeMCU clone flashed with WLED connected to a WS2812B LED strip. A WS2812B LED strip enables each LED to be individually controlled.
Setup
Device
Once connected to the local wifi, setup MQTT on the device:
- Enable MQTT
- Point to the local Mosquitto broker.
- Name the device (Client ID)
- Set the Device Topic
OpenHAB
Things
Notes
- There is a separate things file which defines the bridge to the Mosquitto broker.
- WLED MQTT information is on the WLED wiki. In short, you can command three topics:
- A brightness or power on/off/toggle to the Device Topic (
wled/rgb2
) - A colour in hex to
wled/rgb2/col
- Almost everything using the HTTP API syntax to
wled/rgb2/api
- A brightness or power on/off/toggle to the Device Topic (
- Return topics are almost identical:
- Brightness from
wled/rgb2/g
- Colour from
wled/rgb2/c
- Almost everything in xml format from
wled/rgb2/v
. A typical xml string looks as follows, where all parameters are described at HTTP API syntax:
- Brightness from
<?xml version="1.0" ?><vs><ac>28</ac><cl>255</cl><cl>0</cl><cl>212</cl><cs>0</cs><cs>0</cs><cs>0</cs><ns>0</ns><nr>1</nr><nl>1</nl><nf>1</nf><nd>60</nd><nt>0</nt><fx>9</fx><sx>127</sx><ix>63</ix><fp>0</fp><wv>0</wv><ws>0</ws><ps>0</ps><cy>0</cy><ds>WLED</ds><ss>0</ss></vs>
- The switch channel
- Sends an ON or OFF command on
wled/rgb2
- Receives the full XML string on
wled/rgb2/v
and:- Extracts the resultant brightness (
<ac>
) via XPATH (there is no on/off parameter in the xml), then - Transforms the brightness value into a switch ‘boolean’ through a javascript transform. If brightness is zero, switch is off; if brightness is not zero, switch is on.
- You could probably do this with a single transform by capturing
wled/rgb2/g
instead (see the brightness channel below)
- You could probably do this with a single transform by capturing
- Extracts the resultant brightness (
- Sends an ON or OFF command on
- The colour channel
- Sends a hex colour on
wled/rgb2/col
by- Transforming the RGB colour to hex with an outbound javascript transform.
- Receives the resultant hex colour on
wled/rgb2/c
and- Transforms the hex to RGB with a javascript transform.
- Sends a hex colour on
- The brightness channel
- Sends a brightness value (0-255) on
wled/rgb2
by- Transforming the dimmer percent (0-100%) via an outbound javascript transform.
- Receives the resultant brightness on
wled/rgb2/g
and- Transforms the brightness (0-255) into a dimmer percentage (0-100%) with a javascript transform.
- Sends a brightness value (0-255) on
- The speed channel
- Sends speed value (0-255) on
wled/rgb2/api
by- Transforming the dimmer percent (0-100%) and adding HTTP API specific parameter identifications via an outbound javascript transform.
- Receives the resultant speed on
wled/rgb2/v
and- Extracts the resultant speed (
<sx>
) via XPATH, then - Transforms the speed value (0-255) into a dimmer value through a javascript transform.
- Extracts the resultant speed (
- Sends speed value (0-255) on
- The intensity channel
- Sends intensity value (0-255) on
wled/rgb2/api
by- Transforming the dimmer percent (0-100%) and adding HTTP API specific parameter identifications via an outbound javascript transform.
- Receives the resultant intensity on
wled/rgb2/v
and- Extracts the resultant intensity (
<ix>
) via XPATH, then - Transforms the intensity value (0-255) into a dimmer value through a javascript transform.
- Extracts the resultant intensity (
- Sends intensity value (0-255) on
- The api channel is used to send commands directly from rules, rather than sitemap widgets.
Thing mqtt:topic:swRGB2 "RGB2 lights" (mqtt:broker:MosquittoMqttBroker) {
Channels:
Type switch : switch "Power Switch" [
commandTopic="wled/rgb2",
on="ON",
off="OFF",
stateTopic="wled/rgb2/v",
transformationPattern="XPATH:/vs/ac/text()∩JS:dimmer2switch.js"
]
Type colorRGB : colour "Colour" [
commandTopic="wled/rgb2/col",
transformationPatternOut="JS:rgb2hex.js",
stateTopic="wled/rgb2/c",
transformationPattern="JS:hex2rgb.js"
]
Type dimmer : brightness "Brightness" [
commandTopic="wled/rgb2",
transformationPatternOut="JS:openhab2wled.js",
stateTopic="wled/rgb2/g",
transformationPattern="JS:wled2openhab.js"
]
Type dimmer : speed "Speed" [
commandTopic="wled/rgb2/api",
transformationPatternOut="JS:openhab2wled-speed.js",
stateTopic="wled/rgb2/v",
transformationPattern="XPATH:/vs/sx/text()∩JS:wled2openhab.js"
]
Type dimmer : intensity "Intensity" [
commandTopic="wled/rgb2/api",
transformationPatternOut="JS:openhab2wled-intensity.js",
stateTopic="wled/rgb2/v",
transformationPattern="XPATH:/vs/ix/text()∩JS:wled2openhab.js"
]
Type string : api "api" [
commandTopic="wled/rgb2/api"
]
}
Transforms
Quite frankly, heavily indebted to the generous souls on StackExchange for quite a few of these…!
dimmer2switch.js
(function(x) {
if(x == 0){
return "OFF";
}
else{
return "ON";
}
})(input)
rgb2hex.js
(function(x) {
var splitstring = x.split(',');
var r = splitstring[0];
var g = splitstring[1];
var b = splitstring[2];
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
})(input)
function componentToHex(rgb) {
var hex = Number(rgb).toString(16);
if (hex.length < 2) {
hex = "0" + hex;
}
return hex;
}
hex2rgb.js
(function(colour) {
var r,g,b;
if ( colour.charAt(0) == '#' ) {
colour = colour.substr(1);
}
if ( colour.length == 3 ) {
colour = colour.substr(0,1) + colour.substr(0,1) + colour.substr(1,2) + colour.substr(1,2) + colour.substr(2,3) + colour.substr(2,3);
}
r = colour.charAt(0) + '' + colour.charAt(1);
g = colour.charAt(2) + '' + colour.charAt(3);
b = colour.charAt(4) + '' + colour.charAt(5);
r = parseInt( r,16 );
g = parseInt( g,16 );
b = parseInt( b ,16);
return r + "," +g + "," +b ;
})(input)
openhab2wled.js
(function(x) {
return Math.round(((x / 100) * 255));
})(input)
wled2openhab.js
(function(x) {
return Math.round(((x / 255) * 100));
})(input)
openhab2wled-speed.js
(function(x) {
return "SX=" +Math.round(((x / 100) * 255));
})(input)
openhab2wled-intensity.js
(function(x) {
return "IX=" +Math.round(((x / 100) * 255));
})(input)
Items
Switch sRGB2 "RGB2 lights switch" {channel="mqtt:topic:swRGB2:switch"}
Dimmer dRGB2Brightness "RGB2 Brightness" {channel="mqtt:topic:swRGB2:brightness"}
Color cRGB2Colour "RGB2 colour" {channel="mqtt:topic:swRGB2:colour"}
Dimmer dRGB2Speed "RGB2 speed" {channel="mqtt:topic:swRGB2:speed"}
Dimmer dRGB2Intensity "RGB2 intensity" {channel="mqtt:topic:swRGB2:intensity"}
String strRGB2API "RGB2 API" {channel="mqtt:topic:swRGB2:api"}
Sitemap
Switch item=sRGB2 label="RGB2" icon="light"
Slider item=dRGB2Brightness label="RGB2 brightness [%d %%]" icon="slider" sendFrequency=500
Colorpicker item=cRGB2Colour label="RGB2 colour"
Slider item=dRGB2Speed label="RGB2 speed"
Slider item=dRGB2Intensity label="RGB2 intensity"
Rules
I’m using jython to create rules.
In $OPENHAB_CONF/automation/lib/python/personal
I have a file called personal_functions.py
which contains, amongst other things:
personal_functions.py
def set_wled(switch, colour="op", brightness="op", effect="op", speed="op", intensity="op", nightlight="op"):
a = "&A=" + str(get_8bit_number(brightness)) if brightness != "op" else ""
t = "T=1" if switch else "T=0"
c = "&R=" + str(colour[0]) + "&G=" + str(colour[1]) + "&B=" + str(colour[2]) if colour != "op" else ""
fx = "&FX=" +str(effect) if effect != "op" else ""
sx = "&SX=" + str(get_8bit_number(speed)) if speed != "op" else ""
ix = "&IX=" + str(get_8bit_number(intensity)) if intensity != "op" else ""
nl = "&NL=" + str(nightlight) if nightlight != "op" else ""
return a + t + c + fx + sx + ix + nl
def get_8bit_number(number):
return int(float(number)/100*255)
This function will take in
- a switch state (1 or 0 for on and off)
- a colour in RGB formatted as
(R,G,B)
- a brightness (0-100)
- an effect index
- a speed (0-100)
- an intensity (0-100)
and will return a string formatted for sending to WLED via the api channel. Only the switch
argument is mandatory - all others are optional. This function is used in a number of jython rules, remembering to import the personal_functions.py
into each rule file.
Sample rules
import personal.personal_functions
reload(personal.personal_functions)
from personal.personal_functions import set_wled
----
#Turns the LED strip on, sets the colour to pink, sets brightness to 100%, sets the effect to Dual Scan, sets speed to 95% and intensity to 50%
events.sendCommand("strRGB2API",set_wled(switch=1,colour=(255,192,203),brightness=100,effect=11,speed=95,intensity=50))
To Do
-
Try and reduce the number of different javascript transforms required! (Not sure how!)See first post for how!