Scheduling using cron Help


#1

Hi everyone

so I think my HestiaPi’s are stable now and I haven’t had any problems, so its time to start tinkering again .

Its getting to that time of here now where I think it wont be much longer before the heating goes on .
I know that openhab haven’t created a binding for scheduling just yet, so I know I can use cron to schedule tasks, but there is where my knowledge ends .

I’ve seen a post on the forum here that explains it but I think I’m not quite getting the full picture, is it rules that create the cron job? or is it simply rules that utilise the timer function of cron ?

How do I create a cron job to schedule my heating times and can it set temperature times as well so for example to set the heating to 16°C during the day and 20°C after 17:00 - 22:00 and 06:00 -09:00


Heating relay not clicking
#2

@liamh ? Any help would be appreciated :slight_smile:


#3

@Greylinux So I’ll speak here about the Classic, but since the Touch also uses OpenHAB I don’t it’s much different.

The key thing to realise is that you’re not interacting with the system cron at all - rather, OpenHAB has it’s own internal cron. Things dropped into the rules folder will be parsed by OpenHAB just like things in the sitemaps and items folders. The only “gotcha” is that it’s a slightly different syntax to Linux cron, see this site for details

From the rules files you can pretty much do anything - either react to events, or take actions on a cron-like schedule. Here’s an example:

rule "Study Lights OFF"
when
  Item sonoff_study_all received command OFF
then
  study.members.forEach(s|
  	sendCommand(s, OFF)
  )
end

This rule reacts to an event (specifically a button pressed in the UI) which then loops over all members of the study group and switches them off. Clearly, I have a matching ON rule too :slight_smile:

Here’s a cron example:

 rule "Ensure Study Radiator is off overnight"
when
  Time cron "0 0 18 ? * * *"
then
  if ( sonoff_radiator.state == ON ) {
    logInfo("hestia", "Study Radiator ON: 6pm shutdown")
    sonoff_radiator.sendCommand(OFF)
  }
end

So here we use the cron syntax to make a rule that runs at 18:00 every day and ensure the radiator in my study is off when I’m done working.

You can also shell out to more complex scripts (in the scripts dir) if you find the rules file too limiting. For example:

rule "Check Temperature"
when
  Time cron "0 0/5 * ? * * *"
then
  callScript("hestia_temp_check")
end

This is my main workhorse, it’s called every 5 mins, and the script checks every sensor in the house and makes an aggregate decision on whether or not to fire up the heating for 30min. Since it checks every 5, I’m guaranteed to have the heating on until the required conditions are met, plus 25min (to avoid hysteresis looping).

Hope it helps, sing out if I can help more.


#4

@HestiaPi

Thanks for the help @gwmngilfen, so If I understand it correctly and based on @liamh items,sitemap and rules then this is what I have come up with.
Can you see any errors or problems that might arise before I make the edits to the HestiaPi’s.

default.items

String HeatingTimer "Heating Timer"

default.sitemap

Frame label="Heating" {
Switch item=HeatingMode mappings=[ "ON"="ON", "OFF"="OFF", "TIMER"="TIMER", "Boost"="BOOST"]
Text item=MyTempProxy
Setpoint item=HeatingBoostTime minValue=10 maxValue=1440 step=10 icon="clock"
/**CELSIUS*/ Setpoint item=TempSetpoint minValue=0 maxValue=40 step=0.5 icon="temperature"
//FAHRENHEIT Setpoint item=TempSetpoint minValue=32 maxValue=86 step=1 icon="temperature"
}

default.rules

   rule "Initialisation"
   when
       System started
   then
       MainSwitch.sendCommand("OFF")
       HeatingMode.sendCommand("OFF")
       HotWaterMode.sendCommand("OFF")
       HumiMode.sendCommand("OFF")
       HeatingTimer.sendCommand("OFF")
       /**CELSIUS*/ if(TempSetpoint.state == NULL) postUpdate(TempSetpoint, 18)
       /**CELSIUS*/ if(TempSetpoint.state > 40) postUpdate(TempSetpoint, 18)
       //FAHRENHEIT if(TempSetpoint.state == NULL) postUpdate(TempSetpoint, 70)
       //FAHRENHEIT if(TempSetpoint.state < 32) postUpdate(TempSetpoint, 70)
       if(HumiSetpoint.state == NULL) postUpdate(HumiSetpoint, 50)
       if(HeatingBoostTime.state == NULL) postUpdate(HeatingBoostTime, 10)
       if(HotWaterBoostTime.state == NULL) postUpdate(HotWaterBoostTime, 10)
       if(HumiBoostTime.state == NULL) postUpdate(HumiBoostTime, 10)
       TempSetpointChart.sendCommand(0);
       HumiSetpointChart.sendCommand(0);
       PreviousTempReading.sendCommand(0);
       PreviousHumiReading.sendCommand(0);
       chart_period.sendCommand(0);
