Using the cron trigger as time scheduler for sending commands to items in Jython

Hi,

this is in my opinion a simple and nice solution to perform a sendCommand to an item at a specified time. I tested it with multiple items and item types.

For example, let’s assume that we have the following items:

// Bulb1
Switch  Light1_Toggle       { channel="hue:0210:1:bulb1:color" }
Dimmer  Light1_Dimmer       { channel="hue:0210:1:bulb1:color" }
Color   Light1_Color        { channel="hue:0210:1:bulb1:color" }
Dimmer  Light1_ColorTemp    { channel="hue:0210:1:bulb1:color_temperature" }
String  Light1_Alert        { channel="hue:0210:1:bulb1:alert" }
Switch  Light1_Effect       { channel="hue:0210:1:bulb1:effect" }

// Bulb2
Switch  Light2_Toggle       { channel="hue:0220:1:bulb2:brightness" }
Dimmer  Light2_Dimmer       { channel="hue:0220:1:bulb2:brightness" }
Dimmer  Light2_ColorTemp    { channel="hue:0220:1:bulb2:color_temperature" }

In my solution you then have to create:

// Bulb1
String Light1_Toggle_Scheduler "[%s]"
String Light1_Dimmer_Scheduler "[%s]"
String Light1_Color_Scheduler "[%s]"
String Light1_ColorTemp_Scheduler "[%s]"
String Light1_Alert_Scheduler "[%s]"
String Light1_Effect_Scheduler "[%s]"

// Bulb2
String Light2_Toggle_Scheduler "[%s]"
String Light2_Dimmer_Scheduler "[%s]"
String Light2_ColorTemp_Scheduler "[%s]"

The scheduler rule looks like this:

from datetime import datetime
from core.rules import rule
from core.triggers import when
from core.actions import LogAction
import time
import pytz

def execute_command(item_name, command, schedule_date_str):
    timezone = pytz.timezone('Europe/Berlin')  # Wähle die entsprechende Zeitzone

    current_date = datetime.now(timezone).replace(microsecond=0)  # Setze Mikrosekunden auf 0
    schedule_date = datetime.strptime(schedule_date_str, "%Y-%m-%dT%H:%M:%S")
    schedule_date = timezone.localize(schedule_date).replace(microsecond=0)  # Setze Mikrosekunden auf 0

    if current_date == schedule_date:
        LogAction.logInfo("rules", "Scheduler will sendCommand {} to item {}", command, item_name)
        events.sendCommand(item_name, command)
        time.sleep(1)
        events.postUpdate(item_name, "")

@rule("Command Scheduler")
@when("Time cron * * * * * ?")
def command_scheduler(event):
    items_data = {
        "Light1_Toggle_Scheduler": "Light1_Toggle",
        "Light1_Dimmer_Scheduler": "Light1_Dimmer",
        "Light1_Color_Scheduler": "Light1_Color",
        "Light1_ColorTemp_Scheduler": "Light1_ColorTemp",
        "Light1_Alert_Scheduler": "Light1_Alert",
        "Light1_Effect_Scheduler": "Light1_Effect",
        "Light2_Toggle_Scheduler": "Light2_Toggle",
        "Light2_Dimmer_Scheduler": "Light2_Dimmer",
        "Light2_ColorTemp_Scheduler": "Light2_ColorTemp"
    }

    for item_scheduler, item_name in items_data.items():
        item_state = str(ir.getItem(item_scheduler).state)

        if item_state != "NULL" and item_state != "":
            state = item_state.replace(" ", "").split(",")
            schedule_date = state[0]
            command = state[1]

            execute_command(item_name, command, schedule_date)

On the left side you have to say which schedule item (String type) belongs to which item (this item should receive the command at the specific time) on the right side.

I also had to use pytz to make sure that both datetime objects are really in the same time zone. Otherwise the alignment does not work.

For testing I used the karaf console. As example you can make following command:

openhab:send Light1_Toggle_Scheduler "2023-07-20T13:55:00,ON"

This means that the cron trigger which runs every second will check if 2023-07-20T13:55:00 is reached and then makes an sendCommand with “ON” to Light1_Toggle.

Advantages

  • You can run a command exactly at a specific time. (This could also work after restarting openHAB if you have a persistence for your scheduler item which is a String item.)
  • You can access it easily via the REST API because your schedule is an item.

Disadvantages

  • For each item you have to create a String item which receives the command and splits it into a schedule date and a command
  • You can only create one schedule. As example you can’t say that at 13:15 the bulb should receive an ON command and on 13:30 an OFF command. (Of course, this can still be optimized.)

A second-by-second check does not even increase CPU usage by 1% for me.

Nice if that’s working well for you. Do you have any experience in performance, as your rule is running every second? What platform are you running you system on?

Why do you post an empty state, after you have send the command? Is there any specific usecase?

Alternatively if you want something without code and rules I can recommend the icalendar binding, where you can send commands to items based on calendar events. I use this e.g. to set my heating setpoint to the right level

My CPU utilization is constantly between 3 and 7%. Which I achieved after switching from DSL Rules to Jython Rules. Memory is at 73%, but I would like to increase my RAM. Before I switched the rules to Jython, I was between 60-98.5% CPU utilization. The 98.5% CPU usage meant that openHAB was crashing. I think it is due to the problematic parallelization of the DSL Rules. I am currently using 10237 items with 295 Things and 1877 Rules. Before that, the magic limit was just under 1000 rules, before the CPU usage caused real stress.

I am running openHAB 3.4 on Ubuntu 20.04 in a virtual machine with 12 GB RAM.

I also once had a rule running where I check every second to see if a program is running on another machine or not. If this is not the case, I can start this via SSH when ON. If this is the case, I can end the process by OFF via SSH and kill. Perhaps I take this rule also again purely. Was before unthinkable because of the CPU load.

To be fair, I have to say that I had Python scripts that communicate with openHAB via the REST API, which are now fully implemented as Jython rules. This also drastically reduces CPU usage.

To clearly define what I mean by CPU usage. I’m talking about the value I find via top to Java and see that the process was started by the user openhab. Python, influxdb, habapp, promtail etc are all under 1%. So in interaction to openHAB still runs a lot more. I also see occasional shell commands listed by ExecuteCommandLine. Let the CPU usage of all adjacent processes together still account for 2-3%. Which is rounded up a lot.

I delete the state because I also display the item in the sitemap. Then I see when a schedule should be executed. As soon as this has been executed, it is deleted. This is not repeated anymore. It is virtually a cleanup that this schedule has been processed.

Thank you very much for the tip. I run a lab for smart home projects at a university. We test a lot of things there. Students are supposed to work with the infrastructure. Using the REST API, you can then access one of the items, for example, and use this in the context of an AI that analyzes the probability of me drinking coffee at 9:00 in the morning. If this probability is true and let’s say I set the alarm at 8:45, walked through the door at 8:50, etc., then you can use a scheduler so that at 9:00 sharp, for example, the coffee is prepared and Alexa reads me the latest news.

This is certainly also possible via a calendar. Than that our access from a program is made directly with a command. We also investigate completely different things. Last week I compared 4 different TTS systems in Python. So performance, SSML, that subjective feeling of the sound quality and of course if this is online/offline.

I don’t know if this is possible in Python since no one is really maintaining the helper library any more. In OH 3 a new rule trigger Time is <Item> was introduced. Couple this with some Item metadata and you could trigger the rule based on the state of these DateTime Items and get the command from the Item metadata. You could even get the Items to send the command to from the Item metadata.

The advantage of this approach is that you can configure the actual schedule from the UI with a DateTime Picker.

1 Like