Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 68 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This library is included by default in the openHAB [JavaScript Scripting add-on]
- [Custom Installation](#custom-installation)
- [Compatibility](#compatibility)
- [Configuration](#configuration)
- [UI Based Rules](#ui-based-rules)
- [UI-Based Rules](#ui-based-rules)
- [Adding Triggers](#adding-triggers)
- [Adding Actions](#adding-actions)
- [Event Object](#event-object)
Expand All @@ -34,7 +34,7 @@ This library is included by default in the openHAB [JavaScript Scripting add-on]
- [Log](#log)
- [Utils](#utils)
- [Environment](#environment)
- [File Based Rules](#file-based-rules)
- [File-Based Rules](#file-based-rules)
- [JSRule](#jsrule)
- [Rule Builder](#rule-builder)
- [Advanced Scripting](#advanced-scripting)
Expand All @@ -45,7 +45,7 @@ This library is included by default in the openHAB [JavaScript Scripting add-on]

### Default Installation

Install the openHAB [JavaScript Scripting add-on](https://www.openhab.org/addons/automation/jsscripting/), a version of this library will be automatically installed and available to ECMAScript 2024+ rules created using [File Based Rules](#file-based-rules) or [UI Based Rules](#ui-based-rules).
Install the openHAB [JavaScript Scripting add-on](https://www.openhab.org/addons/automation/jsscripting/), a version of this library will be automatically installed and available to ECMAScript 2024+ rules created using [File-Based Rules](#file-based-rules) or [UI-Based Rules](#ui-based-rules).

openHAB also provides the [JavaScript Scripting (Nashorn) add-on](https://www.openhab.org/addons/automation/jsscriptingnashorn/), which is based on the older Nashorn JavaScript engine. This is referred to as `ECMA - 262 Edition 5.1` or `application/javascript;version=ECMAScript-5.1` in the Main UI.
_This library is not compatible with this older runtime._
Expand Down Expand Up @@ -88,51 +88,94 @@ This will be used instead of the binding provided version.

<!-- Copy everything from here to update the JS Scripting documentation. -->

### UI Based Rules
### Rules in Main UI

The quickest way to add rules is through the openHAB Web UI.
The quickest way to use JavaScript Scripting is to create a rule in Main UI and add a "Script Action", see [Adding Actions](#adding-actions) below.
If you only want to execute code and don't need triggers, you can instead create a script in Main UI.

Advanced users, or users migrating scripts from existing systems may want to use [File Based Rules](#file-based-rules) for managing rules using files in the user configuration directory.
Advanced users, or users migrating scripts from Rules DSL may want to use [File-Based Rules](#file-based-rules) for managing rules using files in the user configuration directory.

### Adding Triggers
#### Adding Triggers

Using the openHAB UI, first create a new rule and set a trigger condition.
Using Main UI, first create a new rule and set a trigger condition.

![openHAB Rule Configuration](doc/rule-config.png)

### Adding Actions
#### Adding Actions

Select "Add Action" and then select "Run Script" with "ECMAScript 262 Edition 11".
It’s important this is "Edition 11" or higher, earlier versions will not work.
Select "Add Action" and then select "Inline Script" with "ECMAScript 262 Edition 11".
This will add a so-called "Script Action" to the rule.
It's important this is "Edition 11" or higher, earlier versions will not work.
This will bring up an empty script editor where you can enter your JavaScript.

![openHAB Rule Engines](doc/rule-engines.png)

You can now write rules using standard ES6 JavaScript along with the included openHAB [standard library](#standard-library).
You can now write rules using standard ES6 JavaScript along with the included openHAB [Standard Library](#standard-library).

![openHAB Rule Script](doc/rule-script.png)

For example, turning a light on:

```javascript
items.KitchenLight.sendCommand("ON");
items.KitchenLight.sendCommand('ON');
console.log("Kitchen Light State", items.KitchenLight.state);
```

Sending a notification

```javascript
actions.NotificationAction.sendNotification("romeo@montague.org", "Balcony door is open");
actions.NotificationAction.sendNotification('romeo@montague.org', 'Balcony door is open');
```

Querying the status of a thing

```javascript
var thingStatusInfo = actions.Things.getThingStatusInfo("zwave:serial_zstick:512");
console.log("Thing status",thingStatusInfo.getStatus());
var thingStatusInfo = actions.Things.getThingStatusInfo('zwave:serial_zstick:512');
console.log('Thing status', thingStatusInfo.getStatus());
```

See [openhab-js](https://openhab.github.io/openhab-js) for a complete list of functionality.
See [Standard Library](#standard-library) for a complete list of functionality.

#### Adding Conditions

If you want the rule to only execute if one or many predefined conditions, e.g. some Item has a given state, are met, select "Add Condition".
Next, select "Script Condition" and, again, "ECMAScript 262 Edition 11".

You can now write conditions for your rule using standard ES6 JavaScript along with the included openHAB [Standard Library](#standard-library).

When writing script conditions, the script has to provide a boolean value (true or false) whether the condition is met.
This can be done in two ways:

- Explicitly using `return`: If the script condition wrapper is enabled (see below), the `return` keyword has to be used to return a boolean value (`true` or `false`). Example:

```javascript
if (items.KitchenWindow.state === 'OPEN') {
return items.OutsideTemperature.quantityState.lessThan('12 °C')
}
return false
```

When using Blockly, there is a `return` block available from the "Run & Process" category.

- Implicitly: If the script condition wrapper is not enabled or not available (see below), the last executed statement needs to evaluate to a boolean value. Example:

```javascript
if (items.KitchenWindow.state === 'OPEN') {
items.OutsideTemperature.quantityState.lessThan('12 °C')
}
false
```

The preferred way is explicitly, as it is way clearer what is returned, however `return` is only supported if the script condition wrapper is enabled.
The script condition wrapper is available since openHAB 5.1.0, previous versions only support implicit return.
It is advised to enable the wrapper and use explicit returns for all new script conditions, and step-by-step migrate existing conditions.

The wrapper can be enabled (and disabled as well) per script condition using the `use wrapper` directive:

- Adding `'use wrapper'` or `'use wrapper=true'` (semicolons can be added) as the **first or second line** enables the wrapper.
- Adding `'use wrapper=false'` instead disables the wrapper.

New users of openHAB, users that haven't used script conditions with JavaScript Scripting before, and users that have migrated (through the directive) all conditions to wrapper use can simply turn on the "Wrap Script Conditions in Self-Executing Function" option in the add-on settings.

### Event Object

Expand Down Expand Up @@ -178,7 +221,7 @@ In case the event object does not provide type-conversed properties for your cho

See [openhab-js : EventObject](https://openhab.github.io/openhab-js/global.html#EventObject) for full API documentation.

When disabling the option _Convert Event from Java to JavaScript type in UI-based scripts_, you will receive a raw Java event object instead of the `event` object described above.
When disabling the option _Convert Event from Java to JavaScript type in UI-based scripts_, you will receive a raw Java event object instead of the `event` object described above in UI-based scripts.
See the expandable section below for more details.

<details>
Expand All @@ -196,7 +239,7 @@ This table gives an overview over the raw Java `event` object of Script Actions
| `event` | string | channel based triggeres | Event data published by the triggering channel. | `receivedEvent` |
| `payload` | JSON formatted string | all | Any additional information provided by the trigger not already exposed. "{}" there is none. | N/A |

Note that in UI based rules `event`, and therefore everything carried by `event` are Java types (not JavaScript). Care must be taken when comparing these with JavaScript types:
Note that in UI-based rules `event`, and therefore everything carried by `event` are Java types (not JavaScript). Care must be taken when comparing these with JavaScript types:

```javascript
var { ON } = require("@runtime")
Expand Down Expand Up @@ -334,7 +377,7 @@ This also works for timers created with [`actions.ScriptExecution.createTimer`](

### Paths

For [file based rules](#file-based-rules), scripts will be loaded from `automation/js` in the user configuration directory.
For [file-based rules](#file-based-rules), scripts will be loaded from `automation/js` in the user configuration directory.

NPM libraries will be loaded from `automation/js/node_modules` in the user configuration directory.

Expand Down Expand Up @@ -1341,23 +1384,23 @@ A word of caution: The `environment` namespace is considered an advanced API and

See [openhab-js : environment](https://openhab.github.io/openhab-js/environment.html) for full API documentation.

## File Based Rules
## File-Based Rules

The JavaScript Scripting automation add-on will load `.js` scripts from `automation/js` in the user configuration directory.
The system will automatically reload a script when changes are detected to the script file or its dependencies.
Local variable state is not persisted among reloads, see using the [cache](#cache) for a convenient way to persist objects.

File based rules normally share the context with the script file that created them.
File-based rules normally share the context with the script file that created them.
This allows sharing functions, classes and variables that are defined outside the rule's execute function across multiple rules from the same script file.
However, this comes with a caveat: Sharing the context across multiple rules imposes the limitation that only a single rule can execute at a time.
However, this comes with a caveat: Sharing the context across multiple rules imposes the limitation that only a single rule from the same script file can execute at a time.
When writing rules that query persistence or wait for other I/O, it can make sense to disable this behaviour by setting the `dedicatedContext` option to `true` for [JSRule](#jsrule).

When setting the `dedicatedContext` option to `true`, the rule's execute function will be executed in a separate context.
When the `dedicatedContext` option is set to `true`, the rule's execute function will be executed in a separate context.
This means that the rule's execute function can **not** access functions, classes or variables from the context of the script file that created the rule.
The benefit of using a dedicated context is that the rule's execute function has its own, dedicated context and can therefore execute at any time, without needing to wait for other rules.
Please note that in most cases, the dedicated context won't be needed, as rule execution is usually rapid and the wait time for the rule to execute is negligible.

File based rules can be created in two different ways: using [JSRule](#jsrule) or the [Rule Builder](#rule-builder).
File-based rules can be created in two different ways: using [JSRule](#jsrule) or the [Rule Builder](#rule-builder).

When a rule is triggered, the script is provided information about the event that triggered the rule in the `event` object.
Please refer to [Event Object](#event-object) for documentation.
Expand Down
6 changes: 3 additions & 3 deletions src/rules/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
* @description
* Configuration object for {@link rules.JSRule}.
* <p>A note about the `dedicatedContext` option:
* When this option is enabled, the `execute` callback runs in a dedicated, separate context.
* When this option is enabled, the `execute` callback runs in a separate dedicated context.
* Therefore, the `execute` callback can **not** access any variables, functions, classes or other objects defined outside the callback function.
* The benefit of this approach is that the rule does not share the context with any other rule and therefore can run at any time without waiting for other rule executions to complete.
* The benefit of this approach is that the rule does not share the context with any other rule from the same script file and therefore can run at any time without waiting for other rule executions to complete.
*/

const SCRIPT_TYPE = 'application/javascript';
Expand Down Expand Up @@ -281,7 +281,7 @@ function _createRule (ruleUID, ruleConfig) {
/**
* Creates a {@link https://www.openhab.org/javadoc/latest/org/openhab/core/automation/module/script/rulesupport/shared/simple/simplerule org.openhab.core.automation.module.script.rulesupport.shared.simple.SimpleRule}.
*
* <p>Simple rules allow creating rules that execute JavaScript code sharing the context of the script that created the rule.
* <p>Simple rules allow creating rules that execute JavaScript code sharing the context with the script that created the rule.
* This allows defining functions, classes and variables outside the rule's callback function and referencing them from within the rule.
* This approach, however, comes with the limitation that only a single SimpleRule created from a script file can execute at a time.
*
Expand Down
Loading