end



rule "checkcurrtemp"
when
       Item TempSetpoint changed or
       Item PreviousTempReading changed
then
       if (MyTempProxy.state > TempSetpoint.state){
      HeatingPin23.sendCommand(OFF)
      TempSetpointChart.sendCommand(0)
      } else if ((MyTempProxy.state < TempSetpoint.state) &&
  ((HeatingMode.state=="ON") || (HeatingMode.state=="Boost"))) || (HeatingTimer.state=="ON"))) {
HeatingPin23.sendCommand(ON)
TempSetpointChart.sendCommand(TempSetpoint.state)
}
end

    rule "Heating Mode"
    when
      Item HeatingMode changed
    then
        switch(HeatingMode.state) {
          case "ON": {
               if (MyTempProxy.state < TempSetpoint.state) {
              HeatingPin23.sendCommand(ON)
              TempSetpointChart.sendCommand(TempSetpoint.state)
    }
              HeatingTimer.sendCommand("OFF")
              MainSwitch.sendCommand("ON")
              HeatingPreviousMode="ON"
    }
          case "OFF": {
              HeatingTimer.sendCommand("OFF")
              HeatingPin23.sendCommand(OFF)
              TempSetpointChart.sendCommand(0)
              HeatingPreviousMode="OFF"
    }
          case "TIMER": {
              HeatingPreviousMode="TIMER"
    }
          case "Boost": {
              HeatingTimer.sendCommand("OFF")
              MainSwitch.sendCommand("ON")
              // See below more...
    }
    }

  end


rule "Heating Timer"

TStartOne = new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 6, 0, 0)
TEndOne   = new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 9,  0, 0)

TStartTwo = new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 17, 0, 0)
TEndTwo   = new DateTime(now.getYear(), now.getMonthOfYear(), now.getDayOfMonth(), 22,  0, 0)

when
      Time cron "0 * * * * ?" or
      Item HeatingMode changed
then
       if((HeatingMode.state == "TIMER") && ((now.isBefore(TEndOne)) && (now.isAfter(TStartOne)) || (now.isBefore(TEndTwo)) && (now.isAfter(TStartTwo)))) {
           postUpdate(TempSetpoint, 20)
           HeatingTimer.sendCommand("ON")
      }

      if((HeatingMode.state == "TIMER") && ((now.isAfter(TEndOne)) && (now.isBefore(TStartTwo)) || (now.isAfter(TEndTwo)) && (now.isBefore(TStartOne)))) {
           postUpdate(TempSetpoint, 16)
           HeatingTimer.sendCommand("ON")
     }

     if (HeatingMode.state == "ON") {
        HeatingTimer.sendCommand("OFF")
     }

     if (HeatingMode.state == "OFF") {
        HeatingTimer.sendCommand("OFF")
     }

     if (HeatingMode.state == "Boost") {
        HeatingTimer.sendCommand("OFF")
     }

end






rule "MainSwitch"
       when
                Item MainSwitch changed
       then
               switch(MainSwitch.state) {
               case ON:{
                // Do nothing
   }
  case OFF:{
    if (HeatingMode.state == "ON") {
      HeatingPreviousMode = "ON"
      HeatingMode.sendCommand("OFF")
    } else if (HeatingMode.state == "TIMER") {
      HeatingPreviousMode = "TIMER"
      HeatingMode.sendCommand("OFF")
    } else if (HeatingMode.state == "Boost") {
      //This should never execute
      HeatingPreviousMode="Boost"
      HeatingMode.sendCommand("OFF")
    }

    if (HeatingTimer.state == "ON") {
	  HeatingTimer.sendCommand("OFF")  
   }
   }
}
end

