Python3 Binding (Discussion)

The pythonscripting binding for openHAB is the long term successor of the jython binding.

It is testet in openhab 5.0.0 and 4.3.x and provides the following features.

  1. Based on graalpy which provides python3.11 support
  2. Included openhab helper library. The main focus is to make the openHAB core API accessible without encapsulating everything again. (Keep it simple and easy to maintain.)
  3. Dependency tracker to reload your rule if an imported module (lib) is updated.
  4. Caching of compiled *.pyc files (to improve startup performance)
  5. Jython emulation (We may remove the feature to reduce the number of error sources - you can still use the jython binding)
  6. Encapsulated datetime <=> ZonedDateTime handling. Everything is now a native python datetime object.
  7. @when and @onlyif decorator support
  8. Easy to use code profiler just by “@rule(profile=1)”
  9. Python and old jython binding can run in parallel for a seamless migration of old scripts

Helper Lib Documentation

Ressources, Downloads & Quick Start Instructions

I made also my own custom “toolbox” lib on top of it, where I’m not sure if some functionalities should be part of the openHAB helper library. (particularly getGroupMemberTrigger and getFilteredGroupMember).

Performance

I ported all my ~9000 lines of jython based python2 code to python3 which is now running with the new python binding. Everything works, but it looks that graalpy is 2-5 times slower then jython. If you run a pure python benchmark, it should be up to 4 times faster according to the graalvm devs. In openhab, however, most of it depends on the Python=>Java bridge, because there is a lot of interaction with java objects. This seems to be the reason for the “bad” performance. For comparison, I reimplemented some rules with the openhab-js binding and got similarly slow results.

Still, I think it’s acceptable because it’s more future-proof. It offers Python 3.11. It offers more features than the Jython binding (e.g. dependency tracker, object wrapping for datetime objects, helper lib). And finally, I think it doesn’t matter if a script runs 0.1ms or 0.8ms

Most of my scripts run between 0.5ms and 8ms. More complex scripts have increased their runtime from 30ms to 60-70ms.

Credits

A lot of thanks goes to @jsjames . He helped, especially in the beginning, to deploy graalpy dependencies correctly. Without this help we would not be where we are now.

I also forgot to mention support from graalpy developers with a lot of helpful hints!

12 Likes

I’m not sure this will work as a market place posting. You’ve deviated from the template in a number of important ways. Have you tested it by installing the add-on from MainUI-> Add-on Store → Bindings or possibly Automation.

You may need to enable “show unpublished” in Settings → Community Marketplace if you haven’t already.

Typically only one resource is allowed per post so you would usually need separate postings for the OH 4 and OH 5 versions and use a support versions string in the title (e.g. Debounce [4.0.0.0;4.9.9.9]). The second link needs to be to the license and there sooner be nothing after the links.

You should not see from the template because OH has to parse it. It’s not just read by humans.

This performance is probably also slower because we don’t have the Graal compiler available for both Python and JS as we run in a stock JDK.
Have you benchmarked on GraalVM?

Not really. I just wanted to prevent people from having too high expectations. :slight_smile:

1 Like

I released an updated version with the following changed

  • added “when” and “onlyif” decorator support
  • Item.getPersistance renamed to Item.getPersistence
  • fixed TimeOfDayTrigger and DateTimeTrigger

An updated download link can be found on the main post of this thread

I released an updated version.

Changelog and Downloadlink can be found in marketplace

Another new version is released.

Changelog and Downloadlink can be found in marketplace

a new day and a new release :slight_smile:

As always, changelog and download link can be found in the marketplace

Hello Holger,
thank you for your efforts to bringing python3 in OH.

I have a quick question:
I’m using some .py files that are based on datetime. So far, I haven’t been able to determine a local time value; I always get GMT.

Is there a standard method to get datetime with the time zone set in OH?

Thank you in advance for your reply :slightly_smiling_face:
Tom

Hi,

if you get the state of a DateTime item, it should always be a datetime with the correct timezone.

if you want to get the current time you should call datetime.now().astimezone()

if you just call datetime.now() you will get a UTC datetime

btw. in my openhab container I provide the env vars

EXTRA_JAVA_OPTS: "-Duser.timezone=Europe/Berlin"

maybe this affects the “astimezone” result too

Holger,
here is what happens
Python code in OH:

def test_dt2():
    dt = datetime.datetime.now().astimezone()
    log_my.info(f"dt-astimezone: {dt} - {dt.tzname()}")
    dt = datetime.datetime.now()
    log_my.info(f"dt: {dt} - {dt.tzname()}")

Output:

2025-02-05 12:05:15.220 [INFO ] [myLogger                            ] - dt-astimezone: 2025-02-05 11:05:15.206000+00:00 - GMT
2025-02-05 12:05:15.222 [INFO ] [myLogger                            ] - dt: 2025-02-05 11:05:15.220000 - None

directly in the server console:

