Python3 Binding (Discussion)

If you say it works with native python, you can also test it with your native graalpy binary from /venv/bin/graalpy.

I already found out that the script tries to read the file .lgd-nfy0 and I already set an environment variable so that the file is put into the temp folder of openhab and has the owner and group set to the openhab user and file permissions are set to 644.

I just tried a simple test script in the pythonscripting console and I have the same error when executing those two lines:

file = open(“/openhab/userdata/tmp/.lgd-nfy0”, “rb”)
file.read(16)

The .lgd-nfy0 file is a pipe file that is created from the native library and it has always the size 0. Could there be a problem reading those files in GraalPy?

yes, it could.

With that way, you can confirm that it is a graalpy problem.

I’ve just tested this with /venv/bin/graalpy (logged in as user openhab) and there is no error when executing the following line:

from adafruit_servokit import ServoKit

The same line in the pythonscripting console throws the error.

What could be different when GraalPy is running inside OpenHab that could cause this problem?

Edit: When launching with /venv/bin/graalpy I also had a second problem with the Adafruit_PureIO/smbus.py file. But I was able to fix this issue. This was the error message:

Traceback (most recent call last):
  File "<internal>", line 1, in <module>
  File "/openhab/userdata/Test.py", line 4, in <module>
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/adafruit_servokit.py", line 91, in __init__
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/adafruit_pca9685.py", line 177, in frequency
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/adafruit_register/i2c_struct.py", line 81, in __get__
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/adafruit_bus_device/i2c_device.py", line 135, in write_then_readinto
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/busio.py", line 240, in writeto_then_readfrom
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/adafruit_blinka/microcontroller/generic_linux/i2c.py", line 98, in writeto_then_readfrom
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/Adafruit_PureIO/smbus.py", line 251, in read_i2c_block_data
  File "/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv/lib/python3.11/site-packages/Adafruit_PureIO/smbus.py", line 93, in make_i2c_rdwr_data
NullPointerException
java.lang.NullPointerException
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodes$PointerArrayToBytesNode.convert(PointerNodes.java:896)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodesFactory$PointerArrayToBytesNodeGen$Inlined.execute(PointerNodesFactory.java:4597)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodes$WriteBytesNode.doPointerArray(PointerNodes.java:335)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodesFactory$WriteBytesNodeGen$Inlined.executeAndSpecialize(PointerNodesFactory.java:1551)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodesFactory$WriteBytesNodeGen$Inlined.execute(PointerNodesFactory.java:1448)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodes$WriteIntNode.doOther(PointerNodes.java:445)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodesFactory$WriteIntNodeGen$Inlined.executeAndSpecialize(PointerNodesFactory.java:2210)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodesFactory$WriteIntNodeGen$Inlined.execute(PointerNodesFactory.java:2178)
        at com.oracle.graal.python.builtins.modules.ctypes.memory.PointerNodes$WriteIntNode.execute(PointerNodes.java:417)

I was able to fix this issue by simply reversing the two lines in the smbus.py file from:

data.msgs = msg_data
data.nmsgs = len(messages)

to

data.nmsgs = len(messages)
data.msgs = msg_data

For some reason GraalPy is not happy when pointers are set before variables ar set.

I can now control my servos using the /venv/bin/graalpy executable. But inside OpenHab I still get the OSError: [Errno 5] Illegal seek. I hope this can be fixed somehow.

good thing is that you made this simple example. I was able to reproduce it and I asked the graal devs whether something like this is supported with named pipes. We’ll see what they say.

1 Like

I have good and some kind of “bad” news.

Bad news is
 Currently we would need to switch to native posix backend to allow reading of named pipes on linux. But this is not possible, because it brings a lot of other limitations. e.g. you can’t change stdin or stdout of python contexts anymore. This means python output will never be redirected to log files. So this is not an option.

Good news is
 a graal dev means, the reason why a posix backend would be necessary can be eliminated. In the current pure java backend, they raise an exception if they seek on a nonseekable pipe like a named pipe.
But in this special scenario they try a seek to 0 which should be possible, because they just jump to the beginning.

Long story short
 They will provide a bugfix soon, which makes this possible in native java backend.

https://github.com/oracle/graalpython/issues/526

1 Like

I am trying to port a rule from JS scripting which retrieves a solar forecast via HTTP API and then has to store it in the persistence database.

In JS, I am successfully using

