[jsscripting] Add type translation for `Instant` & Minor improvements (#14984)
* [jsscripting] Minor code improvements * [jsscripting] Add type mapping for `Instant` * [jsscripting] Upgrade openhab-js to 4.3.0 Signed-off-by: Florian Hotze <florianh_dev@icloud.com>pull/15006/head
parent
d9c12ca659
commit
aa3d6b0dc9
|
@ -18,7 +18,7 @@ to common openHAB functionality within rules including items, things, actions, l
|
|||
- [Timers](#timers)
|
||||
- [Paths](#paths)
|
||||
- [Deinitialization Hook](#deinitialization-hook)
|
||||
- [`SCRIPT` Transformation](#script-transformation)
|
||||
- [`JS` Transformation](#js-transformation)
|
||||
- [Standard Library](#standard-library)
|
||||
- [Items](#items)
|
||||
- [Things](#things)
|
||||
|
@ -204,7 +204,7 @@ JS Scripting provides access to the global `setTimeout`, `setInterval`, `clearTi
|
|||
|
||||
When a script is unloaded, all created timeouts and intervals are automatically cancelled.
|
||||
|
||||
#### 'setTimeout'
|
||||
#### `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.
|
||||
|
@ -217,6 +217,8 @@ var timeoutId = setTimeout(callbackFunction, delay);
|
|||
|
||||
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.
|
||||
|
@ -290,28 +292,31 @@ require('@runtime').lifecycleTracker.addDisposeHook(() => {
|
|||
});
|
||||
```
|
||||
|
||||
## `SCRIPT` Transformation
|
||||
## `JS` Transformation
|
||||
|
||||
openHAB provides several [data transformation services](https://www.openhab.org/addons/#transform) as well as the `SCRIPT` transformation, that is available from the framework and needs no additional installation.
|
||||
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` transformation.
|
||||
See the [transformation docs](https://openhab.org/docs/configuration/transformations.html#script-transformation) for more general information on the usage of script transformations.
|
||||
|
||||
Use the `SCRIPT` transformation with JavaScript Scripting by:
|
||||
Use JavaScript Scripting as script transformation by:
|
||||
|
||||
1. Creating a script in the `$OPENHAB_CONF/transform` folder with the `.script` extension.
|
||||
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
|
||||
return data;
|
||||
// Do some data transformation here, e.g.
|
||||
return "String has" + data.length + "characters";
|
||||
})(input);
|
||||
```
|
||||
|
||||
2. Using `SCRIPT(js:<scriptname>.script):%s` as the transformation profile, e.g. on an Item.
|
||||
3. Passing parameters is also possible by using a URL like syntax: `SCRIPT(js:<scriptname>.script?arg=value):%s`.
|
||||
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).
|
||||
|
@ -355,7 +360,7 @@ Calling `getItem(...)` or `...` returns an `Item` object with the following prop
|
|||
- .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
|
||||
- .quantityState ⇒ [`Quantity|null`](#quantity): Item state as Quantity or `null` if state is not Quantity-compatible or without unit
|
||||
- .rawState ⇒ `HostState`
|
||||
- .members ⇒ `Array[Item]`
|
||||
- .descendents ⇒ `Array[Item]`
|
||||
|
@ -606,7 +611,7 @@ You can also create timers using the [native JS methods for timer creation](#tim
|
|||
Sometimes, using `setTimer` 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 an array and cancel all timers in the [Deinitialization Hook](#deinitialization-hook).
|
||||
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.
|
||||
|
||||
##### `createTimer`
|
||||
|
||||
|
@ -704,7 +709,7 @@ The cache namespace provides both a private and a shared cache that can be used
|
|||
|
||||
The private cache can only be accessed by the same script and is cleared when the script is unloaded.
|
||||
You can use it to 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 [ScriptExecution Actions](#scriptexecution-actions)) stored in its private cache are cancelled.
|
||||
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.
|
||||
|
@ -940,6 +945,8 @@ qty = Quantity('1 m^2 s^2'); // / is required
|
|||
qty = Quantity('1 m2/s2'); // ^ is required
|
||||
```
|
||||
|
||||
Note: It is possible to create a unit-less (without unit) Quantity, however there is no advantage over using a `number` instead.
|
||||
|
||||
#### Conversion
|
||||
|
||||
It is possible to convert a `Quantity` to a new `Quantity` with a different unit or to get a `Quantity`'s amount as integer or float:
|
||||
|
@ -1102,7 +1109,7 @@ Operations and conditions can also optionally take functions:
|
|||
|
||||
```javascript
|
||||
rules.when().item("F1_light").changed().then(event => {
|
||||
console.log(event);
|
||||
console.log(event);
|
||||
}).build("Test Rule", "My Test Rule");
|
||||
```
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</bnd.importpackage>
|
||||
<graal.version>22.0.0.2</graal.version> <!-- DO NOT UPGRADE: 22.0.0.2 is the latest version working on armv7l / OpenJDK 11.0.16 & armv7l / Zulu 17.0.5+8 -->
|
||||
<oh.version>${project.version}</oh.version>
|
||||
<ohjs.version>openhab@4.2.1</ohjs.version>
|
||||
<ohjs.version>openhab@4.3.0</ohjs.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -14,7 +14,6 @@ package org.openhab.automation.jsscripting.internal;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
|
@ -49,15 +48,13 @@ public final class GraalJSScriptEngineFactory implements ScriptEngineFactory {
|
|||
|
||||
private static final GraalJSEngineFactory factory = new GraalJSEngineFactory();
|
||||
|
||||
public static final String MIME_TYPE = "application/javascript";
|
||||
private static final String ALIAS = "graaljs";
|
||||
private static final List<String> SCRIPT_TYPES = createScriptTypes();
|
||||
private static final List<String> scriptTypes = createScriptTypes();
|
||||
|
||||
private static List<String> createScriptTypes() {
|
||||
// Add those for backward compatibility (existing scripts may rely on those MIME types)
|
||||
List<String> backwardCompat = List.of("application/javascript;version=ECMAScript-2021", ALIAS);
|
||||
List<String> backwardCompat = List.of("application/javascript;version=ECMAScript-2021", "graaljs");
|
||||
return Stream.of(factory.getMimeTypes(), factory.getExtensions(), backwardCompat).flatMap(List::stream)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
.toList();
|
||||
}
|
||||
|
||||
private boolean injectionEnabled = true;
|
||||
|
@ -76,7 +73,7 @@ public final class GraalJSScriptEngineFactory implements ScriptEngineFactory {
|
|||
|
||||
@Override
|
||||
public List<String> getScriptTypes() {
|
||||
return SCRIPT_TYPES;
|
||||
return scriptTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,7 +83,7 @@ public final class GraalJSScriptEngineFactory implements ScriptEngineFactory {
|
|||
|
||||
@Override
|
||||
public @Nullable ScriptEngine createScriptEngine(String scriptType) {
|
||||
if (!SCRIPT_TYPES.contains(scriptType)) {
|
||||
if (!scriptTypes.contains(scriptType)) {
|
||||
return null;
|
||||
}
|
||||
return new DebuggingGraalScriptEngine<>(new OpenhabGraalJSScriptEngine(injectionEnabled, useIncludedLibrary,
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.nio.file.Path;
|
|||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
@ -113,6 +114,13 @@ public class OpenhabGraalJSScriptEngine
|
|||
v -> v.hasMember("minusDuration") && v.hasMember("toNanos"),
|
||||
v -> Duration.ofNanos(v.invokeMember("toNanos").asLong()), HostAccess.TargetMappingPrecedence.LOW)
|
||||
|
||||
// Translate JS-Joda Instant to java.time.Instant
|
||||
.targetTypeMapping(Value.class, Instant.class,
|
||||
// picking two members to check as Instant has many common function names
|
||||
v -> v.hasMember("toEpochMilli") && v.hasMember("epochSecond"),
|
||||
v -> Instant.ofEpochMilli(v.invokeMember("toEpochMilli").asLong()),
|
||||
HostAccess.TargetMappingPrecedence.LOW)
|
||||
|
||||
// Translate openhab-js Item to org.openhab.core.items.Item
|
||||
.targetTypeMapping(Value.class, Item.class, v -> v.hasMember("rawItem"),
|
||||
v -> v.getMember("rawItem").as(Item.class), HostAccess.TargetMappingPrecedence.LOW)
|
||||
|
|
|
@ -24,8 +24,6 @@ import org.osgi.service.component.annotations.Deactivate;
|
|||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.osgi.service.component.annotations.ReferenceCardinality;
|
||||
import org.osgi.service.component.annotations.ReferencePolicy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Tracks JS module dependencies
|
||||
|
@ -37,8 +35,6 @@ import org.slf4j.LoggerFactory;
|
|||
@NonNullByDefault
|
||||
public class JSDependencyTracker extends AbstractScriptDependencyTracker {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(JSDependencyTracker.class);
|
||||
|
||||
private static final String LIB_PATH = String.join(File.separator, "automation", "js", "node_modules");
|
||||
|
||||
@Activate
|
||||
|
|
|
@ -17,7 +17,6 @@ import java.nio.file.Path;
|
|||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.automation.jsscripting.internal.GraalJSScriptEngineFactory;
|
||||
import org.openhab.core.automation.module.script.ScriptDependencyTracker;
|
||||
import org.openhab.core.automation.module.script.ScriptEngineManager;
|
||||
import org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher;
|
||||
|
@ -49,11 +48,10 @@ public class JSScriptFileWatcher extends AbstractScriptFileWatcher {
|
|||
|
||||
@Override
|
||||
protected Optional<String> getScriptType(Path scriptFilePath) {
|
||||
if (!scriptFilePath.startsWith(getWatchPath().resolve("node_modules"))
|
||||
&& "js".equals(super.getScriptType(scriptFilePath).orElse(null))) {
|
||||
return Optional.of(GraalJSScriptEngineFactory.MIME_TYPE);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
String scriptType = super.getScriptType(scriptFilePath).orElse(null);
|
||||
if (!scriptFilePath.startsWith(getWatchPath().resolve("node_modules")) && ("js".equals(scriptType))) {
|
||||
return Optional.of(scriptType);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue