Notification of condition with a built in timer and counter

Hey Folks,

I am still on the journey of upgrading from OH2 to OH3 and looking for an alternative (see below) to the rule I use for a variety of circumstances. The issue is that the timer no longer works in OH3.

Uses:

  • With the counter – This is where I need help for an alternative or Java scripting, of which I have no experience. High temperature notification where I receive SMS notifications for high temperature alerts and want to limit how many times the notifications are repeated.
  • Without the counter – Nightlight function where motion is triggered to turn on the lights and motion is rechecked based on the timer to turn them back off. This is performed without a counter and could be done with setting the trigger item to expire. Thus can be done with settings in the UI.

Thank you for your time and attention.

Here’s the EDITED rule from OH2 using a .rules file (not tested)

var Timer timerVar = null

.

rule "Template Alert with counter"
//
//
when
	Item MyTempItem  changed
then  
//
//
//remove Thread::sleep(1000) upon new rule		
//Set counterVar
//Set sendCommand to do something upon initial cycle
//Set conditions for timer to continue
//Set sendCommand to do something if you want to wait for timer to cycle once
//Set sendCommand to do something once the cycle is complete
//
//
var Integer timeoutVar = 5
var Number counterVar = null
var Number tempCheck = 28
//
//
if (MyTempItem.state > tempCheck) 
//Set conditions for timer to start
//
//
{           
	counterVar = 3
	logInfo("Template Alert with counter","Test initialed with MyTempItem=" + MyTempItem + " greater than the vaule of tempCheck= " + tempCheck)
	Thread::sleep(1000)
	logInfo("Template Alert with counter","Test counter start " + counterVar)
	Thread::sleep(1000)	
	logInfo("Template Alert with counter","Test timer create with " + timeoutVar + " seconds")
	timerVar = createTimer(now.plusSeconds(timeoutVar))
	//Set sendCommand to do something upon initial cycle
	[|       
		if ((counterVar != 0) && (MyTempItem.state > tempCheck)
		// Set conditions for timer to continue
		{
			timerVar.reschedule(now.plusSeconds(timeoutVar))
			logInfo("Template Alert with counter","Conditions for timer still applied, but rescheduled again for " + timeoutVar + " seconds")
			//Set sendCommand to do something if you want to wait for timer to cycle at least once
			counterVar = counterVar - 1
			Thread::sleep(1000)	
			logInfo("Template Alert with counter","Test counter cycle after initial. Counter=" + counterVar)
		}
		else
		{           
			logInfo("Template Alert with counter","Test complete with MyTempItem=" + MyTempItem + " less than the vaule of tempCheck= "  + tempCheck)
			timerVar = null
			//Set sendCommand to do something once the cycle is complete
			counterVar = 0
			Thread::sleep(1000)	
			logInfo("Template Alert with counter","Test counter complete. Counter=" + counterVar)
		}
	]
}
1 Like

This rule never could have worked correctly in OH 2 either. The timerVar is defined inside the rule. That means every time the rule runs the handle on the old timer is thrown away. But the timer itself doesn’t go away and is still running there in the background.

Maybe the rule isn’t triggered enough to matter in practice but it will basically never be the case where timerVar !== null in this rule as written. It will always be null.

Another potential problem is that your first if statement doesn’t really do anything but log that the rule was triggered and then you have an opening { that is just hanging out there serving no purpose. Is that bracket supposed to go with the if statement?

There is nothing about this rule that would make it not work as written in OH 3 the same as it “worked” (I’m not convinced this ever worked) in OH 2, assuming you are not trying to copy a full rule from a .rules file into a UI rule’s Script Action. So you need to specify more what you mean by “does not work”.

Having said that it’s way more complicated than necessary with a lot of unnecessary stuff built in. Also the mix of Timers and sleeps only adds to the complications (not to mention. that long sleeps like this were very dangerous to use in OH 2).

The root requirement is to limit how often a high temperature alert gets sent. So let’s just do that. We don’t even need Timers.

var lastAlert = now.minusDays(1) // initialize it to some time in the past so the first time the rule runs it sends the alert

rule "Temperature Alert"
when
    Item MyTempItem changed
then
    if(now.minusMinutes(30).isAfter(lastAlert){ // It's been 30 minutes since the last alert was sent
        // code to send the alert
        lastAlert = now
    }
    // do nothing if it's not been long enough, ignoring the temp change
end

That’s 10% of the lines of much simpler code to meed exactly your stated requirement. Change the minusMinutes to how ever long you want to have between SMS alerts and put the code that sends the alert where the comment is.

I edited the rule above based on your feedback. I half-assed the posted original rule to attempted and make it generic for the forum. The timerVar is not in the rule as was shown. It is at the top of the .rules file.

My challenges with your suggestion is that the rule suggests that the state changes and the .now check is limited to a period of time. I use a variation of my OH2 to accomplish a couple of scenarios based on an Item that only has 2 states (i.e. Motion/Still ON/OFF) that will not change to trigger your proposed rule for the .now check. Additionally, the counter allows me to set the SMS notification in 3 separate locations in the script based on what information I want to receive.

My apologies for the lack of clarity and mistakes made. I hope this clears it up a bit.

Then please post the full set of rule(s) as they work.

Fully explain what doesn’t work.

And fully describe your requirements. My rule is limited to a period of time because it sounded like you wanted to limit the number of alerts based on on time. It was strongly implied by all those complicated timers and sleeps that that is what you were doing. Frankly, that code is so complicated I’m not going to spend the time to try to understand it.

If you want to limit it so that it only sends the alert every 5th time instead of being based on time then just keep a count.

var alertCount = 0

rule "Temperature Alert"
when
    Item MyTempItem changed
then
   if(MyTempItem.state < 28) return; // I forgot to add that to my time based rule above.

    if(alertCount < 5)
        // code to send the alert
        alertCount = (alertCount + 1) % 5 // modulo math, will cycle alert count from 0 to 4 then back to 0
    }
    // do nothing if it's not been five times
end

Maybe you are not explaining your requirements clearly. Maybe you want to keep getting alerts every five minutes as long as the the temp is above the threshold.

var Timer timer = null

rule "Temperature Alert"
when
    Item MyTempItem changed
then
    if(MyTempItem.state < 28) return; // I forgot to add that to my time based rule above.

    if(timer === null){
        createTimer(now, [ |
            if(MyTempItem.state >= 28 {
                // send the alert
                timer.reschedule(now.plusMinutes(5))
            }
            else {
                timer = null
            }
        ])
   }
end

Maybe you want to combine the the two where it will send up to five alerts every five minutes and then stop after the five even if the temp remains above the threshold.

var Timer timer = null


rule "Temperature Alert"
when
    Item MyTempItem changed
then
    if(MyTempItem.state < 28) return; // I forgot to add that to my time based rule above.

    if(timer === null){
        var count = 0
        createTimer(now, [ |
            if(count < 5 && MyTempItem.state >= 28) {
                // send the alert
                count = count  + 1
                timer.reschedule(now.plusMinutes(5))
            }
            else {
                timer = null
            }
        ])

end

When the temp changes above the threshold it creates a timer if there isn’t one already. The timer sends the alert and reschedules itself to run again in 5 minutes for as long as the temperature is above the threshold.

NOTE: These examples are approaches. You’ll need to adapt them to your specific situation.

In general I can’t think of a single case where it would be appropriate to have a sleep in a timer.

-------------Still a work in progress-------------
--------I need to clean up the code a bit.--------

I’ve managed to work up a solution. Here’s the temperature monitor version with notes.

The item ChangeMeTemp could also be a door ChangeMeDoor. Just make sure the the .state is updated in the rules.

There are 3 Rules and more detail is below. Here’s a synopsis of the Actions (commands)

Rule 1 ChangeMe counter every 10 Seconds V1.0

Do something after the first cron has cycled

Maybe you want to allow the trigger event to cycle once for an amount of time based on the cron. i.e. the temperture is over the threshold because the door is open and you don’t want an alert unless the temperature remains to be over the threshold for a period of time.

Do if you want to be notified the trigger items is not resolved and this is the last notification

Just in case you want to be notified that you are not going to recieve any more notifications because the counter is exhausted.

Do if you want to be reminded each time the cron cycles

More frequent reminders.

Rule 2 ChangeMe Temperature Alert Trigger V1.0

Do something here if you want to be notified immediately.

Maybe you want to be alerted if there is motion in the house or have a buzzer go off.

ChangeMe Temperature Alert Cancellation V1.0
Do something here if you want confirmation that the trigger item is resolved.

If you want confirmation that the temperature is OK now or the door is closed.

Notes

  • The only instance you need to create a new rule is if you want a different counter period using a cron trigger.
  • ChangeMe is a quick way to find text you need to change
  • You do not need to create another rule for the counter.
  • Just copy the template and pasted the edited form at the bottom of this script.
  • You’ll need to change varCount to a unique variable name within this script.

------ITEMS------

  • You’ll need the folling items in openhab

Switch ChangeMeSwitch “Alert Status Indicator” [AlertTemplate]
Number ChangeMeTemp “Temperature that is Monitored” [AlertTemplate]
Number ChangeMeCounter “count down to stop sending alerts” [AlertTemplate]

------RULES------

  • Each counter will need a corresponding rule. i.e. If you have 3 counters below, you should have 3 corresponding rules.
    --------REQUIRED RULE--------
    ChangeMe Temperature Alert Trigger V1.0
    --------OPTIONAL RULE--------
    ChangeMe Temperature Alert Cancellation V1.0

Rule 1

ChangeMe counter every 10 Seconds V1.0

triggers:
  - id: "1"
    configuration:
      cronExpression: "*/10 * * * * *"
    type: timer.GenericCronTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >-
        /*--------------- Temperature Alert Template Start



        //The only instance you need to create a new rule is if you want a different coutner period using a cron trigger. 

        //ChangeMe is a quick way to find text you need to change

        //You do not need to create another rule for the counter. 

        //Just copy the template and pasted the edited form at the bottom of this script.

        //You'll need to change varCount to a unique variable name within this script.


        ------ITEMS------


        //You'll need the folling items in openhab


        Switch ChangeMeSwitch "Alert Status Inicator" [AlertTemplate]

        Number ChangeMeTemp "Temperature that is Monitored" [AlertTemplate]

        Number ChangeMeCounter "count down to stop sending alerts" [AlertTemplate]



        //You'll need another rule to set the alert status. 

        //Each counter will need a corresponding rule. i.e. If you have 3 counters below, you should have 3 corresponding rules.

        //1 ChangeMe Temperature Notification V1.0


        ------RULES------


        //Each counter will need a corresponding rule. i.e. If you have 3 counters below, you should have 3 corresponding rules.

        --------REQUIRED RULE--------

        ChangeMe Temperature Alert Trigger V1.0

        --------OPTIONAL RULE--------

        ChangeMe Temperature Alert Cancellation V1.0


        //Start select and copy below this line


        var Number varCount = ChangeMeCounter.state as DecimalType

        if ((varCount > 0) && (ChangeMeSwitch.state == ON))

        {
        	if (varCount == 5)
        	{
        		varCount = varCount - 1
        		logInfo("ChangeMeCounter Rule","ChangeMeCounter initialized. Current varCount is at " + varCount)
        		ChangeMeCounter.postUpdate(varCount)
        		//Do something after the first cron has cycled
        	}
        	else
            {
                if (varCount == 1)
                {
                    varCount = varCount - 1
                    logInfo("ChangeMeCounter Rule","ChangeMeCounter This is the last notification. Current varCount is at " + varCount)
                    ChangeMeCounter.postUpdate(varCount)
                    //Do if you want to be notifed the trigger items is not resolved and this is the last notification
                }
                else
                {           
                    varCount = varCount - 1
                    logInfo("ChangeMeCounter Rule","ChangeMeCounter repeating. Current varCount is at " + varCount)
                    ChangeMeCounter.postUpdate(varCount)
                    //Do if you want to be reminded each time the cron cycles
        	    }
            }
        }

        else

        {           
          ChangeMeCounter.postUpdate(0)
          logInfo("ChangeMeCounter Rule","ChangeMeCounter not intialized. Current varCount is at 0")
        }


        //End select and copy above this line 


        End Temperature Alert Template ----------------*/


        //PASTE YOUR EDITED SCRIPTS BELOW HERE


        if(ChangeMeCounter.state == NULL) 

        {

        ChangeMeCounter.postUpdate(0)

        }


        var Number varCount = ChangeMeCounter.state as DecimalType

        if ((varCount > 0) && (ChangeMeSwitch.state == ON))

        {
        	if (varCount == 5)
        	{
        		varCount = varCount - 1
        		logInfo("ChangeMeCounter Rule","ChangeMeCounter initialized. Current varCount is at " + varCount)
        		ChangeMeCounter.postUpdate(varCount)
        		//Do something after the first cron has cycled
        	}
        	else
            {
                if (varCount == 1)
                {
                    varCount = varCount - 1
                    logInfo("ChangeMeCounter Rule","ChangeMeCounter This is the last notification. Current varCount is at " + varCount)
                    ChangeMeCounter.postUpdate(varCount)
                    //Do if you want to be notifed the trigger items is not resolved and this is the last notification
                }
                else
                {           
                    varCount = varCount - 1
                    logInfo("ChangeMeCounter Rule","ChangeMeCounter repeating. Current varCount is at " + varCount)
                    ChangeMeCounter.postUpdate(varCount)
                    //Do if you want to be reminded each time the cron cycles
        	    }
            }
        }

        else

        {           
          ChangeMeCounter.postUpdate(0)
          logInfo("ChangeMeCounter Rule","ChangeMeCounter not intialized. Current varCount is at 0")
        }
    type: script.ScriptAction

Rule 2

ChangeMe Temperature Alert Trigger V1.0 (required)

triggers:
  - id: "1"
    configuration:
      itemName: ChangeMeTemp
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >
        var Number temp = ChangeMeTemp.state as DecimalType


        if ((temp > 28) && (ChangeMeSwitch.state != ON)) //!= in case switch is null

        {
        	logInfo("ChangeMeTemp Rule","ChangeMeTemp initialized. Current temp is at " + temp)
        	sendCommand(ChangeMeSwitch, ON)
        	ChangeMeCounter.postUpdate(5)
            //Do something here if you want to be notified immediately. 
        }

        else

        {           
        	logInfo("ChangeMeTemp Rule","ChangeMeTemp not intialized. Current temp is at " + temp)
        	sendCommand(ChangeMeSwitch, OFF)
        }
    type: script.ScriptAction

Rule 3

ChangeMe Temperature Alert Cancellation V1.0 (optional)

triggers:
  - id: "1"
    configuration:
      itemName: ChangeMeSwitch
      state: OFF
      previousState: ON
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >
        logInfo("ChangeMeTemp Rule","ChangeMeTemp cancellation. Current temp is
        at " + ChangeMeTemp.state)
        //Do something here if you want confirmation that the trigger item is resolved.
    type: script.ScriptAction

There is a flaw in the Cron approach when trying to emulate a timer in that the first cycle could be any amount of time from the start of the count to the end. Therefore, I’ve created this scheme to resolve the timer issue.

I am trying to solve for the lack of timer functionality in OH3. The 2 rules below, Template Timer w/Counter V2 and Template Timer wo/Counter V2, are their own individual rules with timer functions that fullfill my requirement. The Counter will allow one to limit the number of notifications or actions when a trigger event is initiated. i.e. limit the number of SMS messages.

The item ChangeMeTemp could also be a door ChangeMeDoor. Just make sure the the .state is updated in the rules. Template Timer wo/Counter V2 at the bottom is for a nightlight function.

Notes

  • Each Alert and corresponding item will require it’s own set of rules
  • ChangeMe is a quick way to find text you need to change
  • Change the timer count to suit your needs.

–Existing Item–

ChangeMeTemp = Temperature you are monitoring

–New Items–

Switch ChangeMeSwitch "Template switch V2" [AlertTemplate]  
Switch ChangeMeCounter "Template counter V2" [AlertTemplate]    
Number ChangeMeRestartTimer"Template timer V2" [AlertTemplate]

There are 3 Rules and more detail is below. Here’s a synopsis of the Actions (commands)

Rule 1 Template Timer w/Counter V2

triggers:
itemName: ChangeMeSwitch
command: ON
itemName: ChangeMeRestartTimer
command: ON

Do something after the first timer has cycled

Maybe you want to allow the trigger event to cycle once for an amount of time based on the cron. i.e. the temperture is over the threshold because the door is open and you don’t want an alert unless the temperature remains to be over the threshold for a period of time.

Do if you want to be notified the trigger items is not resolved and this is the last notification

Just in case you want to be notified that you are not going to recieve any more notifications because the counter is exhausted.

Do if you want to be reminded each time the timer cycles

More frequent reminders based on your counter value

—CODE—

triggers:
  - id: "2"
    configuration:
      itemName: ChangeMeSwitch
      command: ON
    type: core.ItemCommandTrigger
  - id: "3"
    configuration:
      itemName: ChangeMeRestartTimer
      command: ON
    type: core.ItemCommandTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >-
        if ((ChangeMeCounter.state > 0) && (ChangeMeSwitch.state == ON))

        {
          logInfo("Test Timer", "timer Created waiting 10 Seconds")
            createTimer(now.plusSeconds(10), 
            [|
            var Number varCount = ChangeMeCounter.state as DecimalType
            if (varCount != 0)
             {
              if (varCount == 5)
              {
                varCount = varCount - 1
                logInfo("Test Timer Rule","Counter initialized. Current varCount is at " + varCount)
                ChangeMeCounter.postUpdate(varCount)
                sendCommand(ChangeMeRestartTimer, ON)
                //Do something after the first timer has cycled
              }
              else
              {
              if (varCount == 1)
                {
                varCount = varCount - 1
                logInfo("Test Timer Rule","Counter This is the last notification. Current varCount is at " + varCount)
                ChangeMeCounter.postUpdate(varCount)
                sendCommand(ChangeMeRestartTimer, ON)
                //Do if you want to be notifed the trigger items is not resolved and this is the last notification
                }
                else
                {           
                varCount = varCount - 1
                logInfo("Test Timer Rule","Counter repeating. Current varCount is at " + varCount)
                ChangeMeCounter.postUpdate(varCount)
                sendCommand(ChangeMeRestartTimer, ON)
                //Do if you want to be reminded each time the timer cycles
                }
              }    
            }
            ])
        } 

        else

        {

        ChangeMeCounter.postUpdate(0)

        logInfo("Test Timer Rule","Counter complete.")

        }
    type: script.ScriptAction

Rule 2 Template Timer Temperature Trigger V2

trigger:
itemName: ChangeMeTemp
Changed

Do something here if you want to be notified immediately.

Maybe you want to be alerted if there is motion in the house or have a buzzer go off.

—CODE—

triggers:
  - id: "1"
    configuration:
      itemName: ChangeMeTemp
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >
        var Number temp = ChangeMeTemp.state as DecimalType


        if ((temp > 28) && (ChangeMeSwitch.state == OFF) || (ChangeMeSwitch.state == NULL)) //!= in case switch is null

        {
          logInfo("Trigger Rule","ChangeMeTemp initialized. Current temp is at " + temp)
          sendCommand(ChangeMeSwitch, ON)
          ChangeMeCounter.postUpdate(5)
          //Do something here if you want to be notified immediately. 
        }

        if ((temp < 28) && (ChangeMeSwitch.state == ON) || (ChangeMeSwitch.state == NULL)) //!= in case switch is null

        {
          logInfo("Trigger Rule","ChangeMeTemp is ok. No trigger initialized. Current temp is at " + temp)
          sendCommand(ChangeMeSwitch, OFF)
          ChangeMeCounter.postUpdate(0)
        }
    type: script.ScriptAction

Rule 3 Template Timer Temperature Cancel V2 (optional)
Do something here if you want confirmation that the trigger item is resolved.

If you want confirmation that the temperature is OK now or the door is closed.

triggers:
itemName: ChangeMeSwitch
state: OFF
previousState: ON

—CODE—

triggers:
  - id: "1"
    configuration:
      itemName: ChangeMeSwitch
      state: OFF
      previousState: ON
    type: core.ItemStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >
        logInfo("ChangeMeTemp Rule","ChangeMeTemp cancellation. Current temp is
        at " + ChangeMeTemp.state) 

        //Do something here if you want confirmation that the trigger item is resolved.
    type: script.ScriptAction

Rule 4 Template Template Timer wo/Counter V2 (optional)
Do something will continue to repeat until resolved.

Recheck for motion to turn nightlights off.

triggers:
itemName: ChangeMeSwitch
command: ON
itemName: ChangeMeRestartTimer
command: ON

—CODE—

triggers:
  - id: "2"
    configuration:
      itemName: ChangeMeSwitch
      command: ON
    type: core.ItemCommandTrigger
  - id: "3"
    configuration:
      itemName: ChangeMeRestartTimer
      command: ON
    type: core.ItemCommandTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >
        if (ChangeMeSwitch.state == ON)

        {
          sendCommand(GroupNightLights, ON)	  
          logInfo("Test Timer","timer Created waiting 10 Seconds")
          createTimer(now.plusSeconds(10), 
          [|
            if (ChangeMeSwitch.state == ON)
            {
              sendCommand(ChangeMeRestartTimer, ON)
            }
            else
            {
              logInfo("Test Timer","Nightlight timer complete. Turning off lights.")                   
              sendCommand(GroupNightLights, OFF)	
            }    
          ])
        } 

        else

        {

        }
    type: script.ScriptAction

I’ve started posting my solutions in the Tutorials & Examples area.