var timeSeries = new items.TimeSeries(‘ADD’);
timeSeries.add(PV_date, (foreCastNW + foreCastSE).toString());
items.getItem(‘bothSides_forecast’).persistence.persist(timeSeries);

In Python, I have tried:

from org.openhab.core.types import TimeSeries
ts = TimeSeries(TimeSeries.Policy.ADD)
ts.add(pv_date, str(total_power))
Registry.getItem(“bothSides_forecast”).getPersistence().persist(ts)

With the ts.add, it says
TypeError: invalid instantiation of foreign object

What do I have to do to correctly use the TimeSeries object? Do I have to convert the timestamp to Instant myself?

The second parameter of ts.add is a State object and not a string. So the correct syntax would be

from org.openhab.core.types import TimeSeries
import scope

ts = TimeSeries(TimeSeries.Policy.ADD)
ts.add(pv_date, scope.DecimalType("2.0"))

But I will implement a generic mapping for State objects soon.

for item.postUpdate or item.sendCommand, we already have this typemapping. Means you can use values like “1.0” there.

I’m implementing now a generic solution which converts String, Integer, Boolean and datetime objects to their related State, if a State object is needed as a parameter type. That solution would work everywhere.

So it was only about the State object, not the Instant. Great. Thanks.

Seems to work!

@schup011 could you please check this version

I implemented the State/Type conversion. So the following code should work

from org.openhab.core.types import TimeSeries

ts = TimeSeries(TimeSeries.Policy.ADD)
ts.add(pv_date, "2.0")
ts.add(pv_date, 1.0)

if this and everything else works for you, I will create another marketplace release.

I have tested this version. Installation process was still a little bumpy, needed two openhab restarts and removal of the official 5.0.0.0 binding, but that could be due to the fact that I was some seconds too late after removing the previous file to also remove the option automation = jsscripting,pythonscripting in addons.cfg. That is why (I think) the 5.0.0.0 binding was installed automatically instead of the kar file I had removed from the folder.

Anyway, works now, tested the runtime_measurement option in order to make the log silent.

Also tested the TimeSeries build up. Worked without errors, I even added integers. I could easily check timestamps of begin and end of the TimeSeries, but not whether the states are in there. Will test with real data from the PV forecast, but that needs some time as the API calls per day are very limited.

EDIT: Works. I am simply feeding the TimeSeries with the numbers and they are correctly written into the persistence database.

1 Like

this happens to me too when I changed this behavior. :slight_smile: It was a leftover from a workaround which is not needed anymore. New versions are installable without this needed change in addons.cfg.

A new release is available for

openhab4 and openhab5

Add-on changes

  • Implement State data type mapping
  • Removed Item conversion, as the Add-on works now with “real” items on python side
  • Fix dependency watcher in case of using compiled pyc files
  • Fix ScriptEngine reinitialisation (in Transformation Services) after Add-on updates
    • Hopefully no “Context execution was cancelled” Exceptions after Add-on updates anymore
    • An Add-on update should work now without any exception (the old one can still raise an exception on shutdown) or openhab restart
  • Additional simplification of PythonScriptEngine & GraalPythonScriptEngine (~20% less code) => easier to understand for other people
  • Refactored VFS
  • Update helper lib to version 1.0.5

Helper lib 1.0.5 changes

  • Removed “Python => Java” type conversion for sendCommand and postUpdate. Is now handled inside the Add-on in a more generic way.
  • Replace error message TypeError: invalid instantiation of foreign object with a more helpful description like One of your function parameters does not match the required value type.
  • Initial integration tests started

I noticed that every time I change something in a Python rule, there are duplicates of .so libraries created in the venv folder. (I have nativeModules enabled)

Here is a screenshot:

Bildschirmfoto 2025-08-07 um 00.03.08

Is this normal or is this an issue of the beta release?

yes, this is a normal behavior of the graalvm which I can’t influence. I know that they want to change this behavior with the next major upgrade of their runtime in (September/Oktober). But I don’t know in which way they change it.

Ok thanks for the hint. I just clean those files up when I restart OpenHab until this is changed.

1 Like

It’s time for a new release for openhab4 and openhab5 :slight_smile:

In addition to many other changes, this time the python autocompletion is most interesting one

Changelog

Add-on changes

  • Fix internal addon log prefix (without .py) to align with script file log prefix
  • Added Type Hint Stub Generator as new console command
  • Fix update check console command
  • Fix ‘update install latest’ command, if a newer preview version was manuell installed
  • Update helper lib to version 1.0.6

