Ivan’s Helper Libraries - OH3, Python, JavaScript

Tags: #<Tag:0x00007fc8fbc090b0> #<Tag:0x00007fc8fbc08e08> #<Tag:0x00007fc8fbc08c28>

Ivan’s Updates to the Helper Libraries

I have decided to start maintaining my own fork of the openHAB Helper Libraries, at least for the time being, in light of the lack of activity in the original repo. There has been growing demand for OH3 support among other things, and I wanted to make that available in one place. In the event that the original maintainer returns, I will work with him to get these changes merged into the original repo.

I will be maintaining a change log detailing what has been done in my fork for reference.

Currently I have not updated the documentation to reflect any of the additions that have been made, I will get to that as soon as possible (any help is appreciated).


Where to get them

You can find my fork on GitHub in the Ivan’s Updates branch, which I have made the default in my repo.

Please use this branch when submitting any PRs, and mind that you make your PR against that branch on GitHub as it will default to the original repo.


Installation

Python

  • openHAB 2.x - Follow the instructions here.
    • Step 7 - Download the contents of this repository.
  • openHAB 3.x - Follow the above instructions with the following exceptions:
    • Step 5 - Install the Jython Scripting add-on.
    • Step 7 - Download the contents of this repository.
    • Step 10 & 11 - skip these steps.

JavaScript (ES5)

Currently this is a work in progress to match the Python functionality

  • openHAB 2.x - Follow the instructions here.
    • Step 7 - Download the contents of this repository.
  • openHAB 3.x - Follow instructions with the following exceptions:
    • Step 5 - skip this step.
    • Step 7 - Download the contents of this repository.

What’s New

Below you will find highlights for things that have been added or updated in each language.

Python

  • Added openHAB 3.x compatibility.
  • Added custom logger class that provides TRACE level logging.
  • Added a simplified getLogger function that will automatically prepend the LOG_PREFIX.
    from core.log import getLogger
    log = getLogger("mylib.log") # will log to '{LOG_PREFIX}.mylib.log'
    
  • Generic Event Triggers are now fixed (OH2 + 3) so you can use Item added/removed/modified triggers as well as Thing Status triggers.
  • Date conversion functions can now accept a DateTime Item and will extract its state.

Python Type Hints!

Typing files are now available in this repo for openHAB 3. Stubs for the Helper Libraries will be added to that repo soon are now available in my Helper Libraries repo in Core/typings.

What this means is you will now have property and method hints right in your IDE when writing rules! No longer will you always need to have the documentation open or dig around in the openHAB code just to find methods of Java objects. The repo contains instructions on installing them and setting up your IDE, along with some examples to get you started.

JavaScript (ES5)

I have done a fair bit of work on getting the JavaScript libraries up to par with the Jython ones. While this is still incomplete, I have published my work so far in a PR for anyone interested in testing them or helping complete the work. Details are in the PR about what is left to do.

  • Port Jython libraries into JavaScript.
  • Added openHAB 3.x compatibility.
  • Generic Event Triggers are now fixed (OH2 + 3) so you can use Item added/removed/modified triggers as well as Thing Status triggers.

New JS Rule Syntax

ECMAScript 5 has no concept of decorators, unlike Python, so the use of the rule and when functions looks a little different but works the same. Below is a rule I made in JS that queries my devices running Tasmota for their state when they come online. The rule and when functions work in the exact same way as they do in Python, so refer to the Python documentation but use them like this.

New Style Javascript Rule Example
var OPENHAB_CONF = Java.type("java.lang.System").getProperty("openhab.conf");
var JS_PATH = OPENHAB_CONF + "/automation/lib/javascript";

load(JS_PATH + "/core/rules.js");
load(JS_PATH + "/core/triggers.js");
load(JS_PATH + "/core/metadata.js");
load(JS_PATH + "/core/log.js");

var LOG = getLogger(LOG_PREFIX + ".tasmota");

