Getting Started Rules (#1809)
* Getting Started Rules Signed-off-by: Richard Koshak <rlkoshak@gmail.com> * Fixing build error Signed-off-by: Richard Koshak <rlkoshak@gmail.com> * Correcting markdown linting errors Signed-off-by: Richard Koshak <rlkoshak@gmail.com> * Fixing more md linting Signed-off-by: Richard Koshak <rlkoshak@gmail.com> * More linting errors Signed-off-by: Richard Koshak <rlkoshak@gmail.com> * Fixing links, minor edits Signed-off-by: Richard Koshak <rlkoshak@gmail.com> * One sentence per line for ordered lists * Apply suggestions from code review Co-authored-by: Jerome Luckenbach <github@luckenba.ch> * Update tutorials/getting_started/tips-and-tricks.md Use separate key repesentations instead of the combo as one key. Co-authored-by: Jerome Luckenbach <github@luckenba.ch> * Linting Signed-off-by: Richard Koshak <rlkoshak@gmail.com> Co-authored-by: Jerome Luckenbach <github@luckenba.ch>pull/1827/head
|
@ -21,13 +21,11 @@ module.exports = [
|
||||||
'tutorial/auto_overview',
|
'tutorial/auto_overview',
|
||||||
'tutorial/item_widgets',
|
'tutorial/item_widgets',
|
||||||
'tutorial/custom_widgets',
|
'tutorial/custom_widgets',
|
||||||
/*'tutorial/pages_intro',
|
'tutorial/rules_introduction',
|
||||||
'tutorial/pages_widgets',
|
'tutorial/rules_basic',
|
||||||
'tutorial/pages_types',
|
'tutorial/rules_blockly',
|
||||||
'tutorial/rules_simple',
|
'tutorial/rules_advanced',
|
||||||
'tutorial/rules_intermediate',
|
'tutorial/tips-and-tricks'
|
||||||
'tutorial/cloud',
|
|
||||||
'tutorial/example'*/
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 6.0 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,253 @@
|
||||||
|
---
|
||||||
|
layout: documentation
|
||||||
|
title: Rules - Advanced
|
||||||
|
---
|
||||||
|
|
||||||
|
# Advanced Rules
|
||||||
|
|
||||||
|
Blockly is super powerful and relatively easy to use but at some point there will be some nuance or use case that cannot be achieved through Blockly.
|
||||||
|
Or perhaps the graphical representation is too limiting.
|
||||||
|
For what ever reason, openHAB has you covered with text based Script Actions and Script Conditions.
|
||||||
|
|
||||||
|
{::options toc_levels="2..4"/}
|
||||||
|
|
||||||
|
- TOC
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
## Languages
|
||||||
|
|
||||||
|
openHAB supports a growing list of programming languages in which rules can be written.
|
||||||
|
openHAB comes with the following languages to choose from:
|
||||||
|
|
||||||
|
| Language | Details | Intended Audience |
|
||||||
|
|--------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| [Blockly]({{base}}/configuration/blockly/) | See the previous page | Non-developers |
|
||||||
|
| [Rules DSL]({{base}}/configuration/rules-dsl.html) | A programming language developed specifically for openHAB based on the Xtend. | Long time openHAB users |
|
||||||
|
| [ECMAScript 5.1]({{base}}/configuration/jsr223.html) | An older version of JavaScript built into Java Virtual Machine 14 and before. It's the language Blockly "compiles" into which can be a powerful learning tool (i.e. build a rule in Blockly and examine the code it generates to see how it works). | Not recommended for use, will go away at sometime soon (Blockly will be updated at that time to use an alternative). |
|
||||||
|
|
||||||
|
In addition to these default choices, one can install a number of different languages as an Automation add-on.
|
||||||
|
Such diverse languages as Python, Ruby, Groovy, Java, and more are available with more to come.
|
||||||
|
See the add-on docs for the reference guide and specific information for how to use each individual add-on.
|
||||||
|
Take note, not all automation add-ons support writing rules in the UI.
|
||||||
|
|
||||||
|
For the rest of this tutorial we will use the [JavaScript Scripting add-on](/addons/automation/jsscripting/) which implements ECMAScript 2021 for Script Actions and Script Conditions in UI rules.
|
||||||
|
See the add-on's reference for how to write rules in text files which is outside the scope of this tutorial.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install a new rules language navigate to Settings -> Automation and install the add-on desired.
|
||||||
|
The next time you create a Script Action or Script Condition, if it's supported, the new language will be selectable.
|
||||||
|
|
||||||
|
## Helper Libraries
|
||||||
|
|
||||||
|
All of the rules languages will run inside the Java Virtual Machine.
|
||||||
|
This gives all these languages access to all of the Java standard library of classes.
|
||||||
|
Furthermore, all direct interactions with openHAB itself is done through Java Objects and Classes.
|
||||||
|
What does this mean?
|
||||||
|
It means that sometimes an `int` isn't just an `int`.
|
||||||
|
If you are going to write rules to directly interact with openHAB, you will have to be very conscious of where the data comes from and what its type is.
|
||||||
|
|
||||||
|
In order to make that process easier, most of the automation add-ons offer a Helper Library.
|
||||||
|
These libraries come in various forms and are installed in different ways but they all endeavor to do the following:
|
||||||
|
|
||||||
|
- make writing rules in text files easier, less verbose, and more natural to the native language
|
||||||
|
- provide utility functions and ways to interact with openHAB using language native Classes, Objects and types
|
||||||
|
- abstract some of the sometimes verbose series of steps requires to do something (e.g. access an Item's metadata) into a single function call.
|
||||||
|
|
||||||
|
See the automation add-on's reference for how to access and install the Helper Library for your language of choice.
|
||||||
|
In the case of JavaScript Scripting, the Helper Library comes with the add-on (nothing to install separately) and it is automatically imported into your rules for you (advanced users can turn off that auto import in MainUI Settings -> JS Scripting).
|
||||||
|
To get the latest version of the Helper Library instead of waiting for the next release of OH, it can be installed using `npm`.
|
||||||
|
Again, see the add-on's reference for details.
|
||||||
|
|
||||||
|
## Creating a Rule
|
||||||
|
|
||||||
|
See the previous pages of this tutorial for details on creating a rule.
|
||||||
|
The steps are the same no matter what language is to be used.
|
||||||
|
Consequently this tutorial won't talk about the rule triggers at all.
|
||||||
|
|
||||||
|
One can combine and use multiple languages in the same rule, for example using a simple UI Condition and a Blockly Script Condition along with a Script Action written in Rules DSL and second one written in ECMAScript 2021.
|
||||||
|
|
||||||
|
For consistency and the ability to compare approaches, this part of the tutorial will use the same set of requirements as was used in the Blockly tutorial.
|
||||||
|
As a reminder, we want to turn on a light when motion is detected and keep that light on for 30 minutes after the last motion was detected.
|
||||||
|
But it should only do this between sunset and 23:00.
|
||||||
|
|
||||||
|
### When: Triggers
|
||||||
|
|
||||||
|
See the previous pages.
|
||||||
|
Triggers are set up the same way for all UI rules.
|
||||||
|
We will use the same trigger from the Blockly tutorial.
|
||||||
|
|
||||||
|
### Then: Actions
|
||||||
|
|
||||||
|
Just like in Blockly, we will create a new Action but instead of choosing Blockly, we will choose ECMAScript 2021.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This will open a blank text field where you can start typing your code.
|
||||||
|
This text field provides text highlighting and some code completion which helps with coding.
|
||||||
|
|
||||||
|
As previously mentioned, the Helper Library for this language comes with the add-on and is imported by default, so see the [JavaScript Scripting add-on's reference](/addons/automation/jsscripting/) for the full guide on how to do anything you might want to do.
|
||||||
|
For help with general JavaScript coding, there are tons of tutorials and reference documents on the web a search away.
|
||||||
|
|
||||||
|
As with the Blockly example, we want to start the rule using a log statement we can see in openhab.log when the rule runs.
|
||||||
|
One can either use the [log actions](/addons/automation/jsscripting/#log) but most will find it easiest to use the more JavaScript native [`console`](/addons/automation/jsscripting/#console).
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.info('Motion was detected');
|
||||||
|
```
|
||||||
|
|
||||||
|
Save and test the rule by running it manually and verify you see this statement in the logs.
|
||||||
|
|
||||||
|
Next we want to `sendCommand` to the light to turn it on.
|
||||||
|
Reading the docs we see that access to the Item registry is provided through [`Items`](/addons/automation/jsscripting/#items) where we can get access to a JavaScript Object that represents the Item.
|
||||||
|
This Object has a `sendCommand()` function that takes the command.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.info('Motion was detected');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('ON');
|
||||||
|
```
|
||||||
|
|
||||||
|
Save and test and verify you see the log statement and the Item receive an ON command.
|
||||||
|
|
||||||
|
Now we want to create a Timer to go off in 30 minutes.
|
||||||
|
We find the Timer creation documented under [ScriptExecution](/addons/automation/jsscripting/#scriptexecution-actions).
|
||||||
|
A Timer will execute a block of code passed to it as the second argument at the time specified by the first argument.
|
||||||
|
|
||||||
|
In JavaScript this block of code would be defined in a function.
|
||||||
|
There are *lots* of ways to define functions in JavaScript including inline anonymous, and more.
|
||||||
|
See one of the many tutorials on JavaScript functions for details (e.g. search "JavaScript functions" on Google).
|
||||||
|
I'll define a separate variable to hold the function here.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.info('Motion was detected');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('ON');
|
||||||
|
var lightsOut = function() {
|
||||||
|
console.info('No more motion, turning off the light');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('OFF');
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
To define date times, the [JS Scripting Helper Library](/addons/automation/jsscripting/#time) includes the JS-Joda library which provides a very robust set of classes to create and manipulate dates and time.
|
||||||
|
We need to use this library to specify the time when the timer should run.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.info('Motion was detected');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('ON');
|
||||||
|
var lightsOut = function() {
|
||||||
|
console.info('No more motion, turning off the light');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('OFF');
|
||||||
|
};
|
||||||
|
ScriptExecution.createTimer(time.ZonedDateTime.now().plusMinutes(30), lightsOut);
|
||||||
|
```
|
||||||
|
|
||||||
|
Save and test that you see the log statement and the Item receive the `ON` command and 30 minutes later the second log statement and the `OFF` command.
|
||||||
|
(hint, change the time passed to the timer to something smaller to make testing easier then change it back once things are working).
|
||||||
|
|
||||||
|
Now all we are lacking is the ability to reschedule that timer if motion is seen again in the 30 minute period.
|
||||||
|
Looking back at the docs we find the [`cache`](/addons/automation/jsscripting/#cache).
|
||||||
|
This is a map of key/value pairs that exists outside of the rule.
|
||||||
|
Given that position it is able to share data between different rules or between runs of the same rule.
|
||||||
|
We will use it to save that Timer so we can reschedule it later when needed.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
console.info('Motion was detected');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('ON');
|
||||||
|
|
||||||
|
timerId = ruleUID+'_timer';
|
||||||
|
var lightsOut = function() {
|
||||||
|
console.info('No more motion, turning off the light');
|
||||||
|
items.getItem('FrontPorchLight').sendCommand('OFF');
|
||||||
|
cache.put(timerId, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
var timer = cache.get(timerId);
|
||||||
|
if(!timer) {
|
||||||
|
cache.put(timerId, ScriptExecution.createTimer(time.ZonedDateTime.now().plusMinutes(30), lightsOut));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timer.reschedule(time.ZonedDateTime.now());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that we use the `ruleUID` which is a variable made available by the Helper Library to ensure that we don't overwrite on something added to the `cache` from another rule.
|
||||||
|
Also notice a line was added to `lightsOut` to delete the entry in the `cache` when the timer ran.
|
||||||
|
That will cause the rule to create a new timer the next time the rule runs.
|
||||||
|
It could be coded to reuse the Timer instead which is an exercise for the reader.
|
||||||
|
|
||||||
|
Save and test that the rule sends the on and off commands as described.
|
||||||
|
|
||||||
|
### But only if: Conditions
|
||||||
|
|
||||||
|
Now we want the rule to only execute between sunset and 23:00.
|
||||||
|
Create a new Condition and choose ECMAScript 2021 as the language.
|
||||||
|
Just like with the Script Action, the Helper Library is available by default.
|
||||||
|
|
||||||
|
As discussed in the Blockly tutorial, the last line of a condition must evaluate to `true` or `false`.
|
||||||
|
When `true` the rule will run, otherwise it's skipped.
|
||||||
|
|
||||||
|
Sunset is available in an Item called `Sunset` and we need to test to see if `now` is between then and `23:00`.
|
||||||
|
In order to do that we need to convert the `Sunset` state to a JS-Joda date time.
|
||||||
|
|
||||||
|
Looking at the docs for [`Item`](/addons/automation/jsscripting/#items) we see that the Helper Library `Item` Class will return the String representation of the Item's state by default.
|
||||||
|
Luckily the Helper Library also has an extension that makes it so JS-Joda can parse that String by default.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var sunset = time.ZonedDateTime.parse(items.getItem('Sunset').state);
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we need to create a `ZonedDateTime` for 23:00 today.
|
||||||
|
Looking at the JS-Joda docs we see how to do that.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var sunset = time.ZonedDateTime.parse(items.getItem('Sunset').state);
|
||||||
|
var endTime = time.ZonedDateTime.now().withHour(23).withMinute(0).withSecond(0).withNano(0);
|
||||||
|
```
|
||||||
|
|
||||||
|
Now all we have to do is see if `now` is between those times.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var sunset = time.ZonedDateTime.parse(items.getItem('Sunset').state);
|
||||||
|
var endTime = time.ZonedDateTime.now().withHour(23).withMinute(0).withSecond(0).withNano(0);
|
||||||
|
now.isAfter(sunset) && now.isBefore(endTime)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Topics
|
||||||
|
|
||||||
|
### Libraries
|
||||||
|
|
||||||
|
Most of the languages support installation and importing of third party libraries.
|
||||||
|
Many of the languages will have special tools to download and install these libraries and there might be extra requirements to use them in openHAB.
|
||||||
|
See the add-on's docs and the forum for details on the automation add-on chosen.
|
||||||
|
|
||||||
|
For JS Scripting, `npm` is supported for the installation of third party libraries.
|
||||||
|
Run the `npm` command from the `$OH_CONF/automation/js` folder.
|
||||||
|
This will create a `node_modules` folder (if it doesn't already exist) and the library will be installed there.
|
||||||
|
Use `var name = require('name_of_lib');` to import the library into your Script Actions and Script Conditions.
|
||||||
|
|
||||||
|
Any language that supports such libraries will also allow you to create your own personal library where you can create functions, data structures, and classes that can be reused across your Scripts Actions/Conditions.
|
||||||
|
For example, some functions to centralize alerting is a good idea.
|
||||||
|
|
||||||
|
For JS Scripting, look up tutorials for how to create an `npm` module for how to write your own personal libraries.
|
||||||
|
I find it easiest to write the library in a separate folder, tar it up and use `npm` to install your library from the `tar` file.
|
||||||
|
That keeps it from being modified or overwritten by `npm` when upgrading or installing/removing other third party libraries.
|
||||||
|
|
||||||
|
There are several third party openHAB specific libraries on `npm` already.
|
||||||
|
Search the forum for details.
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
All the same advice from Blockly applies here too.
|
||||||
|
Gradually build up your rule, testing at each step. Use logging liberally.
|
||||||
|
Break up your code into functions you can test independently. Simplify complicated lines by breaking them up.
|
||||||
|
Watch the logs for errors.
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
- If you find yourself duplicating rules or code, consider ways to extract that into a function or library.
|
||||||
|
|
||||||
|
- When posting to the forum, post the contents of the "Code" tab instead of screen shots.
|
||||||
|
|
||||||
|
- Rules can call other rules which can be another way to break out common code.
|
||||||
|
|
||||||
|
- Use the `cache` to hold things that need to survive the running of a script or be shared with other scripts.
|
||||||
|
The `cache` will be cleared out during a restart.
|
|
@ -0,0 +1,200 @@
|
||||||
|
---
|
||||||
|
layout: documentation
|
||||||
|
title: Rules - Basic
|
||||||
|
---
|
||||||
|
|
||||||
|
# Basic Rules and Rule Templates
|
||||||
|
|
||||||
|
Rules do not always require coding.
|
||||||
|
This section describes two ways to build and configure rules without code.
|
||||||
|
Even though you may not be interested in such simple rules, do not skip this section.
|
||||||
|
The concepts presented here will be referred to later on, and the same concepts apply even for text file based rules.
|
||||||
|
|
||||||
|
{::options toc_levels="2..4"/}
|
||||||
|
|
||||||
|
- TOC
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
## Basic Rules
|
||||||
|
|
||||||
|
Very basic rules can be defined in the UI without the need for code.
|
||||||
|
These rules will follow a `when X event occurred if Y is true do Z` format.
|
||||||
|
For example, when a member of the Temperatures Group changes, if it changed to a value over 70 °F, command the AlertLight Item to "red".
|
||||||
|
|
||||||
|
For this example we will be creating two rules, one that sets a Switch Item to `ON` when the cloudiness reading goes above 50% and another to `OFF` when it falls below 50%.
|
||||||
|
|
||||||
|
### Create the Rule
|
||||||
|
|
||||||
|
Navigate to Settings and Rules and click on the **+** icon in the lower right corner.
|
||||||
|
|
||||||
|
#### Metadata
|
||||||
|
|
||||||
|
Each rule has four pieces of metadata.
|
||||||
|
|
||||||
|
| Metadata | Purpose | Notes |
|
||||||
|
|-------------|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Unique ID | uniquely identifies the rule | must be completely different from all other rule's UIDs, initialized with a random value but I strongly recommend entering your own UID |
|
||||||
|
| Name | "title" of the rule | This will be the name under which the rule is shown in MainUI and is the primary field used for search and sorting |
|
||||||
|
| Description | describes what the rule does | do not skip, provide information about what the rule does and what it does it to; leave breadcrumbs you can use later to associated rules with Items and Channels |
|
||||||
|
| Tags | arbitrary tags | Another place to leave breadcrumbs or to group rules together. Note the `Script` and `Schedule` tags have a special meaning (see previous page), and although Semantic tags are available, rules are not yet integrated into the Semantic Model. |
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Here is how this rule appears in the list of rules (note the use a couple of tags):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
All but the Unique ID can be changed later.
|
||||||
|
In my system this rule is used to drive my lights and it's based on weather readings so it's tagged to indicate that.
|
||||||
|
|
||||||
|
#### Triggers
|
||||||
|
|
||||||
|
Under triggers, click on the green + icon labeled "Add Trigger" to define what events should cause this rule to run.
|
||||||
|
This brings up a form that lets you choose the type of the trigger and enter some information about the trigger.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The title and description are optional and will be automatically populated with meaningful information when you select the trigger.
|
||||||
|
However pay attention and if there is more information that might be useful to you later add that.
|
||||||
|
|
||||||
|
We need an Item changed event to trigger this rule so it runs when the cloudiness percentage Item changes.
|
||||||
|
So I select "Item Event" and select my Item and click "Pick" in the upper right corner.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Choose "changed" since we only care when the Item changes.
|
||||||
|
Notice the title and description is filled out with reasonable information.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
You will notice that there are "from state" and "to state" fields.
|
||||||
|
These can be used to further narrow down when the rule triggers, but do not confuse these with conditions.
|
||||||
|
You can only include a single state here (e.g. from state: ON to state: OFF).
|
||||||
|
You cannot use comparison operations here (e.g. < 70) and need a condition for that.
|
||||||
|
Therefore these are mostly useful with `Switch`, `Contact`, `Player`, and sometimes `String` Items.
|
||||||
|
Any Item that has a range of values rather than a few discrete values will not use these.
|
||||||
|
|
||||||
|
#### Conditions
|
||||||
|
|
||||||
|
In this case we have two conditions.
|
||||||
|
We only want the actions to run when the Item is at or above 50% and the status Item vIsCloudy isn't already ON.
|
||||||
|
|
||||||
|
Click the green + icon under "But only if" labeled "Add Condition".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Choose Item Condition because we want to check the state of an Item and select the Item.
|
||||||
|
Note, sometimes if the Item not part of the model it can be hard to find.
|
||||||
|
If that happens to you, select any Item to start and then you can get a flat list of all your Items on the next screen.
|
||||||
|
|
||||||
|
In this case we want to check if the `vCloudiness` Item is >= to 50 %.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Notice the `%`.
|
||||||
|
This Item is a `Number:Dimensionless` representing a percentage.
|
||||||
|
So I've used the units here.
|
||||||
|
This will work with any supported units of measurement and there will be a warning in the logs if your Item has units but the "State" does not.
|
||||||
|
|
||||||
|
Next we don't want to run the actions if vIsCloudy is already ON.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Actions
|
||||||
|
|
||||||
|
Finally, we want this rule to send the ON command when triggered and the conditions are met.
|
||||||
|
|
||||||
|
Click the green + icon labeled "Add Action" under the "Then" section and choose "Item Action".
|
||||||
|
By now the steps should be familiar, select the Item, in this case `vIsCloudy` and you are presented with the Item action form.
|
||||||
|
In this case we want to send command ON.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The rule is now done!
|
||||||
|
Click Save and watch the log for errors and to see if the rule is working (more on that later).
|
||||||
|
|
||||||
|
### Copy a Rule
|
||||||
|
|
||||||
|
We now have a rule that sets `vIsCloudy` to ON and we need a corresponding rule to trigger it when OFF.
|
||||||
|
Since this second rule is very similar to the one we just created, we can copy/paste/edit.
|
||||||
|
|
||||||
|
First, open the code tab for the rule we just created.
|
||||||
|
This will show a text version of the rule.
|
||||||
|
You should see a section corresponding to each of the parts of the rule (but note that the order of the configured pieces may not match.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Copy the text shown here to the clipboard.
|
||||||
|
|
||||||
|
Create a new rule (see above), enter the metadata for the rule (this time we are commanding the Item to OFF) and then open the code tab for this new rule.
|
||||||
|
Paste the contents of the clipboard to the code tab and make the edits as necessary.
|
||||||
|
|
||||||
|
In this case we want to change the operator for the first condition to <, the state for the second condition to OFF, and the command for the action to OFF.
|
||||||
|
The code would look like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This will work for any UI Rule, not just basic rules.
|
||||||
|
|
||||||
|
Note, when asking for help on the forum, the experts will want to see this code version pasted as text rather than a screen shot of either the Design tab or the Code tab.
|
||||||
|
|
||||||
|
## Rule Templates
|
||||||
|
|
||||||
|
At some point basic rules will not be sophisticated enough to reach your goal.
|
||||||
|
The first place to look when that is the case are the rule templates posted to the Marketplace.
|
||||||
|
You can find them under Settings and Automation.
|
||||||
|
|
||||||
|
### Installation and Configure
|
||||||
|
|
||||||
|
Under the "Languages & Technologies" section there are "Rule Templates" and "Blockly Libraries" sections (discussed on the next page).
|
||||||
|
These are community created capabilities posted to the [Marketplace](https://community.openhab.org/c/marketplace/69).
|
||||||
|
Someone may have already written the code for you so all you have to do is install and configure it.
|
||||||
|
|
||||||
|
In MainUI click on a rule template that seems to do what you want.
|
||||||
|
This will open it's documentation page which explains how the template works, configuration parameters, dependencies, etc.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To install the template, click on "Add" next to the template's logo.
|
||||||
|
If installation was successful, you can now create one or more rules based on the template.
|
||||||
|
|
||||||
|
### Rule Template Instantiation
|
||||||
|
|
||||||
|
Navigate to Rules and click the blue + icon to create a new Rule.
|
||||||
|
|
||||||
|
Fill out the Rule's metadata as usual.
|
||||||
|
However, now you will see a "Create from Template" section listing all the installed templates.
|
||||||
|
Choose the template you want to base this rule on.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The bottom of the form will change from the three sections described previously to a form showing the parameters the rule template defines.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In this case, the parameters are selecting a DateTime Item to trigger the rule and which defines the "alarm time" and a "Script" to call when at the date and time in that DateTime Item.
|
||||||
|
|
||||||
|
### Updates to Templates
|
||||||
|
|
||||||
|
Your rule, once instantiated becomes its own thing separated from the template, just as if you had cut and paste the code as in the first example.
|
||||||
|
Therefore, it will not change even if the rule template is changed or removed.
|
||||||
|
This means the rule will not update based on changes to the template automatically.
|
||||||
|
To update a template and a rule use the following process.
|
||||||
|
|
||||||
|
1. Notice that there has been an update to a template that you want to use (keep checking back).
|
||||||
|
1. Select the template in MainUI and click "Remove".
|
||||||
|
Then click "Add" to refresh the template with the new changes.
|
||||||
|
1. Go to Settings and Rules and navigate to the rule you want to update.
|
||||||
|
Click the pause icon to disable that rule.
|
||||||
|
Or if you don't want to keep a backup, delete it.
|
||||||
|
Note, if you just disable you won't be able to use the same rule UID for the new rule.
|
||||||
|
1. Create the rule following the steps from above.
|
||||||
|
1. Once the new version of the rule is tested, delete the disabled version of the rule.
|
||||||
|
|
||||||
|
### Customization of Rules Created From Templates
|
||||||
|
|
||||||
|
What if a rule template does almost what you need but not everything?
|
||||||
|
Since the rule, once instantiated, becomes severed from the template, you can modify the end rule as much as you want.
|
||||||
|
Don't be afraid to take a rule template as a starting point and further refine it.
|
||||||
|
|
||||||
|
If you make additions to a rule template that you want to share, work with the original poster of the template to add your updates to the original template (posting a new template with only minor differences is against the marketplace rules).
|
|
@ -0,0 +1,315 @@
|
||||||
|
---
|
||||||
|
layout: documentation
|
||||||
|
title: Rules - Blockly
|
||||||
|
---
|
||||||
|
|
||||||
|
# Blockly
|
||||||
|
|
||||||
|
What should one do if the simple "when-then-but only if" rules are not enough?
|
||||||
|
This is where the Script Actions and Script Conditions come into play.
|
||||||
|
Both of these are a way for you to write code that can be as long and complex as it needs to be to achieve your goals.
|
||||||
|
|
||||||
|
As you recall from the previous page, one of the four types of conditions are Script Conditions and one of the four types of actions are Script Actions.
|
||||||
|
Choosing either of those presents you with a choice of language to use.
|
||||||
|
By default there are only three choices: Blockly, ECMAScript, and Rules DSL.
|
||||||
|
If you've installed other automation add-ons, those might be listed as well.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
For this part of the tutorial we will choose "Design with Blockly".
|
||||||
|
|
||||||
|
{::options toc_levels="2..4"/}
|
||||||
|
|
||||||
|
- TOC
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
## Introduction to Blockly
|
||||||
|
|
||||||
|
[Blockly](https://developers.google.com/blockly) is a visual coding language consisting of blocks that fit together like building blocks.
|
||||||
|
For people who prefer visual over text or those who are new to programming, Blockly can be a good choice.
|
||||||
|
It even allows you to learn how write your own JavaScript by looking at the code that Blockly generates in the background and this code could be even copy and pasted into a pure ECMAScript rule as a starting point.
|
||||||
|
If you have been around kids you may be familiar with [Scratch](https://scratch.mit.edu/).
|
||||||
|
Blockly is a similar environment customized for writing openHAB scripts.
|
||||||
|
|
||||||
|
A typical Blockly script might look something like this:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Before continuing, see if you can figure out what that does.
|
||||||
|
This shouldn't be too hard because one of Blockly's intents is to provide visual code that is easy to comprehend.
|
||||||
|
The flow is from top to bottom.
|
||||||
|
|
||||||
|
### Blocks Categories
|
||||||
|
|
||||||
|
Statements in Blockly are defined using blocks.
|
||||||
|
Compatible blocks can attach to each other to build up your program.
|
||||||
|
Incompatible blocks will not fit together making it harder to come up with impossible logic.
|
||||||
|
The code also reads a little bit more like natural language.
|
||||||
|
|
||||||
|
There are a number of categories of blocks which you will find along the left hand side.
|
||||||
|
All but the openHAB and Library categories are generic programming concepts and are where you will find the blocks to create variables, loop through lists, comparisons and conditionals, etc.
|
||||||
|
|
||||||
|
The openHAB category is where all the blocks that interact with openHAB are listed.
|
||||||
|
These are where you'll access Items, call actions, get data from persistence, create Timers (a way to schedule some code to run in the future), etc.
|
||||||
|
|
||||||
|
The Library category is where Blockly Libraries installed from the Marketplace will appear (see below and the previous discussion on Rule Templates).
|
||||||
|
|
||||||
|
This tutorial is not going to be a full tutorial on how to write code in Blockly.
|
||||||
|
See the [Blockly Reference]({{base}}/configuration/blockly/index.html) for a detailed tutorial and reference for doing that.
|
||||||
|
Instead it's a higher level example of building a rule step-by-step using Blockly.
|
||||||
|
|
||||||
|
Some sections will be short.
|
||||||
|
If you are missing details, see the previous section for details as most of the steps are the same for creating *all* rules in the UI.
|
||||||
|
|
||||||
|
## Building a Rule
|
||||||
|
|
||||||
|
For this example, we will build a rule that turns on a light when a motion sensor triggers and leaves it on until thirty minutes after the last motion is detected.
|
||||||
|
But it will only do so from sundown to 23:00 and not do anything the rest of the day.
|
||||||
|
|
||||||
|
### When: Triggers
|
||||||
|
|
||||||
|
The event that we care about in this case is when the motion sensor Item indicates that it has detected motion.
|
||||||
|
How this is represented can vary from one device to another.
|
||||||
|
For example, one might use a Switch Item and send an ON command to that Item every time motion is detected.
|
||||||
|
Others might use a Contact Item and update it to OPEN when motion is detected.
|
||||||
|
Still others might use an Event Channel to trigger a rule without an Item at all.
|
||||||
|
Some motion sensors will keep the Item ON while motion is detected and then set it to OFF, others will only send it to ON and the Item never returns to OFF.
|
||||||
|
|
||||||
|
There can be differences in behavior too.
|
||||||
|
For example some will have a timeout where they won't report newly detected motion for a configured amount of time has passed.
|
||||||
|
You must understand how your motion detector works to choose an appropriate trigger and write an appropriate action.
|
||||||
|
For example, if the motion sensor has a timeout of five minutes (i.e. it only reports motion every five minutes), the timer in your action for the rule must be five minutes or longer.
|
||||||
|
Shorter and the rule will turn off the light even when there is motion.
|
||||||
|
|
||||||
|
See the previous page for details on how to create the trigger you need.
|
||||||
|
In this example we will use an `ON` command to the `MotionSensor` Item as the signal that there is motion.
|
||||||
|
The motion sensor will timeout for 30 seconds before reporting motion again.
|
||||||
|
There are three options to choose from which are explained very well [here](https://www.openhab.org/docs/configuration/rules-dsl.html#manipulating-item-states).
|
||||||
|
In simple terms "changed" means the rule only triggers if the state *has changed* for example from OFF to ON while "was updated" would even trigger the rule if "OFF" was set to an item state that was already OFF.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Then: Actions
|
||||||
|
|
||||||
|
Now let's work on what we want to have happen when the rule triggers.
|
||||||
|
As mentioned above we want to turn the light on and leave it on until thirty minutes after the last motion is detected.
|
||||||
|
|
||||||
|
First create a new Action and choose Script Action and Design with Blockly.
|
||||||
|
|
||||||
|
To start, we want to be able to see that this rule is running.
|
||||||
|
So we will first add a log statement which you will find in the openHAB -> Logging & Output section.
|
||||||
|
We will choose "info" for the level and type a message we want to see in `openhab.log` when this action is executed.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Next we want to command a light to turn `ON`. Under openHAB -> Items & Things you'll find a "send command" block.
|
||||||
|
Drag that over and dock it under the log statement.
|
||||||
|
We want to send `ON` to the Porch Light Item.
|
||||||
|
If the light is already `ON` nothing will happen but, if your device behaves differently you might need to check to see if the light is already `ON` before sending the command.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Next we want to set a timer.
|
||||||
|
A timer is a way to schedule some code to run later on.
|
||||||
|
The rule will continue and exist after the timer is created (i.e. it doesn't wait around) and the timer will execute in the background at the scheduled time.
|
||||||
|
|
||||||
|
In this case we want to wait thirty minutes.
|
||||||
|
But we want to wait for thirty minutes after the last motion.
|
||||||
|
This rule gets triggered every time motion is detected so we need to reschedule the timer every time motion is detected.
|
||||||
|
A reschedule will tell the timer to run at a different time.
|
||||||
|
Only when there has been thirty minutes of no motion will the blocks inside the Timer block be executed.
|
||||||
|
|
||||||
|
Under openHAB -> Timers & Delays drag the "after" block that includes "reschedule" at the bottom.
|
||||||
|
We need to change the time to thirty minutes.
|
||||||
|
Notice the little triangle next to "seconds".
|
||||||
|
Clicking on that will bring up a list of options to choose from.
|
||||||
|
Anywhere you see that triangle there are options (e.g. clicking on "info" for the log can change the logging level, clicking on "send command" lets you change to "post update").
|
||||||
|
|
||||||
|
Give the Timer a meaningful name.
|
||||||
|
It's good to be in the habit of naming everything with a meaningful human readable name.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now we need to add the block to run thirty minutes after the last motion was detected.
|
||||||
|
We want to send an OFF command to the light.
|
||||||
|
But, for testing and debugging we also want to log something out so add a log statement too.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Trigger the rule a few times and watch the logs to make sure you see the commands and logging events as expected.
|
||||||
|
When testing, consider using a smaller timer time (e.g. three seconds) and change back to the desired time once you know it is working.
|
||||||
|
|
||||||
|
### But only if: Conditions
|
||||||
|
|
||||||
|
We have the when and the then so now we need the "But only if" to limit when the rule can run to between sundown and 23:00.
|
||||||
|
Add a condition and choose a Script Condition and select Deign with Blockly as the language.
|
||||||
|
|
||||||
|
A script condition is just like an action except the last statement execute must evaluate to a boolean (`true` or `false`).
|
||||||
|
When `true` the actions will execute.
|
||||||
|
When `false` it won't.
|
||||||
|
|
||||||
|
So we need to have a way to compare the current time with a hard coded time (23:00) and a time held by an Item linked to one of the Astro binding's sunset channels.
|
||||||
|
|
||||||
|
Looking around the available blocks will show us that there are no such blocks built in (as of this writing).
|
||||||
|
The first thing to do in this situation is to check the Marketplace to see if there is a Blockly library that does what we need.
|
||||||
|
Indeed there is one for comparing times!
|
||||||
|
|
||||||
|
#### Install a Library
|
||||||
|
|
||||||
|
Navigate to Settings -> Automation and scroll down to "Block Libraries".
|
||||||
|
Browse around to see if there are relevant libraries, in this case the "Date and Time" one looks most promising.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Click on it and add it and return to your rule.
|
||||||
|
Now you will find that there is a dateTime entry under "Libraries".
|
||||||
|
It looks like we will be using the "now" (the current time), "ZDT from item" (sunset time from the Astro binding), the ZDT with date" (hard coded time), and the "is" block to make the comparisons.
|
||||||
|
|
||||||
|
#### Build the Condition
|
||||||
|
|
||||||
|
Now that we have the blocks we need, let's build the Condition.
|
||||||
|
First notice that we have the same operation that needs to be executed twice.
|
||||||
|
Often, when one encounters this situation, a function is a way to save duplicated effort.
|
||||||
|
Let's try that here.
|
||||||
|
|
||||||
|
Under "Functions" drag the "to do something" block with the return statement.
|
||||||
|
Next to "to" give it a meaningful name, we'll use "compareDTs".
|
||||||
|
In this case, in order to do something useful we need to pass arguments to the function, two date times.
|
||||||
|
To do this click the cog icon.
|
||||||
|
This will bring up a way to add or remove features to any block that has the cog.
|
||||||
|
In this case we want to add two "inputs".
|
||||||
|
Drag them from the left to the right and give them names.
|
||||||
|
You will now see the two inputs listed on the block.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now we want to compare the two passed in date times and return a value to indicate whether time1 is before or after time2.
|
||||||
|
We will use `1` to indicate that time2 is after time1 and -1 to indicate that time2 is before time1.
|
||||||
|
So first we need to create a variable so under Variables create a new variable called "tcomp" for "time compare".
|
||||||
|
Then drag "set tcompt to" and dock it inside the function.
|
||||||
|
Go to Math and drag the "123" block to plug into the "set" block.
|
||||||
|
Change the value to "0".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now we will add an if statement to compare the times.
|
||||||
|
We have two conditions to we will use the cog to add an "else if" to the if statement to.
|
||||||
|
You can find the "if" block under "Logic".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now we will use the new "is" block from the library to see if time1 is before time2 and set tcomp to 1.
|
||||||
|
Drag the 'is" block and plug it into the first if socket.
|
||||||
|
Then under "Variables" drag the "time1" variable and plug it into the first slot after "is".
|
||||||
|
Drag time2 to the the slot after "before".
|
||||||
|
We only care about the time so change the config after "use" to "time" and keep the resolution at "seconds".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In this case we want to return `1` so right click on the "set tcomp" to block and choose "duplicate".
|
||||||
|
This will create a copy of that set of blocks.
|
||||||
|
Set the value to to 1.
|
||||||
|
Drag the block next to the first "do".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Duplicate the "is" and "set" blocks again to populate the "else if", this time setting to -1 when time1 is after time2.
|
||||||
|
|
||||||
|
Finally, drag the "tcomp" variable and plug it into the "return" socket.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The hard work is now done.
|
||||||
|
Now we just need to create a statement when `now` is between sunset and 23:00.
|
||||||
|
To keep the blocks from getting too long and complicated, let's create some variables: "start" for the sunset time and "end" for 23:00.
|
||||||
|
Set "start" to the "ZDT from item", selecting the Sunset Item.
|
||||||
|
Set "end" to the ""ZDT with date" block.
|
||||||
|
Set the time to "23:00:00" (we don't care what the date is set to).
|
||||||
|
|
||||||
|
Notice how the statements are not connected to our function.
|
||||||
|
They represent a completely separate block of code.
|
||||||
|
As previously mentioned, the flow of execution is top to bottom so it is a good idea to put functions above the code that calls them.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now all we need is the condition that evaluates to true or false when now is between start and end.
|
||||||
|
From "Logic" drag the block that has "and" in it to just below our setting of the end variable.
|
||||||
|
Normally a condition like this would dock to an if statement but we are going to use it on it's own.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In the first slot, from the "Logic" category drag the block with "=".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
From "Functions" drag the block named "compareDTs" to the first open slot.
|
||||||
|
This is our function.
|
||||||
|
Plug in "start" from variables for "time1" and "now" from the dateTime library for "time2".
|
||||||
|
From Math plug in "1" in the slot after the "=".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Click under the "=" to select the whole comparison and duplicate it.
|
||||||
|
Plug the duplicate into the slot after the "and" and change "time1" to "now" and "time2" to "end" and the number to "-1".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
That's all there is to it.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- The function probably doesn't add much here and it's use is somewhat contrived to demonstrate how to create and use one.
|
||||||
|
However, it does keep there from being too much all in one set of blocks which can become challenging to read and understand.
|
||||||
|
|
||||||
|
- What if the end time is after midnight?
|
||||||
|
Edge cases like that happen all the time and it will make your code much more complicated.
|
||||||
|
But don't be afraid to deal with them. Take it slow and gradually build up to them.
|
||||||
|
|
||||||
|
- That script condition could use some logging too.
|
||||||
|
What do you think it makes sense to log out?
|
||||||
|
|
||||||
|
## Debugging and Troubleshooting
|
||||||
|
|
||||||
|
No one writes perfect code all the time the first time.
|
||||||
|
So what is one to do when it doesn't work as expected?
|
||||||
|
|
||||||
|
- Add logging and make sure to log out meaningful information such as the values of variables.
|
||||||
|
Look in the "Text" category for operations to combine strings together for logging.
|
||||||
|
|
||||||
|
- Watch the logs for errors, your log statements, and relevant events (see the [Blockly Reference]({{base}}/configuration/blockly/index.html) on how to easily access log information)
|
||||||
|
|
||||||
|
- When asking for help on the forum, don't *just* post a screenshot of the blocks.
|
||||||
|
Also post the code the blocks generate or, even better back out of the script to the rule and post the contents of the "Code" tab which gives the helpers on the forum the full picture.
|
||||||
|
|
||||||
|
Note that you can even add comments to your blocks to document the intention of a block of code.
|
||||||
|
|
||||||
|
These comments are reflected in the generated code.
|
||||||
|
Right click on a block and choose "add comment".
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Comments are a great way to document reminders and give future you a hint on what you were thinking as you wrote this code.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Simplify when a rule doesn't work and gradually rebuild it.
|
||||||
|
|
||||||
|
## Tips and Tricks
|
||||||
|
|
||||||
|
- Build the rule gradually, testing each step of the way.
|
||||||
|
|
||||||
|
- Make heavy use of logging and watch the logs when testing.
|
||||||
|
|
||||||
|
- Create functions rather than repeat functionality.
|
||||||
|
|
||||||
|
- Functions can also be a way to break up big and complicated blocks into easier to manage and understand chunks.
|
||||||
|
|
||||||
|
- Use right-click and duplicate to create copies of blocks that are close to what you want.
|
||||||
|
|
||||||
|
- Look at the code that the blocks will render into to learn more about how the blocks work.
|
||||||
|
Click on the blue file icon in the lower right corner. 
|
||||||
|
You can also use the <kbd>Ctrl-B</kbd> keyboard shortcut to switch between the blocks and the code preview.
|
||||||
|
|
||||||
|
- In a pinch, under the "Run and Process" category there is an "inline script" block (since 3.1).
|
||||||
|
That will let you write your own custom few lines of ECMAScript 5.1 code.
|
||||||
|
This is a great way to deal with missing features in the Blockly blocks or to do something more efficiently than can be done with the blocks.
|
|
@ -0,0 +1,136 @@
|
||||||
|
---
|
||||||
|
layout: documentation
|
||||||
|
title: Rules - Introduction
|
||||||
|
---
|
||||||
|
|
||||||
|
# Introduction to Rules
|
||||||
|
|
||||||
|
Thus far we've connected OH to devices through Things, modeled the devices with Items, discussed persistence and how to build the display to control your home.
|
||||||
|
But all of that amounts to home control, not automation.
|
||||||
|
To create home automation we need to define behaviors.
|
||||||
|
In openHAB behaviors are defined using rules.
|
||||||
|
Almost anything you can think of can be done as long as you have a relevant event to kick it off and access to the data needed to decide what to do.
|
||||||
|
|
||||||
|
{::options toc_levels="2..4"/}
|
||||||
|
|
||||||
|
- TOC
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
## Event Driven
|
||||||
|
|
||||||
|
openHAB is an event driven system.
|
||||||
|
What that means is an event happens and parts of openHAB that watch for that event can react.
|
||||||
|
Persistence will see an Item change event and save that new state to the database.
|
||||||
|
The UIs will watch for that same Item change event and update the UI widgets as necessary.
|
||||||
|
And some rules will *trigger* when that Item change event occurs to create some behavior.
|
||||||
|
|
||||||
|
For example, the behavior we want is to turn on a light (represented by the Item `Light`) when motion is detected (represented by the Item `Motion`).
|
||||||
|
To create this behavior we could create a rule that triggers on the event of the `Motion` Item receiving the command `ON` that sends the command `ON` to the `Light` Item.
|
||||||
|
|
||||||
|
This is a very simple example though.
|
||||||
|
What if you want to keep the light on until 5 minutes after the last motion was detected, but only between the hours of 16:00 and 23:00 on weekdays or when no one is home?
|
||||||
|
Rules can do that too.
|
||||||
|
|
||||||
|
## Parts of a Rule
|
||||||
|
|
||||||
|
A rule consists of three parts.
|
||||||
|
|
||||||
|
| Name | MainUI Section | Purpose |
|
||||||
|
|-------------|----------------|-------------------------------------------------------------------------------------|
|
||||||
|
| `Trigger` | When | List of the events that cause the rule to run. |
|
||||||
|
| `Condition` | But Only If | Conditions which must be true before the rule is allowed to run even when triggered |
|
||||||
|
| `Action` | Then | Actions to take when the rule runs. |
|
||||||
|
|
||||||
|
Any single rule is not required to have all of these (although a rule without an action is not very sensible) and in many cases multiple instances may be required.
|
||||||
|
A rule that should always run when triggered will have 0 conditions.
|
||||||
|
A rule that should respond to several different types of events can have multiple different triggers.
|
||||||
|
A rule that is only ever called by another rule or run manually can have 0 triggers.
|
||||||
|
|
||||||
|
### Triggers
|
||||||
|
|
||||||
|
Rule triggers are always evaluated with an `OR`.
|
||||||
|
If there are two triggers listed, read that as "if x occurs or if y occurs".
|
||||||
|
The following table is a list of the most common events that can be used to trigger a rule.
|
||||||
|
Triggers of different types can be used on the same rule.
|
||||||
|
The following table is not an exhaustive list of potential triggers.
|
||||||
|
|
||||||
|
| Event | Category | Purpose |
|
||||||
|
|---------------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| was updated | Item | triggers the rule when an Item received an update, does not necessarily mean it changed state |
|
||||||
|
| was changed | Item | triggers the rule when an Item changed state after an update |
|
||||||
|
| received command | Item | triggers the rule when an Item is commanded; a command does not by itself imply that the Item received an update or changed |
|
||||||
|
| a member was updated | Group | triggers the rule when any member of the Group is updated |
|
||||||
|
| a member changed | Group | triggers the rule when any member of the Group changes state |
|
||||||
|
| a member received command | Group | triggers the rule when any member of the Group receives a command |
|
||||||
|
| a trigger channel fired | Thing | triggers the rule when a trigger type Channel on a Thing generates an event |
|
||||||
|
| status was updated | Thing | when a Thing's status is updated, does not necessarily mean it changed |
|
||||||
|
| status changed | Thing | when a Thing's status changed (e.g. went OFFLINE) |
|
||||||
|
| on a schedule | Time | uses a cron expression to generate a periodic trigger for the rule (e.g. every five minutes) |
|
||||||
|
| at a fixed time of day | Time | selects a specific time of day to trigger the rule |
|
||||||
|
| the system is being initialized | System | when openHAB is first starting up and reaches a given runlevel |
|
||||||
|
|
||||||
|
In addition to these events which are configured on the rule, a rule can be called from another rule and a rule can be run manually.
|
||||||
|
|
||||||
|
Almost all of the time, an Item or a Time based trigger will be used by most users.
|
||||||
|
System triggers are useful to do some sort of initialization step when openHAB is starting.
|
||||||
|
|
||||||
|
### Conditions
|
||||||
|
|
||||||
|
Conditions are primarily a concept in UI rules though some rules languages support them in files too.
|
||||||
|
These are a list of comparisons that each must evaluate to true for the actions to be run.
|
||||||
|
For example, if a given rule should only run when no one is home, a condition can check the Item that represents the home/away state so it only runs when everyone is away.
|
||||||
|
There are four categories of conditions.
|
||||||
|
|
||||||
|
| Condition | Type | Purpose |
|
||||||
|
|---------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Item Condition | Item states | tests to see if an Item's state meets a comparison (e.g. == ON, <= 65 °F, etc.) |
|
||||||
|
| Time Condition | Time | a selection of the days of the week and a time period when the rule can run |
|
||||||
|
| Ephemeris Condition | Ephemeris | openHAB has a subsystem that tracks types of day (e.g. weekends, weekdays) and local holidays; this condition allows one to specify what types of days it's allowed to run |
|
||||||
|
| Script Condition | Script | code whose last line evaluates to true of false. |
|
||||||
|
|
||||||
|
When a rule is triggered from another rule, there is an option to allow or ignore the called rule's conditions.
|
||||||
|
When run manually, the conditions are ignored.
|
||||||
|
|
||||||
|
### Actions
|
||||||
|
|
||||||
|
If triggers define when the rule runs and conditions define what must be true for the rule to run, the actions define the rule's behaviors.
|
||||||
|
There are four categories of Actions.
|
||||||
|
|
||||||
|
| Action | Purpose |
|
||||||
|
|-----------------|---------------------------------------------------------|
|
||||||
|
| Item Action | to command or update an Item |
|
||||||
|
| Other Rules | to cause another rule to run |
|
||||||
|
| Audio and Voice | to cause TTS or an audio file to play on an audio sink |
|
||||||
|
| Script Action | to execute some code written in a supported rule action |
|
||||||
|
|
||||||
|
### A Note on Rule Languages
|
||||||
|
|
||||||
|
There is a growing list of options of ways for users to create rules and many of them will have two flavors: UI and file based.
|
||||||
|
This tutorial will not discuss file based rules.
|
||||||
|
See the docs for the chosen language for details on how to write rules in files for that language.
|
||||||
|
|
||||||
|
openHAB comes with three languages by default (Rules DSL, ECMAScript 5.1, and Blockly) and has a number of add-ons to add more.
|
||||||
|
For this tutorial we will be using Blockly and the [JavaScript Scripting](/addons/automation/jsscripting/) add-on.
|
||||||
|
But most of the concepts presented can be applied to the other languages too.
|
||||||
|
|
||||||
|
## What's the Difference Between a Rule, Script, and Schedule?
|
||||||
|
|
||||||
|
This is a trick question because there is nothing very significantly different between the three.
|
||||||
|
All are rules.
|
||||||
|
|
||||||
|
| Type | What's Unique | Purpose |
|
||||||
|
|----------|----------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Rule | | This is any unit of automation which can contain zero or more of triggers, actions and, conditions |
|
||||||
|
| Script | rule consisting of a single Script Action and has the tag "Script", there are no triggers so they must be run manually or called from another rule | A great place to store examples when you figure something out and want to remember how you did it later. Can include reusable code called from other rules (though there are often better choices). Very useful for adhoc and pseudo-unit testing of your rules. Despite being a rule, Scripts do not appear in the Rules page of MainUI. |
|
||||||
|
| Schedule | any rule with a time based trigger and the tag "Schedule" | This page is a good place to see when your rules are scheduled to run. However, note that it only shows statically timed rules. |
|
||||||
|
|
||||||
|
Important note on the term "Script".
|
||||||
|
Unfortunately this term is overloaded in openHAB and has multiple meanings based on the context.
|
||||||
|
|
||||||
|
- a UI rule consisting of a single Script Action with the tag "Script"
|
||||||
|
|
||||||
|
- a special piece of Rules DSL code placed in the `$OH_CONF/scripts` folder that can be called from other rules using the `callScript` action (note that `callScript` cannot call UI Scripts)
|
||||||
|
|
||||||
|
- a file consisting of code that is read and interpreted by a script engine, such as Bash or Python; usually called from a command line in OH using the `executeCommandLine` action or the `Exec` binding; these are not related to Rules DSL scripts and should not be placed in the `$OH_CONF/scripts` folder
|
||||||
|
|
||||||
|
- in text based rules, a script is a file that creates one or more rules; as opposed to a module which is a library that might be used by a script or other modules.
|
|
@ -0,0 +1,150 @@
|
||||||
|
---
|
||||||
|
layout: documentation
|
||||||
|
title: Tips and Tricks
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tips and Tricks
|
||||||
|
|
||||||
|
This section is dedicated to generic tips and tricks to make your use and configuration of openHAB easier.
|
||||||
|
They are discussed in no particular order.
|
||||||
|
|
||||||
|
{::options toc_levels="2..4"/}
|
||||||
|
|
||||||
|
- TOC
|
||||||
|
{:toc}
|
||||||
|
|
||||||
|
## Backups
|
||||||
|
|
||||||
|
Always create and maintain a recent backup of your openHAB config.
|
||||||
|
When installed on Linux there will be an `openhab-cli` command line tool that supports backup and restore.
|
||||||
|
For other systems, under `$OH_HOME/runtime/bin` you will find `backup.sh` and `restore.sh` (or `.bat`).
|
||||||
|
Note that these will back up both text based configs and anything done through the UI.
|
||||||
|
However, it only does backup and restore for openHAB, not external services.
|
||||||
|
|
||||||
|
When you maintain backups, you have the freedom to explore and experiment without risking breaking everything.
|
||||||
|
|
||||||
|
## Don't Wait, Try it Out!
|
||||||
|
|
||||||
|
If you know what you want to try but you just don't know what will happen, don't post a new thread on the forum or spend hours reading through the docs and the forum.
|
||||||
|
Instead, try it out and see what happens.
|
||||||
|
If you have backups, there is little you could do that will actually break things.
|
||||||
|
|
||||||
|
## Developer Sidebar
|
||||||
|
|
||||||
|
MainUI has a tool called the Developer Sidebar.
|
||||||
|
Bring up the sidebar through Developer Tools or the key combination <kbd>Alt</kbd><kbd>Shift</kbd><kbd>D</kbd>.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Note that the sidebar will not come up if your display is to narrow.
|
||||||
|
It will not work on a phone nor on most tablet displays.
|
||||||
|
|
||||||
|
There are four tabs on the sidebar.
|
||||||
|
|
||||||
|
### Pinned Objects
|
||||||
|
|
||||||
|
Let's say you are working on your lighting automations which involves Things, Items, and Rules.
|
||||||
|
Instead of keeping a dozen browser tabs open and flipping between them you can pin these objects to the sidebar.
|
||||||
|
In the search box at the top, enter a search term which can be part of an object's name, ID, label, etc.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once you find an object that you want to pin, click the push-pin icon to keep it in the list.
|
||||||
|
Once you've pinned all the objects you want, click the `x` icon in the search bar.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once pinned each entry will show some relevant controls and status.
|
||||||
|
All object will have a pencil icon which will take you straight to that object's page where it can be edited.
|
||||||
|
Clicking the red push-pin icon will unpin the object from the sidebar.
|
||||||
|
|
||||||
|
To the right of Items will be their current state and often clicking on that state will bring up that Item's default widget where it can be controlled.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Things will show the current status of the Thing to the right and additionally have a pause icon that can be used to disable the Thing.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Rules show the rule's current status to the right.
|
||||||
|
This is one way to monitor when a rule is running as the current status will change to show the rule is running.
|
||||||
|
However, if the rule runs very fast it might flash too quickly to see, so there are other ways to monitor that (see below).
|
||||||
|
|
||||||
|
In addition to the enable/disable control, rules also have a play icon where the rule can be manually triggered.
|
||||||
|
|
||||||
|
Once everything you are currently working on is pinned in one place, developing capabilities becomes much easier because everything you need is right there and you can get to everything you need to with one click.
|
||||||
|
Objects will remain pinned until the sidebar is closed, even if you click to other tabs in the sidebar or navigate to another page in MainUI.
|
||||||
|
|
||||||
|
### Event Monitor
|
||||||
|
|
||||||
|
The next tab is the Event Monitor.
|
||||||
|
This tab provides a filterable raw stream of all the events happening in openHAB.
|
||||||
|
Believe it or not, what you see in events.log is a greatly reduced set of events.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Item events (update, changed, received command), Thing events (e.g. change in status), and rule events will appear in this stream.
|
||||||
|
But on a typical system a lot of events will be happening all the time.
|
||||||
|
Often one will want to filter.
|
||||||
|
Clicking on the filter icon lets you enter a comma separated list of events that should be streamed.
|
||||||
|
The list is `OR` meaning is any one matches the event will be shown.
|
||||||
|
For example, entering `*Hum*` would only show those events that include "Hum" in the names.
|
||||||
|
|
||||||
|
This tab is a great tool to use when debugging a problem and developing rules and for many is more convenient than monitoring the logs in another tab.
|
||||||
|
|
||||||
|
### Code Tools
|
||||||
|
|
||||||
|
The next tab presents a couple of tools to aid in coding: expression tester and scratchpad.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### Expression Tester
|
||||||
|
|
||||||
|
This tool is useful when developing custom widgets.
|
||||||
|
Once can enter and see the results from expressions used to make widgets more dynamic and it is a great place to gradually build up a complicated expression, testing it each step of the way.
|
||||||
|
|
||||||
|
#### Scratchpad
|
||||||
|
|
||||||
|
Clicking on this will open up a Script (rule with a single Script Action, no triggers or conditions) named "-Scratchpad-".
|
||||||
|
This is intended to be a free form place to put in and manually execute some arbitrary and temporary rules code to test out how to do something or cause a complex set of behaviors to run.
|
||||||
|
For this reason it's great for testing and debug.
|
||||||
|
|
||||||
|
### Create Shortcuts
|
||||||
|
|
||||||
|
Finally, when developing a new capability a number of new objects will need to be created, such as adding a new Thing, creating some Items linked to that Thing, a rule or two and a custom widget or two.
|
||||||
|
This fourth tab has quick links to create any sort of Object that can be created in OH saving you from needing to click through the Settings menu multiple times to create different but related objects.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Save Examples
|
||||||
|
|
||||||
|
When you figure out how to do something tricky in rules that you may want to use again, create a Script with an example.
|
||||||
|
Over time you will build up a catalog of references so when you need to remeber how to create a timer (for example) you can bring up that Script and see the code.
|
||||||
|
|
||||||
|
## Where's it all Being Saved?
|
||||||
|
|
||||||
|
Everything presented in this tutorial is "managed" and saved to a text based JSON formatted database in `$OH_USERDATA/jsondb`.
|
||||||
|
This folder contains a number of files, one for each type of entity within openHAB.
|
||||||
|
These files are automatically backed up on changes.
|
||||||
|
If you ever want to see the raw config data, these are the files to look in.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
openHAB, like many systems has a robust REST API through which external software can interact with openHAB (e.g. push an update to the state of an Item).
|
||||||
|
An interactive and detailed documentation for this API can be found under Developer Tools -> API Explorer.
|
||||||
|
This is another place where one can see the raw config data for objects in openHAB (e.g. an Item's config).
|
||||||
|
|
||||||
|
## Asking for Help
|
||||||
|
|
||||||
|
The forum is a great place to ask for help when you get stuck.
|
||||||
|
Most posts are answered within a couple of hours.
|
||||||
|
To maximize the likelihood of getting good help, please consider the following:
|
||||||
|
|
||||||
|
- do not withold information (except security related stuff like passwords); if it doesn't work you likely don't know what is relevant or not
|
||||||
|
- provide lots of details
|
||||||
|
- post code, logs, and other configs using code fences to preserve formatting
|
||||||
|
- avoid posting screen shots of configs or logs where possible; post the text from the "Code" tab
|
||||||
|
- answer all questions when asked for more details
|
||||||
|
- be polite.
|
||||||
|
|
||||||
|
A detailed tutorial on how to ask for help can be found at [Help Us Help You](https://community.openhab.org/t/how-to-ask-a-good-question-help-us-help-you/58396).
|
|
@ -461,7 +461,7 @@ Adds a time-series chart object for the display of logged data.
|
||||||
- `legend` is used to show or to hide the chart legend.
|
- `legend` is used to show or to hide the chart legend.
|
||||||
Valid values are `true` (always show the legend) and `false` (never show the legend).
|
Valid values are `true` (always show the legend) and `false` (never show the legend).
|
||||||
If this parameter is not set, the legend is hidden if there is only one chart series.
|
If this parameter is not set, the legend is hidden if there is only one chart series.
|
||||||
|
|
||||||
- `forceAsItem` is used to show the value of a `Group` instead of showing a graph for each member (which is the default).
|
- `forceAsItem` is used to show the value of a `Group` instead of showing a graph for each member (which is the default).
|
||||||
|
|
||||||
Visit [Charts](https://github.com/openhab/openhab/wiki/Charts) in the Wiki for examples.
|
Visit [Charts](https://github.com/openhab/openhab/wiki/Charts) in the Wiki for examples.
|
||||||
|
|