[wiki] Getting Started with OH3: rewriting the tutorial - 8. Rules

Tags: #<Tag:0x00007f433edd4d88> #<Tag:0x00007f433edd4cc0>

This Wiki topic’s goal is to serve as a working document, in order to provide an up-to-date tutorial for OH3 aimed at new users and novices. Everyone is welcome and encouraged to edit it, when we’re satisfied it will make its way to the official docs. You may reply for the specific purpose of improving the article are allowed, but please, this is NOT a support thread: if you’re stuck while reading this and are simply seeking support please open another thread. Thanks!


To this point we have connected Things to devices, modeled our Things by creating Items, and built the start of a UI to control the devices. But all of that just enables control of devices. How does one cause something to happen in response to events in openHAB, i.e. automation? For example, turn on the porch light when someone returns home or turn on an exhaust fan when the humidity rises above a threshold? This is what rules are for.

Event driven

One important concept about rules that must always be kept in mind is that they are event driven. An event occurs and any rule may run in response. A single rule can run based on more than one event but it will only be triggered on the event, not on the states of Item. You cannot trigger a rule based on two events (e.g. Item Foo changed to OFF and Item Bar changed to ON) because no two events actually occur at exactly the same time. Also, you cannot define a state condition in a trigger because a state condition (e.g. Foo > 25) isn’t an event, it’s a state.

Two Approaches

There are two primary approaches for writing rules and one can combine the two if required.

Text based

Text based rules are written with text editors and saved in openHAB’s conf folder. Regardless of the language chosen (more below) the code files must:

  1. define rules and their triggers
  2. define the code to run when the triggers happen.

Text based rule files are loaded when openHAB starts up and when the files are changed.

Pros:

  • More natural approach for developers who already know how to code.
  • Easier to share data between separate rules.
  • The only viable way to write libraries of code called from multiple rules.

Cons:

  • Slightly slower to be parsed and loaded.
  • Lacks a “but only if” clause (see below) so any conditions have to be included as part of the body of the rule.
  • Requires significantly more code to write.
  • All rules in the same text file will be deleted and recreated even if just one of them are changed in that file.

UI Created

UI created rules are created through MainUI. Much of the pro forma required for the creation of the rule and its triggers are handled for you. UI Created rules also includes an additional “but only if” clause. This is a place where the rule can be prevented from running unless certain state conditions are met (e.g. it’s between 12:00 and 17:00 time).

Pros:

  • Simple rules do not even require code.
  • Blockly is an option for writing the code in a graphical style.
  • Can be accessed and modified from any browser that can access your openHAB server.

Cons:

  • Sharing variables between rules or even preserving rules from one run to the next is not as simple.
  • Clicking around the browser is not as efficient for some users as writing text, though the Code tab helps to address some of that.

This tutorial will only present the UI created rules approach.

Languages

As of this writing, there are five supported programming languages to choose from for writing rules. The following table presents their pros and cons.

Language Pros Cons
Blockly Graphical, renders to JavaScript Slow to develop, hard to change, large rules become unruly
Rules DSL Comes out-of-the-box, has the most examples on the forum Does not support all features of OH or normal programming languages such as classes, functions, access to Item metadata, etc.
Jython Second most number of examples, most mature third party helper libraries Stuck on Python 2.7 for now, requires a separate add-on
JavaScript Comes out-of-box Stuck on Nashorn which is ECMAScript 5.1, awkward method for importing libraries that make most generic JavaScript third party libraries unusable
Groovy Has the least number of examples (fewest number of users?), requires a separate add-on

As you can see, there are significant limitations for all of the language choices. If you are totally new to programming, Blockly would be a good choice. This tutorial will present JavaScript as the example language for advanced programming, but the concepts presented will work with any of the languages. You can use more than one language at a time if you choose. In UI rules you can even use more than one language in the same rule, just not in the same Script Action or Script Condition (more on that later).

Anatomy of a Rule

Each rule has three main sections:

Section Also Called Purpose
When Triggers Defines the events that will cause the rule to run.
But only if Conditions Defines the conditions that must be true for the rule to run when triggered by an event.
Then Actions Defines what to do when the rule is triggered and conditions are met.

The flow therefore is

  1. event occurs to trigger a rule
  2. conditions are checked to see if the rule can run
  3. if the conditions pass, the actions are executed.

When

Rule triggers are always events. When the defined event occurs, the execution is passed off to the But only if to see if the rule is allowed to run. The types of event that can trigger a rule include:

  • Item events: received command, changed state, received update
  • Thing events: a Thing status changes or updates, events on a special type of Channel called an “event trigger”
  • Member of Group events: when a member of a Group receives one of the Item events
  • Time events: cron defined times, fixed times of day
  • System events: e.g. when openHAB first starts

But only if