var TOPICS = [
    "POWER",
    "SWITCH"
]

var mqtt = actions.get("mqtt", "mqtt:broker:alfred");

function tasmota_online(event) {
    if (event.itemState === ON) {
        LOG.debug(
            "Polling device '{}'",
            event.itemName
        )
        var device_topic = get_value(event.itemName, "topic");
        if (device_topic) {
            TOPICS.forEach(function(topic) {
                for (var i = 0; i < 9; i++) {
                    mqtt.publishMQTT(device_topic + "/cmnd/" + topic + (i > 0 ? i : ""), "");
                }
            });
        }
    }
}
when("Descendent of equipment_status_tasmota changed")(tasmota_online);
rule(
    "Tasmota Device Online Query",
    "Queries Tasmota devices for relay, switch, etc states when they come online."
)(tasmota_online);
16 Likes

Thank you for this. I will test this out this weekend when i get the chance to start migrating my openhab instance. The helper libraries is what i have been waiting on as i rely heavily on javascript rules.

2 Likes

Step 7 says

Download the contents of this repository.

with the link pointing to the openHAB GitHub, not yours. Is that the intention? If so, at what point during this installation are we using any of your work?

Additionally

Step 5 - Install the Python Automation add-on.

Should that say

Step 5 - Install the Jython Automation add-on.

?

I left that alone because I want to limit the changes to the repo, at least for now, to ease reintegration. Assuming you came from the instructions you are quoting above, I would think it is implied that you should download my fork from the link you went past in the opening of the post. I will add a note above with the link again.

No, I believe in OH3 the add-on is actually called “Python” not “Jython”. That also makes sense, because you are coding in the language “Python” but we are using the interpreter “Jython” instead of the more familiar “CPython”. I am not able to confirm the add-on name right now, but if you can check and confirm the name is “Jython” I will update it.

Because other links in those step by step instructions link to your fork, except for the one highlighted one might assume it’s deliberate.

It’s Jython

I haven’t touched that page, that is odd. Which links point to my fork?

Updated! Thanks

In the same Step 7, the link for File Locations seems to be relatively linked, rather than absolutely, so it links to your fork. All other links, except for another in Step 15, are external though, so I over-egged it in my previous post!

I see you’ve added the step into your OP - that’s great, it was the only thing that made me think twice!

1 Like

Thanks a lot for the quick port to OH3 @CrazyIvan359!

I’ve started from scratch (removed automation folder and use the install steps you’ve described), but I’m still getting the following error:

2021-02-23 21:56:18.463 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/etc/openhab/automation/jsr223/python/core/000_Startup.py'
2021-02-23 21:56:25.177 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Initialized a custom ScriptEngineFactory for jython (2.7.2): supports python (2.7) with file extensions [py], names [python, jython], and mimetypes [text/python, application/python, text/x-python, application/x-python]
2021-02-23 21:56:25.432 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'py' with identifier: file:/etc/openhab/automation/jsr223/python/core/000_Startup.py
2021-02-23 21:56:25.558 [WARN ] [jython.startup                      ] -

*******************************************************************************
Jython version:             2.7.2.final
Operating system:           Linux
OS Version:                 5.10.11-v7l+
Java vendor:                Azul Systems, Inc.
Java VM name:               OpenJDK Client VM
Java runtime name:          OpenJDK Runtime Environment
Java runtime version:       11.0.9+11-LTS
configuration.py installed: False
sys.path:                   /etc/openhab/automation/lib/python"
                            /var/lib/openhab/cache/org.eclipse.osgi/258/0/bundleFile/Lib
                            __classpath__
                            __pyclasspath__/
*******************************************************************************

