Any way on which writing rules is not a huge PITA?

Sorry if this develops a little bit into rant. OpenHAB is great, but this is a recurring issue for me that is just hugely frustrating. Let me say in advance, I really do not want to click any rules in any webinterface. I want to keep writing them in text files, versioning them in GIT and so on.

I am trying to write some rules for automatic wakeup light. Tasker on my phone will send the next alert time to an MQTT channel at 1 o’clock in the night, and OpenHAB rules will set up a Timer start the light at minimum and then after a defined time increase brightness until a target brightness is reached at wakeup time.
I have this working in another room. Now I tried to set it up for our bedroom, with warm and cold lights to be controlled independently.

Unfortunately, I get this very informative error in the log:
08:30:53.948 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'LE_FirstFloor_Bedroom_AlertMinute_Rule': org.eclipse.smarthome.core.library.types.DecimalType

This is problem number one with OpenHAB: The log is just absolutely useless. Either it throws about a million log lines per second at you, or it doesnt even tell you that you forgot the item= in the sitemap. This is the most useless log entry for a rule I ever got, but all the rest is not far away.

So I tried to fire up the SmarthomeDesigner, which I hate, (I like to just write that stuff in vim in an ssh session, like every other config on the server). Anyways, the OpenHABDesigner in OH1 was halfway useful to find issues. Unfortunately, it shows me this:

Apparently, even postUpdate() is allegedly undefined.

can anybody tell me if there is a way to develop rules that doesn’t make me want to electrocute myself with a zwave powerswitch?

Although I am not primarily looking for help on that particular ruleset, but on developing rules in general, here is the code for completeness (EDIT: The screenshot above is the WORKING code for the other room. Below is the adaption for the main bedroom)

import org.eclipse.smarthome.core.library.types.*
import org.joda.time.*
import java.lang.Math

var Timer masterAlarmTimeWarm = null
var Timer masterAlarmTimeCold = null

rule LE_FirstFloor_Bedroom_Mainlight_Warm_Min_Rule
    when
        Item LE_FirstFloor_Bedroom_Mainlight_Warm_Min received command ON
    then
        postUpdate(LE_FirstFloor_Bedroom_Mainlight_Warm_Min, OFF);
        sendCommand(LE_FirstFloor_Bedroom_Mainlight_Warm1, 1);
        sendCommand(LE_FirstFloor_Bedroom_Mainlight_Warm2, 1);
    end

rule LE_FirstFloor_Bedroom_Mainlight_Cold_Min_Rule
    when
        Item LE_FirstFloor_Bedroom_Mainlight_Cold_Min received command ON
    then
        postUpdate(LE_FirstFloor_Bedroom_Mainlight_Cold_Min, OFF);
        sendCommand(LE_FirstFloor_Bedroom_Mainlight_Cold1, 1);
        sendCommand(LE_FirstFloor_Bedroom_Mainlight_Cold2, 1);
    end
    
rule LE_FirstFloor_Bedroom_AlertMinute_Rule
    when
        Item LE_FirstFloor_Bedroom_AlertMinute received update 
    then
		var String alarmTimeStr = now.getYear() + "-" + now.getMonthOfYear() + "-" + now.getDayOfMonth() + "T" + (LE_FirstFloor_Bedroom_AlertHour.state as DecimalType).intValue() + ":" + (LE_FirstFloor_Bedroom_AlertMinute.state as DecimalType).intValue()

        var DateTime alarmTimeWarm = DateTime::parse(alarmTimeStr)
        alarmTimeWarm = alarmTimeWarm.minusMinutes((LE_FirstFloor_Bedroom_AlertStartMinWarm.state as DecimalType).intValue)
        if (alarmTimeWarm.beforeNow)
            alarmTimeWarm = alarmTimeWarm.plusDays(1)
        masterAlarmTimeWarm = createTimer(alarmTimeWarm) [| 
            LE_FirstFloor_Bedroom_AlertEvent_Warm.sendCommand(ON)
        ]

        var DateTime alarmTimeCold = DateTime::parse(alarmTimeStr)
        alarmTimeCold = alarmTime.minusMinutes((LE_FirstFloor_Bedroom_AlertStartMinCold.state as DecimalType).intValue)
        if (alarmTimeCold.beforeNow)
            alarmTimeCold = alarmTimeCold.plusDays(1)
        masterAlarmTimeCold = createTimer(alarmTimeCold) [| 
            LE_FirstFloor_Bedroom_AlertEvent_Cold.sendCommand(ON)
        ]

        logInfo("Wakeup Light", "Alarm time "+alarmTimeWarm)
    end                                                                                                                                                                          

