executeCommandLine causes PolyglotException

As already mentioned somewhere else, I can turn off the heating of a tado TVR, but to return to schedule, I have to make a call to tado’s official API.

I’ve got a rule that turns off a specific TVR when a window is opened, by sending command 0 to e.g. Item Verwarming_badkamer_Heating_Cooling_Target. If the window is closed, that same rule sends command SCHEDULE to Item Verwarming_badkamer_mode. The window in the kitchen switches both the heating in the kitchen and the heating in the living room. The rule then simply iterates between the two relevant Items.

Another rule is triggered when Verwarming_badkamer_mode has received command SCHEDULE. This works, but not for the heating in the living room if the window is closed quickly after opening it (e.g. when it was only opened to let the dog in). I don’t really understand why, but since the thermostat is also connected to that tado “zone”, I assume there’s an internal process within the tado system, which takes too long for the SCHEDULE command to be processed properly.

I therefore tried to add a “double check” to this “tado rule”, via a cache:

configuration: {}
triggers:
  - id: "2"
    configuration:
      command: SCHEDULE
      itemName: Verwarming_badkamer_mode
    type: core.ItemCommandTrigger
  - id: "3"
    configuration:
      command: SCHEDULE
      itemName: Verwarming_dressing_mode
    type: core.ItemCommandTrigger
  - id: "4"
    configuration:
      command: SCHEDULE
      itemName: Verwarming_eetkamer_mode
    type: core.ItemCommandTrigger
  - id: "5"
    configuration:
      command: SCHEDULE
      itemName: Verwarming_gang_gelijkvloers_mode
    type: core.ItemCommandTrigger
  - id: "6"
    configuration:
      command: SCHEDULE
      itemName: Verwarming_keuken_mode
    type: core.ItemCommandTrigger
  - id: "7"
    configuration:
      command: SCHEDULE
      itemName: Verwarming_studieruimte_mode
    type: core.ItemCommandTrigger
  - id: "8"
    configuration:
      itemName: Bewoners_Thuis
    type: core.ItemStateChangeTrigger
  - id: "9"
    configuration:
      itemName: testswitch
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript
      script: |-
        var pythoninvenv = '/var/lib/openhab/bin/python/tado-manual-control/venv/bin/python';
        var pythonscript = '/var/lib/openhab/bin/python/tado-manual-control/tado-manual-control.py';
        var pythondurationinseconds = 3;
        var controletijd = 5;

        var tadoZones = {
          "Verwarming_badkamer_mode": "5" ,
          "Verwarming_dressing_mode": "9",
          "Verwarming_eetkamer_mode": "2",
          "Verwarming_gang_gelijkvloers_mode": "8",
          "Verwarming_studieruimte_mode": "3",
          "Verwarming_keuken_mode": "6"
        }


        function schemaActiveren(zonemodusitemnaam) {
          var zone = tadoZones[zonemodusitemnaam];
          var command = [pythoninvenv, pythonscript, 'back_to_schedule', '-z',  zone];
          callPython(command);
          var cachenaam = "cachevoorcontrole_zone_"+zone;
          if (cache.private.exists(cachenaam)) {
            clearTimeout(cache.private.get(cachenaam));
          }
          var controlezometeen = setTimeout(schemaActiveren, controletijd * 1000, zone, zonemodusitemnaam);
          cache.private.put(cachenaam, controlezometeen);
        }


        function activatieControle(zone, zonemodusitemnaam) {
          var zonemodusitem = items[zonemodusitemnaam];
          var statusitem = zonemodusitemnaam.split("_mode")[0]+"_Heating_Cooling_Target";
          if (zonemodusitem.state == "SCHEDULE" && !zonemodusitem.persistence.changedSince(time.toZDT().minusMinutes(controletijd)) && parseInt(items[statusitem].state) == 0) {
            var command = [pythoninvenv, pythonscript, 'back_to_schedule', '-z',  zone];
            callPython(command);
          }
        }


        function statusBewonersAanpassen(thuis) {
          var status = thuis == "ON" ? 'True' : 'False';
          var command = [pythoninvenv, pythonscript, 'set_home_state', '-s',  status];
          callPython(command);
        }


        function callPython(command) {
          console.log("Wachttijd voor pythonscript: "+pythondurationinseconds+" seconden.")
          var pythonOutput = actions.Exec.executeCommandLine(time.Duration.ofSeconds(pythondurationinseconds), command);
          console.log("Output pythonscript: "+pythonOutput);
        }


        function main() {
          var triggeringItemnaam = event.itemName;
          console.log(triggeringItemnaam);
          if (triggeringItemnaam.includes("_mode")) {
            schemaActiveren(triggeringItemnaam)
          } else if (triggeringItemnaam == "Bewoners_Thuis") {
            statusBewonersAanpassen(event.newState);
          }
        }

        main()
    type: script.ScriptAction