2021-02-23 21:56:25.561 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/etc/openhab/automation/jsr223/python/core/components/100_DirectoryTrigger.py'
2021-02-23 21:56:28.126 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'py' with identifier: file:/etc/openhab/automation/jsr223/python/core/components/100_DirectoryTrigger.py
2021-02-23 21:56:28.174 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/python/core/components/100_DirectoryTrigger.py': ImportError: No module named core in <script> at line number 23
2021-02-23 21:56:28.175 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/etc/openhab/automation/jsr223/python/core/components/100_StartupTrigger.py'
2021-02-23 21:56:29.896 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'py' with identifier: file:/etc/openhab/automation/jsr223/python/core/components/100_StartupTrigger.py
2021-02-23 21:56:29.933 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab/automation/jsr223/python/core/components/100_StartupTrigger.py': ImportError: No module named core in <script> at line number 9

It looks like it can’t load the core module, which I’ve correctly placed in /etc/openhab/automation/lib/python/:

╰─$ ll /etc/openhab/automation/lib/python/core
total 128K
-rw-rw-r-- 1 openhab openhab 2.3K Feb 23 21:13 actions.py
-rw-rw-r-- 1 openhab openhab  15K Feb 23 21:13 date.py
-rw-rw-r-- 1 openhab openhab    0 Feb 23 21:13 __init__.py
-rw-rw-r-- 1 openhab openhab 4.8K Feb 23 21:13 items.py
-rw-rw-r-- 1 openhab openhab 2.4K Feb 23 21:13 jsr223.py
-rwxrwxr-x 1 openhab openhab 4.1K Feb 23 21:13 links.py
-rw-rw-r-- 1 openhab openhab 4.0K Feb 23 21:13 log.py
-rw-rw-r-- 1 openhab openhab  13K Feb 23 21:13 metadata.py
drwxrwxr-x 2 openhab openhab 4.0K Feb 23 21:13 osgi
-rw-rw-r-- 1 openhab openhab 5.3K Feb 23 21:13 rules.py
-rw-rw-r-- 1 openhab openhab 2.9K Feb 23 21:13 testing.py
-rwxrwxr-x 1 openhab openhab  40K Feb 23 21:13 triggers.py
-rw-rw-r-- 1 openhab openhab  12K Feb 23 21:13 utils.py

One weird thing though is this line in the startup script’s output: configuration.py installed: False.
I actually have a configuration.py present in /etc/openhab/automation/lib/python/:

╰─$ ll /etc/openhab/automation/lib/python/
total 20K
drwxrwxr-x 3 openhab openhab 4.0K Feb 23 21:40 community
-rw-rw-r-- 1 openhab openhab 2.1K Feb 23 21:21 configuration.py
-rw-rw-r-- 1 openhab openhab 3.8K Feb 23 21:40 configuration.py.example
drwxrwxr-x 3 openhab openhab 4.0K Feb 23 21:13 core
drwxrwxr-x 3 openhab openhab 4.0K Feb 23 21:40 personal

Would there be any cache somewhere with the jython module? (other than the *.$py.class files)

Thanks!

1 Like

Nevermind, finally found the issue…

I did a manual install of Jython first, so my /etc/default/openhab entry looked like this when I commented it out:

EXTRA_JAVA_OPTS="-Dgnu.io.rxtx.SerialPorts=/dev/ttyZWAVE:/dev/ttyUSB0:/dev/ttyS0:/dev/ttyS2:/dev/ttyACM0:/dev/ttyAMA0" # -Xbootclasspath/a:/etc/openhab/automation/jython/jython-standalone-2.7.0.jar -Dpython.home=/etc/openhab/automation/jython -Dpython.path=/etc/openhab/automation/lib/python"

But apparently the # I used was not taken into account, resulting in the following being used as EXTRA_JAVA_OPTS:

[...snipped...] -Djava.awt.headless=true -Dgnu.io.rxtx.SerialPorts=/dev/ttyZWAVE:/dev/ttyUSB0:/dev/ttyS0:/dev/ttyS2:/dev/ttyACM0:/dev/ttyAMA0# -Xbootclasspath/a:/etc/openhab/automation/jython/jython-standalone-2.7.0.jar -Dpython.home=/etc/openhab/automation/jython -Dpython.path=/etc/openhab/automation/lib/python" --add-reads=java.xml=java.logging [...snipped...]

