How to Create Rules in OpenHAB 3 using JavaScript / ECMAScript

Johannes Schildgen
(Smart)²Home
Published in
9 min readDec 27, 2020

OpenHAB 3 added a completely new rule engine, new scripting languages, and an interactive web interface to create rules. In this article, you will learn the steps and commands to create powerful rules in OpenHAB 3 using the language JavaScript.

!!! In the recent December 2021 release (openHAB 3.2), the JavaScript commands changed. Read here: Improved JavaScript-Rule Features in openHAB 3.2

Rules in OpenHAB 1 and 2

Up to version 2, rules had to be created in *.rule files using a domain-specific language (DSL based on Xbased, similar to the language Xtend). Within such a file, each rule has the following format:

rule "name_of_the_rule"
when <event>
then <action>
end

For very simple things, this way of creating rules is absolutely sufficient. But when computations with dates and timestamps, string manipulations, and API calls become necessary, it is often very annoying not having a “real” programming language.

Next-Gen Rules

Since OpenHAB 2.4, it is possible to create rules using a graphical interface or using a scripting language like JavaScript or Groovy. In OpenHAB 2, this was a feature within the PaperUI, since OpenHAB 3, rules can be directly created and edited in the main UI.

Take a look at the rule above. It is a complete rule definition. We give each rule a name, define one or more triggers, and specify the action that should be executed when the given trigger fires. All this is done in textual notation using the OpenHAB DSL.

We will now split the rule-creation process into two parts:

  1. Creating the rule in the OpenHAB 3 UI, giving it a name, and setting the triggers when the rule should be executed.
  2. Developing the desired action using JavaScript.

For very simple rules, step 2 can also be replaced by simply setting the actions without coding, directly in the UI.

When-this-then-that

A rule has the form: When-this-then-that. The “When” part specifies what activates the rule. This is also called a trigger. Actually, triggers are optional in OpenHAB 3. When there is none, the rule action can only be executed manually, or through other rules.

In the literature, the concept of rules is also called ECA rules: Event, Condition, Action. Always when a specific event occurs, check if a condition is fulfilled, if yes, then execute the action. When you take a look a the rule-creation dialog in OpenHAB 3, you can specify these elements there:

Creating rules without coding in OpenHAB 3

As mentioned above, only the “Then” part (the action) is required. When no triggers are set, rules can still be executed manually or from other rules. When multiple triggers are set, this stands for an “or” connection of triggers. So, when one of the specified events occurs, the rule is executed. The condition is also optional. When no condition is defined, the actions are always executed. When there are multiple conditions, it can be seen as an “and” connection of those. So only if all given conditions are evaluated to true, the action is executed.

The example from above defines a very simple rule: Each time, the humidity changes, the condition is checked whether this value is 60 or higher. If yes, a dehumidifier is turned on. Mind that we should create a second rule that turns it off later when the humidity is less than 55 again, for example.

A trigger can be item-based, time-based, or system-based. Item-based triggers are fired when an item state changes, or when an item receives a command. Time-based triggers are executed at certain time intervals using a cron expression. Cron: 0 0 8 ? * * * stands for: Every day at 8 o’clock. (0 is the second, 0 the minute, 8 the hour, ? stands for: independent of the day, and * * * means every month, every day of week, and every year). And system-based triggers can be executed when the OpenHAB system is started, for example.

JavaScript Actions

Simple actions as seen in the example above can be: send a command to an item or run another rule. For more complex things, you can choose the action “Run Script”. These scripts can be developed in a language of your choise: OpenHAB DSL, ECMAScript (that’s basically JavaScript), Python, Groovy, or using Blockly. Blockly is not a textual programming language, you can visually create if-conditions, loops, variables, and so on using your mouse. In this article, I fill focus on ECMAScript.

Hint: When you have no idea how to do a specific thing in ECMAScript, do it in Blockly and switch to the code view using the blue button. For the following block, it will show this command: itemRegistry.getItem('...').getState();

Coding the rule action with Blockly

Auto-Completion within the Code Editor

When you choose the language ECMAScript, and click on the action “execute a given script”, an editor opens where you can develop your script. The editor offers syntax highlighting and auto-completion. Press Ctrl+Space to auto-complete variable names, methods, and OpenHAB item names. In the example above, we can write ite, press Ctrl+Space, and it will be automatically completed to itemRegistry. Then press the dot, and a list of all methods will show up. Choose getItem. Then open parenthesis and a double-quote, again press Ctrl+Space, and all item names will be shown. This is a very useful feature in the code editor.

Auto-completion within the code editor

Logging

Developing rules is quite different than developing Hello-World programs. You cannot simply press execute and see what your program does. Okay, there actually is a Play button to test the action of your rule, but without using a logger, rules do not produce an output. They just have an effect, for example, the dehumidifier will be turned on (or not).

Each rule should start with the definition of a logger:

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);

The argument of getLogger is a String that will be shown as an identifier within the logs. The best practice is to use 'org.openhab.rule.' + ctx.ruleUID here. So, it will always add the Unique ID of the rule. That’s an ID that you can configure when creating the rule. Mind that this UID cannot be changed later.

Now, you can use logger.info("..."); to produce a log output row. Besides info, there are also the methods debug, error, warn, and trace. The following example shows how to inspect the value of a variable:

x = new Date();
h = x.getHours();
logger.info("Current hour: "+h);

When the rule is executed, the log message is appended to the OpenHAB log. You can look into this log file by connecting to your OpenHAB machine via SSH and using less /var/log/openhab/openhab.log When using tail -f instead of less, it shows you a live view of the log. You can open this in a terminal and watch what your script is doing.

