It’s not. That’s a very old post. JS wasn’t really viable for use in 2017.
That’s Rules DSL and would need to go in a .rules file in the rules folder. However, it’s also going to need some changes to deal with syntax changes and other breaking changes that have happened over the past six years of development.
Theoretically those sorts of issues should be handled by the binding. But if not there are ways to address that.
Since OH 3.0 rules are thread safe. Only one instance of a rule can be running at a time. If additional triggers happen while it’s running, those triggers are queued up.
You can but the syntax and approach will be significantly different. But it’s going to be pretty different anyway so there’s no reason to let that scare you away.
The key is to understand the parts of a rule (see the Concepts section of the docs) and the rule syntax (see the Rules Configuration page for Rules DSL syntax).
Assuming they are all in a Group called AllLights:
That should only be used as a last resort in all circumstances.
Rules - Introduction | openHAB should give you the basics for all things rules in OH 3/4. In truth, I cannot stress enough how important it is to go through Getting Started in general.
With those basics you can choose the language you want to use going forward. If you’ve been in programming for 40 years Blockly might feel like tying with mittens on. I’d recommend JS Scripting or jRuby, both of which have excellent docs and support. If you really like Groovy or Java there are options for those. If Python is your thing see HABApp.
I’d use JS Scripting. Add a Inline Script, choose ECMAScript and go from there.
I’m just typing this in so there are likely going to be typos. Figuring out the trigger and conditions (if any) you want the rule to run under is up to you, but you’ll need to use an Item, not a Channel trigger because we need the Item to know when to stop (I’ll call it ColorRampSwitch). This is just the Script Action. I’m assuming the Group AllLights
are all Color Items and once per second it decrements the hue by 5 until it gets to the end and then reverses direction.
I’m going to use my openHAB Rules Tools Announcements library here as well just to show how you can use a library in your rules.
// Import looping timer
var LoopingTimer = require('openhab_rules_tools');
// Increment function, increment/decrement the hue by 5, change direction if boundary is met
var increment = (item) => {
// get the current state or initialize if the Item is NULL or UNDEF, split out the three values
const currState = (item.isUninitialized) ? ['0','75','100'] : item.state.split(',');
// Calculate the new hue incremented/decremented by 5
var newHue = parseInt(currState[0]) + (5 * cache.private.get(item.name+'_direction', () => 1));
// Change the direction if we hit a boundary
if(newHue >= 360) {
newHue = 360;
cache.private.put(item.name+'_direction', -1);
}
else if(newHue <= 0) {
newHue = 0;
cache.private.put(item.name+'_direction', 1);
}
// command the Item with the new Hue
item.sendCommand(newHue+','+currState[1]+','+currState[2]);
}
// Timer function, loop through all the members of AllLights and increment the hue
var loopFunction = () => {
items.AllLights.members.forEach(item => {
increment(item);
// add a tiny sleep here if commands get lost
// Java.type('java.lang.Thread').sleep(100);
})
// Looping timer reschedules itself based on the return value
// Keep on changing the colors
if(items.ColorRampSwitch.state == 'ON') return 200;
// Stop looping and changing the colors
return null;
}
// Create the looping timer if one doesn't already exist
var loop = cache.private.get('timer', () => LoopingTimer());
// There isn't already a looping timer running
if(loop === null || loop.hasTerminated()) {
// Make sure all the lights are ON
items.AllLights.sendCommand('ON');
// Start the color changing loop
loop.loop(loopFunction, 0);
}
// else ignore the trigger if the loop is already running
Where you see =>
that’s creating an anonymous function. We are storing data we want to persist between runs of the rule in the private cache. When calling get
on the cache, the passed in function will initialize that property if it’s not already defined.
I don’t know if this has the behavior you are after or not but it should give you a start. What it does is
- assumption: trigger the rule when ColorRampSwitch receives command ON
- command all the members of AllLights to ON (note this won’t work if the binding cannot handle commands too fast)
- create a looping timer that will start running immediately. It’s better to use Timers than to monopolize the Rule thread with sleeps and long running so we don’t use a while loop or something like that
- in the looping timer we loop through all members of AllLights and call
increment
on each in turn with a tiny sleep between if the binding cannot handle commanding the Items too fast - The
increment
function we get the current state of the Item, increment or decrement it based on a flag stored in the private cache and then. command the Item. If the Item is NULL or UNDEF (which really shouldn’t be possible because of the ON command we sent previouslty) we start from Hue 0. When a boundary is hit, we change directions. - If the ColorRampSwitch is still ON, reschedule the looping timer to run again in 200 milliseconds. If not, exit the looping timer.
Each light moves through it’s colors independently. If one started at green and another at blue, they will start incrementing/decrementing from that starting point individually. The rule could be made slightly simpler if all are at the same color and kept in sync.