From my logic this should if Timer is “ON”, turn the heating ON and set the temp at 20°C between the hours of 06:00 - 09:00 and 17:00 -22:00 and then set the temp to 16°C at all other times is this correct?


#5

Sorry for the slow reply :slight_smile:

Yeah, I think that’s right. You’ve a lot going on in that rules file, so I can’t follow all of it, but it seems roughly right. You may need to move the definitions of T{Start,End}{One,Two} into the then block, I don’t think they can be defined in the rule section (but I may be wrong…)


#6

Thanks Greg for just checking it over , yeah it is a bit overcrowded in my rules file .
I see your logic that every minute it checks the current time and then carries out the if statement .

:thinking:


#7

I still havent found time to implement this timer but before I try it on my HestiaPI’s,
I had a thought I have a Pi 3 somewhere could I manuallly install hestia pi without the LCD install and then connect a LED to the GPio’s that represent the heating relay. This would allow me to test the Timer and any other changes I try with out causing problems with the HestiaPi. @HestiaPi could you shed any light on this ?


#8

Sure! Use the pin numbers from your items:
So for Heating

Switch HeatingPin23 ...

use GPIO23 on pin 16.

Please note the 2 different numbering.
GPIOXX (23 in this example) refers to the Broadcom MCU pin (which is the one we need to refer to from OpenHAB) and the pin number YY (16 in this example) that simply refers to the pinout on each RasPi board.

Use a 330Ohm resistor between the RasPi’s pin and the +pin of the LED. The -pin of the LED goes to the ground.


#9

so today I’ve been experimenting quite a bit firstly I installed the 10.3 image on to one of my Hestia’s and I can confirm that the logs are working and the as per the 10.2 image the load average is low. Great news :+1:

@HestiaPi following your advice with the heating pins and the LED I set up a Pi3

I had to add a BME280 sensor to get the readings so that the rules would work

interestingly I tried to manually install HestiaPi as per your instructions and initially it worked but after reboot I lost my openhab log file and using

openhab-cli showlogs

just presented an error as no long file was present .

using the image 10.3 I got the Pi up and running and can turn the LED On/Off via the basic UI.

so now to the Cron /Timer situation, I initially edited the items, rules and sitemap but was presented an error which I solved by adding global variables for the DateTime object in my rule

var DateTime TStartOne = null 

this worked and if I set times in the rules for all 4 times about 2 mintues apart It would turn the LED On/Off and set the temp for all times except the last one where the LED would stay on . I think this is something to do with the syntax of my rule.
This did however get me thinking that its not the best of timers having a cron rule that runs the script every minute and checks whether a statement is true . so I think a cron timer similar to yours @gwmngilfen woud be a better idea .

so here is my question, for your example using cron would I have to have 4 rules 2 with start times and 2 with end times ?

I was thinking something like this

rule "Heating Timer"

when
Time cron "“0 0 9 ? * * *” or
Item HeatingMode changed
then

if((HeatingMode.state == "TIMER")) {
     postUpdate(TempSetpoint, 20)
     HeatingTimer.sendCommand("ON")
}

if (HeatingMode.state == "ON") {
     HeatingTimer.sendCommand("OFF")
}

if (HeatingMode.state == "OFF") {
     HeatingTimer.sendCommand("OFF")
}

if (HeatingMode.state == "Boost") {
     HeatingTimer.sendCommand("OFF")
}

end

any advice ?


#10

The manual instructions were not updated. They are now! :slight_smile:
Simply run
sudo nano /etc/fstab
and comment out the last line. Then reboot
#tmpfs /var/log tmpfs nodev,nosuid,size=50M 0 0

Can you make sure your quotes on the cron line are correct. I see one extra character before the first 0 and I also see the wrong type of quotes. Please note the difference between " and “. The correct is " like you have below at the if conditions. After saving the rules file, while OH is running and you monitor the logs, I assume there will be an error thrown.
It may also be the forum’s formating that causes it. Just check your end end.


#11

Whoops! I didn’t notice the extra " I think the forum formatting changed it in the test editor it shows straight quotations .

so I’ve made some changes and created some Cron Expression rules , if it helps anyone else I found a tool to help with times here