rule LE_FirstFloor_Bedroom_AlertEvent_Warm_Rule
    when 
        Item LE_FirstFloor_Bedroom_AlertEvent_Warm received command ON
    then
    	sendCommand(LE_FirstFloor_Bedroom_Mainlight_Warm2, 1);

        var int sleepTime = ((LE_FirstFloor_Bedroom_AlertStartMinWarm.state as DecimalType).intValue - (LE_FirstFloor_Bedroom_AlertIncreaseMinWarm.state as DecimalType).intValue)*60*1000
    	logInfo("Wakeup Light", "Sleeping "+sleepTime+" milliseconds")
    	Thread::sleep(sleepTime)

    	var int sleepTime2 = 1000*60*(LE_FirstFloor_Bedroom_AlertIncreaseMinWarm.state as DecimalType).intValue / ((LE_FirstFloor_Bedroom_AlertEndBrightnessWarm.state as DecimalType).intValue-1)
    	logInfo("Wakeup Light", "Sleeping "+sleepTime2+" milliseconds between increases")
        while ((LE_FirstFloor_Bedroom_Mainlight_Warm2.state as DecimalType).intValue < (LE_FirstFloor_Bedroom_AlertEndBrightnessWarm.state as DecimalType).intValue) {
            Thread::sleep(sleepTime2)
    	    sendCommand(LE_FirstFloor_Bedroom_Mainlight_Warm1, ((LE_FirstFloor_Bedroom_Mainlight_Warm1.state as DecimalType).intValue+1);
        }       
        postUpdate(LE_FirstFloor_Bedroom_AlertHour, -1)
        postUpdate(LE_FirstFloor_Bedroom_AlertMinute, -1)
    end
    
rule LE_FirstFloor_Bedroom_AlertEvent_Cold_Rule
    when 
        Item LE_FirstFloor_Bedroom_AlertEvent_Cold received command ON
    then
    	sendCommand(LE_FirstFloor_Bedroom_Mainlight_Cold1, 1);
    	sendCommand(LE_FirstFloor_Bedroom_Mainlight_Cold2, 1);

        var int sleepTime = ((LE_FirstFloor_Bedroom_AlertStartMinCold.state as DecimalType).intValue - (LE_FirstFloor_Bedroom_AlertIncreaseMinCold.state as DecimalType).intValue)*60*1000
    	logInfo("Wakeup Light", "Sleeping "+sleepTime+" milliseconds")
    	Thread::sleep(sleepTime)

    	var int sleepTime2 = 1000*60*(LE_FirstFloor_Bedroom_AlertIncreaseMinCold.state as DecimalType).intValue / ((LE_FirstFloor_Bedroom_AlertEndBrightnessCold.state as DecimalType).intValue-1)
    	logInfo("Wakeup Light", "Sleeping "+sleepTime2+" milliseconds between increases")
        while ((LE_FirstFloor_Bedroom_Mainlight_Cold2.state as DecimalType).intValue < (LE_FirstFloor_Bedroom_AlertEndBrightnessCold.state as DecimalType).intValue) {
            Thread::sleep(sleepTime2)
    	    sendCommand(LE_FirstFloor_Bedroom_Mainlight_Cold1, ((LE_FirstFloor_Bedroom_Mainlight_Cold1.state as DecimalType).intValue+1);
    	    sendCommand(LE_FirstFloor_Bedroom_Mainlight_Cold2, ((LE_FirstFloor_Bedroom_Mainlight_Cold2.state as DecimalType).intValue+1);
        }       
        postUpdate(LE_FirstFloor_Bedroom_AlertHour, -1)
        postUpdate(LE_FirstFloor_Bedroom_AlertMinute, -1)
    end
1 Like