I tested this with the bathroom (“badkamer”) window, and it threw an error:

2026-01-20 20:03:18.502 [INFO ] [automation.jsscripting.rule.tado-API - 47446     ] - Verwarming_badkamer_mode
2026-01-20 20:03:18.505 [INFO ] [automation.jsscripting.rule.tado-API - 47446     ] - Wachttijd voor pythonscript: 3 seconden.
2026-01-20 20:03:19.421 [INFO ] [automation.jsscripting.rule.tado-API - 47446     ] - Output pythonscript: Login successful
Reactivating the schedule for zone 5...

2026-01-20 20:03:24.423 [INFO ] [automation.jsscripting.rule.tado-API - 47247     ] - Wachttijd voor pythonscript: 3 seconden.
2026-01-20 20:03:24.427 [WARN ] [ore.internal.scheduler.SchedulerImpl - 47247     ] - Scheduled job 'org.openhab.automation.jsscripting.rule.tado-API.timeout.1' failed and stopped
org.graalvm.polyglot.PolyglotException: null
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1108) ~[?:?]
	at java.lang.ProcessBuilder.start(ProcessBuilder.java:1089) ~[?:?]
	at org.openhab.core.io.net.exec.ExecUtil.executeCommandLineAndWaitResponse(ExecUtil.java:81) ~[bundleFile:?]
	at org.openhab.core.model.script.actions.Exec.executeCommandLine(Exec.java:56) ~[bundleFile:?]
	at <js>.callPython(<eval>:50) ~[?:?]
	at <js>.schemaActiveren(<eval>:21) ~[?:?]
	at <js>.:=>(@jsscripting-globals.js:200) ~[?:?]
	at com.oracle.truffle.polyglot.PolyglotFunctionProxyHandler.invoke(PolyglotFunctionProxyHandler.java:158) ~[bundleFile:?]
	at jdk.proxy1.$Proxy1420.run(Unknown Source) ~[?:?]
	at org.openhab.automation.jsscripting.internal.threading.ThreadsafeTimers.lambda$1(ThreadsafeTimers.java:114) ~[bundleFile:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$12(SchedulerImpl.java:189) ~[?:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$1(SchedulerImpl.java:88) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) ~[?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:317) ~[?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
	at java.lang.Thread.run(Thread.java:1583) [?:?]

So the first command was processed properly, but the second one threw an error. Initially, I thought it was because I had set pythondurationinseconds to 10, which is longer than controletijd, although I would have been surprised that would be a problem, since a different thread is used. But that’s not it (which is probably a good thing).

But what is the problem? Does anyone have any idea?

@Nadahar, I think to understand that you’re trying to battle thread pool problems, so I thought I’d ping you. But feel free to not engage, of course. :slight_smile: Maybe this has nothing to to with thread pools, but layman me sees it written in the error stack.

The exception is thrown from ProcessBuilder here:

        for (String arg : cmdarray)
            if (arg == null)
                throw new NullPointerException();

So, as you can see, the concrete cause of this is that one of the arguments in the “command array” passed to the process builder is null. This array is provided to org.openhab.core.model.script.actions.Exec.executeCommandLine() directly from the Python engine, so I can’t track it any further. But it seems likely that you provide the source for this array, or the array itself.

Maybe it’s this line?

If it is, then one of the variables you provide is null, which isn’t allowed.

1 Like

Thanks for the pointer. I’ll run some tests tomorrow. I’m not sure why it would be null, but that’s something to figure out then.

1 Like

Idiot me… I refered to the wrong function in setTimeout()

1 Like