rule "Heating Timer - Morning Start"


when
	Time cron "0 0 6 ? * MON-FRI" or
	Item HeatingMode changed
then
      
    if((HeatingMode.state == "TIMER")) {
         postUpdate(TempSetpoint, 21)
         HeatingTimer.sendCommand("ON")
         logInfo("Morning Start Timer")
    }

    if (HeatingMode.state == "ON") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "OFF") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "Boost") {
	     HeatingTimer.sendCommand("OFF")
    }
  
end


rule "Heating Timer - Morning End"


when
	Time cron "0 30 9 ? * MON-FRI" or
	Item HeatingMode changed
then
      
    if((HeatingMode.state == "TIMER")) {
         postUpdate(TempSetpoint, 17)
         HeatingTimer.sendCommand("ON")
         logInfo("Morning End Timer")
    }

    if (HeatingMode.state == "ON") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "OFF") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "Boost") {
	     HeatingTimer.sendCommand("OFF")
    }
  
end

rule "Heating Timer - Evening Start"


when
	Time cron "0 0 16 ? * MON-FRI" or
	Item HeatingMode changed
then
      
    if((HeatingMode.state == "TIMER")) {
         postUpdate(TempSetpoint, 21)
         HeatingTimer.sendCommand("ON")
         logInfo("Evening Start Timer")
    }

    if (HeatingMode.state == "ON") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "OFF") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "Boost") {
	     HeatingTimer.sendCommand("OFF")
    }
  
end

rule "Heating Timer - Evening End"


when
	Time cron "0 0 22 ? * MON-FRI" or
	Item HeatingMode changed
then
      
    if((HeatingMode.state == "TIMER")) {
         postUpdate(TempSetpoint, 17)
         HeatingTimer.sendCommand("ON")
         logInfo("Morning End Timer")
    }

    if (HeatingMode.state == "ON") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "OFF") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "Boost") {
	     HeatingTimer.sendCommand("OFF")
    }
  
end


rule "Heating Timer - Weekend Start"


when
	Time cron "0 0 6 ? * SAT,SUN" or
	Item HeatingMode changed
then
      
    if((HeatingMode.state == "TIMER")) {
         postUpdate(TempSetpoint, 21)
         HeatingTimer.sendCommand("ON")
         logInfo("Weekend Start Timer")
    }

    if (HeatingMode.state == "ON") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "OFF") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "Boost") {
	     HeatingTimer.sendCommand("OFF")
    }
  
end


rule "Heating Timer - Weekend End"


when
	Time cron "0 0 22 ? * SAT,SUN" or
	Item HeatingMode changed
then
      
    if((HeatingMode.state == "TIMER")) {
         postUpdate(TempSetpoint, 17)
         HeatingTimer.sendCommand("ON")
         logInfo("Weekend End Timer")
    }

    if (HeatingMode.state == "ON") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "OFF") {
	     HeatingTimer.sendCommand("OFF")
    }

    if (HeatingMode.state == "Boost") {
	     HeatingTimer.sendCommand("OFF")
    }
  
end

however I wonder if I’m over complicating things in my set up with having an item

String HeatingTimer “Heating Timer”

would it be better to have if heating mode is “TIMER” then when Cron trigger happens

HeatingPin23.sendCommand(ON)

Anyway

I have the times and they work but some things I notice are :

  1. when a reboot happens is there a way to incorporate persistance for the previous HeatingMode state ?, in the rules file it will set everything to off including heating mode which in turn changes from “TIMER” to “OFF” ( not that this is essential but just a thought )

2)If I have it set to “TIMER” on a weekday and I want to “BOOST” for an hour in the middle of the day and then set it back to “TIMER” this wont change anything until the next cron expression time however if I “BOOST” for say 2 hours and this lapses past a Cron expression time . when I return back to “TIMER” nothing will happen until the the Next Cron expression .

For Example say I have the times above as 06:00 - 09:30 at 21°C
then after 9:30 it changes to 17°C until 16:00. But I boost at 14:30 for 2 hours this lapses past the Cron trigger . I then switch back to “TIMER” ideally I would like it then realise its in the time bracket 16:00 - 22:00 and set the Heating to “ON” and the temp to 21°C . any Ideas?