JS Scripting - loading config file

Hi,
has someone an idea for ‘hot’ loading a config file in textbased js rules?

At the moment i’m using this:

const configData = require('../configFile.js')

But this is a very bad solution, because require is caching data and so i must reload the rule file, when the config file is changed.

A second way is to use this:

const pathObj = java.nio.file.Paths.get("/etc/openhab/automation/js/conf/configFile.js")
const bytesObj = java.nio.file.Files.readAllBytes(pathObj);
const bytes = Java.from(bytesObj)
let content = String.fromCharCode.apply(null, bytes)

This works but has another missleading thing. I must define the absolute path to the configFile. This is not generic and i have not find a way to use a relative path to the OH conf folder.

So, two questions:

  1. Is there a way to avoid the caching from require?
  2. Is there a way to use realtive paths when using the java class?

No I don’t think so, but you can wipe the cached “module” from the object that tracks the caching. I assume this works in text-based rules. Something like

delete require.cache['../configFile.js']

You might need the full path, I don’t know if the cache keys use the relative paths or resolve them to the full path name.

Put that before your declaration and require will reload the file every time. But, unless this file of yours changes very frequently, I would think it would be less of an issue to just reload the rules file occasionally than to read that config file every single time the rule runs.

A third way is to use executeCommandLine and cat to read in a text file. Yes a quick one liner and you don’t need to mess with Java io.

But if this “config” changes frequently you should consider whether some of the values should be Items so they can be more easily changed through the UI instead of editing a text file on the host.

I’m sure you could use a restrictive path, but the path is going to be relative to the directory OH started in which is likely /var/lib/openHAB (which corresponds to the openhab 's home directly) or it might be /etc/openHAB.

And a fourth way is to create your own node module that includes that config file. When node modules are changed, all depending rules and scripts are reloaded to pick up that change.

Have a look at the JS Scripting README to see how to create a node module.

thanks for all the answers.

For my usecases i would see some pro’s and con’s.
One usecase is the timeline picker and here there are more users with differnet OS. So i’m looking for a ‘robust’ solution.

Pro: pure js
con: it dosen`t work (see below)

pro: short
con: it’s not portable, windows dosen’t know cat

pro: All my rules in one place in this module
con: I think the folder node_modules is not the right place for configs

I tried delete require.cache['../test-function.js'] with relative and absolute path.
Unfortunaly i get the following error message. The relative path as key nor the absolute path as key changing this behavior.

2023-05-13 12:24:24.192 [ERROR] [b.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: TypeError: Cannot delete property "/etc/openhab/automation/js/test-function.js" of undefined
        at <js>.:program(Unknown) ~[?:?]
        at org.graalvm.polyglot.Context.eval(Context.java:399) ~[?:?]
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458) ~[?:?]
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:400) ~[?:?]
        at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249) ~[java.scripting:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:58) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:84) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:58) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:84) ~[?:?]
        at org.openhab.core.automation.module.script.internal.ScriptEngineManagerImpl.loadScript(ScriptEngineManagerImpl.java:185) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher.createAndLoad(AbstractScriptFileWatcher.java:276) ~[?:?]
        at org.openhab.automation.jsscripting.internal.fs.watch.JSScriptFileWatcher.createAndLoad(JSScriptFileWatcher.java:63) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher.importFile(AbstractScriptFileWatcher.java:256) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher.lambda$5(AbstractScriptFileWatcher.java:248) ~[?:?]
        at java.util.Optional.ifPresent(Optional.java:183) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher.importFileWhenReady(AbstractScriptFileWatcher.java:246) ~[?:?]
        at java.lang.Iterable.forEach(Iterable.java:75) ~[?:?]
        at org.openhab.core.automation.module.script.rulesupport.loader.AbstractScriptFileWatcher.processWatchEvent(AbstractScriptFileWatcher.java:223) ~[?:?]
        at org.openhab.automation.jsscripting.internal.fs.watch.JSScriptFileWatcher.processWatchEvent(JSScriptFileWatcher.java:56) ~[?:?]
        at org.openhab.core.service.WatchQueueReader.lambda$5(WatchQueueReader.java:357) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at java.lang.Thread.run(Thread.java:829) [?:?]
2023-05-13 12:24:24.202 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/js/test-require.js': org.graalvm.polyglot.PolyglotException: TypeError: Cannot delete property "/etc/openhab/automation/js/test-function.js" of undefined

The require object exist and i can write this to the log:

console.info(require)

log-output:
2023-05-13 12:24:24.189 [INFO ] [tomation.script.file.test-require.js] - org.openhab.automation.jsscripting.internal.OpenhabGraalJSScriptEngine$$Lambda$1652/0x6433ee28@4aebb7

Is there something to get access to the propertys of the require object?

JS Scripting does not provide a real NodeJS environment. The JS Scripting provides a require, but it is implemented in the Java layer of the binding and I guess doesn’t behave like the original require in all aspects.

I would like to add a fifth way:

Create one script that puts your configuration in the shared cache. This allows you to access your configuration from any script in any language supported in openHAB.
If you update your conf script, the shared cache entry will be updated and the next time your rules are run they will use the new conf because they get the conf from the shared script every time.

2 Likes

@florian-h05 many thanks florian. The way with shared cache is perfekt.

1 Like

PowerShell knows cat.

Has Rules DSL been updated? I may have missed that PR but last I looked it still didn’t support cache.

It should have: DSL support for sharedCache/privateCache by J-N-K · Pull Request #3187 · openhab/openhab-core · GitHub

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.