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

Tags: #<Tag:0x00007f1738d915b0> #<Tag:0x00007f1738d914e8>

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, build the start of a UI to control the devices. But how does one cause something to happen in response to events in openHAB? For example, turn on the porch light when I return 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 is they are event driven. An event occurs and any rule that gets triggered by that will run. A single rule can run based on more than one event but it will only be triggered on the event. You cannot trigger a rule based on two events (e.g. Item Foo changed to OFF and Item Bar > 23) because no two events actually occur at exactly the same time.

Two Approaches

There are two primary approaches for writing rules.

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

Cons:

  • slightly slower to be parsed and loaded
  • lacks a “but only if” clause so any conditions have to be included as part of the body of the rule

UI Created

UI created rules are created through MainUI. Much of the proforma required for the creation of the rule and it’s 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 at all.
  • Blockly is an option for writing the code in a graphical style.

Cons:

  • Sharing variables between rules 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 UI created rules.

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 helper libraries Stuck on Python 2.7 for now, requires a separate addon
JavaScript Comes out-of-box Stuck on Nashorn which is ECMAScript 5.1, awkward method for importing libraries that make most third party libraries unusable
Groovy Has the least number of examples (fewest number of users?), requires a separate addon

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, but the concepts presented will work with any of the languages. You can use more than one language at the same time should you choose.

Anatomy of a Rule

Each rule has three main sections:

Section Also Called Purpose
When Triggers Defined the events that will cause the rule to run
But only if Conditions Defined the conditions that must be true for the rule to run, even when the triggering event occur
Then Actions Defines what to do when the rule runs

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 including ==, !=, <, >, <=, >= comparisons
  • 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

NOTE: As of this writing all binding Actions are also listed as options for Actions. These do not work and an issue is already opened to address it.

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

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.

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.

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.

3 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