Python 3.11.2 (main, Nov 30 2024, 21:22:50) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2025, 2, 5, 12, 1, 40, 433927)
>>> datetime.datetime.now().astimezone()
datetime.datetime(2025, 2, 5, 12, 1, 50, 941992, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'CET'))

Do you have any further ideas?

I will do some investigation later; checking the implementation from Python to, see if I can get a possible root cause.

looks like something with your timezone setting is wrong.

If I run your example. I get

2025-02-05 13:25:03.033 [INFO ] [ore.automation.pythonscripting.test.] - dt-astimezone: 2025-02-05 13:25:03.031000+01:00 - MEZ
2025-02-05 13:25:03.034 [INFO ] [ore.automation.pythonscripting.test.] - dt: 2025-02-05 13:25:03.034000 - None

there are 2 possible timezone settings.

one is inside the ui. or if you have a file based deployment it is “/conf/services/runtime.cfg” => org.openhab.i18n:timezone=Europe/Berlin

and the other one is the mentioned environment variable used for container based deployments.

a new bug fix release is available in the marketplace

I looked deeper in my timezone issue, but was not successful finding the root cause.
What I found out, that I also have this issue in Python 2.7 Scripting.
I created a workaround for me.

In JavaScripting I get the correct time.

My current setup
Python 2.7

sys.version = 2.7.3 (tags/v2.7.3:5f29801fe, Sep 10 2022, 18:52:49)
[Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)]
sys.version_info = sys.version_info(major=2, minor=7, micro=3, releaselevel='final', serial=0)

Python 3.11

sys_version = 3.11.7 (Fri Oct 04 14:24:04 CEST 2024) [Graal, Interpreted, Java 17.0.11 (amd64)]
sys.version_info = sys.version_info(major=3, minor=11, micro=7, releaselevel='alpha', serial=0)

@tpnrw maybe a little bit more details would help.

can you show us the output of you default timezone

from openhab import logger
import time
logger.info(str(time.tzname))

additionally I like to know

  • how are you deploying openhab. Is it running as a container?
  • if yes, is the environment variable, I mentioned, configured?
  • is the runtime configuration “org.openhab.i18n:timezone” correct
  • what version of openhab are you running
  • which os are you using
  • what java version

if you running on linux, the output of “date +%Z” would be helpful too. If you run a container, the previous mentioned command should be run inside the container.

and what is your workaround?

Hello Holger,
thank you for looking for my issue.

default timezone: (‘GMT’,)

  • how are you deploying openhab. Is it running as a container?
    directly in linux
root@its:~# lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 12 (bookworm)
Release:        12
Codename:       bookworm
  • if yes, is the environment variable, I mentioned, configured?
    no container, but set
    EXTRA_JAVA_OPTS="-Duser.timezone=Europe/Berlin"
    in /etc/openhab/linux.parameters

  • is the runtime configuration “org.openhab.i18n:timezone” correct
    set org.openhab.i18n:timezone=Europe/Berlin in /etc/openhab/services/runtime.cfg

  • what version of openhab are you running
    openhab-addons/stable,now 4.3.2-1 all [installiert]

  • which os are you using
    see above

  • what java version

root@its:~# java --version
java 17.0.11 2024-04-16 LTS
Java(TM) SE Runtime Environment (build 17.0.11+7-LTS-207)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.11+7-LTS-207, mixed mode, sharing)

my workaround:
because no timezones are availble; I use zoneinfo.reset_tzpath(("/usr/share/zoneinfo",)) to relaod all available timezones from OS and create default_timezone = zoneinfo.ZoneInfo("Europe/Berlin") which I use in every call for calls i.e.

s_dt_file = str(datetime.datetime.fromtimestamp(st_info.st_mtime, its.default_timezone))

I tried to understand the problem from @tpnrw a bit more, but so far without success. I tried to reproduce it.

  • First, I installed debian bookworm inside virtual box.
  • There I installed java via “apt install openjdk-17-jdk”
  • Additionally I installed openhab, following the instructions here openHAB on Linux | openHAB
  • During the initial startup of openhab, I configured the timezone “Europe/Berlin” via webui
  • At least, I installed the python binding via webui.

Then I created a test.py with the following code

from openhab import logger
import time
logger.info(str(time.tzname))

The output was again

('MEZ', 'MESZ')

So, with a “clean” setup, everything works as expected.

Maybe someone else has an idea what could impact a wrong timezone setting in openhab / jvm?

Timezone can come from one of three places: the OS, MainUI, and arguments passed to the JVM. It’s the last one that usually causes problems. To address this one needs to pass the timezone as an argument to the JVM when starting OH by adding it to EXTRA_JAVA_OPTS in /etc/default/openhab. E.g.

-Duser.timezone=US/Mountain

There is an option in openhabian-config that does this for you but if you are not running openHABian you need to do this yourself.

I’m not sure we really know why this is sometimes required but usually not required.

1 Like