From bb7d578fbdc140357fa1d8b5a5e5171da9edd7f7 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Tue, 8 Feb 2022 23:59:19 +0100 Subject: [PATCH 01/19] Time: Add `updateTimeout` & `ZDTtoMillisFromNow` updateTimeout can reschedule a timer created by setTimeout. ZDTtoMillisFromNow parses the duration from now to the given ZDT in milliseconds. Signed-off-by: Florian Hotze --- README.md | 41 ++++++++++++++++++++++++++++++++++++++--- time.js | 43 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 12a49958e..e12a26482 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,9 @@ var timeoutID = setTimeout(function[, 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. +See https://developer.mozilla.org/en-US/docs/Web/API/setTimeout for more information about setTimeout. + +openHAB additionally allows you to reschedule the timer, see [`updateTimeout`](#updatetimeout). ### SetInterval @@ -169,7 +171,7 @@ The global `clearInterval()` method cancels a timed, repeating action which was 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. +See https://developer.mozilla.org/en-US/docs/Web/API/setInterval for more information about setInterval. ### ScriptLoaded @@ -453,6 +455,8 @@ 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 that a native Java `ZonedDateTime` or `Duration` is required, the runtime will automatically convert a JS-Joda `ZonedDateTime` or `Duration` to its Java counterpart. +This namespace also provides additional functionality. + Examples: ```javascript var now = time.ZonedDateTime.now(); @@ -466,7 +470,38 @@ console.log("averageSince", item.history.averageSince(yesterday)); 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. +#### updateTimeout + +The `updateTimeout()` reschedules a timer created by [`setTimeout`](#settimeout) to expire at a new time. + +It takes two arguments: +- `timeoutID` the timer returned by `setTimeout` +- `delay` the time in milliseconds until the timer expires + +```javascript +time.updateTimeout(timeoutID, delay); +``` +Example: +```javascript +var timeoutID = setTimeout({ console.log('Info'); }, 1000); +time.updateTimeout(timeoutID, 2000); +time.updateTimeout(timeoutID, time.Duration.ofSeconds(2).toMillis()); +``` + +#### ZDTtoMillisFromNow + +Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. + +```javascript +time.ZDTtoMillisFromNow(ZonedDateTime); +``` +Example: +```javascript +time.ZDTtoMillisFromNow(time.ZonedDateTime.now().plusSeconds(1)); // returns (about) 1000 +``` + +See [openhab-js : time](https://openhab.github.io/openhab-js/time.html) for full API documentation (for the additions). +See [JS-Joda](https://js-joda.github.io/js-joda/) for more examples and complete API usage (of JS-Joda). ### Utils diff --git a/time.js b/time.js index 5bc73e175..7d59cb0e1 100644 --- a/time.js +++ b/time.js @@ -1,12 +1,47 @@ +/** + * Time namespace. + * This namespace exports the {@link https://js-joda.github.io/js-joda/ JS-Joda library}, but also provides additional functionality. + * + * @namespace time + */ require('@js-joda/timezone'); const time = require('@js-joda/core'); -//openHAB uses a RFC DateTime string, js-joda defaults to the ISO version, this defaults RFC instead +// openHAB uses a RFC DateTime string, js-joda defaults to the ISO version, this defaults RFC instead const rfcFormatter = time.DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS[xxxx][xxxxx]"); const targetParse = time.ZonedDateTime.prototype.parse; time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { - return targetParse(text, formatter); -} + return targetParse(text, formatter); +}; -module.exports = time; +/** + * Reschedule a timeout. + * + * @memberOf time + * @param {*} timer the timer returned by {@link https://github.com/openhab/openhab-js#settimeout setTimeout} + * @param {number} delay the time in milliseconds that the timer should wait before it expires and executes the function + */ +const updateTimeout = function (timer, delay) { + if (timer !== undefined && timer.isActive() && !timer.isRunning()) { + timer.reschedule(time.ZonedDateTime.now().plusNanos(delay * 1000000)); + } +}; + +/** + * Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. + * + * @memberOf time + * @param {ZonedDateTime} zdt ZonedDateTime representation to parse + * @returns {number} duration from now to the ZonedDateTime in milliseconds + */ +const ZDTtoMillisFromNow = function (zdt) { + const duration = time.Duration.between(time.ZonedDateTime.now(), zdt); + return duration.toMillis(); +}; + +module.exports = { + ...time, + updateTimeout: updateTimeout, + ZDTtoMillisFromNow: ZDTtoMillisFromNow +}; From 0d56361554a42bc9fe51e5a902283b9bb056b52b Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Fri, 11 Feb 2022 16:41:56 +0100 Subject: [PATCH 02/19] Time: Remove unnecessary checks from `updateTimeout` Signed-off-by: Florian Hotze --- time.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/time.js b/time.js index 7d59cb0e1..5c9bd0ef8 100644 --- a/time.js +++ b/time.js @@ -17,13 +17,14 @@ time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { /** * Reschedule a timeout. + * This can also be called after a timer has terminated, which will result in another execution of the code. * * @memberOf time * @param {*} timer the timer returned by {@link https://github.com/openhab/openhab-js#settimeout setTimeout} * @param {number} delay the time in milliseconds that the timer should wait before it expires and executes the function */ const updateTimeout = function (timer, delay) { - if (timer !== undefined && timer.isActive() && !timer.isRunning()) { + if (timer !== undefined) { timer.reschedule(time.ZonedDateTime.now().plusNanos(delay * 1000000)); } }; From 4e0e2f01ec8d93d681293a0f7f1a972b5827df21 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Sat, 12 Feb 2022 22:50:22 +0100 Subject: [PATCH 03/19] Time: Monkey-patch ZonedDateTime `millisFromNow` & Add README section about monkey patches Signed-off-by: Florian Hotze --- README.md | 36 +++++------------------------------- time.js | 10 ++++------ 2 files changed, 9 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e12a26482..51930fa57 100644 --- a/README.md +++ b/README.md @@ -455,7 +455,8 @@ 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 that a native Java `ZonedDateTime` or `Duration` is required, the runtime will automatically convert a JS-Joda `ZonedDateTime` or `Duration` to its Java counterpart. -This namespace also provides additional functionality. +The openHAB-provided [JS-Joda](#https://js-joda.github.io/js-joda/) library has some extra features that are added on top (so called monkey-patched). +Those extras are all descibed in [openHAB-specific additions to JS-Joda](#openhab-specific-additions-to-js-joda). Examples: ```javascript @@ -470,37 +471,10 @@ console.log("averageSince", item.history.averageSince(yesterday)); actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), 'echo', 'Hello World!'); ``` -#### updateTimeout +#### openHAB-specific additions to JS-Joda +* `ZonedDateTime` + * `millisFromNow()` => Returns the milliseconds from now until the ZonedDateTime representation. -The `updateTimeout()` reschedules a timer created by [`setTimeout`](#settimeout) to expire at a new time. - -It takes two arguments: -- `timeoutID` the timer returned by `setTimeout` -- `delay` the time in milliseconds until the timer expires - -```javascript -time.updateTimeout(timeoutID, delay); -``` -Example: -```javascript -var timeoutID = setTimeout({ console.log('Info'); }, 1000); -time.updateTimeout(timeoutID, 2000); -time.updateTimeout(timeoutID, time.Duration.ofSeconds(2).toMillis()); -``` - -#### ZDTtoMillisFromNow - -Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. - -```javascript -time.ZDTtoMillisFromNow(ZonedDateTime); -``` -Example: -```javascript -time.ZDTtoMillisFromNow(time.ZonedDateTime.now().plusSeconds(1)); // returns (about) 1000 -``` - -See [openhab-js : time](https://openhab.github.io/openhab-js/time.html) for full API documentation (for the additions). See [JS-Joda](https://js-joda.github.io/js-joda/) for more examples and complete API usage (of JS-Joda). ### Utils diff --git a/time.js b/time.js index 5c9bd0ef8..28f14d073 100644 --- a/time.js +++ b/time.js @@ -31,18 +31,16 @@ const updateTimeout = function (timer, delay) { /** * Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. + * This is a monkey-patched function on JS-Joda's ZonedDateTime. * - * @memberOf time - * @param {ZonedDateTime} zdt ZonedDateTime representation to parse * @returns {number} duration from now to the ZonedDateTime in milliseconds */ -const ZDTtoMillisFromNow = function (zdt) { - const duration = time.Duration.between(time.ZonedDateTime.now(), zdt); +time.ZonedDateTime.prototype.millisFromNow = function () { + const duration = time.Duration.between(time.ZonedDateTime.now(), this); return duration.toMillis(); }; module.exports = { ...time, - updateTimeout: updateTimeout, - ZDTtoMillisFromNow: ZDTtoMillisFromNow + updateTimeout: updateTimeout }; From 3a861df7bdda95c6cbee5e778ba2df3d02dd590d Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Sat, 12 Feb 2022 22:57:00 +0100 Subject: [PATCH 04/19] Time: Remove `updateTimeout` & Document the returned Timer Signed-off-by: Florian Hotze --- README.md | 12 +++++++++++- time.js | 19 +------------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 51930fa57..1399449ae 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,17 @@ The global `clearTimeout()` method cancels a timeout previously established by c See https://developer.mozilla.org/en-US/docs/Web/API/setTimeout for more information about setTimeout. -openHAB additionally allows you to reschedule the timer, see [`updateTimeout`](#updatetimeout). +openHAB does not return the integer timeout id as standard JS does, instead it returns an openHAB Timer. +This allows you to execute all methods described in [openHAB JavaDoc - Timer](https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/timer). + +You can reschedule a timer by running the `reschedule(ZonedDateTime)` method on the return of `setTimeout()`. +`reschedule(ZonedDateTime)` can also be called after a timer has terminated, which will result in another execution of the code. + +Example: +```javascript +var timeoutID = setTimeout(function, 1000); // Expire in 1 second. +timeoutID.reschedule(time.ZonedDateTime.now().plusSeconds(5)); // Reschedule timer to expire in 5 seconds. +``` ### SetInterval diff --git a/time.js b/time.js index 28f14d073..91ecd9ef4 100644 --- a/time.js +++ b/time.js @@ -15,20 +15,6 @@ time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { return targetParse(text, formatter); }; -/** - * Reschedule a timeout. - * This can also be called after a timer has terminated, which will result in another execution of the code. - * - * @memberOf time - * @param {*} timer the timer returned by {@link https://github.com/openhab/openhab-js#settimeout setTimeout} - * @param {number} delay the time in milliseconds that the timer should wait before it expires and executes the function - */ -const updateTimeout = function (timer, delay) { - if (timer !== undefined) { - timer.reschedule(time.ZonedDateTime.now().plusNanos(delay * 1000000)); - } -}; - /** * Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. * This is a monkey-patched function on JS-Joda's ZonedDateTime. @@ -40,7 +26,4 @@ time.ZonedDateTime.prototype.millisFromNow = function () { return duration.toMillis(); }; -module.exports = { - ...time, - updateTimeout: updateTimeout -}; +module.exports = time; From d8cf009915a73f8a94fc31935d687e1cb8a5c71e Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Sun, 13 Feb 2022 02:09:01 +0100 Subject: [PATCH 05/19] Time: `millisFromNow` improvements Signed-off-by: Florian Hotze --- time.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/time.js b/time.js index 91ecd9ef4..2eace6e93 100644 --- a/time.js +++ b/time.js @@ -17,13 +17,12 @@ time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { /** * Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. - * This is a monkey-patched function on JS-Joda's ZonedDateTime. * + * @memberof time * @returns {number} duration from now to the ZonedDateTime in milliseconds */ time.ZonedDateTime.prototype.millisFromNow = function () { - const duration = time.Duration.between(time.ZonedDateTime.now(), this); - return duration.toMillis(); + return time.Duration.between(time.ZonedDateTime.now(), this).toMillis(); }; module.exports = time; From 840f29089d8952224eef8a9c96b81e49469a1088 Mon Sep 17 00:00:00 2001 From: Florian Hotze Date: Sun, 13 Feb 2022 02:38:29 +0100 Subject: [PATCH 06/19] README: Improvements for JS-Joda and Timers Signed-off-by: Florian Hotze --- README.md | 70 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 1399449ae..46743f5f2 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ binding](https://www.openhab.org/addons/automation/jsscripting/) - [Scripting Basics](#scripting-basics) - [Require](#require) - [Console](#console) - - [SetTimeout](#settimeout) - - [SetInterval](#setinterval) + - [Timers](#timers) - [ScriptLoaded](#scriptloaded) - [ScriptUnLoaded](#scriptunloaded) - [Paths](#paths) @@ -144,9 +143,11 @@ The string representations of each of these objects are appended together in the see https://developer.mozilla.org/en-US/docs/Web/API/console for more information about console logging. -### SetTimeout +### Timers -The global setTimeout() method sets a timer which executes a function or specified piece of code once the timer expires. +#### 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]); @@ -156,19 +157,9 @@ The global `clearTimeout()` method cancels a timeout previously established by c See https://developer.mozilla.org/en-US/docs/Web/API/setTimeout for more information about setTimeout. -openHAB does not return the integer timeout id as standard JS does, instead it returns an openHAB Timer. -This allows you to execute all methods described in [openHAB JavaDoc - Timer](https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/timer). - -You can reschedule a timer by running the `reschedule(ZonedDateTime)` method on the return of `setTimeout()`. -`reschedule(ZonedDateTime)` can also be called after a timer has terminated, which will result in another execution of the code. - -Example: -```javascript -var timeoutID = setTimeout(function, 1000); // Expire in 1 second. -timeoutID.reschedule(time.ZonedDateTime.now().plusSeconds(5)); // Reschedule timer to expire in 5 seconds. -``` +openHAB does not return the integer timeoutID as standard JS does, instead it returns an instance of [openHAB Timer](#openhab-timer). -### SetInterval +#### SetInterval The setInterval() method repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. @@ -183,6 +174,30 @@ NOTE: Timers will not be canceled if a script is deleted or modified, it is up t See https://developer.mozilla.org/en-US/docs/Web/API/setInterval for more information about 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 + +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.'); +timer.cancel(); +if (timer.isCancelled()) console.log('Timer has been cancelled.'); +timer.reschedule(time.ZonedDateTime.now().plusSeconds(2)); // Logs 'Timer expired.' in 2s. +``` + +See [openHAB JavaDoc - Timer](https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/timer) for full API documentation. + ### ScriptLoaded For file based scripts, this function will be called if found when the script is loaded. @@ -312,11 +327,11 @@ See [openhab-js : actions](https://openhab.github.io/openhab-js/actions.html) fo #### Audio Actions -See [openhab-js : actions.Audio](https://openhab.github.io/openhab-js/actions.html#.Audio) for complete documentation +See [openhab-js : actions.Audio](https://openhab.github.io/openhab-js/actions.html#.Audio) for complete documentation. #### BusEvent -See [openhab-js : actions.BusEvent](https://openhab.github.io/openhab-js/actions.html#.BusEvent) for complete documentation +See [openhab-js : actions.BusEvent](https://openhab.github.io/openhab-js/actions.html#.BusEvent) for complete documentation. #### Ephemeris Actions @@ -333,7 +348,7 @@ let weekend = actions.Ephemeris.isWeekend(); #### Exec Actions -See [openhab-js : actions.Exec](https://openhab.github.io/openhab-js/actions.html#.Exec) for complete documentation +See [openhab-js : actions.Exec](https://openhab.github.io/openhab-js/actions.html#.Exec) for complete documentation. Execute a command line. @@ -422,7 +437,7 @@ Replace `` with the notification text. The cache namespace provides a default cache that can be use to set and retrieve objects that will be persisted between reloads of scripts. -See [openhab-js : cache](https://openhab.github.io/openhab-js/cache.html) for full API documentation +See [openhab-js : cache](https://openhab.github.io/openhab-js/cache.html) for full API documentation. * cache : object * .get(key, defaultSupplier) ⇒ Object | null @@ -465,27 +480,28 @@ 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 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 openHAB-provided [JS-Joda](#https://js-joda.github.io/js-joda/) library has some extra features that are added on top (so called monkey-patched). -Those extras are all descibed in [openHAB-specific additions to JS-Joda](#openhab-specific-additions-to-js-joda). +#### openHAB-JS extensions to JS-Joda +The exported [JS-Joda](#https://js-joda.github.io/js-joda/) library is also extended with convenient functions relevant to openHAB usage. + +* `ZonedDateTime` + * `millisFromNow()` ⇒ `number`: Milliseconds from now until the `time.ZonedDateTime` representation. Examples: ```javascript var now = time.ZonedDateTime.now(); var yesterday = time.ZonedDateTime.now().minusHours(24); +var millis = now.plusSeconds(5).millisFromNow(); var item = items.getItem("Kitchen"); console.log("averageSince", item.history.averageSince(yesterday)); +console.log("5 seconds in the future is " + millis + " milliseconds."); ``` ```javascript actions.Exec.executeCommandLine(time.Duration.ofSeconds(20), 'echo', 'Hello World!'); ``` -#### openHAB-specific additions to JS-Joda -* `ZonedDateTime` - * `millisFromNow()` => Returns the milliseconds from now until the ZonedDateTime representation. - -See [JS-Joda](https://js-joda.github.io/js-joda/) for more examples and complete API usage (of JS-Joda). +See [JS-Joda](https://js-joda.github.io/js-joda/) for more examples and complete API usage. ### Utils From 28acaeb2a6d125f7d4ec1d6158802ac8c5b1d4e6 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Thu, 17 Feb 2022 13:18:44 -0700 Subject: [PATCH 07/19] Implemented a toggle method on Item Signed-off-by: Richard Koshak --- items/managed.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/items/managed.js b/items/managed.js index 150ac175a..d72d4e9d7 100644 --- a/items/managed.js +++ b/items/managed.js @@ -6,7 +6,7 @@ const log = require('../log')('items'); const metadata = require('../metadata'); const ItemHistory = require('./item-history'); -const { UnDefType, events, itemRegistry } = require('@runtime'); +const { UnDefType, OnOffType, OpenClosedType, events, itemRegistry } = require('@runtime'); const itemBuilderFactory = osgi.getService("org.openhab.core.items.ItemBuilderFactory"); @@ -182,6 +182,28 @@ class Item { return false; } + /** + * Toggles the state of the Item. If the Item is undefined or the Item type + * does not support OnOffType commands/updates or OpenClosedType updates + * an error is thrown. To toggle Contacts, update must be true or an error + * is thrown (Contacts cannot be commanded). + * @param {Boolean=false} update when true the toggle will be sent as an update instead of a command + * @throws error if the Item cannot be toggled + */ + toggle(update=false) { + var newState = null; + var oldState = this.rawItem.getStateAs(OnOffType); + if(oldState) { + newState = (oldState.toString() == 'OFF') ? 'ON' : 'OFF'; + } + else if(update && this.rawItem.getStateAs(OpenClosedType)) { + newState = (this.rawItem.getStateAs(OpenClosedType).toString() == 'CLOSED' ? 'OPEN' : 'CLOSED'); + } + if(newState && update) this.postUpdate(newState); + else if(newState && !update) this.sendCommand(newState); + else throw Error('Items of type ' + this.rawItem.getType() + ' cannot be toggled via ' + ((update) ? 'an update' : 'a command')); + } + /** * Posts an update to the item * @param {String|HostState} value the value of the command to send, such as 'ON' From 94e8b95f36393e7396899b81918de488ddaac80d Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Thu, 17 Feb 2022 13:37:38 -0700 Subject: [PATCH 08/19] Fixed docstring error breaking the build Signed-off-by: Richard Koshak --- items/managed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/items/managed.js b/items/managed.js index d72d4e9d7..5992ccba5 100644 --- a/items/managed.js +++ b/items/managed.js @@ -187,7 +187,7 @@ class Item { * does not support OnOffType commands/updates or OpenClosedType updates * an error is thrown. To toggle Contacts, update must be true or an error * is thrown (Contacts cannot be commanded). - * @param {Boolean=false} update when true the toggle will be sent as an update instead of a command + * @param {Boolean} [update=false] when true the toggle will be sent as an update instead of a command * @throws error if the Item cannot be toggled */ toggle(update=false) { From 28b3a215c3ed1935d047477e041cc3a0b67a1e9d Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Thu, 17 Feb 2022 13:18:44 -0700 Subject: [PATCH 09/19] Implemented a toggle method on Item Signed-off-by: Richard Koshak --- items/managed.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/items/managed.js b/items/managed.js index 8443fd039..8e8011e56 100644 --- a/items/managed.js +++ b/items/managed.js @@ -6,7 +6,7 @@ const log = require('../log')('items'); const metadata = require('../metadata'); const ItemHistory = require('./item-history'); -const { UnDefType, events, itemRegistry } = require('@runtime'); +const { UnDefType, OnOffType, OpenClosedType, events, itemRegistry } = require('@runtime'); const itemBuilderFactory = osgi.getService("org.openhab.core.items.ItemBuilderFactory"); @@ -182,6 +182,28 @@ class Item { return false; } + /** + * Toggles the state of the Item. If the Item is undefined or the Item type + * does not support OnOffType commands/updates or OpenClosedType updates + * an error is thrown. To toggle Contacts, update must be true or an error + * is thrown (Contacts cannot be commanded). + * @param {Boolean=false} update when true the toggle will be sent as an update instead of a command + * @throws error if the Item cannot be toggled + */ + toggle(update=false) { + var newState = null; + var oldState = this.rawItem.getStateAs(OnOffType); + if(oldState) { + newState = (oldState.toString() == 'OFF') ? 'ON' : 'OFF'; + } + else if(update && this.rawItem.getStateAs(OpenClosedType)) { + newState = (this.rawItem.getStateAs(OpenClosedType).toString() == 'CLOSED' ? 'OPEN' : 'CLOSED'); + } + if(newState && update) this.postUpdate(newState); + else if(newState && !update) this.sendCommand(newState); + else throw Error('Items of type ' + this.rawItem.getType() + ' cannot be toggled via ' + ((update) ? 'an update' : 'a command')); + } + /** * Posts an update to the item * @param {String|HostState} value the value of the command to send, such as 'ON' From 2bc83d7e2e636d0704023e5521d44894cd949ec6 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Thu, 17 Feb 2022 13:37:38 -0700 Subject: [PATCH 10/19] Fixed docstring error breaking the build Signed-off-by: Richard Koshak --- items/managed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/items/managed.js b/items/managed.js index 8e8011e56..8c6bd5b5a 100644 --- a/items/managed.js +++ b/items/managed.js @@ -187,7 +187,7 @@ class Item { * does not support OnOffType commands/updates or OpenClosedType updates * an error is thrown. To toggle Contacts, update must be true or an error * is thrown (Contacts cannot be commanded). - * @param {Boolean=false} update when true the toggle will be sent as an update instead of a command + * @param {Boolean} [update=false] when true the toggle will be sent as an update instead of a command * @throws error if the Item cannot be toggled */ toggle(update=false) { From 0bcb93fd93895754ce49097925198a2334712e63 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 1 Mar 2022 11:47:20 -0700 Subject: [PATCH 11/19] Addressing comments Signed-off-by: Richard Koshak --- items/managed.js | 63 +++++++++++++++++++++++++++----------- rules/operation-builder.js | 16 +--------- 2 files changed, 46 insertions(+), 33 deletions(-) diff --git a/items/managed.js b/items/managed.js index 8c6bd5b5a..c61330b78 100644 --- a/items/managed.js +++ b/items/managed.js @@ -181,28 +181,55 @@ class Item { return false; } - + /** - * Toggles the state of the Item. If the Item is undefined or the Item type - * does not support OnOffType commands/updates or OpenClosedType updates - * an error is thrown. To toggle Contacts, update must be true or an error - * is thrown (Contacts cannot be commanded). - * @param {Boolean} [update=false] when true the toggle will be sent as an update instead of a command - * @throws error if the Item cannot be toggled + * Calculates the toggled state of this Item. For Items like Color and + * Dimmer, getStateAs(OnOffType) is used and the toggle calculated off + * of that. + * @returns the toggled state (e.g. 'OFF' if the Item is 'ON') + * @throws error if the Item is uninitialized or is a type that doesn't make sense to toggle */ - toggle(update=false) { - var newState = null; - var oldState = this.rawItem.getStateAs(OnOffType); - if(oldState) { - newState = (oldState.toString() == 'OFF') ? 'ON' : 'OFF'; + #getToggleState() { + if(this.isUninitialized) { + throw Error('Cannot toggle uninitialized Items'); + } + switch (this.type) { + case 'PlayerItem' : + return this.state == 'PAUSE' ? 'PLAY' : 'PAUSE'; + case 'ContactItem' : + return this.state == 'OPEN' ? 'CLOSED' : 'OPEN'; + default: { + const oldState = this.rawItem.getStateAs(OnOffType); + if(oldState) { + return oldState.toString() == 'ON' ? 'OFF' : 'ON'; + } + else { + throw Error('Toggle not supported for items of type ' + this.type); + } + } } - else if(update && this.rawItem.getStateAs(OpenClosedType)) { - newState = (this.rawItem.getStateAs(OpenClosedType).toString() == 'CLOSED' ? 'OPEN' : 'CLOSED'); + } + + /** + * Sends a command to flip the Item's state (e.g. if it is 'ON' an 'OFF' + * command is sent). + * @throws error if the Item is uninitialized or a type that cannot be toggled or commanded + */ + toggleCommand() { + if(this.type == 'ContactItem'){ + throw Error('Cannot command Contact Items'); } - if(newState && update) this.postUpdate(newState); - else if(newState && !update) this.sendCommand(newState); - else throw Error('Items of type ' + this.rawItem.getType() + ' cannot be toggled via ' + ((update) ? 'an update' : 'a command')); - } + this.sendCommand(this.#getToggleState()); + } + + /** + * Posts an update to flip the Item's state (e.g. if it is 'ON' an 'OFF' + * update is posted). + * @throws error if the Item is uninitialized or a type that cannot be toggled + */ + toggleUpdate() { + this.postUpdate(this.#getToggleState()); + } /** * Posts an update to the item diff --git a/rules/operation-builder.js b/rules/operation-builder.js index 694f662e1..da404516c 100644 --- a/rules/operation-builder.js +++ b/rules/operation-builder.js @@ -404,21 +404,7 @@ class ToggleOperation extends OperationConfig { */ doToggle() { let item = items.getItem(this.itemName); - - switch (item.type) { - case "SwitchItem": { - let toSend = ('ON' == item.state) ? 'OFF' : 'ON'; - item.sendCommand(toSend); - break; - } - case "ColorItem": { - let toSend = ('0' != item.rawState.getBrightness().toString()) ? 'OFF' : 'ON'; - item.sendCommand(toSend); - break; - } - default: - throw Error(`Toggle not supported for items of type ${item.type}`); - } + item.toggleCommand(); } } From 35f0108ee453c334da9b56a9dd30b04292cf6b46 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 1 Mar 2022 11:52:21 -0700 Subject: [PATCH 12/19] Forgot to remove the old toggle method-- Signed-off-by: Richard Koshak --- items/managed.js | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/items/managed.js b/items/managed.js index da10185e1..c61330b78 100644 --- a/items/managed.js +++ b/items/managed.js @@ -231,28 +231,6 @@ class Item { this.postUpdate(this.#getToggleState()); } - /** - * Toggles the state of the Item. If the Item is undefined or the Item type - * does not support OnOffType commands/updates or OpenClosedType updates - * an error is thrown. To toggle Contacts, update must be true or an error - * is thrown (Contacts cannot be commanded). - * @param {Boolean} [update=false] when true the toggle will be sent as an update instead of a command - * @throws error if the Item cannot be toggled - */ - toggle(update=false) { - var newState = null; - var oldState = this.rawItem.getStateAs(OnOffType); - if(oldState) { - newState = (oldState.toString() == 'OFF') ? 'ON' : 'OFF'; - } - else if(update && this.rawItem.getStateAs(OpenClosedType)) { - newState = (this.rawItem.getStateAs(OpenClosedType).toString() == 'CLOSED' ? 'OPEN' : 'CLOSED'); - } - if(newState && update) this.postUpdate(newState); - else if(newState && !update) this.sendCommand(newState); - else throw Error('Items of type ' + this.rawItem.getType() + ' cannot be toggled via ' + ((update) ? 'an update' : 'a command')); - } - /** * Posts an update to the item * @param {String|HostState} value the value of the command to send, such as 'ON' From 1f8a8003a0ceed87d33d55b71af34435176f1bbf Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 15 Mar 2022 12:08:11 -0600 Subject: [PATCH 13/19] README.md Undoing erroneous merge Signed-off-by: Richard Koshak --- README.md | 51 ++++++++------------------------------------------- 1 file changed, 8 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 46743f5f2..64ca75af5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ binding](https://www.openhab.org/addons/automation/jsscripting/) - [Scripting Basics](#scripting-basics) - [Require](#require) - [Console](#console) - - [Timers](#timers) + - [SetTimeout](#settimeout) + - [SetInterval](#setinterval) - [ScriptLoaded](#scriptloaded) - [ScriptUnLoaded](#scriptunloaded) - [Paths](#paths) @@ -143,9 +144,7 @@ The string representations of each of these objects are appended together in the see https://developer.mozilla.org/en-US/docs/Web/API/console for more information about console logging. -### Timers - -#### SetTimeout +### SetTimeout The global `setTimeout()` method sets a timer which executes a function or specified piece of code once the timer expires. ```javascript @@ -157,9 +156,7 @@ The global `clearTimeout()` method cancels a timeout previously established by c See https://developer.mozilla.org/en-US/docs/Web/API/setTimeout for more information about setTimeout. -openHAB does not return the integer timeoutID as standard JS does, instead it returns an instance of [openHAB Timer](#openhab-timer). - -#### SetInterval +### SetInterval The setInterval() method repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. @@ -172,31 +169,7 @@ The global `clearInterval()` method cancels a timed, repeating action which was 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. - -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 - -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.'); -timer.cancel(); -if (timer.isCancelled()) console.log('Timer has been cancelled.'); -timer.reschedule(time.ZonedDateTime.now().plusSeconds(2)); // Logs 'Timer expired.' in 2s. -``` - -See [openHAB JavaDoc - Timer](https://www.openhab.org/javadoc/latest/org/openhab/core/model/script/actions/timer) for full API documentation. +see https://developer.mozilla.org/en-US/docs/Web/API/setInterval for more information about setInterval. ### ScriptLoaded @@ -327,7 +300,7 @@ See [openhab-js : actions](https://openhab.github.io/openhab-js/actions.html) fo #### Audio Actions -See [openhab-js : actions.Audio](https://openhab.github.io/openhab-js/actions.html#.Audio) for complete documentation. +See [openhab-js : actions.Audio](https://openhab.github.io/openhab-js/actions.html#.Audio) for complete documentation #### BusEvent @@ -348,7 +321,7 @@ let weekend = actions.Ephemeris.isWeekend(); #### Exec Actions -See [openhab-js : actions.Exec](https://openhab.github.io/openhab-js/actions.html#.Exec) for complete documentation. +See [openhab-js : actions.Exec](https://openhab.github.io/openhab-js/actions.html#.Exec) for complete documentation Execute a command line. @@ -437,7 +410,7 @@ Replace `` with the notification text. The cache namespace provides a default cache that can be use to set and retrieve objects that will be persisted between reloads of scripts. -See [openhab-js : cache](https://openhab.github.io/openhab-js/cache.html) for full API documentation. +See [openhab-js : cache](https://openhab.github.io/openhab-js/cache.html) for full API documentation * cache : object * .get(key, defaultSupplier) ⇒ Object | null @@ -480,21 +453,13 @@ 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 that a native Java `ZonedDateTime` or `Duration` is required, the runtime will automatically convert a JS-Joda `ZonedDateTime` or `Duration` to its Java counterpart. -#### openHAB-JS extensions to JS-Joda -The exported [JS-Joda](#https://js-joda.github.io/js-joda/) library is also extended with convenient functions relevant to openHAB usage. - -* `ZonedDateTime` - * `millisFromNow()` ⇒ `number`: Milliseconds from now until the `time.ZonedDateTime` representation. - Examples: ```javascript var now = time.ZonedDateTime.now(); var yesterday = time.ZonedDateTime.now().minusHours(24); -var millis = now.plusSeconds(5).millisFromNow(); var item = items.getItem("Kitchen"); console.log("averageSince", item.history.averageSince(yesterday)); -console.log("5 seconds in the future is " + millis + " milliseconds."); ``` ```javascript From a874def1a3ce2f00785b83e519a6b952fadad07e Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 15 Mar 2022 12:10:21 -0600 Subject: [PATCH 14/19] Addressing comment to make cleaner Signed-off-by: Richard Koshak --- rules/operation-builder.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rules/operation-builder.js b/rules/operation-builder.js index da404516c..c37d709c3 100644 --- a/rules/operation-builder.js +++ b/rules/operation-builder.js @@ -403,8 +403,7 @@ class ToggleOperation extends OperationConfig { * @returns {OperationBuilder.SendCommandOrUpdateOperation} this */ doToggle() { - let item = items.getItem(this.itemName); - item.toggleCommand(); + items.getItem(this.itemName).toggleCommand(); } } From 94a2f929350fbc17062979e69124c80cadebcfce Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 15 Mar 2022 12:12:10 -0600 Subject: [PATCH 15/19] time.js: removing changes from erroneous merger Signed-off-by: Richard Koshak --- time.js | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/time.js b/time.js index 2eace6e93..3044b002b 100644 --- a/time.js +++ b/time.js @@ -1,28 +1,12 @@ -/** - * Time namespace. - * This namespace exports the {@link https://js-joda.github.io/js-joda/ JS-Joda library}, but also provides additional functionality. - * - * @namespace time - */ require('@js-joda/timezone'); const time = require('@js-joda/core'); -// openHAB uses a RFC DateTime string, js-joda defaults to the ISO version, this defaults RFC instead +//openHAB uses a RFC DateTime string, js-joda defaults to the ISO version, this defaults RFC instead const rfcFormatter = time.DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS[xxxx][xxxxx]"); const targetParse = time.ZonedDateTime.prototype.parse; time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { return targetParse(text, formatter); }; -/** - * Parses a ZonedDateTime to milliseconds from now until the ZonedDateTime. - * - * @memberof time - * @returns {number} duration from now to the ZonedDateTime in milliseconds - */ -time.ZonedDateTime.prototype.millisFromNow = function () { - return time.Duration.between(time.ZonedDateTime.now(), this).toMillis(); -}; - module.exports = time; From c7432593bc686752fb631e423919c02068f1d695 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 15 Mar 2022 12:14:19 -0600 Subject: [PATCH 16/19] Removing remenants of bad merge Signed-off-by: Richard Koshak --- README.md | 6 +++--- time.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 64ca75af5..12a49958e 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ see https://developer.mozilla.org/en-US/docs/Web/API/console for more informatio ### SetTimeout -The global `setTimeout()` method sets a timer which executes a function or specified piece of code once the timer expires. +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]); @@ -154,7 +154,7 @@ var timeoutID = setTimeout(function[, 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. +see https://developer.mozilla.org/en-US/docs/Web/API/setTimeout for more information about setTimeout. ### SetInterval @@ -304,7 +304,7 @@ See [openhab-js : actions.Audio](https://openhab.github.io/openhab-js/actions.ht #### BusEvent -See [openhab-js : actions.BusEvent](https://openhab.github.io/openhab-js/actions.html#.BusEvent) for complete documentation. +See [openhab-js : actions.BusEvent](https://openhab.github.io/openhab-js/actions.html#.BusEvent) for complete documentation #### Ephemeris Actions diff --git a/time.js b/time.js index 3044b002b..cae5fc2f0 100644 --- a/time.js +++ b/time.js @@ -7,6 +7,6 @@ const rfcFormatter = time.DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS const targetParse = time.ZonedDateTime.prototype.parse; time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { return targetParse(text, formatter); -}; +} module.exports = time; From 97c7297d145f3490d900aa4a07506553029fe894 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Tue, 15 Mar 2022 12:15:18 -0600 Subject: [PATCH 17/19] Removing remnants of bad merge Signed-off-by: Richard Koshak --- time.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/time.js b/time.js index cae5fc2f0..5bc73e175 100644 --- a/time.js +++ b/time.js @@ -6,7 +6,7 @@ const time = require('@js-joda/core'); const rfcFormatter = time.DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSS[xxxx][xxxxx]"); const targetParse = time.ZonedDateTime.prototype.parse; time.ZonedDateTime.prototype.parse = function (text, formatter = rfcFormatter) { - return targetParse(text, formatter); + return targetParse(text, formatter); } module.exports = time; From e67d5a50905d2afe18748e5d7a35326f8105e771 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Wed, 27 Apr 2022 13:03:28 -0600 Subject: [PATCH 18/19] changed names of toggle methods Signed-off-by: Richard Koshak --- items/managed.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/items/managed.js b/items/managed.js index c61330b78..f2f865219 100644 --- a/items/managed.js +++ b/items/managed.js @@ -215,7 +215,7 @@ class Item { * command is sent). * @throws error if the Item is uninitialized or a type that cannot be toggled or commanded */ - toggleCommand() { + sendToggleCommand() { if(this.type == 'ContactItem'){ throw Error('Cannot command Contact Items'); } @@ -227,7 +227,7 @@ class Item { * update is posted). * @throws error if the Item is uninitialized or a type that cannot be toggled */ - toggleUpdate() { + postToggleUpdate() { this.postUpdate(this.#getToggleState()); } From 5b925b8afdc2af0eb06658fa5ecb3568e1338901 Mon Sep 17 00:00:00 2001 From: Richard Koshak Date: Mon, 2 May 2022 10:07:24 -0600 Subject: [PATCH 19/19] Addressing @florian-h05's comments. Signed-off-by: Richard Koshak --- rules/operation-builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/operation-builder.js b/rules/operation-builder.js index c37d709c3..90cc10457 100644 --- a/rules/operation-builder.js +++ b/rules/operation-builder.js @@ -403,7 +403,7 @@ class ToggleOperation extends OperationConfig { * @returns {OperationBuilder.SendCommandOrUpdateOperation} this */ doToggle() { - items.getItem(this.itemName).toggleCommand(); + items.getItem(this.itemName).sendToggleCommand(); } }