The conditions are very similar to the rule triggers except they operate on state. The sorts of states that can be used include:

  • Item states comparisons including ==, !=, <, >, <=, and >=
  • A range of time over the course of the day (e.g. between 12:00 and 17:00)
  • A certain type of day as defined by Ephemeris
  • A script evaluates to true

The Script Condition is particularly powerful as you can create quite complicated comparisons and even add error correction to the script if necessary (e.g. if a String Item only has a few valid states set the Item back to it’s previous state if it changes to an invalid state). One particularly useful comparison is to test whether the Item is changed from NULL or UNDEF and not run the rule if it did.

event.oldState.class == UnDefType.class

Then

This is the part of the rule where you define the stuff that should occur when the trigger occurred and the conditions are met. The sorts of things you can do as actions include:

  • enable or disable other rules
  • run another rule
  • send a command to an Item
  • execute a given script

The first two are interesting options. For example, one might have a bunch of smart plugs that normally run humidifiers but during Christmas time they run the Christmas lights. One can use the enable/disable Action to disable the humidifier rule and enable the Christmas lights rule when a Switch (or cron expression) indicates that it’s Christmas lights time.

The most commonly used action though will be execute a given script.

Your First Rule

Prerequisites:

  • A Switch Item to control a Light, in this case we will use an Item named MasterBedroom_Light_Power
  • A light level sensor that the light should be controlled by, in this case we will use an Item named MasterBedroomSensors_LightLevel

Scenario: When the light level measured by the sensor goes above a threshold value or it’s 08:00 turn on the light, but only on weekdays.

Navigate to Settings -> Rules and click the + icon to create a new Rule.

Create a reasonable Unique ID, Name, and Description.

We have two events where we want to run this rule. First is when the sensor goes above a certain threshold. That’s a state comparison though so we want to trigger the rule when ever the sensor changes to any state. We will check the level in the but only if section.

Click “Add Trigger” and choose “an Item state changes” as the trigger type. Select the MasterBedroomSensors_LightLevel as the Item and return to the Create Rule main page but clicking Done.

Next we need a trigger at 08:00. Again click to add a trigger and this time choose “it’s a fixed time of day.” Select 08:00 for the time.

The rule should look like this.

Now let’s set the conditions. There are two conditions. The first is that the light sensor is below a threshold value. Click to “Add Condition” and choose “an Item has a given state”. Select the light sensor Item, < as the operator, and the threshold value.

The second condition is that it’s a weekday. For this we will use the day of week condition. Create another condition and choose “it is a certain day of the week” and choose the weekdays.

Now we need the action, turning on the light. Click “Add Action” and choose “send a command” and select the light’s Item and set the command to ON.

The rule now looks like this.

There is one more detail to add to the rule. This is a rule that triggers on a schedule. These rules can be added to the schedule view by adding a “Schedule” tag at the bottom of the page.

The rule is now complete, click Save or hit ctrl-s. Click on “Code” and you can see what the rule looks like a YAML.

triggers:
  - id: "1"
    configuration:
      itemName: MasterBedroomSensors_LightLevel
    type: core.ItemStateChangeTrigger
  - id: "2"
    configuration:
      time: 08:00
    type: timer.TimeOfDayTrigger
conditions:
  - inputs: {}
    id: "3"
    configuration:
      itemName: MasterBedroomSensors_LightLevel
      state: "50"
      operator: <
    type: core.ItemStateCondition
  - inputs: {}
    id: "4"
    configuration:
      days:
        - MON
        - TUE
        - WED
        - THU
        - FRI
    type: timer.DayOfWeekCondition
actions:
  - inputs: {}
    id: "5"
    configuration:
      itemName: MasterBedroomLights_Power
      command: ON
    type: core.ItemCommandAction

Note that you can edit the rule through the code tab if that works better for you and the yaml is much better for sharing.

Because we’ve tagged this with “Schedule”, it will also appear on Settings -> Schedule.


You can see the new rule will run every weekday at 08:00.

Previous -> Pages
Next -> Putting it all together
Previous <- Pages - Custom Widgets

7 Likes

@ysc, I took a stab at the missing rules page for the tutorial. I didn’t have the gardenia sensor nor a rollershutter but I think I’m able to show the same intent with my diy sensor and a light. Edits are welcome.

3 Likes

Awesome! Thanks for taking care of this, I’ve been falling behind…

Great work on these new tutorials. I’ve only just started on the OH3 journey, but this page has been really helpful. Is there an opportunity to describe how to trigger a message-type action? I’ve been trying to get telegram to send me a simple string, but i cant seem to figure out the syntax in the code.

I have telegram being triggered from a dsl rule

val tactions = getActions("telegram","telegram:telegramBot:6d7da6547d")
logInfo("telegram debug","doorbell rule ran {}",newState.toString())
tactions.sendTelegram("Doorbell Rang")

yaml for complete rule

