[jsscripting] Update openhab-js to 2.0.3 (#13385)

* [jsscripting] Update openhab-js to 2.0.3
* [jsscripting] Update README

Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
pull/13325/head^2
Florian Hotze 2022-09-13 21:57:09 +02:00 committed by GitHub
parent a963c79e8b
commit 39d221d3a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 246 additions and 211 deletions

View File

@ -6,10 +6,10 @@ Also included is [openhab-js](https://github.com/openhab/openhab-js/), a fairly
to common openHAB functionality within rules including items, things, actions, logging and more.
- [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)
- [UI Event Object](#ui-event-object)
- [Scripting Basics](#scripting-basics)
- [Require](#require)
- [Console](#console)
@ -26,37 +26,39 @@ to common openHAB functionality within rules including items, things, actions, l
- [File Based Rules](#file-based-rules)
- [JSRule](#jsrule)
- [Rule Builder](#rule-builder)
- [Event Object](#event-object-1)
- [Event Object](#event-object)
- [Initialization hook: scriptLoaded](#initialization-hook-scriptloaded)
- [Deinitialization hook: scriptUnloaded](#deinitialization-hook-scriptunloaded)
- [Advanced Scripting](#advanced-scripting)
- [@runtime](#runtime)
## Configuration
This add-on includes by default the [openhab-js](https://github.com/openhab/openhab-js/) NPM library and exports it's namespaces onto the global namespace.
This add-on includes by default the [openhab-js](https://github.com/openhab/openhab-js/) NPM library and exports its namespaces onto the global namespace.
This allows the use of `items`, `actions`, `cache` and other objects without the need to explicitly import using `require()`.
This functionality can be disabled for users who prefer to manage their own imports via the add-on configuration options.
This functionality can be disabled for users who prefer to manage their own imports via the add-on configuration options.
![openHAB Rule Configuration](doc/settings.png)
## UI Based Rules
<!-- Paste the copied docs from openhab-js under this comment. -->
### UI Based Rules
The quickest way to add rules is through the openHAB Web 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.
### Adding Triggers
Using the openHAB UI, first create a new rule and set a trigger condition.
![openHAB Rule Configuration](doc/rule-config.png)
### Adding Actions
Select "Add Action" and then select "Run Script" with "ECMAScript 262 Edition 11".
Its important this is "Edition 11" or higher, earlier versions will not work.
This will bring up a empty script editor where you can enter your JavaScript.
This will bring up an empty script editor where you can enter your JavaScript.
![openHAB Rule Engines](doc/rule-engines.png)
@ -65,17 +67,20 @@ You can now write rules using standard ES6 JavaScript along with the included op
![openHAB Rule Script](doc/rule-script.png)
For example, turning a light on:
```javascript
items.getItem("KitchenLight").sendCommand("ON");
console.log("Kitchen Light State", items.getItem("KitchenLight").state);
```
Sending a notification
```javascript
actions.NotificationAction.sendNotification("romeo@montague.org", "Balcony door is open");
```
Querying the status of a thing
```javascript
const thingStatusInfo = actions.Things.getThingStatusInfo("zwave:serial_zstick:512");
console.log("Thing status",thingStatusInfo.getStatus());
@ -83,16 +88,16 @@ console.log("Thing status",thingStatusInfo.getStatus());
See [openhab-js](https://openhab.github.io/openhab-js) for a complete list of functionality.
### Event Object
### UI Event Object
**NOTE**: Note that `event` object is different in UI based rules and file based rules! This section is only valid for UI based rules. If you use file based rules, refer to [file based rules event object documentation](#event-object-1).
**NOTE**: Note that `event` object is different in UI based rules and file based rules! This section is only valid for UI based rules. If you use file based rules, refer to [file based rules event object documentation](#event-object).
When you use "Item event" as trigger (i.e. "[item] received a command", "[item] was updated", "[item] changed"), there is additional context available for the action in a variable called `event`.
This tables gives an overview over the `event` object for most common trigger types:
| Property Name | Type | Trigger Types | Description | Rules DSL Equivalent |
| -------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------- |
|----------------|----------------------------------------------------------------------------------------------------------------------|----------------------------------------|---------------------------------------------------------------------------------------------------------------|------------------------|
| `itemState` | sub-class of [org.openhab.core.types.State](https://www.openhab.org/javadoc/latest/org/openhab/core/types/state) | `[item] changed`, `[item] was updated` | State that triggered event | `triggeringItem.state` |
| `oldItemState` | sub-class of [org.openhab.core.types.State](https://www.openhab.org/javadoc/latest/org/openhab/core/types/state) | `[item] changed` | Previous state of Item or Group that triggered event | `previousState` |
| `itemCommand` | sub-class of [org.openhab.core.types.Command](https://www.openhab.org/javadoc/latest/org/openhab/core/types/command) | `[item] received a command` | Command that triggered event | `receivedCommand` |
@ -129,7 +134,7 @@ The library search will look in the path `automation/js/node_modules` in the use
### Console
The JS Scripting binding supports the standard `console` object for logging.
Script debug logging is enabled by default at the `INFO` level, but can be configured using the [console logging]({{base}}/administration/logging.html) commands.
Script debug logging is enabled by default at the `INFO` level, but can be configured using the console logging commands.
```text
log:set DEBUG org.openhab.automation.script
@ -142,6 +147,7 @@ console.loggerName = "custom"
```
Supported logging functions include:
- `console.log(obj1 [, obj2, ..., objN])`
- `console.info(obj1 [, obj2, ..., objN])`
- `console.warn(obj1 [, obj2, ..., objN])`
@ -152,22 +158,21 @@ Supported logging functions include:
Where `obj1 ... objN` is a list of JavaScript objects to output.
The string representations of each of these objects are appended together in the order listed and output.
See https://developer.mozilla.org/en-US/docs/Web/API/console for more information about console logging.
See <https://developer.mozilla.org/en-US/docs/Web/API/console> for more information about console logging.
### Timers
#### SetTimeout
The global `setTimeout()` method sets a timer which executes a function or specified piece of code once the timer expires.
```javascript
var timeoutID = setTimeout(function[, delay, arg1, arg2, ...]);
var timeoutID = setTimeout(function[, delay]);
var ohTimer = setTimeout(callbackFunction, delay);
```
The global `clearTimeout()` method cancels a timeout previously established by calling `setTimeout()`.
See https://developer.mozilla.org/en-US/docs/Web/API/setTimeout for more information about `setTimeout()`.
The openHAB implementation of `setTimeout()` differs from the [HTML DOM API's `setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout).
openHAB does not return the integer timeoutID as standard JS does, instead it returns an instance of [openHAB Timer](#openhab-timer).
#### SetInterval
@ -175,30 +180,31 @@ openHAB does not return the integer timeoutID as standard JS does, instead it re
The setInterval() method repeatedly calls a function or executes a code snippet, with a fixed time delay between each call.
```javascript
var intervalID = setInterval(func, [delay, arg1, arg2, ...]);
var intervalID = setInterval(function[, delay]);
var ohIntervalTimer = setInterval(callbackFunction, delay);
```
The global `clearInterval()` method cancels a timed, repeating action which was previously established by a call to `setInterval()`.
NOTE: Timers will not be canceled if a script is deleted or modified, it is up to the user to manage timers. See using the [cache](#cache) namespace as well as [ScriptLoaded](#scriptloaded) and [ScriptUnLoaded](#scriptunloaded) for a convenient way of managing persisted objects, such as timers between reloads or deletions of scripts.
See https://developer.mozilla.org/en-US/docs/Web/API/setInterval for more information about `setInterval()`.
NOTE: Timers will not be canceled if a script is deleted or modified, it is up to the user to manage timers.
See using the [cache](#cache) namespace as well as [ScriptLoaded](#initialization-hook-scriptloaded) and [ScriptUnLoaded](#deinitialization-hook-scriptunloaded) for a convenient way of managing persisted objects, such as timers between reloads or deletions of scripts.
The openHAB implementation of `setInterval()` differs from the [HTML DOM API's `setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval).
openHAB does not return the integer timeoutID as standard JS does, instead it returns an instance of [openHAB Timer](#openhab-timer).
#### openHAB Timer
A native openHAB Timer instance has the following methods:
* `cancel()`: Cancels the timer. ⇒ `boolean`: true, if cancellation was successful
* `getExecutionTime()`: The scheduled execution time or null if timer was cancelled. ⇒ `time.ZonedDateTime` or `null`
* `isActive()`: Whether the scheduled execution is yet to happen. ⇒ `boolean`
* `isCancelled()`: Whether the timer has been cancelled. ⇒ `boolean`
* `isRunning()`: Whether the scheduled code is currently executed. ⇒ `boolean`
* `hasTerminated()`: Whether the scheduled execution has already terminated. ⇒ `boolean`
* `reschedule(time.ZonedDateTime)`: Reschedules a timer to a new starting time. This can also be called after a timer has terminated, which will result in another execution of the same code. ⇒ `boolean`: true, if rescheduling was successful
- `cancel()`: Cancels the timer. ⇒ `boolean`: true, if cancellation was successful
- `getExecutionTime()`: The scheduled execution time or null if timer was cancelled. ⇒ `time.ZonedDateTime` or `null`
- `isActive()`: Whether the scheduled execution is yet to happen. ⇒ `boolean`
- `isCancelled()`: Whether the timer has been cancelled. ⇒ `boolean`
- `isRunning()`: Whether the scheduled code is currently executed. ⇒ `boolean`
- `hasTerminated()`: Whether the scheduled execution has already terminated. ⇒ `boolean`
- `reschedule(time.ZonedDateTime)`: Reschedules a timer to a new starting time. This can also be called after a timer has terminated, which will result in another execution of the same code. ⇒ `boolean`: true, if rescheduling was successful
Examples:
```javascript
var timer = setTimeout(() => { console.log('Timer expired.'); }, 10000); // Would log 'Timer expired.' in 10s.
if (timer.isActive()) console.log('Timer is waiting to execute.');
@ -225,14 +231,14 @@ The items namespace allows interactions with openHAB items.
See [openhab-js : items](https://openhab.github.io/openhab-js/items.html) for full API documentation.
* items : <code>object</code>
* .getItem(name, nullIfMissing) ⇒ <code>Item</code>
* .getItems() ⇒ <code>Array.&lt;Item&gt;</code>
* .getItemsByTag(...tagNames) ⇒ <code>Array.&lt;Item&gt;</code>
* .addItem([itemConfig](#itemconfig))
* .removeItem(itemOrItemName) ⇒ <code>Boolean</code>
* .replaceItem([itemConfig](#itemconfig))
* .safeItemName(s) ⇒ <code>String</code>
- items : <code>object</code>
- .getItem(name, nullIfMissing) ⇒ <code>Item</code>
- .getItems() ⇒ <code>Array.&lt;Item&gt;</code>
- .getItemsByTag(...tagNames) ⇒ <code>Array.&lt;Item&gt;</code>
- .addItem([itemConfig](#itemconfig))
- .removeItem(itemOrItemName) ⇒ <code>Boolean</code>
- .replaceItem([itemConfig](#itemconfig))
- .safeItemName(s) ⇒ <code>String</code>
```javascript
const item = items.getItem("KitchenLight");
@ -243,29 +249,29 @@ console.log("Kitchen Light State", item.state);
Calling `getItem(...)` returns an `Item` object with the following properties:
* Item : <code>object</code>
* .type ⇒ <code>String</code>
* .name ⇒ <code>String</code>
* .label ⇒ <code>String</code>
* .history ⇒ <code>ItemHistory</code>
* .state ⇒ <code>String</code>
* .rawState ⇒ <code>HostState</code>
* .members ⇒ <code>Array.&lt;Item&gt;</code>
* .descendents ⇒ <code>Array.&lt;Item&gt;</code>
* .isUninitialized ⇒ <code>Boolean</code>
* .groupNames ⇒ <code>Array.&lt;String&gt;</code>
* .tags ⇒ <code>Array.&lt;String&gt;</code>
* .getMetadataValue(namespace) ⇒ <code>String</code>
* .updateMetadataValue(namespace, value) ⇒ <code>String</code>
* .upsertMetadataValue(namespace, value) ⇒ <code>Boolean</code>
* .updateMetadataValues(namespaceToValues)
* .sendCommand(value)
* .sendCommandIfDifferent(value) ⇒ <code>Boolean</code>
* .postUpdate(value)
* .addGroups(...groupNamesOrItems)
* .removeGroups(...groupNamesOrItems)
* .addTags(...tagNames)
* .removeTags(...tagNames)
- Item : <code>object</code>
- .type ⇒ <code>String</code>
- .name ⇒ <code>String</code>
- .label ⇒ <code>String</code>
- .history ⇒ [`ItemHistory`](#itemhistory)
- .state ⇒ <code>String</code>
- .rawState ⇒ <code>HostState</code>
- .members ⇒ <code>Array.&lt;Item&gt;</code>
- .descendents ⇒ <code>Array.&lt;Item&gt;</code>
- .isUninitialized ⇒ <code>Boolean</code>
- .groupNames ⇒ <code>Array.&lt;String&gt;</code>
- .tags ⇒ <code>Array.&lt;String&gt;</code>
- .getMetadataValue(namespace) ⇒ <code>String</code>
- .updateMetadataValue(namespace, value) ⇒ <code>String</code>
- .upsertMetadataValue(namespace, value) ⇒ <code>Boolean</code>
- .updateMetadataValues(namespaceToValues)
- .sendCommand(value)
- .sendCommandIfDifferent(value) ⇒ <code>Boolean</code>
- .postUpdate(value)
- .addGroups(...groupNamesOrItems)
- .removeGroups(...groupNamesOrItems)
- .addTags(...tagNames)
- .removeTags(...tagNames)
```javascript
const item = items.getItem("KitchenLight");
@ -278,24 +284,26 @@ console.log("KitchenLight state", item.state)
```
#### `itemConfig`
Calling `addItem(itemConfig)` or `replaceItem(itemConfig)` requires the `itemConfig` object with the following properties:
* itemConfig : <code>object</code>
* .type ⇒ <code>String</code>
* .name ⇒ <code>String</code>
* .label ⇒ <code>String</code>
* .category (icon) ⇒ <code>String</code>
* .groups ⇒ <code>Array.&lt;String&gt;</code>
* .tags ⇒ <code>Array.&lt;String&gt;</code>
* .channels ⇒ <code>String|Object { channeluid: { config } }</code>
* .metadata ⇒ <code>Object { namespace: value }|Object { namespace: { value: value , config: { config } } }</code>
* .giBaseType ⇒ <code>String</code>
* .groupFunction ⇒ <code>String</code>
- itemConfig : <code>object</code>
- .type ⇒ <code>String</code>
- .name ⇒ <code>String</code>
- .label ⇒ <code>String</code>
- .category (icon) ⇒ <code>String</code>
- .groups ⇒ <code>Array.&lt;String&gt;</code>
- .tags ⇒ <code>Array.&lt;String&gt;</code>
- .channels ⇒ <code>String|Object { channeluid: { config } }</code>
- .metadata ⇒ <code>Object { namespace: value }|Object { namespace: { value: value , config: { config } } }</code>
- .giBaseType ⇒ <code>String</code>
- .groupFunction ⇒ <code>String</code>
Note: `.type` and `.name` are required.
Basic UI and the mobile apps need `metadata.stateDescription.config.pattern` to render the state of an Item.
Example:
```javascript
// more advanced example
items.replaceItem({
@ -335,63 +343,76 @@ items.replaceItem({
});
```
See [openhab-js : ItemConfig](https://openhab.github.io/openhab-js/items.html#.ItemConfig) for full API documentation.
See [openhab-js : ItemConfig](https://openhab.github.io/openhab-js/global.html#ItemConfig) for full API documentation.
#### `item.history`
Calling `item.history` returns a ItemHistory object with the following functions:
#### `ItemHistory`
* ItemHistory : <code>object</code>
* .averageSince(timestamp, serviceId) ⇒ <code>Number</code>
* .changedSince(timestamp, serviceId) ⇒ <code>Number</code>
* .deltaSince(timestamp, serviceId) ⇒ <code>Number</code>
* .deviationSince(timestamp, serviceId) ⇒ <code>Number</code>
* .evolutionRate(timestamp, serviceId) ⇒ <code>Number</code>
* .historicState(timestamp, serviceId) ⇒ <code>state</code>
* .lastUpdate(serviceId) ⇒ <code>Date</code>
* .latestState(serviceId) ⇒ <code>state</code>
* .maximumSince(timestamp,serviceId) ⇒ <code>state</code>
* .minimumSince(timestamp,serviceId) ⇒ <code>state</code>
* .persist(serviceId)
* .previousState(skipEqual,serviceId) ⇒ <code>state</code>
* .sumSince(timestamp, serviceId) ⇒ <code>Number</code>
* .updatedSince(timestamp, serviceId) ⇒ <code>Boolean</code>
* .varianceSince(timestamp,serviceId) ⇒ <code>state</code>
Calling `Item.history` returns a `ItemHistory` object with the following functions:
Note: `serviceId` is optional, if omitted, the default persistance service will be used.
- ItemHistory :`object`
- .averageBetween(begin, end, serviceId) ⇒ `number | null`
- .averageSince(timestamp, serviceId) ⇒ `number | null`
- .changedBetween(begin, end, serviceId) ⇒ `boolean`
- .changedSince(timestamp, serviceId) ⇒ `boolean`
- .deltaBetween(begin, end, serviceId) ⇒ `number | null`
- .deltaSince(timestamp, serviceId) ⇒ `number | null`
- .deviationBetween(begin, end, serviceId) ⇒ `number | null`
- .deviationSince(timestamp, serviceId) ⇒ `number | null`
- .evolutionRateBetween(begin, end, serviceId) ⇒ `number | null`
- .evolutionRateSince(timestamp, serviceId) ⇒ `number | null`
- .historicState(timestamp, serviceId) ⇒ `string | null`
- .lastUpdate(serviceId) ⇒ `ZonedDateTime | null`
- .latestState(serviceId) ⇒ `string | null`
- .maximumBetween(begin, end, serviceId) ⇒ `string | null`
- .maximumSince(timestamp,serviceId) ⇒ `string | null`
- .minimumSince(begin, end, serviceId) ⇒ `string | null`
- .minimumSince(timestamp, serviceId) ⇒ `string | null`
- .persist(serviceId)
- .previousState(skipEqual, serviceId) ⇒ `string | null`
- .sumBetween(begin, end, serviceId) ⇒ `number | null`
- .sumSince(timestamp, serviceId) ⇒ `number | null`
- .updatedBetween(begin, end, serviceId) ⇒ `boolean`
- .updatedSince(timestamp, serviceId) ⇒ `boolean`
- .varianceBetween(begin, end, serviceId) ⇒ `number | null`
- .varianceSince(timestamp, serviceId) ⇒ `number | null`
Note: `serviceId` is optional, if omitted, the default persistence service will be used.
```javascript
var yesterday = new Date(new Date().getTime() - (24 * 60 * 60 * 1000));
var item = items.getItem("KitchenDimmer");
console.log("KitchenDimmer averageSince", item.history.averageSince(yesterday));
var item = items.getItem('KitchenDimmer');
console.log('KitchenDimmer averageSince', item.history.averageSince(yesterday));
```
See [openhab-js : ItemHistory](https://openhab.github.io/openhab-js/items.ItemHistory.html) for full API documentation.
### Things
The Things namespace allows to interact with openHAB Things.
See [openhab-js : things](https://openhab.github.io/openhab-js/things.html) for full API documentation.
* things : <code>object</code>
* .getThing(uid, nullIfMissing) ⇒ <code>Thing</code>
* .getThings() ⇒ <code>Array.&lt;Thing&gt;</code>
- things : <code>object</code>
- .getThing(uid, nullIfMissing) ⇒ <code>Thing</code>
- .getThings() ⇒ <code>Array.&lt;Thing&gt;</code>
#### `getThing(uid, nullIfMissing)`
Calling `getThing(...)` returns a `Thing` object with the following properties:
* Thing : <code>object</code>
* .bridgeUID ⇒ <code>String</code>
* .label ⇒ <code>String</code>
* .location ⇒ <code>String</code>
* .status ⇒ <code>String</code>
* .statusInfo ⇒ <code>String</code>
* .thingTypeUID ⇒ <code>String</code>
* .uid ⇒ <code>String</code>
* .isEnabled ⇒ <code>Boolean</code>
* .setLabel(label)
* .setLocation(location)
* .setProperty(name, value)
* .setEnabled(enabled)
- Thing : <code>object</code>
- .bridgeUID ⇒ <code>String</code>
- .label ⇒ <code>String</code>
- .location ⇒ <code>String</code>
- .status ⇒ <code>String</code>
- .statusInfo ⇒ <code>String</code>
- .thingTypeUID ⇒ <code>String</code>
- .uid ⇒ <code>String</code>
- .isEnabled ⇒ <code>Boolean</code>
- .setLabel(label)
- .setLocation(location)
- .setProperty(name, value)
- .setEnabled(enabled)
```javascript
const thing = things.getThing('astro:moon:home');
@ -470,17 +491,16 @@ Replace `<url>` with the request url.
See [openhab-js : actions.ScriptExecution](https://openhab.github.io/openhab-js/actions.html#.ScriptExecution) for complete documentation.
```javascript
let now = time.ZonedDateTime.now();
// Function to run when the timer goes off.
function timerOver () {
logger.info('The timer is over.');
console.info('The timer is over.');
}
// Create the Timer.
this.myTimer = actions.ScriptExecution.createTimer(now.plusSeconds(10), timerOver);
this.myTimer = actions.ScriptExecution.createTimer('My Timer', now.plusSeconds(10), timerOver);
// Cancel the timer.
this.myTimer.cancel();
@ -491,6 +511,7 @@ let active = this.myTimer.isActive();
// Reschedule the timer.
this.myTimer.reschedule(now.plusSeconds(5));
```
#### Semantics Actions
See [openhab-js : actions.Semantics](https://openhab.github.io/openhab-js/actions.html#.Semantics) for complete documentation.
@ -526,21 +547,23 @@ The cache namespace provides a default cache that can be used to set and retriev
See [openhab-js : cache](https://openhab.github.io/openhab-js/cache.html) for full API documentation.
* cache : <code>object</code>
* .get(key, defaultSupplier) ⇒ <code>Object | null</code>
* .put(key, value) ⇒ <code>Previous Object | null</code>
* .remove(key) ⇒ <code>Previous Object | null</code>
* .exists(key) ⇒ <code>boolean</code>
- cache : <code>object</code>
- .get(key, defaultSupplier) ⇒ <code>Object | null</code>
- .put(key, value) ⇒ <code>Previous Object | null</code>
- .remove(key) ⇒ <code>Previous Object | null</code>
- .exists(key) ⇒ <code>boolean</code>
The `defaultSupplier` provided function will return a default value if a specified key is not already associated with a value.
**Example** *(Get a previously set value with a default value (times &#x3D; 0))*
```js
let counter = cache.get("counter", () => ({ "times": 0 }));
console.log("Count",counter.times++);
```
**Example** *(Get a previously set object)*
```js
let counter = cache.get("counter");
if(counter == null){
@ -549,10 +572,11 @@ if(counter == null){
}
console.log("Count",counter.times++);
```
### Log
By default the JS Scripting binding supports console logging like `console.log()` and `console.debug()` to the openHAB default log.
Additionally scripts may create their own native openHAB logger using the log namespace.
By default, the JS Scripting binding supports console logging like `console.log()` and `console.debug()` to the openHAB default log.
Additionally, scripts may create their own native openHAB logger using the log namespace.
```javascript
let logger = log('my_logger');
@ -564,26 +588,29 @@ logger.debug("Hello {}!", "world");
### Time
openHAB internally makes extensive use of the `java.time` package.
openHAB-JS exports the excellent [JS-Joda](#https://js-joda.github.io/js-joda/) library via the `time` namespace, which is a native JavaScript port of the same API standard used in Java for `java.time`.
openHAB-JS exports the excellent [JS-Joda](https://js-joda.github.io/js-joda/) library via the `time` namespace, which is a native JavaScript port of the same API standard used in Java for `java.time`.
Anywhere that a native Java `ZonedDateTime` or `Duration` is required, the runtime will automatically convert a JS-Joda `ZonedDateTime` or `Duration` to its Java counterpart.
The exported JS-Joda library is also extended with convenient functions relevant to openHAB usage.
Examples:
```javascript
var now = time.ZonedDateTime.now();
var yesterday = time.ZonedDateTime.now().minusHours(24);
var item = items.getItem("Kitchen");
console.log("averageSince", item.history.averageSince(yesterday));
```
```javascript
actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), 'echo', 'Hello World!');
```
See [JS-Joda](https://js-joda.github.io/js-joda/) for more examples and complete API usage.
#### `time.toZDT()`
There will be times where this automatic conversion is not available (for example when working with date times within a rule).
There will be times when this automatic conversion is not available (for example when working with date times within a rule).
To ease having to deal with these cases a `time.toZDT()` function will accept almost any type that can be converted to a `time.ZonedDateTime`.
The following rules are used during the conversion:
@ -602,7 +629,6 @@ The following rules are used during the conversion:
| `"kk:mm[:ss][ ]a"` (12 hour time) | today's date with the time indicated, the space between the time and am/pm and seconds are optional | `time.toZDT('1:23:45 PM');` |
| Duration String | any duration string supported by `time.Duration` added to `now()`, see [the docs](https://js-joda.github.io/js-joda/class/packages/core/src/Duration.js~Duration.html#static-method-parse) for details | `time.toZDT('PT1H4M6.789S');` |
When a type or string that cannot be handled is encountered, an error is thrown.
#### `toToday()`
@ -616,7 +642,7 @@ const alarm = items.getItem('Alarm');
alarm.postUpdate(time.toZDT(alarm).toToday());
```
#### `betweenTimes(start, end)`
#### `isBetweenTimes(start, end)`
Tests whether this `time.ZonedDateTime` is between the passed in `start` and `end`.
However, the function only compares the time portion of the three, ignoring the date portion.
@ -626,9 +652,9 @@ The function takes into account times that span midnight.
Examples:
```javascript
time.toZDT().betweenTimes('22:00', '05:00') // currently between 11:00 pm and 5:00 am
time.toZDT().betweenTimes(items.getItem('Sunset'), '11:30 PM') // is now between sunset and 11:30 PM?
time.toZDT(items.getItem('StartTime')).betweenTimes(time.toZDT(), 'PT1H'); // is the state of StartTime between now and one hour from now
time.toZDT().isBetweenTimes('22:00', '05:00') // currently between 11:00 pm and 5:00 am
time.toZDT().isBetweenTimes(items.getItem('Sunset'), '11:30 PM') // is now between sunset and 11:30 PM?
time.toZDT(items.getItem('StartTime')).isBetweenTimes(time.toZDT(), 'PT1H'); // is the state of StartTime between now and one hour from now
```
#### `isClose(zdt, maxDur)`
@ -638,7 +664,9 @@ Tests to see if the delta between the `time.ZonedDateTime` and the passed in `ti
```javascript
const timestamp = time.toZDT();
// do some stuff
if(timestamp.isClose(time.toZDT(), time.Duration.ofMillis(100))) { // did "do some stuff" take longer than 100 msecs to run?
if(timestamp.isClose(time.toZDT(), time.Duration.ofMillis(100))) {
// did "do some stuff" take longer than 100 msecs to run?
}
```
#### `getMillisFromNow`
@ -652,12 +680,11 @@ console.log(timestamp.getMillisFromNow());
### Utils
openHAB internally is a Java program.
openHAB internally is a Java program.
openHAB-JS converts between Java and JavaScript data types and reverse.
See [openhab-js : utils](https://openhab.github.io/openhab-js/utils.html) for full API documentation.
## File Based Rules
The JS Scripting binding will load scripts from `automation/js` in the user configuration directory.
@ -666,7 +693,7 @@ Local variable state is not persisted among reloads, see using the [cache](#cach
File based rules can be created in 2 different ways: using [JSRule](#jsrule) or the [Rule Builder](#rule-builder).
See [openhab-js : rules ](https://openhab.github.io/openhab-js/rules.html) for full API documentation.
See [openhab-js : rules](https://openhab.github.io/openhab-js/rules.html) for full API documentation.
### JSRule
@ -727,9 +754,13 @@ triggers.SystemStartlevelTrigger(100) // Startup Complete
triggers.GenericCronTrigger('0 30 16 * * ? *');
triggers.TimeOfDayTrigger('19:00');
triggers.DateTimeTrigger('MyDateTimeItem');
```
See [openhab-js : triggers ](https://openhab.github.io/openhab-js/triggers.html) in the API documentation for a full list of all triggers.
You can use `null` for a trigger parameter to skip its configuration.
See [openhab-js : triggers](https://openhab.github.io/openhab-js/triggers.html) in the API documentation for a full list of all triggers.
### Rule Builder
@ -742,12 +773,12 @@ Rules are started by calling `rules.when()` and can chain together [triggers](#r
rules.when().triggerType()...if().conditionType().then().operationType()...build(name, description, tags, id);
```
Rule are completed by calling `.build(name, description, tags, id)` , all parameters are optional and reasonable defaults will be used if omitted.
Rule are completed by calling `.build(name, description, tags, id)` , all parameters are optional and reasonable defaults will be used if omitted.
- `name` String rule name - defaults generated name
- `description` String Rule description - defaults generated description
- `tags` Array of string tag names - defaults empty array
- `id` String id - defaults random UUID
- `id` String id - defaults random UUID
A simple example of this would look like:
@ -762,69 +793,72 @@ rules.when().item("F1_light").changed().then(event => {
console.log(event);
}).build("Test Rule", "My Test Rule");
```
See [Examples](#rule-builder-examples) for further patterns.
#### Rule Builder Triggers
* `when()`
* `or()`
* `.channel(channelName)` Specifies a channel event as a source for the rule to fire.
* `.triggered(event)` Trigger on a specific event name
* `.cron(cronExpression)` Specifies a cron schedule for the rule to fire.
* `.item(itemName)` Specifies an item as the source of changes to trigger a rule.
* `.for(duration)`
* `.from(state)`
* `.to(state)`
* `.fromOff()`
* `.toOn()`
* `.receivedCommand()`
* `.receivedUpdate()`
* `.memberOf(groupName)`
* `.for(duration)`
* `.from(state)`
* `.to(state)`
* `.fromOff()`
* `.toOn()`
* `.receivedCommand()`
* `.receivedUpdate()`
* `.system()`
* `.ruleEngineStarted()`
* `.rulesLoaded()`
* `.startupComplete()`
* `.thingsInitialized()`
* `.userInterfacesStarted()`
* `.startLevel(level)`
* `.thing(thingName)`
* `changed()`
* `updated()`
* `from(state)`
* `to(state)`
- `when()`
- `or()`
- `.channel(channelName)` Specifies a channel event as a source for the rule to fire.
- `.triggered(event)` Trigger on a specific event name
- `.cron(cronExpression)` Specifies a cron schedule for the rule to fire.
- `.item(itemName)` Specifies an item as the source of changes to trigger a rule.
- `.for(duration)`
- `.from(state)`
- `.to(state)`
- `.fromOff()`
- `.toOn()`
- `.receivedCommand()`
- `.receivedUpdate()`
- `.memberOf(groupName)`
- `.for(duration)`
- `.from(state)`
- `.to(state)`
- `.fromOff()`
- `.toOn()`
- `.receivedCommand()`
- `.receivedUpdate()`
- `.system()`
- `.ruleEngineStarted()`
- `.rulesLoaded()`
- `.startupComplete()`
- `.thingsInitialized()`
- `.userInterfacesStarted()`
- `.startLevel(level)`
- `.thing(thingName)`
- `changed()`
- `updated()`
- `from(state)`
- `to(state)`
Additionally all the above triggers have the following functions:
* `.if()` or `.if(fn)` -> a [rule condition](#rule-builder-conditions)
* `.then()` or `.then(fn)` -> a [rule operation](#rule-builder-operations)
* `.or()` -> a [rule trigger](#rule-builder-triggers) (chain additional triggers)
- `.if()` or `.if(fn)` -> a [rule condition](#rule-builder-conditions)
- `.then()` or `.then(fn)` -> a [rule operation](#rule-builder-operations)
- `.or()` -> a [rule trigger](#rule-builder-triggers) (chain additional triggers)
#### Rule Builder Conditions
* `if(optionalFunction)`
* `.stateOfItem(itemName)`
* `is(state)`
* `in(state...)`
- `if(optionalFunction)`
- `.stateOfItem(itemName)`
- `is(state)`
- `in(state...)`
#### Rule Builder Operations
* `then(optionalFunction)`
* `.build(name, description, tags, id)`
* `.copyAndSendState()`
* `.copyState()`
* `.inGroup(groupName)`
* `.postIt()`
* `.postUpdate(state)`
* `.send(command)`
* `.sendIt()`
* `.sendOff()`
* `.sendOn()`
* `.sendToggle()`
- `then(optionalFunction)`
- `.build(name, description, tags, id)`
- `.copyAndSendState()`
- `.copyState()`
- `.inGroup(groupName)`
- `.postIt()`
- `.postUpdate(state)`
- `.send(command)`
- `.sendIt()`
- `.sendOff()`
- `.sendOn()`
- `.sendToggle()`
#### Rule Builder Examples
@ -832,7 +866,7 @@ Additionally all the above triggers have the following functions:
// Basic rule, when the BedroomLight1 is changed, run a custom function
rules.when().item('BedroomLight1').changed().then(e => {
console.log("BedroomLight1 state", e.newState)
}.build();
}).build();
// Turn on the kitchen light at SUNSET
rules.when().timeOfDay("SUNSET").then().sendOn().toItem("KitchenLight").build("Sunset Rule","turn on the kitchen light at SUNSET");
@ -862,33 +896,34 @@ rules.when().item('HallLight').receivedUpdate().then().copyState().fromItem('Bed
**NOTE**: The `event` object is different in UI Based Rules and File Based Rules!
This section is only valid for File Based Rules.
If you use UI Based Rules, refer to [UI based rules event object documentation](#event-object).
If you use UI Based Rules, refer to [UI based rules event object documentation](#ui-event-object).
When a rule is triggered, the script is provided the event instance that triggered it.
The specific data depends on the event type.
The `event` object provides several information about that trigger.
The `event` object provides some information about that trigger.
This tables gives an overview over the `event` object:
| Property Name | Trigger Types | Description | Rules DSL Equivalent |
|-------------------|-----------------------------------------------------|-------------------------------------------------------------------------------------|------------------------|
| `oldState` | `ItemStateChangeTrigger`, `GroupStateChangeTrigger` | Previous state of Item or Group that triggered event | `previousState` |
| `newState` | `ItemStateChangeTrigger`, `GroupStateChangeTrigger` | New state of Item or Group that triggered event | N/A |
| `receivedState` | `ItemStateUpdateTrigger`, `GroupStateUpdateTrigger` | State of Item that triggered event | `triggeringItem.state` |
| `receivedCommand` | `ItemCommandTrigger`, `GroupCommandTrigger` | Command that triggered event | `receivedCommand` |
| `itemName` | `Item****Trigger` | Name of Item that triggered event | `triggeringItem.name` |
| `receivedEvent` | `ChannelEventTrigger` | Channel event that triggered event | N/A |
| `channelUID` | `ChannelEventTrigger` | UID of channel that triggered event | N/A |
| `oldStatus` | `ThingStatusChangeTrigger` | Previous state of Thing that triggered event | N/A |
| `newStatus` | `ThingStatusChangeTrigger` | New state of Thing that triggered event | N/A |
| `status` | `ThingStatusUpdateTrigger` | State of Thing that triggered event | N/A |
| `thingUID` | `Thing****Trigger` | UID of Thing that triggered event | N/A |
| `eventType` | all | Type of event that triggered event (change, command, time, triggered, update) | N/A |
| `triggerType` | all except `PWMTrigger`, `PIDTrigger` | Type of trigger that triggered event (for `TimeOfDayTrigger`: `GenericCronTrigger`) | N/A |
| Property Name | Trigger Types | Description | Rules DSL Equivalent |
|-------------------|------------------------------------------------------|-------------------------------------------------------------------------------------|------------------------|
| `oldState` | `ItemStateChangeTrigger`, `GroupStateChangeTrigger` | Previous state of Item or Group that triggered event | `previousState` |
| `newState` | `ItemStateChangeTrigger`, `GroupStateChangeTrigger` | New state of Item or Group that triggered event | N/A |
| `receivedState` | `ItemStateUpdateTrigger`, `GroupStateUpdateTrigger` | State of Item that triggered event | `triggeringItem.state` |
| `receivedCommand` | `ItemCommandTrigger`, `GroupCommandTrigger` | Command that triggered event | `receivedCommand` |
| `itemName` | `Item****Trigger` | Name of Item that triggered event | `triggeringItem.name` |
| `receivedEvent` | `ChannelEventTrigger` | Channel event that triggered event | N/A |
| `channelUID` | `ChannelEventTrigger` | UID of channel that triggered event | N/A |
| `oldStatus` | `ThingStatusChangeTrigger` | Previous state of Thing that triggered event | N/A |
| `newStatus` | `ThingStatusChangeTrigger` | New state of Thing that triggered event | N/A |
| `status` | `ThingStatusUpdateTrigger` | State of Thing that triggered event | N/A |
| `thingUID` | `Thing****Trigger` | UID of Thing that triggered event | N/A |
| `eventType` | all except `PWMTrigger`, `PIDTrigger`, time triggers | Type of event that triggered event (change, command, triggered, update) | N/A |
| `triggerType` | all except `PWMTrigger`, `PIDTrigger`, time triggers | Type of trigger that triggered event | N/A |
All properties are typeof `string`.
**NOTE:**
`Group****Trigger`s use the equivalent `Item****Trigger` as trigger for each member.
Time triggers do not provide any event instance, therefore no property is populated.
See [openhab-js : EventObject](https://openhab.github.io/openhab-js/rules.html#.EventObject) for full API documentation.
@ -898,8 +933,8 @@ For file based scripts, this function will be called if found when the script is
```javascript
scriptLoaded = function () {
console.log("script loaded");
loadedDate = Date.now();
console.log("script loaded");
loadedDate = Date.now();
};
```
@ -909,9 +944,9 @@ For file based scripts, this function will be called if found when the script is
```javascript
scriptUnloaded = function () {
console.log("script unloaded");
// clean up rouge timers
clearInterval(timer);
console.log("script unloaded");
// clean up rouge timers
clearInterval(timer);
};
```
@ -933,7 +968,7 @@ var { ON, OFF, QuantityType } = require("@runtime");
```
| Variable | Description |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `State` | [`org.openhab.core.types.State`](https://www.openhab.org/javadoc/latest/org/openhab/core/types/state) |
| `Command` | [`org.openhab.core.types.Command`](https://www.openhab.org/javadoc/latest/org/openhab/core/types/command) |
| `URLEncoder` | [`java.net.URLEncoder`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/URLEncoder.html) |

View File

@ -25,7 +25,7 @@
<graal.version>21.3.0</graal.version>
<asm.version>6.2.1</asm.version>
<oh.version>${project.version}</oh.version>
<ohjs.version>openhab@2.0.0</ohjs.version>
<ohjs.version>openhab@2.0.3</ohjs.version>
</properties>
<build>