if(now.getHourOfDay() ) not triggering

  • Hardware: A very old Mac Mini

  • OS: Debian GNU/Linux 10

  • Java Runtime Environment: Zulu11

  • openHAB version: 3.3.0

  • Issue of the topic:
    I have a cron rule that disconnects my house’s solar backup system from utility power 5 minutes before an expected power outage occurs. That rule is working.

When disconnecting the solar system, I want to check if it is evening or not. If it is evening, I want to switch on an outdoor light.

My code to disconnect the house is working. In the example below, I get the “Loadshedding Phase 1” notification. However, no matter what the time of the day, I never get the “Buitelig aan” notification.

  • Please post configurations (if applicable):
 when Time cron "0 55 7 30 * ? *"
 or Time cron "0 55 15 31 * ? *"
then
        if (loadshedding.state.toString == "1" || loadshedding.state.toString == "2" || loadshedding.state.toString == "3" || load$
        {
                //This part is working
                sendBroadcastNotification("Loadshedding Phase 1")
                val mqttActions = getActions("mqtt","mqtt:broker:number")
                mqttActions.publishMQTT("W/anothernumber/vebus/261/Mode",'{"value": 2}')

                //This part is not working
                if(now.getHourOfDay() > 16 || now.getHourOfDay() < 6)
                {
                        sendBroadcastNotification("Buitelig aan")
                        Buitelig.sendCommand(ON)
                }
        }
end

“Buitelig” is a Tasmota flashed Sonoff switch. It works perfectly with manual switching.

In openHAB3 there is no longer a method .getHourOfDay(), but a methd .getHour().
This is due to the fact that Joda Time was replaced by JavaTime.

Please be aware that your code is incomplete.
Maybe consider to change the line

if (loadshedding.state.toString == "1" || loadshedding.state.toString == "2" || loadshedding.state.toString == "3" || load$

to something better, i.e. (I guess loadshedding is of Type Number):

val nLoadShed = (loadshedding.state as Number).intValue
if (nLoadShed > 0 && nLoadShed < 5) ...

(given that 4 is the last value to be compared… line is incomplete…)

Thanks! I’ll update my rules to the new .getHour() method.

I’ve been wanting to make the if(loadshedding.state) code better since I first wrote it, but I’ve never known how. Thanks for the tip - I’ll do that once I got the .getHour() part working.

Are there triggers that are not shown? This rule will only run at 07:55 and 15:55, both of which are before 16:00 and after 06:00 so that if should not run.

You could improve the rule trigger too. I think the Time is <item> rule trigger was added by 3.3. But you could put a DateTime Item, or a few DateTime Items on your MainUI where you can have a nice date/time picker (see DateTime Standalone Widget and DateTime List Item) where you enter the times and then your rule will trigger based on the state of that Item(s).

That would be a bit more user friendly than manually adding constantly changing cron expressions to a text based rule.

Yes, there’s a long list of times to test, and as that part of the code (clumsy though it may be) was working, I didn’t want to clutter up the part that I was struggling with. The full (updated, but not tidied up), rule below.

Background to the rules:
Our (South Africa) electrical utility provider can’t supply enough electricity. They now have loadshedding levels and schedules, so if the country is on Loadshedding Level 1, they switch off your neighbourhood’s power according to your schedule. To make sure that my solar inverter doesn’t go into a fault mode, I disconnect it from the utility’s grid 5 minutes before my slot. So I tell Openhab which Level I have, and Openhab, 5 mins before every possible timeslot, check what level we’re on, and if the Level matches the timeslot, disconnects the inverter.

It is a real pain to reprogram when they change our schedules.

//rules to switch to "inverter only" before loadshedding starts

rule "loadshedding_stage_1"
when Time cron "0 55 13 1 * ? *"
 or Time cron "0 55 21 2 * ? *"
 or Time cron "0 55 5 4 * ? *"
 or Time cron "0 55 11 5 * ? *"
 or Time cron "0 55 19 6 * ? *"
 or Time cron "0 55 3 8 * ? *"
 or Time cron "0 55 9 9 * ? *"
 or Time cron "0 55 17 10 * ? *"
 or Time cron "0 55 1 12 * ? *"
 or Time cron "0 55 7 13 * ? *"
 or Time cron "0 55 15 14 * ? *"
 or Time cron "0 55 23 15 * ? *"
 or Time cron "0 55 5 17 * ? *"
 or Time cron "0 55 13 18 * ? *"
 or Time cron "0 55 21 19 * ? *"
 or Time cron "0 55 3 21 * ? *"
 or Time cron "0 55 11 22 * ? *"
 or Time cron "0 55 19 23 * ? *"
 or Time cron "0 55 1 25 * ? *"
 or Time cron "0 55 9 26 * ? *"
 or Time cron "0 55 17 27 * ? *"
 or Time cron "0 55 23 29 * ? *"
 or Time cron "0 55 7 30 * ? *"
 or Time cron "0 55 15 31 * ? *"
then
        if (loadshedding.state.toString == "1" || loadshedding.state.toString == "2" || loadshedding.state.toString == "3" || load$
        {
                //logInfo("loadshedding.rules","enter")
                sendBroadcastNotification("Loadshedding Phase 1")
                val mqttActions = getActions("mqtt","mqtt:broker:8c4396f4")
                mqttActions.publishMQTT("W/7c3866560fa4/vebus/261/Mode",'{"value": 2}')
                //logInfo("loadshedding.rules","exit")

                if(now.getHour() > 16 || now.getHour() < 6)
                {
                        sendBroadcastNotification("Buitelig aan")
                        Buitelig.sendCommand(ON)
                }
        }
end

rule "loadshedding_stage_2"
when Time cron "0 55 5 1 * ? *"
 or Time cron "0 55 13 2 * ? *"
 or Time cron "0 55 21 3 * ? *"
 or Time cron "0 55 3 5 * ? *"
 or Time cron "0 55 11 6 * ? *"
 or Time cron "0 55 19 7 * ? *"
 or Time cron "0 55 1 9 * ? *"
 or Time cron "0 55 9 10 * ? *"
 or Time cron "0 55 17 11 * ? *"
 or Time cron "0 55 23 12 * ? *"
 or Time cron "0 55 7 14 * ? *"
 or Time cron "0 55 15 15 * ? *"
 or Time cron "0 55 5 18 * ? *"
 or Time cron "0 55 13 19 * ? *"
 or Time cron "0 55 21 20 * ? *"
 or Time cron "0 55 3 22 * ? *"
 or Time cron "0 55 11 23 * ? *"
 or Time cron "0 55 19 24 * ? *"
 or Time cron "0 55 1 26 * ? *"
 or Time cron "0 55 9 27 * ? *"
 or Time cron "0 55 17 28 * ? *"
 or Time cron "0 55 23 30 * ? *"
 or Time cron "0 55 7 31 * ? *"
then
        if (loadshedding.state.toString == "2" || loadshedding.state.toString == "3" || loadshedding.state.toString == "4")
        {
                //logInfo("loadshedding.rules","enter")
                sendBroadcastNotification("Loadshedding Phase 2")
                val mqttActions = getActions("mqtt","mqtt:broker:8c4396f4")
                mqttActions.publishMQTT("W/7c3866560fa4/vebus/261/Mode",'{"value": 2}')
                //logInfo("loadshedding.rules","exit")

                if(now.getHour() > 16 || now.getHour() < 6)
                {
                        sendBroadcastNotification("Buitelig aan")
                        Buitelig.sendCommand(ON)
                }
        }
end

rule "loadshedding_stage_3"
when Time cron "0 55 5 2 * ? *"
 or Time cron "0 55 13 3 * ? *"
 or Time cron "0 55 21 4 * ? *"
 or Time cron "0 55 3 6 * ? *"
 or Time cron "0 55 11 7 * ? *"
 or Time cron "0 55 19 8 * ? *"
 or Time cron "0 55 1 10 * ? *"
 or Time cron "0 55 9 11 * ? *"
 or Time cron "0 55 17 12 * ? *"
 or Time cron "0 55 23 14 * ? *"
 or Time cron "0 55 7 15 * ? *"
 or Time cron "0 55 15 16 * ? *"
 or Time cron "0 55 21 17 * ? *"
 or Time cron "0 55 5 19 * ? *"
 or Time cron "0 55 13 20 * ? *"
 or Time cron "0 55 19 21 * ? *"
 or Time cron "0 55 3 23 * ? *"
 or Time cron "0 55 11 24 * ? *"
 or Time cron "0 55 17 25 * ? *"
 or Time cron "0 55 1 27 * ? *"
 or Time cron "0 55 9 28 * ? *"
 or Time cron "0 55 15 29 * ? *"
 or Time cron "0 55 23 31 * ? *"
then
        if (loadshedding.state.toString == "3" || loadshedding.state.toString == "4")
        {
                //logInfo("loadshedding.rules","enter")
                sendBroadcastNotification("Loadshedding Phase 3")
                val mqttActions = getActions("mqtt","mqtt:broker:8c4396f4")
                mqttActions.publishMQTT("W/7c3866560fa4/vebus/261/Mode",'{"value": 2}')
                //logInfo("loadshedding.rules","exit")

                if(now.getHour() > 16 || now.getHour() < 6)
                {
                        sendBroadcastNotification("Buitelig aan")
                        Buitelig.sendCommand(ON)
                }

        }
end

rule "loadshedding_stage_4"
when Time cron "0 55 21 1 * ? *"
 or Time cron "0 55 5 3 * ? *"
 or Time cron "0 55 13 4 * ? *"
 or Time cron "0 55 19 5 * ? *"
 or Time cron "0 55 3 7 * ? *"
 or Time cron "0 55 11 8 * ? *"
 or Time cron "0 55 17 9 * ? *"
 or Time cron "0 55 1 11 * ? *"
 or Time cron "0 55 9 12 * ? *"
 or Time cron "0 55 15 13 * ? *"
 or Time cron "0 55 23 15 * ? *"
 or Time cron "0 55 7 16 * ? *"
 or Time cron "0 55 13 17 * ? *"
 or Time cron "0 55 21 18 * ? *"
 or Time cron "0 55 5 20 * ? *"
 or Time cron "0 55 11 21 * ? *"
 or Time cron "0 55 19 22 * ? *"
 or Time cron "0 55 3 24 * ? *"
 or Time cron "0 55 9 25 * ? *"
 or Time cron "0 55 17 26 * ? *"
 or Time cron "0 55 1 28 * ? *"
 or Time cron "0 55 7 29 * ? *"
 or Time cron "0 55 15 30 * ? *"
then
        if (loadshedding.state.toString == "4")
        {
                //logInfo("loadshedding.rules","enter")
                sendBroadcastNotification("Loadshedding Phase 4")
                val mqttActions = getActions("mqtt","mqtt:broker:8c4396f4")
                mqttActions.publishMQTT("W/7c3866560fa4/vebus/261/Mode",'{"value": 2}')
                //logInfo("loadshedding.rules","exit")

                if(now.getHour() > 16 || now.getHour() < 6)
                {
                        sendBroadcastNotification("Buitelig aan")
                        Buitelig.sendCommand(ON)
                }

}
end

So maybe it would be better to have one rule, which is started this way:

Time cron "0 55 1/2 * * ?"

then get information about actual day and hour (.getDay, .getHour) then lookup if there is a stage to happen.

And there is a pattern in the stages as well as in the hours and days.(at least for the first 28 days). But as there is no chance to get this pattern correct every time :slight_smile: maybe better a hashMap:

//rule to switch to "inverter only" before loadshedding starts

Time cron "0 55 1/2 * * ?"
then
    val iDay = now.getDay
    val iHour = now.getHour
    val strIndex = iDay.toString+String.format("%01d",iHour)
    var strHour = ""
    if((iDay >  0 && iDay <  5) || (iDay > 16 && iDay < 21)) // 5,13,21
        strHour = "5,13,21"
    if((iDay >  4 && iDay <  9) || (iDay > 20 && iDay < 25)) // 3,11,19
        strHour = "3,11,19"
    if((iDay >  8 && iDay < 13) || (iDay > 24 && iDay < 29)) // 1, 9,17
        strHour = "1,9,17"
    if((iDay > 12 && iDay < 17) || (iDay > 28 && iDay < 32)) // 7,15,23
        strHour = "7,15,23"

    if(!(strHour.contains(iHour.toString))) // not the time to do something
        return;

    // hashMap created through Excel, last two digits are hour, remainig digits are day, mapping to stage
    val HashMap<String,String> hmStage = [ "105"->"2","113"->"1","121"->"4","205"->"3","213"->"2",
    "221"->"1","305"->"4","313"->"3","321"->"2","405"->"1","413"->"4","421"->"3","503"->"2",
    "511"->"1","519"->"4","603"->"3","611"->"2","619"->"1","703"->"4","711"->"3","719"->"2",
    "803"->"1","811"->"4","819"->"3","901"->"2","909"->"1","917"->"4","1001"->"3","1009"->"2",
    "1017"->"1","1101"->"4","1109"->"3","1117"->"2","1201"->"1","1209"->"4","1217"->"3","1223"->"2",
    "1307"->"1","1315"->"4","1407"->"2","1415"->"1","1423"->"3","1507"->"3","1515"->"2","1523"->"1",
    "1523"->"4","1607"->"4","1615"->"3","1705"->"1","1713"->"4","1721"->"3","1805"->"2","1813"->"1",
    "1821"->"4","1905"->"3","1913"->"2","1921"->"1","2005"->"4","2013"->"3","2021"->"2","2103"->"1",
    "2111"->"4","2119"->"3","2203"->"2","2211"->"1","2219"->"4","2303"->"3","2311"->"2","2319"->"1",
    "2403"->"4","2411"->"3","2419"->"2","2501"->"1","2509"->"4","2517"->"3","2601"->"2","2609"->"1",
    "2617"->"4","2701"->"3","2709"->"2","2717"->"1","2801"->"4","2809"->"3","2817"->"2","2907"->"4",
    "2915"->"3","2923"->"1","3007"->"1","3015"->"4","3023"->"2","3107"->"2","3115"->"1","3123"->"3"]

    val iShed = (loadshedding.state as Number).intValue

    val iStage = Integer.parseInt(hmStage.get(strIndex))
    if(iShed < iStage)  // not the time to do something
        return;

    sendBroadcastNotification("Loadshedding Phase " + iStage.toString)
    val mqttActions = getActions("mqtt","mqtt:broker:8c4396f4")
    mqttActions.publishMQTT("W/7c3866560fa4/vebus/261/Mode",'{"value": 2}')
    if(iHour() > 16 || iHour() < 6) {
        sendBroadcastNotification("Buitelig aan")
        Buitelig.sendCommand(ON)
    }
end

Building the hashMap is simple (if you know how to…)
There may be some issues, didn’t have the time to test the code yet.

Thanks! That certainly looks more elegant than my or, or, or… statements. Seems like I have some learning to do when I have some spare time. It also seems easier to update when they change the schedules.

I have to admit I did not really completely understand the definition of the schedule, but it might be worth checking whether such thing can be done using the icalendar binding - then you can maintain the schedules in a calendar instead of rules…
I use this to turn heating on and off depending whether I’m in home office or at work, and to control the wake-up light depending on whether it’s a working day or vacation :slight_smile:

rfu

The point is: there are three scheduled times per day, There is sort of a pattern, but it’s not continuous and the scheme is changed sometimes. I think a hashMap is less painful as planning three appointments per day.
Of course the best option would be a web service to get scheduled shedding information.