Notice the commented out part is in the ps output…

1 Like

First of all thank you very much for picking up the work on Helper Libraries, highly appreciated!
I tried to follow your instructions for Javascript. I set the debug level, took the files from your repo , created the directory structure and tried the HelloWorld.js . However I always get the following error in the logs (last line):

2021-02-24 14:00:47.439 [INFO ] [.core.internal.i18n.I18nProviderImpl] - Time zone set to 'Europe/Berlin'.
2021-02-24 14:00:47.448 [INFO ] [.core.internal.i18n.I18nProviderImpl] - Locale set to 'de_DE'.
2021-02-24 14:00:47.896 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Initialized a generic ScriptEngineFactory for Oracle Nashorn (11.0.2): supports ECMAScript (ECMA - 262 Edition 5.1) with file extensions [js], names [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript], and mimetypes [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
2021-02-24 14:00:47.902 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Initialized a custom ScriptEngineFactory for Oracle Nashorn (11.0.2): supports ECMAScript (ECMA - 262 Edition 5.1) with file extensions [js], names [nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript], and mimetypes [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
2021-02-24 14:00:50.877 [WARN ] [org.openhab.core.net.NetUtil        ] - Found multiple local interfaces - ignoring 192.168.44.53
2021-02-24 14:01:02.404 [DEBUG] [vider.AbstractResourceBundleProvider] - Parse rules from bundle 'org.openhab.core.automation' 
2021-02-24 14:01:02.406 [DEBUG] [vider.AbstractResourceBundleProvider] - Parse rules from bundle 'org.openhab.core.automation.module.script.rulesupport' 
2021-02-24 14:01:02.426 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added CoreModuleHandlerFactory
2021-02-24 14:01:02.427 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added TimerModuleHandlerFactory
2021-02-24 14:01:02.428 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added ScriptModuleHandlerFactory
2021-02-24 14:01:02.429 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added ScriptedCustomModuleHandlerFactory
2021-02-24 14:01:02.430 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added ScriptedPrivateModuleHandlerFactory
2021-02-24 14:01:02.431 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added AnnotatedActionModuleTypeProvider
2021-02-24 14:01:02.432 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added AnnotatedThingActionModuleTypeProvider
2021-02-24 14:01:02.433 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added EphemerisModuleHandlerFactory
2021-02-24 14:01:03.907 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Initialized a custom ScriptEngineFactory for null (null): supports Rule DSL (v1) with file extensions null, names null, and mimetypes [application/vnd.openhab.dsl.rule]
2021-02-24 14:01:03.991 [INFO ] [.core.model.lsp.internal.ModelServer] - Started Language Server Protocol (LSP) service on port 5007
2021-02-24 14:01:05.207 [DEBUG] [e.automation.internal.RuleEngineImpl] - ModuleHandlerFactory added MediaModuleHandlerFactory
2021-02-24 14:01:05.383 [INFO ] [org.openhab.ui.internal.UIService   ] - Started UI on port 8080
2021-02-24 14:01:07.538 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js'
2021-02-24 14:01:07.575 [INFO ] [e.automation.internal.RuleEngineImpl] - Rule engine started.
2021-02-24 14:01:08.024 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'js' with identifier: file:/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js
2021-02-24 14:01:08.545 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js': java.lang.ClassNotFoundException: org.eclipse.smarthome.model.script.actions.ScriptExecution cannot be found by org.openhab.core.automation.module.script_3.1.0.202102210257

So apparently there are still some outdated pieces of code that try to access objects in the eclipse.smarthome namespace.
Do you know how I can fix that?

1 Like

Ok, figured the first problem out by myself, I had to replace eclipse.smarthome with openhab.core
However, now I am facing the next error:

2021-02-24 15:47:56.247 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js'
2021-02-24 15:47:57.070 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'js' with identifier: file:/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js
2021-02-24 15:47:57.821 [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js': java.lang.ClassNotFoundException: org.apache.commons.io.IOUtils cannot be found by org.openhab.core.automation.module.script_3.1.0.202102210257
2021-02-24 15:48:01.276 [INFO ] [e.automation.internal.RuleEngineImpl] - Rule engine started.

So basically org.apache.commons.io.IOUtils cannot be found. This is referenced in utils.js. But in what way do I have to change the code?

Ok, i now replaced all core files with the files from “js-rewrite”-branch. Now my rule at least stops throwing errors:

2021-02-24 16:48:11.678 [INFO ] [org.openhab.ui.internal.UIService   ] - Started UI on port 8080
2021-02-24 16:48:12.046 [INFO ] [ab.ui.habpanel.internal.HABPanelTile] - Started HABPanel at /habpanel
2021-02-24 16:48:12.902 [INFO ] [ab.core.service.AbstractWatchService] - Loading script '/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js'
2021-02-24 16:48:13.515 [DEBUG] [ipt.internal.ScriptEngineManagerImpl] - Added ScriptEngine for language 'js' with identifier: file:/C:/OH310_~1/conf/automation/jsr223/javascript/personal/HelloWorld.js
2021-02-24 16:48:17.947 [INFO ] [e.automation.internal.RuleEngineImpl] - Rule engine started.

So far, so good. However the rule is not working, there are no log entries produced by the rule. Here is my modified rule source code:

'use strict';

var OPENHAB_CONF = Java.type("java.lang.System").getenv("OPENHAB_CONF");
load(OPENHAB_CONF+'/automation/lib/javascript/core/rules.js');
load(OPENHAB_CONF+'/automation/lib/javascript/core/triggers.js');
load(OPENHAB_CONF+'/automation/lib/javascript/core/log.js');
var me = "HelloWorld.js";

rule({
    name: "Javascript Hello World (GenericCronTrigger raw API with JS helper libraries)",
    description: "This is an example Jython cron rule using the raw API",
    triggers: [
        CronTrigger("0/10 * * * * ?")
    ],
    execute: function( module, inputs){
        logInfo("Hello World!");
    }
});

Thank you for any help or guidance!

1 Like

@openhabbe glad you got it sorted, I wasn’t sure what to suggest. Thank you for the detailed information in your initial request and for explaining what the fix was. I guess inline comments don’t work, only full line ones.

1 Like

@vossivossi as you correctly determined you need to use the js-rewrite branch, which the link in my paragraph about JS should link to (does it not?)

That branch is a work in progress and contains several breaking changes. For anyone with current JS rules they will not work as the old rule function has been replaced with one that mimics the Python decorator.

I believe I put an example in the rule and when decorators. The syntax is similar to the Python functions but because there is no concept of a decorator in ES5 you have to call the functions on your rule function. I will post examples when I am in front of a computer.

1 Like

Unfortunately the link points to the “ivans-updates” branch, which still includes the old libs.

Ok, that explains it :wink:

That would be highly appreciated. Thank you very much for this contribution!

Had to fix some code in idealarm as well to make it work (mainly jodatime refs).

I’ll take the time to make a PR to your repo :slight_smile:

@openhabbe a PR is welcome, I have not touched any examples or community libraries yet. Please make sure to keep changes backwards compatible with OH2. If you aren’t sure how to do that, submit your PR that works on OH3 and I will take a look and tell you what to do to make it work on both.

1 Like

Thanks, I will fix that. I added some additional links to the instructions and didn’t think to change the links for JS.

1 Like

@vossivossi I have updated the first post to include a complete example rule file using the new syntax.

2 Likes

Just looking at implementing this version of the Python/Jython OH Helper libraries on a dev machine and I’ve noticed that the RuleSimple.pyi file has been truncated in GitHub.