Javascript rule to run Linux bash script or python script

Is it possible to run/trigger a Linux bash script or a python from a Javascript rule, and use a return from it?

To avoid xy problems: I want to retrieve a value from the Tractive API using this python script (HowTo Start? · Issue #32 · zhulik/aiotractive · GitHub).

Yes, of course.

Beyond the Exec binding which works for every language since it works off of commands to Items, there’s the executeCommandLine Action.

1 Like

Aha!

I started off with something simple:

var antwoord = actions.Exec.executeCommandLine(Duration.ofSeconds(5), 'ls')
console.log(antwoord)

But:

[ERROR] [b.automation.script.file.testRegel.js] - Failed to execute rule Te-negeren-test-dfe74dd6-c797-421c-9138-0dade3bad567: ReferenceError: "Duration" is not defined: ReferenceError: "Duration" is not defined
        at execute (testRegel.js:16)
        at doExecute (/node_modules/openhab.js:2)

Strange…?

I did manually install nodejs earlier today, in order to make a JS file run outside of openHAB. Did I break something by doing that? But removing it might just make matters worse, who knows? :slight_smile:

Not so strange. You didn’t follow the example in the docs.

actions.Exec.executeCommandLine(**time.**Duration.ofSeconds(20), 'echo', 'Hello World!');

You have to access Duration and anything else to do with date times through time..

time.Duration.ofSeconds(5)

No, that’s fine. You didn’t break anything. You just didn’t reference time.Duration correctly.

1 Like

And aren’t I the one constant complaining young people these days don’t know how to read properly anymore… :face_with_peeking_eye:

Now, the ls command succeeded!

But taking it a bit (well…) further:

var antwoord = actions.Exec.executeCommandLine(time.Duration.ofSeconds(5), 'python /etc/openhab/automation/python/tractive.py')
console.log(antwoord)

… didn’t succeed:

[WARN ] [org.openhab.core.io.net.exec.ExecUtil] - Failed to execute commandLine '[python /etc/openhab/automation/python/tractive.py]'

I earlier tried something similar, modifying code I found on Stackoverflow:

const { promisify } = require('util');
const exec = promisify(require('child_process').exec)

console.log("console werkt")

module.exports.millieThuis = async function millieThuis() {
  try {
    // Exec output contains both stderr and stdout outputs
    //const nameOutput = await exec('git config --global user.name')
    const thuisofniet = await exec('/etc/openhab/automation/python/tractive.py')

    return thuisofniet
    console.log(thuisofniet)
  }
  catch (e) {
    console.log("Er liep iets mis: "+e)
  }
};

console.log(module.exports.millieThuis())

That also failed:

Er liep iets mis: Error: Command failed: /etc/openhab/automation/python/tractive.py
/bin/sh: 1: /etc/openhab/automation/python/tractive.py: Permission denied

So the permission monster strikes again, I assume?

Tiny errors like these are hard to spot when you aren’t used to looking for them.

Try separating the command and the argument. python is the command and the path to the script is the argument. See Actions | openHAB. Sadly the docs for JS Scripting shows how to call the action, but the actual docs for the actions are elsewhere. Be aware that the docs are Rules DSL centric so there might need to be some translation (e.g. Duration verses time.Duration.

Remember that openHAB is executing this script as user openhab, not the user you log in as. User openhab needs to have execute permissions on the script.

Note, /etc/openhab/automation/python is intended to be where one puts Jython rules code, not random personal Python scripts. You can put those just about anywhere but it can be dangerous to put them in a folder that OH expects to contain something else.

Fair point. Is this better? Or also not ideal?

var antwoord = actions.Exec.executeCommandLine(time.Duration.ofSeconds(5), 'python', '/home/erik/persoonlijkeToevoegingenOpenHAB/python/tractive.py')
console.log(antwoord)

I once again tried Google, and Stackoverflow suggested:

sudo chown openhab: /home/erik/persoonlijkeToevoegingenOpenHAB/
namei -l /home/erik/persoonlijkeToevoegingenOpenHAB/

That gave this output:

f: /home/erik/persoonlijkeToevoegingenOpenHAB/
drwxr-xr-x root    root    /
drwxr-xr-x root    root    home
drwx------ erik    erik    erik
drwxrwxr-x openhab openhab persoonlijkeToevoegingenOpenHAB

But the above script still gave this output:

[INFO ] [b.automation.script.file.testRegel.js] - python: can't open file '/home/erik/persoonlijkeToevoegingenOpenHAB/python/tractive.py': [Errno 13] Permission denied

So long as you are not using a folder that has some other purpose it’s fine. Beyond that what’s most important is that the path makes sense to you.

I would probably create my own separate folder, maybe /etc/openhab/bin so that everything associated with OH are in OH folders.

Another option is to put them in /var/lib/openhab/bin. as /var/lib/openhab is the openhab user’s home directory. Then you can omit the full path and just use bin/tractive.py in the configs.

It’s all up to you.

The folder has OK permissions but what about the file tractive.py? On Linux permissions are not inherited. Each file has it’s own set of permissions.

This seemed to be the easiest option. It also fixed the permission problem.

But…

[INFO ] [b.automation.script.file.testRegel.js] - Traceback (most recent call last):
  File "/var/lib/openhab/bin/python/tractive.py", line 3, in <module>
    from aiotractive import Tractive
ModuleNotFoundError: No module named 'aiotractive'

I installed the aiotractive library as user erik, but that’s not enough, it seems… Should I sudo pip install aiotractive? I’ve read that sudo pip really shouldn’t be used (and that even pip is to be avoided, but I took my chances)…

I think to remember that such libararies are installed locally to the user that runs the install if you don’t use sudo.So the libs are installed relative to user erik while the script is executed with permissions of user openhab.
I think best would be to install

Hmmm. But I have no idea what that user’s password might be…

Nope. As @Wolfgang_S indicates, installing a Python library as a regular user installs it only for that user. You can install it as root which globally installs the library but if you are on bookworm that’s been disabled. So you’ll either need to install it as user openhab or create a virtualenv like @Wolfgang_S describes.

To install it as the openHAB user use sudo -u openhab pip install aiotractive

Besides the supply chain issues with package managers like pip and npm, are there other reasons you’ve read not to use pip?

The openhab user account quite deliberately does not have a password nor does it have a shell. You cannot log in as the openhab user. But you can use sudo to run a command as any user.

1 Like

Success!

Thanks a lot, once again!

https://forums.linuxmint.com/viewtopic.php?p=2349502#p2349502

OK, well it’s important to understand that the reply is in the context of Linux Mint, with some relevance to Ubuntu. The main takeaway is to not change the global Python environment because apt is written in and depends on Python (yum is too all you Fedora users out there).

Using pip to install libraries for a regular user should not cause problems and using a vritualenv is best practice and using pip there is OK too.

I did install a virtual environment (pyenv - [Tutorial] Safely install and use other Python versions for coding (Mint 20 and 21, 64-bit) - Linux Mint Forums), which worked on user erik, but I didn’t figure out how to do that for user openhab.

shouldn’t be any difference on doing that for user openhab …
Main difference is that user openhab does not have a shell but with sudo you can get one for openhab.

With the command

sudo su -s /bin/bash openhab

you get a shell for user openhab. From that I think you should be able to continue to build the virtual environment - or use habapp which also install one.

1 Like

The only thing I have to add to @Wolfgang_S’s answer is that your venv will have it’s very own copy of python so make sure to use that copy of the python to run the script. I think that’s sufficient to make it use the venv. If not, you may need to cd to the folder first in which case you’ll need a slightly more complicated command line.