Hi @holger_hees ,
as I just updated my used image to 5.1.4, I uploaded my dockerfile to git: HomeAutomationStuff/OH_DockerImagePython at main · Urmel/HomeAutomationStuff · GitHub.
Maybe this helps someone. ![]()
Hi @holger_hees ,
as I just updated my used image to 5.1.4, I uploaded my dockerfile to git: HomeAutomationStuff/OH_DockerImagePython at main · Urmel/HomeAutomationStuff · GitHub.
Maybe this helps someone. ![]()
@holger_hees, thank you for all of your hard work. I have gone from Rules DSL in the beginning, to Jython (jsr233), then HABapp and now I’m trying to move back over to Python3 for a new install I am doing. I am trying to learn the way to write rules, and I’m hitting a wall trying to get the “itemEvent” when a rule is triggered. Lets say I have a switch, and it receives a command, how do I get all the various parameters of the triggering device (Name, Label, Tags, State, Previous State, etc…) and, if it was a Change vs. Update vs. Command, is there a different method to call to get the Commanded state vs. the new state? Here is my code and where I’m not sure what to do:
# ------------------------ RULE ------------------------
@rule("TEST.TestSwitch1", # Note: description and tags are optional
description="Rule that is triggered by TestSwitch1",
tags=["TestSwitch1",] )
@when("Item TestSwitch1 received command")
def tts1(module, input):
# I've tried logging as strings both "module" and "input" to see if this info is carried in either of those and it doesn't seem to be in those objects?
tName = str(event.itemName) # ??? (I know this doesn't work)
tItem = Registry.getItem(tName) # ??? (I think this would work if I knew the tName
tLabel = str(tItem.label) # ??? (Not sure on this one)
tState = str(event.itemCommand) # ??? State for "Received Command"
#tState = str(items[tName]) # ??? State for "Changed/Updated"
My apologies for the elementary nature of my request, but I have tried to find it on the GitHub page as well as the OpenHAB docs and can’t seem to.
Thank you!
Python examples of simple rules can be found here
This url is also linked in the binding documentation itself.
Thank you for the link. For others who have questions about how to get events, items, name, label, etc, here’s a consolidated list I’ve figured out so far:
# Use these within received command, received update or changed rules
event = input['event']
eventType = input['event'].getType()
eventItemName = input['event'].getItemName()
eventCommand = input['command'] # Received Command rules ONLY
# or input['event'].getItemCommand()
eventState = input['event'].getItemState() # Received Update/Changed rules ONLY
previousState = input['event'].getOldItemState() # Changed rules ONLY
# Use these once the item name is known
i = "myItemName"
item = Registry.getItem(i)
itemState = Registry.getItem(i).getState()
itemName = Registry.getItem(i).getName()
itemLabel = Registry.getItem(i).getLabel()
I’ve run into another issue, I have several working python3 binding scripts now, doing all sorts of things, but this one is just blowing my mind. My computer (win11 laptop) is on US central time. That is the time that’s listed in the system tray in the bottom right, it’s also verifiable through several other means, as well as openHAB when I start it up and look at the logs. My openhab settings (main UI) are also set to US central time (-05:00). Everything seems to be fine in my openhab system until I make one particular call, a datetime.strptime(), and on that call my OpenHAB time (the time listed in the log) changes to UTC time. Then over a short amount of time that UTC time propagates to all of the other time related items I reference in python, which should be in local (operating system) time. Here is the offending code (all of the print statements were to help me figure out where exactly in my code this was happening, and the sleep calls were to verify it wasn’t a timing coincidence, it really is the strptime call somehow doing this).
if TEST:
print(f"TTa: {datetime.now()}")
time.sleep(1.0)
print(f"TTb: {datetime.now()}")
time.sleep(4.0)
print(f"TTc: {datetime.now()}")
dt_string = "2026-07-12T15:34:46-0500"
time.sleep(1.0)
print(f"TTd: {datetime.now()}")
time.sleep(1.0)
print(f"TTe: {datetime.now()}")
dt_obj = datetime.strptime(dt_string, "%Y-%m-%dT%H:%M:%S%z")
print(f"DTO: {dt_obj}")
time.sleep(2.0)
print(f"TTf: {datetime.now()}")
And here are the logs that happen while this code is running. You’ll see where the time jumps from 15:57 to 20:57. I’ve tried this 15 times at least and it happens at the exact same line, every time. I’ve tried changing the variable names, making sure I was importing datetime correctly, ie. from datetime import datetime. The strptime call works fine and you can see it creates the datetime object just fine (I’ve also changed the formatting so that it’s incorrect and throws a valueError. When I do that the OpenHAB time still changes on this line.
15:57:11.882 [DEBUG] [scripting.internal.PythonScriptEngine] - Initializing GraalPython script engine 'C:\OPENHA~1\conf\automation\python\new_rtl.py' ...
15:57:13.313 [WARN ] [.io.openhabcloud.internal.CloudClient] - Error connecting to the openHAB Cloud instance: not authorized. Reconnecting after 24813 ms.
15:57:13.835 [INFO ] [ab.automation.pythonscripting.new_rtl] - TTa: 2026-07-12 15:57:13.793000
15:57:14.841 [INFO ] [ab.automation.pythonscripting.new_rtl] - TTb: 2026-07-12 15:57:14.838000
15:57:18.846 [INFO ] [ab.automation.pythonscripting.new_rtl] - TTc: 2026-07-12 15:57:18.843000
15:57:19.855 [INFO ] [ab.automation.pythonscripting.new_rtl] - TTd: 2026-07-12 15:57:19.849000
15:57:20.862 [INFO ] [ab.automation.pythonscripting.new_rtl] - TTe: 2026-07-12 15:57:20.857000
20:57:21.060 [INFO ] [ab.automation.pythonscripting.new_rtl] - DTO: 2026-07-12 15:34:46-05:00
20:57:23.064 [INFO ] [ab.automation.pythonscripting.new_rtl] - TTf: 2026-07-12 15:57:23.062000
20:57:23.591 [DEBUG] [ab.automation.pythonscripting.new_rtl] - 16 RTL Time(s) Updated
20:57:23.651 [INFO ] [n.pythonscripting.new_rtl.exampleRule] - Rule 'RTL.Example_Rule' initialised
20:57:23.675 [INFO ] [tion.pythonscripting.new_rtl.highRate] - Rule 'RTL.High_Rate' initialised
20:57:23.704 [INFO ] [ation.pythonscripting.new_rtl.lowRate] - Rule 'RTL.Low_Rate' initialised
20:57:23.716 [INFO ] [tomation.pythonscripting.new_rtl.test] - Rule 'RTL.Test' initialised
20:58:00.903 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Uptime_Secs' changed from 36650.648799 to 36680.64809 (source: org.openhab.core.automation.module.script)
20:58:00.908 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Uptime' changed from 10:10:51 to 10:11:21 (source: org.openhab.core.automation.module.script)
20:58:00.961 [INFO ] [.pythonscripting.time.systemDateTimes] - RULE FIRED: Update System DateTimes (Every min)
20:58:01.284 [WARN ] [.pythonscripting.time.systemDateTimes] - Raw Datetime:2026-07-12 20:58:01.275554+00:00
20:58:01.287 [WARN ] [.pythonscripting.time.systemDateTimes] - Local Time: 2026-07-12 20:58:00.962000+00:00
20:58:01.288 [WARN ] [.pythonscripting.time.systemDateTimes] - UTC Time: 2026-07-12 20:58:00.968000+00:00
20:58:01.289 [WARN ] [.pythonscripting.time.systemDateTimes] - UTC String: 20:58, 07/12/2026
20:58:01.290 [WARN ] [.pythonscripting.time.systemDateTimes] - TZ Offset: 00:00
20:58:01.290 [WARN ] [.pythonscripting.time.systemDateTimes] - TZ Name: GMT
20:58:01.291 [WARN ] [.pythonscripting.time.systemDateTimes] - TZ Abbrev: GMT
20:58:01.295 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Local_DateTime' changed from 2026-07-12T20:56:00.707+0000 to 2026-07-12T20:58:00.962+0000 (source: org.openhab.core.automation.module.script)
20:58:01.298 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'OH_Update' changed from 2026-07-12T20:56:00.707+0000 to 2026-07-12T20:58:00.962+0000 (source: org.openhab.core.automation.module.script)
20:58:01.298 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'UTC_DateTimeStr' changed from 20:56, 07/12/2026 to 20:58, 07/12/2026 (source: org.openhab.core.automation.module.script)
20:58:25.999 [WARN ] [.io.openhabcloud.internal.CloudClient] - Error connecting to the openHAB Cloud instance: not authorized. Reconnecting after 24606 ms.
Any ideas would be greatly appreciated!
We’ve seen this bug before I believe - it’s a bug in GraalPy, and it propagates to OH because OH use the default timezone many places where it shouldn’t.
Any code running on a JVM can change the default timezone at any time, which is why you’re not supposed to use it. datetime.strptime() does something really stupid and ends up changing the default timezone, and the result is what it is.
There is no “local operating system time”, there is only the JVM default timezone, and the Python function changes that.
Until such a time that OH is independent of the JVM default timezone, you simply can’t use that function.
Wow, that’s suprising that a python function would somehow change the code. Thank you for the information. Should I file a PR about this, and under what program? Is it something that could be fixed in @holger_hees helpers or I’m guessing it’s lower level than that. Just let me know if there’s anything I can do to help. For now I’ll see if there’s a java equivalent of strptime that I can use.
I actually don’t know - I’ve always assumed that it was part of GraalPy itself. But, if it’s part of the helper library, we can actually fix it. @holger_hees where does this bug come from?
Hello,
Yes, this error is known. I debugged it to the point where I could rule out the helper libs as the cause; it occurs even with a “pure” GraalPy context.
The current workaround is to avoid using this function. I will add a note about this to the binding README.
and I created a bug report.
Thanks all for the help and verifying that I wasn’t totally out to lunch! I’ve avoided using the strptime function by instead using the java functions and formatters, it took a while to find the code, but I got it working, so until strptime is ready, I should be ok. Here’s a reminder on how to do that if anyone needs help:
#python3.12
from java.time import ZonedDateTime
from java.time.format import DateTimeFormatter
from datetime import datetime, timezone, timedelta
t_string = "2026-07-12T15:34:46-05:00"
fmt = DateTimeFormatter.ofPattern(f"yyyy-MM-dd'T'HH:mm:ssXXX") # +00:00, (no decimal seconds, colon)
java_zdt = ZonedDateTime.parse(t_string, fmt)
# Java ZDT should be able to update a OpenHAB item, but if you need python datetime...
py_dt = datetime( # Build the datetime object
java_zdt.getYear(),
java_zdt.getMonthValue(),
java_zdt.getDayOfMonth(),
java_zdt.getHour(),
java_zdt.getMinute(),
java_zdt.getSecond(),
int(java_zdt.getNano() / 1000)
)
# Add z-offset to make it timezone aware
offset = int(java_zdt.getOffset().getTotalSeconds() / 60)
py_dt = py_dt.replace(tzinfo=timezone(timedelta(minutes=offset)))
print(py_dt) # python datetime object, wasn't that fun?!
One more little issue I’ve found, now that I’m messing around with datetimes a bunch: using isinstance() on the various time types available in the python3 binding does not work out as expected and is pretty tricky. If I run the following code (it’s modified slightly for notes and won’t actually run now…) you will see that isinstance() on either a Python datetime or Java ZonedDateTime will come back as true, which breaks a bunch of my own personal helper scripts. So now I’ve reverted to running type(obj).__name__, figuring out what the name is, and then using a simple if statement to test if it’s the right type object.
dt_string = "2026-07-12T15:34:46.123456-0500"
java_zdt = dts_to_zdt(dt_string) # Java ZonedDateTime Object
python_dt = to_python_datetime(java_zdt) # Python datetime Object
type(java_zdt).__name__ = 'Java_java.time.ZonedDateTime_generated'
type(python_dt).__name__ = 'datetime'
if isinstance(python_dt, ZonedDateTime) = True # This should be false!
if isinstance(java_zdt, datetime) = True # This should be false!
So my hacky test of “isinstance” is now:
if "ZonedDateTime" in type(java_zdt).__name__:
# Do ZonedDateTime things
if "datetime" in type(python_dt).__name__:
# Do datetime things
I’m not sure if this is also an underlying Graalvm issue or what?
This is a normal behavior, because Java ZonedDateTime is transparently mapped into a python datetime object
to differentiate between both, there is a special isinstance function inside the java package.
from java.time import ZonedDateTime
from java.time.format import DateTimeFormatter
from datetime import datetime, timezone, timedelta
import java
t_string = "2026-07-12T15:34:46-05:00"
fmt = DateTimeFormatter.ofPattern(f"yyyy-MM-dd'T'HH:mm:ssXXX") # +00:00, (no decimal seconds, colon)
java_zdt = ZonedDateTime.parse(t_string, fmt)
python_dt = datetime.now().astimezone()
print( type(java_zdt).__name__ ) #=> "Java_java.time.ZonedDateTime_generated"
print(java_zdt)
print( isinstance(java_zdt, ZonedDateTime) ) #=> true
print( isinstance(java_zdt, datetime) ) #=> true
print( java.instanceof(java_zdt, ZonedDateTime) ) #=> true
print( type(python_dt).__name__ ) #=> "Java_java.time.ZonedDateTime_generated"
print(python_dt)
print( isinstance(python_dt, ZonedDateTime) ) #=> true
print( isinstance(python_dt, datetime) ) #=> true
print( java.instanceof(python_dt, ZonedDateTime) ) #=> false
will result
2026-07-13 10:39:54.913 [INFO ] [nhab.automation.pythonscripting.test] - Java_java.time.ZonedDateTime_generated
2026-07-13 10:39:54.914 [INFO ] [nhab.automation.pythonscripting.test] - 2026-07-12 15:34:46-05:00
2026-07-13 10:39:54.915 [INFO ] [nhab.automation.pythonscripting.test] - True
2026-07-13 10:39:54.915 [INFO ] [nhab.automation.pythonscripting.test] - True
2026-07-13 10:39:54.915 [INFO ] [nhab.automation.pythonscripting.test] - True
2026-07-13 10:39:54.915 [INFO ] [nhab.automation.pythonscripting.test] - datetime
2026-07-13 10:39:54.916 [INFO ] [nhab.automation.pythonscripting.test] - 2026-07-13 08:39:54.911000+00:00
2026-07-13 10:39:54.916 [INFO ] [nhab.automation.pythonscripting.test] - True
2026-07-13 10:39:54.916 [INFO ] [nhab.automation.pythonscripting.test] - True
2026-07-13 10:39:54.916 [INFO ] [nhab.automation.pythonscripting.test] - False
I got already a response from the graal devs. They belive that this issues is fixed in Graal Version 25.1.3. As soon as we updated openhab to use this version, I will test again. Hopefully it is fixed there.
Ok, thanks for the special java.instanceof() test. It looks like that works properly to test that an item is a ZonedDateTime object, but doesn’t work the other way around to test that something is explicitly a Python datetime object. When I run this code I get an error:
print(isinstance(python_dt, ZonedDateTime)) # True - known issue
print(java.instanceof(python_dt, ZonedDateTime)) # False - use this test instead
print(isinstance(java_zdt, datetime)) # True - known issue
print(java.instanceof(java_zdt, datetime)) # Throws a TypeError
Output
18:23:10.028 [INFO ] [ab.automation.pythonscripting.new_rtl] - True
18:23:10.030 [INFO ] [ab.automation.pythonscripting.new_rtl] - False
18:23:10.030 [INFO ] [ab.automation.pythonscripting.new_rtl] - True
18:23:10.103 [ERROR] [ab.automation.pythonscripting.new_rtl] - TypeError, unsupported instanceof(Java_java.time.ZonedDateTime_generated, type)
I’m guessing that this is because the java side doesn’t recognize what the datetime object is? So there is no really explicit way to confirm that something is a Python datetime, but if I check that it is a Python datetime using the Python isinstance() and then check that it’s not a Java ZonedDateTime or LocalDateTime using the Java instanceof(), I can be pretty sure that it is a Python Datetime.
@holger_hees and others, I’m sorry to bother you and the community with another question, but I am running this on Win11, the Python Scripting is working wonderfully, but then I realized that “requests” isn’t part of python3. Now I’ve read through all of the docs on enabling the the VEnv, I’ve followed the instructions and tried it multiple times, even to the point of completely rebuilding my OpenHAB installation. The docs don’t really cover a windows install so here’s what I’ve done:
c:\graalpyc:\graalpy\bin I run the command graalpy -m venv c:/openhab/userdata/cache/org.openhab.automation.pythonscripting/venvc:/openhab/userdata/cache/org.openhab.automation.pythonscripting/venv folder and it has this directory tree in it: Include (folder), Lib (folder), Scripts (folder), pyvenv.cfg (file). If I check the properties of the venv folder there are 863 files, 105 folders, just for reference.openhab> pythonscripting info and there it tells me VEnv state: disabled.Do you have any guesses as to what I’m missing or what could be wrong? (As a side note, I think it’s funny that your example in the docs is loading the requests package. Seriously, why isn’t it built in to python3!?)
To check whether or not venv is activated, the system looks for the bin/graalpy binary in the directory .../userdata/cache/org.openhab.automation.pythonscripting/venv. The file .../userdata/cache/org.openhab.automation.pythonscripting/venv/bin/graalpy should therefore be present.
By the way, I had the same challenge with the requests library. However, since that would have been my only use case for venvs and the whole setup seemed too unstable to me… I use the openHAB HTTP action instead. It doesn’t cover every use case, of course, but it was sufficient for my needs.
from openhab.actions import HTTP
response = HTTP.sendHttpGetRequest("http://www.website.de")
Hello,
I’ve encountered problem with pythonscripting on OH 5.2.1 when I use VEnv. I’ve described details in 5.2 release discussion thread, but it seems it drowned in other posts
Does anyone have any idea what might be the problem here?
Have you any other language, like javascript activated?
Are there any other errors before?
Can you share the result of the commandline output of pythonscripting info
Could you provide a bundle list?
– UPDATE –
Maybe I have an idea. It looks like the reason are the preinstalled pip modules.
The exception raised during this phase, but the engine is not initialized.
I fixed it right now, but I can’t upload the new kar file to github yet. It looks like they have server problems. I will do hit later.
In the meantime, I uploaded the fixed kar to my google drive
If you want to test, please deinstall your current pythonscritping addon. Wait until this procedure is done. After that, download the mentioned kar file above and put it to your addon folder. Now it should not crash anymore during the pip module initialization.
If the fix is successful, I will prepare a new marketplace release.
I’ll confirm the points above and provide the last answer after I perform another upgrade to 5.2.1.
Ok. Will test it in a few minutes.