2020–12–16 16:15:00.030 [INFO ] [org.openhab.rule.MyRule ] — Current hour: 16

openhab.log shows all log messages from OpenHAB, bindings, and your scripts, as well as errors and exceptions. Another helpful file is events.log. It contains every single change of all item states:

2020–12–19 07:23:52.927 [INFO ] [openhab.event.ItemStateChangedEvent ] — Item ‘Humidity’ changed from 50 to 49

Interacting with Items

The following three commands can be used to read the current state of an item and update an item state:

  • itemRegistry.getItem("MyItem").getState()
  • events.sendCommand("MyItem", “NewState")
  • events.postUpdate("MyItem", "Command")

For first command, there’s also a shorter version: items["MyItem"]

It depends on the items that you are using whether to send a command or post an update. Updating only changes the state value of an item, sending a command will notify the binding about the new state. For example, for turning on the light, use sendCommand. For storing the information that you are sleeping use postUpdate.

I did not talk about data types so far. Items can have a type, e.g. String, Switch, Number, Rollershutter, or a number with a dimension (Number:Temperature, …). In most cases, you can set the new state of the item to a String representation of your desired state and it will be automatically cast to the target type. For example:

events.postUpdate("Dehumidifier", "ON");

The same for reading:

if(itemRegistry.getItem("Dehumidifier").getState() == "ON") { … }

When working with Numbers, Dates or other types, it may be neccessary to manually cast between state values and your JavaScript variable types. Use logger.info(typeof x); to see the datatype of a variable x.

The “event” variable

Above, we already used the variables itemRegistry (you can also use its alias ir) and events. There is another variable event that contains information about the event that triggered the execution of the rule. Mind that you get the following error when you want to access event and the rule is not triggered by an item, but, for example, manually by pressing the play button:

ReferenceError: “”event” is not defined in <eval>

In a regular case, e.g. when the rule is fired because an item state changed, event.itemName gives you the name of that item, event.itemState its new state, and event.oldItemState the previous state before the change.

The Rest is just JavaScript

Basically, the commands listed above are all you need to know to develop JavaScript rules in OpenHAB 3. The most important ones are: logger.info, itemRegistry.getItem, events.sendCommand, and events.postUpdate. The rest is just JavaScript. If you want to know how to use if, while, for loops, and more, simply use a search engine or take a look at online tutorials about JavaScript. A very simple way to quickly try out JavaScript statements is pressing F12 in your web browser. Choose “Console” and simply write JavaScript commands in an interactive mode. This way, you can simply test your statements and copy-paste them into your rules.

Further Reading

There are many more possible statements that you can use within rules and scripts as described in this article. You can send emails, send a message through Telegram messenger, access an HTTP API, ask a persistence service for historical states of items, and so on. A lot of inspiration I extracted from this documentation: https://openhab-scripters.github.io/openhab-helper-libraries/

As a little teaser: A command-line script can be executed as follows:

var Exec = Java.type('org.openhab.core.model.script.actions.Exec');
Exec.executeCommandLine("/home/openhabian/test", 5000);

Here is another great article and here forum post (both by Rich Koshak) with a lot of examples for JavaScript rules. They show how to send notfications, and how to store a state in a variable this.someVariable which is accessible the next time the same rule will run. Furthermore, the articles explain how to import libraries and link to some libraries for working with date, time, and timers.

Example Rule

The example from above was very simple: If the humidity is 60% or higher, turn on the humidifier. But my problem is: I have two rooms with two humidity sensors: Cellar1 and Cellar2. But I only have one dehumidifier. I sometimes move it in one room, sometimes in the other. I have an item “Dehumidifier_Mode” of type String which can be set to the values OFF, CELLAR1, CELLAR2, and ON using a sitemap.

Sitemap in Basic UI and the OpenHAB app to change the dehumidifier mode
Text icon="humidity" label="Cellar 1 - Humidity [%d %%]" item=Cellar1_Humidity
Text icon="humidity" label="Cellar 2 - Humidity [%d %%]" item=Cellar1_Humidity
Switch icon="fan" label="Dehumidifier" item=Dehumidifier_Mode mappings=[OFF="Off",CELLAR1="Cellar1",CELLAR2="Cellar2",ON="On"]

The following rule turns the dehumidifier on and off depending on its mode and the humidity in the respective room. It is triggered whenever one of the humidity values or the dehumidifier mode changes.

As you can see, the rule has no condition. The condition is checked within the script:

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);var hum1 = itemRegistry.getItem("Cellar1_Humidity").getState();
var hum2 = itemRegistry.getItem("Cellar2_Humidity").getState();
var mode = itemRegistry.getItem("Dehumidifier_Mode").getState();
logger.info("hum1 = "+hum1+", hum1 = "+hum2+", mode = "+mode);if(mode == "OFF") {
events.sendCommand("Dehumidifier", "OFF");
} else if(mode == “ON”) {
events.sendCommand("Dehumidifier", "ON");
} else if(mode == “CELLAR1” && hum_h >= 60) {
events.sendCommand("Dehumidifier", "ON");
} else if(mode == “CELLAR1” && hum_h <= 55) {
events.sendCommand("Dehumidifier", "OFF");
} else if(mode == “CELLAR2” && hum_s >= 60) {
events.sendCommand("Dehumidifier", "ON");
} else if(mode == “CELLAR2” && hum_s <= 55) {
events.sendCommand("Dehumidifier", "OFF");
}

That’s it!

Now try out creating rules on your own. For simple rules, you can just click through the UI. And for more complex rules, use JavaScript. Rules can be deactivated and activated later, and you can manually execute their action if you want. This is very useful for testing rules. Have fun!

--

--