Creating a custom nodeJS module

Hi,

I’m using OH 3.2 version and decided to give a shot to the new JavaScript engine. Previously I’ve used DSL rules only. The biggest reason why I’m trying the JS scripting is to generalize some of my code, to improve the maintenance and to use a better language than DSL. I’ve some JS background and a little experience with nodeJS, but I’m not an expert on them.
Right now, I’m trying to figure out, how to create a module, where some helper libraries will be implemented.
After some hours of fight, I’m finally at the point, that my first version of my library is loaded in OH and I’m able to call 1 sample function from a script (OH UI). But, once I update the code in it, the script is still using the old code. In the log, I see, the module is unloaded and then loaded again, but for some reason, it is still using the old code.

Module code:

exports.printMsg = function() {
    console.log("Hello from MyLib2!");
}

scriptLoaded = function () {
    console.log("MyLib loaded!");
}

scriptUnloaded = function () {
    console.log("MyLib unloaded!");
}

Script:

pl1 = require('pingilib');
pl1.printMsg();

Log:

2022-01-07 15:58:03.028 [INFO ] [org.openhab.automation.script       ] - MyLib unloaded!

2022-01-07 15:58:03.032 [INFO ] [rulesupport.loader.ScriptFileWatcher] - Loading script '/openhab/conf/automation/js/node_modules/pingilib/index.js'

2022-01-07 15:58:03.740 [INFO ] [org.openhab.automation.script       ] - MyLib loaded!
2022-01-07 15:58:57.383 [INFO ] [org.openhab.automation.script       ] - Hello from MyLib!

I think, it is somehow related, how the packages are linked. I read something about linking a module as a local module. Can someone advice here? My OH instance is running in Docker (on RPI4).

I’m assuming a UI rule? The script gets instantiated once the first time it’s run. The second time it runs the require will see that pingilib is already loaded so it does nothing. You are probably seeing the warning about script dependency tracking being disabled which I think is related to this.

To force it to reload the rule you simply need to save the rule, even without changes. That will give the rule a clean slate.

Ahh Rich, thanks a lot! I could have been thinking about :frowning:

Hi, interesting. Where do you put the module? Is it an HO3 script or do you place it in a specific folder? Thx

$OH_CONF/automation/js/node_modules

But be aware it’s more than just putting some js files there. Look for some tutorials online.

I’m back to this topic again. In the meanwhile I was able to implement my library, which does not use any external dependencies and is just working fine. I can call it from rules etc.

BUT, now, I’m trying to use and rewrite another node module (GitHub - Plustig/Android-Phone-Finder: Node.js app to automate google's find my device service; described here: Android Phone Finder from OpenHAB (Google Assistant and Alexa) - #3 by Plustig) which has external dependencies. The current implementation is not like a real node_module, what I can load and reference from rules. I want to have it in the same way as my lib, to be able to reference from rules.
Based on my minimal nodeJS knowledge, the code must be adjusted a little bit, an export must be created etc. Then it must be copied to OH and installed (so npm will install the dependencies). And here comes the problem. How? Where to put the module? Most probably I have to put into the same node_modules folders, but how can I install it?

My OH is running in a docker container …

Install the module using npm. Run npm install <name of module> from the $OH_CONF/automation/js folder. Note, if your library isn’t installed using npm your library will be removed.

Thanks Rich for your response. So, my module must be also put into the node_modules folder and the install must be executed from the parent folder. Right? Currently the module is not installed by NPM, it is only available locally.

My dilemma here: I forgot to mention, that my OH is running in a container. So NPM is not installed by default. I can install it by apt also can install the module, BUT I’ve read another topic (Executing node.js script in openhab (docker) - #5 by rlkoshak), where you suggested that the module had to be rather put into a different container. This make sense from container architecture wise, but isn’t this a huge overhead?
If I install the module directly into OH modules, then I can simply call it from the rules.
If I want to move it out to a separate container, then a webservice must be developed which will work as a proxy between the caller (OH) and the app itself. A container must be built and deployed

What is your thoughts here?

Right. You’ll have to create your personal library as an NPM module and install it using npm. Note that npm has the ability to install a module from a .tar file so you can just tar it up and call npm to install that tar file. That’s what I do.

So do I. That’s irrelevant. Presumably you have your $OH_CONF folder mounted into the container as a volume. If you mounted a folder on the host, run npm from there. If not, install npm into the container and run it from inside the container. I’m sure there are lots of other ways too.

That’s a completely different situation. That user wanted to run a totally separate Node.js program along side openHAB. That discussion had nothing to do with writing openHAB rules in ECMAScript 11 which runs on openHAB, not along side openHAB as a separate program.

As for the overhead, that’s kind of the point of containers. At the cost of some more overhead you get more isolation between services and all the benefits that come from that. If you are worried about overhead, you should not be running in containers.

If it’s a properly formatted and installed NPM module then you can requires that library the same as any others, including openhab-js which gets imported by default unless you change the setting to make it so you have to import even that manually.

Yes, if you don’t want to or can’t use this from openHAB (there is no guarantee that all npm modules are runnable in openHAB rules, in particular modules that try to create multiple threads won’t work) you will have to write your own service that communicates with OH in one of the standard ways (MQTT, REST API, etc.). And if you are running OH in a container, you probably want to run all of your services in containers because that’s kind of the point of containers.

Thanks for the comprehensive answer. Learned a lot new again :slight_smile:

The tar seems to me a very good option, I’ll give it a shot. Thanks!

I do not fully agree on that. Till now, I really like the containers (the existing ones :slight_smile:) as it is/was much easier and cleaner to install/backup/re-install/update. This is all good. But to build a new container, write an API, solve the API security just for one simple function seems to me an overkill. But you are right, the microservices architecture is about that.