diff --git a/bundles/org.openhab.automation.jsscripting/README.md b/bundles/org.openhab.automation.jsscripting/README.md index 9cc2031727b..49ff42f4034 100644 --- a/bundles/org.openhab.automation.jsscripting/README.md +++ b/bundles/org.openhab.automation.jsscripting/README.md @@ -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 + + +### 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". It’s 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 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 : object - * .getItem(name, nullIfMissing) ⇒ Item - * .getItems() ⇒ Array.<Item> - * .getItemsByTag(...tagNames) ⇒ Array.<Item> - * .addItem([itemConfig](#itemconfig)) - * .removeItem(itemOrItemName) ⇒ Boolean - * .replaceItem([itemConfig](#itemconfig)) - * .safeItemName(s) ⇒ String +- items : object + - .getItem(name, nullIfMissing) ⇒ Item + - .getItems() ⇒ Array.<Item> + - .getItemsByTag(...tagNames) ⇒ Array.<Item> + - .addItem([itemConfig](#itemconfig)) + - .removeItem(itemOrItemName) ⇒ Boolean + - .replaceItem([itemConfig](#itemconfig)) + - .safeItemName(s) ⇒ String ```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 : object - * .type ⇒ String - * .name ⇒ String - * .label ⇒ String - * .history ⇒ ItemHistory - * .state ⇒ String - * .rawState ⇒ HostState - * .members ⇒ Array.<Item> - * .descendents ⇒ Array.<Item> - * .isUninitialized ⇒ Boolean - * .groupNames ⇒ Array.<String> - * .tags ⇒ Array.<String> - * .getMetadataValue(namespace) ⇒ String - * .updateMetadataValue(namespace, value) ⇒ String - * .upsertMetadataValue(namespace, value) ⇒ Boolean - * .updateMetadataValues(namespaceToValues) - * .sendCommand(value) - * .sendCommandIfDifferent(value) ⇒ Boolean - * .postUpdate(value) - * .addGroups(...groupNamesOrItems) - * .removeGroups(...groupNamesOrItems) - * .addTags(...tagNames) - * .removeTags(...tagNames) +- Item : object + - .type ⇒ String + - .name ⇒ String + - .label ⇒ String + - .history ⇒ [`ItemHistory`](#itemhistory) + - .state ⇒ String + - .rawState ⇒ HostState + - .members ⇒ Array.<Item> + - .descendents ⇒ Array.<Item> + - .isUninitialized ⇒ Boolean + - .groupNames ⇒ Array.<String> + - .tags ⇒ Array.<String> + - .getMetadataValue(namespace) ⇒ String + - .updateMetadataValue(namespace, value) ⇒ String + - .upsertMetadataValue(namespace, value) ⇒ Boolean + - .updateMetadataValues(namespaceToValues) + - .sendCommand(value) + - .sendCommandIfDifferent(value) ⇒ Boolean + - .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 : object - * .type ⇒ String - * .name ⇒ String - * .label ⇒ String - * .category (icon) ⇒ String - * .groups ⇒ Array.<String> - * .tags ⇒ Array.<String> - * .channels ⇒ String|Object { channeluid: { config } } - * .metadata ⇒ Object { namespace: value }|Object { namespace: { value: value , config: { config } } } - * .giBaseType ⇒ String - * .groupFunction ⇒ String +- itemConfig : object + - .type ⇒ String + - .name ⇒ String + - .label ⇒ String + - .category (icon) ⇒ String + - .groups ⇒ Array.<String> + - .tags ⇒ Array.<String> + - .channels ⇒ String|Object { channeluid: { config } } + - .metadata ⇒ Object { namespace: value }|Object { namespace: { value: value , config: { config } } } + - .giBaseType ⇒ String + - .groupFunction ⇒ String 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 : object - * .averageSince(timestamp, serviceId) ⇒ Number - * .changedSince(timestamp, serviceId) ⇒ Number - * .deltaSince(timestamp, serviceId) ⇒ Number - * .deviationSince(timestamp, serviceId) ⇒ Number - * .evolutionRate(timestamp, serviceId) ⇒ Number - * .historicState(timestamp, serviceId) ⇒ state - * .lastUpdate(serviceId) ⇒ Date - * .latestState(serviceId) ⇒ state - * .maximumSince(timestamp,serviceId) ⇒ state - * .minimumSince(timestamp,serviceId) ⇒ state - * .persist(serviceId) - * .previousState(skipEqual,serviceId) ⇒ state - * .sumSince(timestamp, serviceId) ⇒ Number - * .updatedSince(timestamp, serviceId) ⇒ Boolean - * .varianceSince(timestamp,serviceId) ⇒ state +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 : object - * .getThing(uid, nullIfMissing) ⇒ Thing - * .getThings() ⇒ Array.<Thing> +- things : object + - .getThing(uid, nullIfMissing) ⇒ Thing + - .getThings() ⇒ Array.<Thing> #### `getThing(uid, nullIfMissing)` Calling `getThing(...)` returns a `Thing` object with the following properties: -* Thing : object - * .bridgeUID ⇒ String - * .label ⇒ String - * .location ⇒ String - * .status ⇒ String - * .statusInfo ⇒ String - * .thingTypeUID ⇒ String - * .uid ⇒ String - * .isEnabled ⇒ Boolean - * .setLabel(label) - * .setLocation(location) - * .setProperty(name, value) - * .setEnabled(enabled) +- Thing : object + - .bridgeUID ⇒ String + - .label ⇒ String + - .location ⇒ String + - .status ⇒ String + - .statusInfo ⇒ String + - .thingTypeUID ⇒ String + - .uid ⇒ String + - .isEnabled ⇒ Boolean + - .setLabel(label) + - .setLocation(location) + - .setProperty(name, value) + - .setEnabled(enabled) ```javascript const thing = things.getThing('astro:moon:home'); @@ -470,17 +491,16 @@ Replace `` 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 : object - * .get(key, defaultSupplier) ⇒ Object | null - * .put(key, value) ⇒ Previous Object | null - * .remove(key) ⇒ Previous Object | null - * .exists(key) ⇒ boolean +- cache : object + - .get(key, defaultSupplier) ⇒ Object | null + - .put(key, value) ⇒ Previous Object | null + - .remove(key) ⇒ Previous Object | null + - .exists(key) ⇒ boolean 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 = 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) | diff --git a/bundles/org.openhab.automation.jsscripting/pom.xml b/bundles/org.openhab.automation.jsscripting/pom.xml index 2dac934dd8d..972bba650f0 100644 --- a/bundles/org.openhab.automation.jsscripting/pom.xml +++ b/bundles/org.openhab.automation.jsscripting/pom.xml @@ -25,7 +25,7 @@ 21.3.0 6.2.1 ${project.version} - openhab@2.0.0 + openhab@2.0.3