UNPKG

openhab

Version:
979 lines (730 loc) 85.6 kB
# openHAB JavaScript Library [![Build Status](https://github.com/openhab/openhab-js/actions/workflows/build.yaml/badge.svg)](https://github.com/openhab/openhab-js/actions/workflows/build.yaml) [![npm version](https://badge.fury.io/js/openhab.svg)](https://badge.fury.io/js/openhab) This library aims to be a fairly high-level ES6 library to support automation in openHAB. It provides convenient access to common openHAB functionality within rules including Items, Things, actions, logging and more. This library is included by default in the openHAB [JavaScript Scripting add-on](https://www.openhab.org/addons/automation/jsscripting/). - [Installation](#installation) - [Default Installation](#default-installation) - [Custom Installation](#custom-installation) - [Compatibility](#compatibility) - [Configuration](#configuration) - [UI Based Rules](#ui-based-rules) - [Adding Triggers](#adding-triggers) - [Adding Actions](#adding-actions) - [UI Event Object](#ui-event-object) - [Scripting Basics](#scripting-basics) - [`let` and `const`](#let-and-const) - [`require`](#require) - [`console`](#console) - [Timers](#timers) - [Paths](#paths) - [Deinitialization Hook](#deinitialization-hook) - [`JS` Transformation](#js-transformation) - [Standard Library](#standard-library) - [Items](#items) - [Things](#things) - [Actions](#actions) - [Cache](#cache) - [Time](#time) - [Quantity](#quantity) - [Log](#log) - [Utils](#utils) - [File Based Rules](#file-based-rules) - [JSRule](#jsrule) - [Rule Builder](#rule-builder) - [Event Object](#event-object) - [Advanced Scripting](#advanced-scripting) - [Libraries](#libraries) - [@runtime](#runtime) ## Installation ### 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). 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.* ### Custom Installation If you want to install the openHAB JavaScript library manually, you need to disable the caching of the internal library in the [add-on settings](https://www.openhab.org/addons/automation/jsscripting/#configuration). On openHABian: - Open the openHABian config tool: `sudo openhabian-config`. - Select `40 | openHAB Related`. - Select `46 | Install openhab-js`. Manually: - Go to the JavaScript user scripts directory: `cd $OPENHAB_CONF/automation/js`. - You may need to install `npm`. - Install the latest release: Run `npm i openhab`. - Install the latest development version: Run `npm i git+https://github.com/openhab/openhab-js.git`. NPM will create a `node_modules` directory containing the latest version of this library. This will be used instead of the binding provided version. ## Compatibility | openHAB version | Minimum `openhab-js` version | Maximum `openhab-js` version | |-----------------|------------------------------|------------------------------| | 3.2.x | 1.0.0 | 4.7.0 | | 3.3.x | 1.0.0 | 4.7.0 | | 3.4.x | 3.1.0 | 4.7.0 | | 4.0.x | 4.2.1 | 4.7.0 | | 4.1.0 | 4.2.1 | 4.7.0 | | 4.1.1 etc. | 4.7.2 | 4.9.0 | | 4.2.x | 5.0.0 | 5.7.2 | | 4.3.x | 5.0.0 | 5.9.0 | | 5.0.x | 5.0.0 | - | ## Configuration <!-- Copy everything from here to update the JS Scripting documentation. --> ### 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 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). ![openHAB Rule Script](doc/rule-script.png) For example, turning a light on: ```javascript 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"); ``` Querying the status of a thing ```javascript 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. ### 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). Note that `event` object is only available when the UI based rule was triggered by an event and is not manually run! Trying to access `event` on manual run does not work (and will lead to an error), use `this.event` instead (will be `undefined` in case of manual run). 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 table 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` | | `itemName` | string | all | Name of Item that triggered event | `triggeringItem.name` | | `type` | string | all | Type of event that triggered event (`"ItemStateEvent"`, `"ItemStateChangedEvent"`, `"ItemCommandEvent"`, ...) | N/A | Note that in UI based rules `event.itemState`, `event.oldItemState`, and `event.itemCommand` are Java types (not JavaScript), and care must be taken when comparing these with JavaScript types: ```javascript var { ON } = require("@runtime") console.log(event.itemState == "ON") // WRONG. Java type does not equal with string, not even with "relaxed" equals (==) comparison console.log(event.itemState.toString() == "ON") // OK. Comparing strings console.log(event.itemState == ON) // OK. Comparing Java types ``` **NOTE**: Even with `String` items, simple comparison with `==` is not working as one would expect! See below example: ```javascript // Example assumes String item trigger console.log(event.itemState == "test") // WRONG. Will always log "false" console.log(event.itemState.toString() == "test") // OK ``` ## Scripting Basics The openHAB JavaScript Scripting runtime attempts to provide a familiar environment to JavaScript developers. ### `let` and `const` Due to the way how openHAB runs UI based scripts, `let`, `const` and `class` are not supported at top-level. Use `var` instead or wrap your script inside a self-invoking function: ```javascript // Wrap script inside a self-invoking function: (function (data) { const C = 'Hello world'; console.log(C); })(this.event); // Defining a class using var: var Tree = class { constructor (height) { this.height = height; } } ``` ### `require` Scripts may include standard NPM based libraries by using CommonJS `require`. The library search will look in the path `automation/js/node_modules` in the user configuration directory. See [libraries](#libraries) for more information. ### `console` The JS Scripting binding supports the standard `console` object for logging. Script logging is enabled by default at the `INFO` level (messages from `console.debug` and `console.trace` won't be displayed), but can be configured using the [openHAB console](https://www.openhab.org/docs/administration/console.html): ```text log:set DEBUG org.openhab.automation.script log:set TRACE org.openhab.automation.script log:set DEFAULT org.openhab.automation.script ``` The default logger name consists of the prefix `org.openhab.automation.script` and the script’s individual part `.file.filename` or `.ui.ruleUID`. This logger name can be changed by assigning a new string to the `loggerName` property of the console: ```javascript console.loggerName = 'org.openhab.custom'; ``` Please be aware that messages do not appear in the logs if the logger name does not start with `org.openhab`. This behaviour is due to [log4j2](https://logging.apache.org/log4j/2.x/) requiring a setting for each logger prefix in `$OPENHAB_USERDATA/etc/log4j2.xml` (on openHABian: `/srv/openhab-userdata/etc/log4j2.xml`). Supported logging functions include: - `console.log(obj1 [, obj2, ..., objN])` - `console.info(obj1 [, obj2, ..., objN])` - `console.warn(obj1 [, obj2, ..., objN])` - `console.error(obj1 [, obj2, ..., objN])` - `console.debug(obj1 [, obj2, ..., objN])` - `console.trace(obj1 [, obj2, ..., objN])` 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. Note: [openhab-js](https://github.com/openhab/openhab-js/) is logging to `org.openhab.automation.openhab-js`. ### Timers JS Scripting provides access to the global `setTimeout`, `setInterval`, `clearTimeout` and `clearInterval` methods specified in the [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API). When a script is unloaded, all created timeouts and intervals are automatically cancelled. #### `setTimeout` The global [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) method sets a timer which executes a function once the timer expires. `setTimeout()` returns a `timeoutId` (a positive integer value) which identifies the timer created. ```javascript var timeoutId = setTimeout(callbackFunction, delay, param1, /* ... */ paramN); ``` `delay` is an integer value that represents the amount of milliseconds to wait before the timer expires. `param1` ... `paramN` are optional, additional arguments which are passed through to the `callbackFunction`. The global [`clearTimeout(timeoutId)`](https://developer.mozilla.org/en-US/docs/Web/API/clearTimeout) method cancels a timeout previously established by calling `setTimeout()`. If you need a more verbose way of creating timers, consider to use [`createTimer`](#createtimer) instead. #### `setInterval` The global [`setInterval()`](https://developer.mozilla.org/en-US/docs/Web/API/setInterval) method repeatedly calls a function, with a fixed time delay between each call. `setInterval()` returns an `intervalId` (a positive integer value) which identifies the interval created. ```javascript var intervalId = setInterval(callbackFunction, delay, param1, /* ... */ paramN); ``` `delay` is an integer value that represents the amount of milliseconds to wait before the timer expires. `param1` ... `paramN` are optional, additional arguments which are passed through to the `callbackFunction`. The global [`clearInterval(intervalId)`](https://developer.mozilla.org/en-US/docs/Web/API/clearInterval) method cancels a timed, repeating action which was previously established by a call to `setInterval()`. #### Accessing Variables You can access all variables of the current context in the created timers. Note: Variables can be mutated (changed) after the timer has been created. Be aware that this can lead to unattended side effects, e.g. when you change the variable after timer creation, which can make debugging quite difficult! ```javascript var myVar = 'Hello world!'; // Schedule a timer that expires in ten seconds setTimeout(() => { console.info(`Timer expired with variable value = "${myVar}"`); }, 10000); myVar = 'Hello mutation!'; // When the timer runs, it will log "Hello mutation!" instead of "Hello world!" ``` If you need to pass some variables to the timer but avoid that they can get mutated, pass those variables as parameters to `setTimeout`/`setInterval` or `createTimer`: ```javascript var myVar = 'Hello world!'; // Schedule a timer that expires in ten seconds setTimeout((myVariable) => { console.info(`Timer expired with variable value = "${myVariable}"`); }, 10000, myVar); // Pass one or more variables as parameters here. They are passed through to the callback function. myVar = 'Hello mutation!'; // When the timer runs, it will log "Hello world!" ``` This also works for timers created with [`actions.ScriptExecution.createTimer`](#createtimer). ### Paths 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. ### Deinitialization Hook It is possible to hook into unloading of a script and register a function that is called when the script is unloaded. ```javascript require('@runtime').lifecycleTracker.addDisposeHook(() => functionToCall()); // Example require('@runtime').lifecycleTracker.addDisposeHook(() => { console.log("Deinitialization hook runs...") }); ``` ## `JS` Transformation openHAB provides several [data transformation services](https://www.openhab.org/addons/#transform) as well as the script transformations, that are available from the framework and need no additional installation. It allows transforming values using any of the available scripting languages, which means JavaScript Scripting is supported as well. See the [transformation docs](https://openhab.org/docs/configuration/transformations.html#script-transformation) for more general information on the usage of script transformations. Use JavaScript Scripting as script transformation by: 1. Creating a script in the `$OPENHAB_CONF/transform` folder with the `.js` extension. The script should take one argument `input` and return a value that supports `toString()` or `null`: ```javascript (function(data) { // Do some data transformation here, e.g. return "String has" + data.length + "characters"; })(input); ``` 2. Using `JS(<scriptname>.js):%s` as Item state transformation. 3. Passing parameters is also possible by using a URL like syntax: `JS(<scriptname>.js?arg=value)`. Parameters are injected into the script and can be referenced like variables. Simple transformations can aso be given as an inline script: `JS(|...)`, e.g. `JS(|"String has " + input.length + "characters")`. It should start with the `|` character, quotes within the script may need to be escaped with a backslash `\` when used with another quoted string as in text configurations. ## Standard Library Full documentation for the openHAB JavaScript library can be found at [openhab-js](https://openhab.github.io/openhab-js). The openHAB JavaScript library provides type definitions for most of its APIs to enable code completion is IDEs like [VS Code](https://code.visualstudio.com). To use the type definitions, install the [`openhab` npm package](https://npmjs.com/openhab) (read the [installation guide](https://github.com/openhab/openhab-js#custom-installation) for more information), and import the used namespaces with `const { rules, triggers, items } = require('openhab');` (adjust this to your needs). If an API does not provide type definitions and therefore autocompletion won't work, the documentation will include a note. ### Items The `items` namespace allows interactions with openHAB Items. Anywhere a native openHAB `Item` is required, the runtime will automatically convert the JS-`Item` to its Java counterpart. See [openhab-js : items](https://openhab.github.io/openhab-js/items.html) for full API documentation. - items : `object` - .NAME ⇒ `Item` - .existsItem(name) ⇒ `boolean` - .getItem(name, nullIfMissing) ⇒ `Item` - .getItems() ⇒ `Array[Item]` - .getItemsByTag(...tagNames) ⇒ `Array[Item]` - .addItem([itemConfig](#itemconfig)) ⇒ `Item` - .removeItem(itemOrItemName) ⇒ `Item|null` - .replaceItem([itemConfig](#itemconfig)) ⇒ `Item|null` - .safeItemName(s) ⇒ `string` ```javascript var item = items.KitchenLight; console.log("Kitchen Light State", item.state); ``` #### `getItem(name, nullIfMissing)` Calling `getItem(...)` or `...` returns an `Item` object with the following properties: - Item : `object` - .rawItem ⇒ `HostItem` - .persistence ⇒ [`ItemPersistence`](#itempersistence) - .semantics ⇒ [`ItemSemantics`](https://openhab.github.io/openhab-js/items.ItemSemantics.html) - .type ⇒ `string` - .name ⇒ `string` - .label ⇒ `string` - .state ⇒ `string` - .numericState ⇒ `number|null`: State as number, if state can be represented as number, or `null` if that's not the case - .quantityState ⇒ [`Quantity|null`](#quantity): Item state as Quantity or `null` if state is not Quantity-compatible or without unit - .rawState ⇒ `HostState` - .previousState ⇒ `string|null`: Previous state as string, or `null` if not available - .previousNumericState ⇒ `number|null`: Previous state as number, if state can be represented as number, or `null` if that's not the case or not available - .previousQuantityState ⇒ [`Quantity|null`](#quantity): Previous item state as Quantity or `null` if state is not Quantity-compatible, without unit or not available - .previousRawState ⇒ `HostState` - .lastStateUpdateTimestamp ⇒ [`time.ZonedDateTime`](#time): The time the state was last updated as ZonedDateTime or `null` if not available - .lastStateUpdateInstant ⇒ [`time.Instant`](#time): The time the state was last updated as Instant or `null` if not available - .lastStateChangeTimestamp ⇒ [`time.ZonedDateTime`](#time): The time the state was last changed as ZonedDateTime or `null` if not available - .lastStateChangeInstant ⇒ [`time.Instant`](#time): The time the state was last changed as Instant or `null` if not available - .members ⇒ `Array[Item]` - .descendents ⇒ `Array[Item]` - .isUninitialized ⇒ `boolean` - .groupNames ⇒ `Array[string]` - .tags ⇒ `Array[string]` - .getMetadata(namespace) ⇒ `object|null` - .replaceMetadata(namespace, value, configuration) ⇒ `object` - .removeMetadata(namespace) ⇒ `object|null` - .sendCommand(value): `value` can be a string, a number, a [`time.ZonedDateTime`](#time), a [`time.Instant`](#time) or a [`Quantity`](#quantity) - .sendCommand(value, expire): `expire` is a [`time.Duration`](#time), this will return the Item to its previous state after the given `expire` duration - .sendCommand(value, expire, onExpire): `onExpire` can be the same type as `value`, this will return the Item to the given `onExpire` value after the given `expire` duration - .sendCommandIfDifferent(value) ⇒ `boolean`: `value` can be a string, a number, a [`time.ZonedDateTime`](#time), a [`time.Instant`](#time) or a [`Quantity`](#quantity) - .sendIncreaseCommand(value) ⇒ `boolean`: `value` can be a number, or a [`Quantity`](#quantity) - .sendDecreaseCommand(value) ⇒ `boolean`: `value` can be a number, or a [`Quantity`](#quantity) - .sendToggleCommand(): Sends a command to flip the Item's state (e.g. if it is 'ON' an 'OFF' command is sent). - .postUpdate(value): `value` can be a string, a [`time.ZonedDateTime`](#time) or a [`Quantity`](#quantity) - .addGroups(...groupNamesOrItems) - .removeGroups(...groupNamesOrItems) - .addTags(...tagNames) - .removeTags(...tagNames) ```javascript // Equivalent to items.KitchenLight var item = items.getItem("KitchenLight"); // Send an ON command item.sendCommand("ON"); // Post an update item.postUpdate("OFF"); // Get state console.log("KitchenLight state", item.state); ``` See [openhab-js : Item](https://openhab.github.io/openhab-js/items.Item.html) for full API documentation. #### `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` 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({ type: 'String', name: 'Hallway_Light', label: 'Hallway Light', category: 'light', groups: ['Hallway', 'Light'], tags: ['Lightbulb'], channels: { 'binding:thing:device:hallway#light': {}, 'binding:thing:device:livingroom#light': { profile: 'system:follow' } }, metadata: { expire: '10m,command=1', stateDescription: { config: { pattern: '%d%%', options: '1=Red, 2=Green, 3=Blue' } } } }); // minimal example items.replaceItem({ type: 'Switch', name: 'MySwitch', metadata: { stateDescription: { config: { pattern: '%s' } } } }); ``` See [openhab-js : ItemConfig](https://openhab.github.io/openhab-js/global.html#ItemConfig) for full API documentation. #### `ItemPersistence` Calling `Item.persistence` returns an `ItemPersistence` object with the following functions: - ItemPersistence :`object` - .averageSince(timestamp, riemannType, serviceId) ⇒ `PersistedState | null` - .averageUntil(timestamp, riemannType, serviceId) ⇒ `PersistedState | null` - .averageBetween(begin, end, riemannType, serviceId) ⇒ `PersistedState | null` - .changedSince(timestamp, serviceId) ⇒ `boolean` - .changedUntil(timestamp, serviceId) ⇒ `boolean` - .changedBetween(begin, end, serviceId) ⇒ `boolean` - .countSince(timestamp, serviceId) ⇒ `number` - .countUntil(timestamp, serviceId) ⇒ `number` - .countBetween(begin, end, serviceId) ⇒ `number` - .countStateChangesSince(timestamp, serviceId) ⇒ `number` - .countStateChangesUntil(timestamp, serviceId) ⇒ `number` - .countStateChangesBetween(begin, end, serviceId) ⇒ `number` - .deltaSince(timestamp, serviceId) ⇒ `PersistedState | null` - .deltaUntil(timestamp, serviceId) ⇒ `PersistedState | null` - .deltaBetween(begin, end, serviceId) ⇒ `PersistedState | null` - .deviationSince(timestamp, riemannType, serviceId) ⇒ `PersistedState | null` - .deviationUntil(timestamp, riemannType, serviceId) ⇒ `PersistedState | null` - .deviationBetween(begin, end, riemannType, serviceId) ⇒ `PersistedState | null` - .evolutionRateSince(timestamp, riemannType, serviceId) ⇒ `number | null` - .evolutionRateUntil(timestamp, riemannType, serviceId) ⇒ `number | null` - .evolutionRateBetween(begin, end, riemannType, serviceId) ⇒ `number | null` - .getAllStatesSince(timestamp, serviceId) ⇒ `Array[PersistedItem]` - .getAllStatesUntil(timestamp, serviceId) ⇒ `Array[PersistedItem]` - .getAllStatesBetween(begin, end, serviceId) ⇒ `Array[PersistedItem]` - .lastUpdate(serviceId) ⇒ `ZonedDateTime | null` - .nextUpdate(serviceId) ⇒ `ZonedDateTime | null` - .lastChange(serviceId) ⇒ `ZonedDateTime | null` - .nextChange(serviceId) ⇒ `ZonedDateTime | null` - .maximumSince(timestamp, serviceId) ⇒ `PersistedItem | null` - .maximumUntil(timestamp, serviceId) ⇒ `PersistedItem | null` - .maximumBetween(begin, end, serviceId) ⇒ `PersistedItem | null` - .minimumSince(timestamp, serviceId) ⇒ `PersistedItem | null` - .minimumUntil(timestamp, serviceId) ⇒ `PersistedItem | null` - .minimumBetween(begin, end, serviceId) ⇒ `PersistedItem | null` - .medianSince(timestamp, serviceId) ⇒ `PersistedState | null` - .medianUntil(timestamp, serviceId) ⇒ `PersistedState | null` - .medianBetween(begin, end, serviceId) ⇒ `PersistedState | null` - .persist(serviceId): Tells the persistence service to store the current Item state, which is then done asynchronously. **Warning:** This has the side effect, that if the Item state changes shortly after `.persist` has been called, the new Item state will be persisted. See [JSDoc](https://openhab.github.io/openhab-js/items.ItemPersistence.html#persist) for a possible work-around. - .persist(timestamp, state, serviceId): Tells the persistence service to store the given state at the given timestamp, which is then done asynchronously. - .persist(timeSeries, serviceId): Tells the persistence service to store the given [`TimeSeries`](#timeseries), which is then done asynchronously. - .persistedState(timestamp, serviceId) ⇒ `PersistedItem | null` - .previousState(skipEqual, serviceId) ⇒ `PersistedItem | null` - .nextState(skipEqual, serviceId) ⇒ `PersistedItem | null` - .riemannSumSince(timestamp, riemannType, serviceId) ⇒ `PersistedState | null` - .riemannSumUntil(timestamp, riemannType, serviceId) ⇒ `PersistedState | null` - .riemannSumBetween(begin, end, riemannType, serviceId) ⇒ `PersistedState | null` - .sumSince(timestamp, serviceId) ⇒ `PersistedState | null` - .sumUntil(timestamp, serviceId) ⇒ `PersistedState | null` - .sumBetween(begin, end, serviceId) ⇒ `PersistedState | null` - .updatedSince(timestamp, serviceId) ⇒ `boolean` - .updatedUntil(timestamp, serviceId) ⇒ `boolean` - .updatedBetween(begin, end, serviceId) ⇒ `boolean` - .varianceSince(timestamp, serviceId) ⇒ `PersistedState | null` - .varianceUntil(timestamp, serviceId) ⇒ `PersistedState | null` - .varianceBetween(begin, end, serviceId) ⇒ `PersistedState | null` `riemannType` is an optional argument for methods that require calculating an approximation of the integral value. The approximation is calculated using a Riemann sum, with left, right, trapezoidal or midpoint value approximations. The argument is a Java RiemannType enum with possible values: `RiemannType.LEFT`, `RiemannType.RIGHT`, `RiemannType.TRAPEZOIDAL` or `RiemannType.MIDPOINT`. If omitted, `RiemannType.LEFT` is used. The RiemannType enum can be statically accessed on the `items` namespace, e.g.: ```javascript items.RiemannType.LEFT ``` A Riemann sum is always calculated using seconds as unit for time. As an example, the Riemann sum of power values in `kW` will result in an energy measurement in `kWs`. You can rely on framework functionality to convert to the appropriate unit (e.g. `kWh`), or do an explicit conversion. If you don't use units, be aware of this time factor. 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.KitchenDimmer; console.log('KitchenDimmer averageSince', item.persistence.averageSince(yesterday)); ``` The `PersistedState` object contains the following properties, representing Item state: - `state`: State as string - `numericState`: State as number, if state can be represented as number, or `null` if that's not the case - `quantityState`: Item state as [`Quantity`](#quantity) or `null` if state is not Quantity-compatible - `rawState`: State as Java `State` object The `PersistedItem` object extends `PersistedState` with the following properties, representing Item state and the respective timestamp: - `timestamp`: Timestamp as [`time.ZonedDateTime`](#time) - `instant`: Timestamp as [`time.Instant`](#time) ```javascript var midnight = time.toZDT('00:00'); var historic = items.KitchenDimmer.persistence.maximumSince(midnight); console.log('KitchenDimmer maximum was ', historic.state, ' at ', historic.timestamp); ``` See [openhab-js : ItemPersistence](https://openhab.github.io/openhab-js/items.ItemPersistence.html) for full API documentation. #### `TimeSeries` A `TimeSeries` is used to transport a set of states together with their timestamp. It is usually used for persisting historic state or forecasts in a persistence service by using [`ItemPersistence.persist`](#itempersistence). When creating a new `TimeSeries`, a policy must be chosen - it defines how the `TimeSeries` is persisted in a persistence service: - `ADD` adds the content to the persistence, well suited for persisting historic data. - `REPLACE` first removes all persisted elements in the timespan given by begin and end of the `TimeSeries`, well suited for persisting forecasts. A `TimeSeries` object has the following properties and methods: - `policy`: The persistence policy, either `ADD` or `REPLACE`. - `begin`: Timestamp of the first element of the `TimeSeries`. - `end`: Timestamp of the last element of the `TimeSeries`. - `size`: Number of elements in the `TimeSeries`. - `states`: States of the `TimeSeries` together with their timestamp and sorted by their timestamps. Be aware that this returns a reference to the internal state array, so changes to the array will affect the `TimeSeries`. - `add(timestamp, state)`: Add a given state to the `TimeSeries` at the given timestamp. The following example shows how to create a `TimeSeries`: ```javascript var timeSeries = new items.TimeSeries('ADD'); // Create a new TimeSeries with policy ADD timeSeries.add(time.toZDT('2024-01-01T14:53'), Quantity('5 m')).add(time.toZDT().minusMinutes(2), Quantity('0 m')).add(time.toZDT().plusMinutes(5), Quantity('5 m')); console.log(ts); // Let's have a look at the TimeSeries items.getItem('MyDistanceItem').persistence.persist(timeSeries, 'influxdb'); // Persist the TimeSeries for the Item 'MyDistanceItem' using the InfluxDB persistence service ``` ### 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) ⇒ <code>Thing|null</code> - .getThings() ⇒ <code>Array[Thing]</code> #### `getThing(uid, nullIfMissing)` Calling `getThing(uid)` 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) ```javascript var thing = things.getThing('astro:moon:home'); console.log('Thing label: ' + thing.label); // Set Thing location thing.setLocation('living room'); // Disable Thing thing.setEnabled(false); ``` ### Actions The actions namespace allows interactions with openHAB actions. The following are a list of standard actions. **Warning:** Please be aware, that (unless not explicitly noted) there is **no** type conversion from Java to JavaScript types for the return values of actions. Read the JavaDoc linked from the JSDoc to learn about the returned Java types. Please note that most of the actions currently do **not** provide type definitions and therefore auto-completion does not work. See [openhab-js : actions](https://openhab.github.io/openhab-js/actions.html) for full API documentation and additional actions. #### Audio Actions See [openhab-js : actions.Audio](https://openhab.github.io/openhab-js/actions.html#.Audio) for complete documentation. #### BusEvent Actions See [openhab-js : actions.BusEvent](https://openhab.github.io/openhab-js/actions.html#.BusEvent) for complete documentation. #### CoreUtil Actions See [openhab-js : actions.CoreUtil](https://openhab.github.io/openhab-js/actions.html#.CoreUtil) for complete documentation. The `CoreUtil` actions provide access to parts of the utilities included in openHAB core, see [org.openhab.core.util](https://www.openhab.org/javadoc/latest/org/openhab/core/util/package-summary). These include several methods to convert between color types like HSB, RGB, sRGB, RGBW and XY. #### Ephemeris Actions See [openhab-js : actions.Ephemeris](https://openhab.github.io/openhab-js/actions.html#.Ephemeris) for complete documentation. Ephemeris is a way to determine what type of day today or a number of days before or after today is. For example, a way to determine if today is a weekend, a public holiday, someone’s birthday, trash day, etc. Additional information can be found on the [Ephemeris Actions Docs](https://www.openhab.org/docs/configuration/actions.html#ephemeris) as well as the [Ephemeris JavaDoc](https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/ephemeris). ```javascript var weekend = actions.Ephemeris.isWeekend(); ``` #### Exec Actions See [openhab-js : actions.Exec](https://openhab.github.io/openhab-js/actions.html#.Exec) for complete documentation. Execute a command line. ```javascript // Execute command line. actions.Exec.executeCommandLine('echo', 'Hello World!'); // Execute command line with timeout. actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), 'echo', 'Hello World!'); // Get response from command line with timeout. var response = actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), 'echo', 'Hello World!'); ``` #### HTTP Actions See [openhab-js : actions.HTTP](https://openhab.github.io/openhab-js/actions.html#.HTTP) for complete documentation. ```javascript // Example GET Request var response = actions.HTTP.sendHttpGetRequest('<url>'); ``` Replace `<url>` with the request url. #### Ping Actions See [openhab-js : actions.Ping](https://openhab.github.io/openhab-js/actions.html#.Ping) for complete documentation. ```javascript // Check if a host is reachable var reachable = actions.Ping.checkVitality(host, port, timeout); // host: string, port: int, timeout: int ``` #### ScriptExecution Actions The `ScriptExecution` actions provide the `callScript(string scriptName)` method, which calls a script located at the `$OH_CONF/scripts` folder, as well as the `createTimer` method. You can also create timers using the [native JS methods for timer creation](#timers), your choice depends on the versatility you need. Sometimes, using `setTimeout` is much faster and easier, but other times, you need the versatility that `createTimer` provides. Keep in mind that you should somehow manage the timers you create using `createTimer`, otherwise you could end up with unmanageable timers running until you restart openHAB. A possible solution is to store all timers in the [private cache](#cache) and let openHAB automatically cancel them when the script is unloaded and the cache is cleared. When using `createTimer`, please read [Accessing Variables](#accessing-variables) to avoid having unexpected results when using variables in timers. ##### `createTimer` ```javascript actions.ScriptExecution.createTimer(time.ZonedDateTime zdt, function functionRef, any param1, /* ... */ paramN); actions.ScriptExecution.createTimer(string identifier, time.ZonedDateTime zdt, function functionRef, any param1, /* ... */ paramN); ``` `createTimer` accepts the following arguments: - `string` identifier (optional): Identifies the timer by a string, used e.g. for logging errors that occur during the callback execution. - [`time.ZonedDateTime`](#timetozdt) zdt: Point in time when the callback should be executed. - `function` functionRef: Callback function to execute when the timer expires. - `*` param1, ..., paramN: Additional arguments which are passed through to the function specified by `functionRef`. `createTimer` returns an openHAB Timer, that provides 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` - `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 ```javascript var now = time.ZonedDateTime.now(); // Function to run when the timer goes off. function timerOver () { console.info('The timer expired.'); } // Create the Timer. var myTimer = actions.ScriptExecution.createTimer('My Timer', now.plusSeconds(10), timerOver); // Cancel the timer. myTimer.cancel(); // Check whether the timer is active. Returns true if the timer is active and will be executed as scheduled. var active = myTimer.isActive(); // Reschedule the timer. myTimer.reschedule(now.plusSeconds(5)); ``` See [openhab-js : actions.ScriptExecution](https://openhab.github.io/openhab-js/actions.ScriptExecution.html) for complete documentation. #### Transformation Actions openHAB provides various [data transformation services](https://www.openhab.org/addons/#transform) which can translate between technical and human-readable values. Usually, they are used directly on Items, but it is also possible to access them from scripts. ```javascript console.log(actions.Transformation.transform('MAP', 'en.map', 'OPEN')); // open console.log(actions.Transformation.transform('MAP', 'de.map', 'OPEN')); // offen ``` See [openhab-js : actions.Transformation](https://openhab.github.io/openhab-js/actions.Transformation.html) for complete documentation. #### Voice Actions See [openhab-js : actions.Voice](https://openhab.github.io/openhab-js/actions.html#.Voice) for complete documentation. #### Cloud Notification Actions Requires the [openHAB Cloud Connector](https://www.openhab.org/addons/integrations/openhabcloud/) to be installed. Notification actions may be placed in rules to send alerts to mobile devices registered with an [openHAB Cloud instance](https://github.com/openhab/openhab-cloud) such as [myopenHAB.org](https://myopenhab.org/). There are three different types of notifications: - Broadcast Notifications: Sent to all registered devices and shown as notification on these devices. - Standard Notifications: Sent to the registered devices of the specified user and shown as notification on his devices. - Log Notifications: Only shown in the notification log, e.g. inside the Android and iOS Apps. In addition to that, notifications can be updated later be re-using the same `referenceId` and hidden/removed either by `referenceId` or `tag`. To send these three types of notifications, use the `notificationBuilder(message)` method of the `actions` namespace. `message` is optional and may be omitted. It returns a new `NotificationBuilder` object, which by default sends a broadcast notification and provides the following methods: - `.logOnly()`: Send a log notification only. - `.hide()`: Hides notification(s) with the specified `referenceId` or `tag` (`referenceId` has precedence over `tag`). - `.addUserId(emailAddress)`: By adding the email address(es) of specific openHAB Cloud user(s), the notification is only sent to this (these) user(s). To add multiple users, either call `addUserId` multiple times or pass mutiple emails as multiple params, e.g. `addUserId(emailAddress1, emailAddress2)`. - `.withIcon(icon)`: Sets the icon of the notification. - `.withTag(tag)`: Sets the tag of the notification. Used for grouping notifications and to hide/remove groups of notifications. - `.withTitle(title)`: Sets the title of the notification. - `.withReferenceId(referenceId)`: Sets the reference ID of the notification. If none is set, but it might be useful, a random UUID will be generated. The reference ID can be used to update or hide the notification later by using the same reference ID again. - `.withOnClickAction(action)`: Sets the action to be executed when the notification is clicked. - `.withMediaAttachmentUrl(mediaAttachmentUrl)`: Sets the URL of a media attachment to be displayed with the notification. This URL must be reachable by the push notification client. - `.addActionButton(label, action)`: Adds an action button to the notification. Please note that due to Android and iOS limitations, only three action buttons are supported. - `.send()` ⇒ `string|null`: Sends the notification and returns the reference ID or `null` for log notifications and when hiding notifications. The syntax for the `action` parameter is described in [openHAB Cloud Connector: Action Syntax](https://www.openhab.org/addons/integrations/openhabcloud/#action-syntax). The syntax for the `mediaAttachmentUrl` parameter is described in [openHAB Cloud Connector](https://www.openhab.org/addons/integrations/openhabcloud/). ```javascript // Send a simple broadcast notification actions.notificationBuilder('Hello World!').send(); // Send a broadcast notification with icon, tag and title actions.notificationBuilder('Hello World!') .withIcon('f7:bell_fill').withTag('important').withTitle('Important Notification').send(); // Send a broadcast notification with icon, tag, title, media attachment URL and actions actions.notificationBuilder('Hello World!') .withIcon('f7:bell_fill').withTag('important').withTitle('Important Notification') .withOnClickAction('ui:navigate:/page/my_floorplan_page').withMediaAttachmentUrl('http://example.com/image.jpg') .addActionButton('Turn Kitchen Light ON', 'command:KitchenLights:ON').addActionButton('Turn Kitchen Light OFF', 'command:KitchenLights:OFF').send(); // Send a simple standard notification to two specific users actions.notificationBuilder('Hello World!').addUserId('florian@example.com').addUserId('florian@example.org').send(); // Send a standard notification with icon, tag and title to two specific users actions.notificationBuilder('Hello World!').addUserId('florian@example.com').addUserId('florian@example.org') .withIcon('f7:bell_fill').withTag('important').withTitle('Important notification').send(); // Sends a simple log notification actions.notificationBuilder('Hello World!').logOnly().send(); // Sends a simple log notification with icon and tag actions.notificationBuilder('Hello World!').logOnly() .withIcon('f7:bell_fill').withTag('important').send(); // Sends a notification about a temperature change ... actions.notificationBuilder('new temperature: xyz').withIcon('oh:temperature').withTag('Temperature change').withReferenceId('livingRoom').send(); // ... and hides it again after 10 minutes setTimeout(() => { actions.notificationBuilder().hide().withReferenceId('livingRoom').send(); }, 10 * 60 * 1000); ``` See [openhab-js : actions.NotificationBuilder](https://openhab.github.io/openhab-js/actions.html#.notificationBuilder) for complete documentation. ### Cache The cache namespace provides both a private and a shared cache that can be used to set and retrieve data that will be persisted between subsequent runs of the same or between scripts. The private cache can only be accessed by the same script and is cleared when the script is unloaded. You can use it to store primitives and objects, e.g. store timers or counters between subsequent runs of that script. When a script is unloaded and its cache is cleared, all timers (see [`createTimer`](#createtimer)) stored in its private cache are automatically cancelled. The shared cache is shared across all rules and scripts, it can therefore be accessed from any automation language. The access to every key is tracked and the key is removed when all scripts that ever accessed that key are unloaded. If that key stored a timer, the timer will be cancelled. You can use it to store primitives and **Java** objects, e.g. store timers or counters between multiple scripts. Due to a multi-threading limitation in GraalJS (the JavaScript engine used by JavaScript Scripting), it is not recommended to store JavaScript objects in the shared cache. Multi-threaded access to JavaScript objects will lead to script execution failure! You can work-around that limitation by either serialising and deserialising JS objects or by switching to their Java counterparts. Timers as created by [`createTimer`](#createtimer) can be stored in the shared cache. The ids of timers and intervals as created by `setTimeout` and `setInterval` cannot be shared across scripts as these ids are local to the script where they were created. See [openhab-js : cache](https://openhab.github.io/openhab-js/cache.html) for full API documentation. - cache : <code>object</code> - .private - .get(key, defaultSupplier) ⇒ <code>* | null</code> - .put(key, value) ⇒ <code>Previous * | null</code> - .remove(key) ⇒ <code>Previous * | null</code> - .exists(key) ⇒ <code>boolean</code> - .shared - .get(key, defaultSupplier) ⇒ <code>* | null</code> - .put(key, value) ⇒ <code>Previous * | null</code> - .remove(key) ⇒ <code>Previous * | 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 = 0))* ```js var counter = cache.shared.get('counter', () => 0); console.log('Counter: ' + counter); ``` **Example** *(Get a previously set value, modify and store it)* ```js var counter = cache.private.get('counter'); counter++; console.log('Counter: ' + counter); cache.private.put('counter', counter); ``` ### 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`. Anywhere a native Java `ZonedDateTime`, `Instant`, or `Duration` is required, the runtime will automatically convert a JS-Joda `ZonedDateTime`, `Instant`, 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.Kitchen; console.log("averageSince", item.persistence.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. #### Parsing and Formatting Occasionally, one will need to parse a non-supported date time string or generate one from a ZonedDateTime. To do this you will use [JS-Joda DateTimeFormatter and potentially your Locale](https://js-joda.github.io/js-joda/manual/formatting.html). However, shipping all the locales with the openhab-js library would lead to an unacceptable large size. Therefore, if you attempt to use the `DateTimeFormatter` and receive an error saying it cannot find your locale, you will need to manually install your locale and import it into your rule. [JS-Joda Locales](https://github.com/js-joda/js-joda/tree/master/packages/locale#use-prebui