triggers:
  - id: "2"
    configuration:
      itemName: CBUSDoorBell28_LightChannel
      state: ON
      previousState: OFF
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: |
        
        val tactions = getActions("telegram","telegram:telegramBot:6d7da6547d")
        logInfo("telegram debug","doorbell rule ran {}",newState.toString())
        tactions.sendTelegram("Doorbell Rang")
    type: script.ScriptAction

​```

That’s a great help @jpharvey - it will get me get things moving, thank you.

Perhaps I’m misunderstanding how rules are intended to function.
With the default code generated by the GUI, I was expecting to be able to set a simple string parameter into the actions: configuration: section. Telegram is just one example, but there must be other alert type bindings that would use this type of rule, hence the suggestion of building an example for it. :innocent:

actions:
  - inputs: {}
    id: "2"
    configuration:
      config: telegram:telegramBot:<botID>
    type: telegram.sendTelegram

@mjdyson i think i tried that and i cant make it work. It seems as though although the magic part of the system that understands there is an action that can be called doesnt seem to pass on the parameters. So its not obvious how to add them to the configuration section and whether or not they would be passed on.
I am not sure if that is something the binding needs to do now or whether we need to add some metadata or something similar somewhere.
Hopefully someone who understand how these things join together can explain what is missing since having actions from bindings magically appear is great but not useful if we cant use them.

@mjdyson Looks like this doesn’t work at the moment. See https://github.com/openhab/openhab-core/issues/1745 and https://github.com/openhab/openhab-core/issues/1878

As I said in the tutorial, even though the Actions that come from bindings are listed, they are not functional. There is no way to supply the arguments to the Action. For now you have to do it in a Script Action in the rule instead of selecting it from the list of actions.

The specific syntax will depend on the language selected. John supplied the syntax for Rule DSL. The JavaScript syntax would be (Note, I don’t use telegram so here is a call to sendMail):

actions.get("mail", "mail:smtp:gmail").sendMail("rlkoshak@gmail.com", "openHAB 3 Alert", message);

You pull the action using get where you indicate the binding and the Thing ID and then call the action. Details on which actions are available will be in the binding docs.

Like I said, that’s not working but there is an Issue open for that.

It needs to be implemented in the core first.

1 Like

That’s great! Thanks @rlkoshak - you’ve done an amazing job with the tutorial posts. It must have taken you ages! They are such a great tool for those trying to piece together bits an pieces from working examples.

1 Like

90% of them were written by Yannick (@ysc). Credit where credit is due. I’ve done some minor editing and added a couple of pages.

4 Likes

Be careful about this, I just saw that you have a rule called “Ephemeris” in the schedule. Unfortunately since it’s analyzed on the client-side, the Ephemeris conditions are not taken into account there. Only cron and time of day triggers, and day of week/time of day conditions are. I should put a warning icon on those at least maybe.

Yes, in aware of that. That screen shot need to be recreated probably. Ephemeris is in the name of rule and used in the script action but but in the trigger or conditional. I already discovered that the Ephemeris stuff in triggers and conditionals are not recognized by the schedule page.

That rule is my time if day rule and it runs at midnight to recreate the tigers for the new day, which is what you are in that screenshot.

Hello! I am running a test environment with OH3 snapshot.
I am starting to get confident with new rules and i like a lot to define rules from the new UI. But since i am working on a fully text-based solution, i would like to move those rules to .rules files.
I.E. i designed a simple rule, if i go to the CODE tab it looks like this:

triggers:
  - id: "1"
    configuration:
      channelUID: astro:sun:home:set#event
    type: core.ChannelEventTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      itemName: Terrace_Plug
      command: OFF
    type: core.ItemCommandAction

I cannot copy/paste this to a .rules file, the definition is missing.
What is the right way to do that? Any explanation?
Thanks!

Just to make it clear, rules created through the UI are stored in the JSONDB which is also text based. If the only concern is having text (e.g. for checking into git) there is no reason to move stuff out of JSONDB.

For a rule like that you’ll have to rewrite it. Look at the Rules docs page for the syntax to write a rule. It will look something like

rule "Terrace plug rule"
when
    Channel "astro:sun:home:set#event" triggered START
then
    Terrace_Plug.sendCommand(OFF)
end

NOTE: There is not conditions clause in text based rules. You’ll have to re-implement those as if statements at the start of your rule.

Thank you very much!
Indeed in the JSONDB files i can find everything.
So we cannot use YAML in text based rules?

No, the YAML is only for rules that end up being stored in the JSONDB. There are some fundamental differences in how these rules are structured, stored, and run that make a direct copying from them to .rules files impossible.

If a rule has multiple triggers with an “or-condition”, is it possible to create this rule with the UI?

For example:
When
item1 changed or
item2 changed
then…

Yes, just add a separate in trigger for each. All triggers are or.

Hi, what about the conditions in a single rule? Are they connected with OR or with AND