Helper lib 1.0.6 changes

  • Register lifecycleTracker dispose hook only if a Timer is used
  • Global ZonedDateTime to datetime conversion. ZonedDateTime & Instant is now a registered interop_type
  • Add Registry.removeItem, Item.getThings, Item.getChannels, Item.getChannelUIDs, Item.getLinks, Item.linkChannel & Item.unlinkChannel => API documentation
  • Improved error logs for catched java exceptions in python.
  • Fix simple rule decorator (rule instead of rule()). Both variants works now.
  • Preview for type hinting (autocompletion)
  • BREAKING CHANGE: Changed parameter of Registry.addItem
  • BREAKING CHANGE: Replace NotInitialisedException with NotFoundException for getItem, removeItem, getThing, getChannel

Another release with just fixes for openhab4 and openhab5

Add-on changes

  • Type hint generator fixes
    • Path handling on windows
    • Fix for dictionaries
    • Fix nested lists
    • Fix for type parameters
  • Pip console command fixed

A new release for openhab4 and openhab5 with only minor changes.

I’m just releasing it, because I want to get the breaking change out as soon as possible.

Add-on changes

  • Type hint generator fixes
    • Remove instanceid from generated value representations
  • Update helper lib to version 1.0.7

Helper lib 1.0.7 changed

  • BREAKING CHANGE openhab.Timer removed.
    • Can be replaced with default Threading or Timer implementation.
    • No need to register shutdown procedure in the lifecycleTracker anymore
    • Old timer can be found as an example

Hi,

Anyone else got problem with this?

Code:

import http.client
conn = http.client.HTTPSConnection(“www.sunet.org”)
print(“AAAAA”)
conn.request(“GET”, “/”)
print(“BBBBB”)
r1 = conn.getresponse()
print(“CCCCC”)
while chunk := r1.read(200):
print(“DDDDD”)
print(repr(chunk))

Result (fails at conn.request) It works using http.client.HTTPConnection, ( no SSL )

SystemError, Option python.NativeModules is set to ‘true’ and a second GraalPy context attempted to load a native module ‘_cpython_unicodedata’ from path ‘/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/graalpy24.2/modules/_cpython_unicodedata.graalpy242-311-native-x86_64-linux.so’. At least one context in this process runs with ‘IsolateNativeModules’ set to false. Depending on the order of context creation, this means some contexts in the process cannot use native module, all other contexts must fall back and set python.NativeModules to ‘false’ to run native extensions in LLVM mode. This is recommended only for extensions included in the Python standard library. Running a 3rd party extension in LLVM mode requires a custom build of the extension and is generally discouraged due to compatibility reasons.

Traceback (most recent call last):

File “”, line 5, in

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/http/client.py”, line 1294, in request

self.\_send_request(method, url, body, headers, encode_chunked)

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/http/client.py”, line 1340, in _send_request

self.endheaders(body, encode_chunked=encode_chunked)

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/http/client.py”, line 1289, in endheaders

self.\_send_output(message_body, encode_chunked=encode_chunked)

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/http/client.py”, line 1048, in _send_output

self.send(msg)

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/http/client.py”, line 986, in send

self.connect()

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/http/client.py”, line 1466, in connect

self.sock = self.\_context.wrap_socket(self.sock,

            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/ssl.py”, line 518, in wrap_socket

return self.sslsocket_class.\_create(

       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/ssl.py”, line 1049, in _create

self.server_hostname = context.\_encode_hostname(server_hostname)

                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/ssl.py”, line 508, in _encode_hostname

return hostname.encode('idna').decode('ascii')

       ^^^^^^^^^^^^^^^^^^^^^^^

File “/etc/openhab/automation/python/lib/openhab/_wrapper_.py”, line 93, in importWrapper

return importOrg(name, globals, locals, fromlist, level)

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/encodings/idna.py”, line 3, in

import stringprep, re, codecs

File “/etc/openhab/automation/python/lib/openhab/_wrapper_.py”, line 93, in importWrapper

return importOrg(name, globals, locals, fromlist, level)

       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

File “/var/lib/openhab/.cache/org.graalvm.polyglot/python/python-home/e41832d1c8ba537cb2b83ce5a12dcd2887406294/lib/python3.11/stringprep.py”, line 8, in

from unicodedata import ucd_3_2_0 as unicodedata

File “/etc/openhab/automation/python/lib/openhab/_wrapper_.py”, line 93, in importWrapper

return importOrg(name, globals, locals, fromlist, level)