Design Pattern: Motion Sensor Timer

It is a very basic thing but this approach with counters is not a very easy way to do it.

Using the example at the top of the thread…

var Timer ezMotionTimer = null

rule "EZ Auge - Licht Aus"
when
    Item EZ_Motion received command
then
    Scene_EZ.sendCommand(3)

    if(ezMotionTimer != null) {
        ezMotionTimer.reschedule
    }
    else {
        ezMotionTimer = createTimer(now.plusMinutes(5), [|
            Scene_EZ.sendCommand(1)
            ezMotionTimer = null
        }
    }
end

The above will turn on the Lights (i.e. sendCommand 3 to EZ_Scene). Five minutes after the last time the EZ_Motion receives a command the Timer will go off and turn off the lights.

I’ve a few other minor comments about your rule. These are not problems but could help you avoid problems in the future.

It is better to use the sendCommand and postUpdate method on the Item rather than the actions. So you would rewrite:

sendCommand(Scene_ColorSelect_EZ_Deckenlamp, new HSBType(new DecimalType(0),new PercentType(0),new PercentType(70)))

as

Scene_ColorSelect_EZ_Deckenlamp.sendCommand(new HSBType(new DecimalType(0),new PercentType(0),new PercentType(70)))

ColorItems can receive ONOFFType and PercentType commands. So where you set the bulb to HSBType(0,0,0) you can just sendCommand(OFF).

1 Like

Thanks for the hints, I changed my code as you recommend.

But the timer code is still not working.

This is working -> items and sensor are OK

	rule "WZ Auge - Licht"
	when
    Item WZ_Burglar received update
	then
    Scene_WZ.sendCommand(2)
    end

But this does nothing

	var Timer ezMotionTimer = null

	rule "WZ Auge - Licht Timer"
	when
    Item WZ_Burglar received update
	then
    Scene_WZ.sendCommand(3)

    if(ezMotionTimer != null) {
        ezMotionTimer.reschedule
    }
    else {
    	ezMotionTimer = createTimer(now.plusMinutes(1))
    	Scene_WZ.sendCommand(1)
    	ezMotionTimer = null
    	}
    }
	end	
    else {
    	ezMotionTimer = createTimer(now.plusMinutes(1))
    	Scene_WZ.sendCommand(1)
    	ezMotionTimer = null
    	}
    }

Needs to be

    else {
    	ezMotionTimer = createTimer(now.plusMinutes(1)) [ |
    	    Scene_WZ.sendCommand(1)
    	    ezMotionTimer = null
    	]
    }

Thanks very much, but still no reaction

    var Timer ezMotionTimer = null

	rule "Licht Timer"
	when
    Item EZ_Auge_Burglar changed from OFF to ON
	then
    Scene_EZ.sendCommand(3)

    if(ezMotionTimer != null) {
        ezMotionTimer.reschedule
    }
    else {
    	ezMotionTimer = createTimer(now.plusMinutes(1)) [ |
    	    Scene_EZ.sendCommand(1)
    	    ezMotionTimer = null
    	]
    }
	end 

May I have to import some bibs or place the var at the top of the .rules or need some addons or extra services or so?

ezMotionTimer needs to be declared above any rule in that file.

Add logging statements so you know whether the rule is triggering.

That was the problem, now it works great.
Thank you very much :slight_smile:

In this topic I’ll go forward with some more terms: Motion detection -> lights on - if it’s dark and within time period, with timer - Help needed

I don´t want to make the variables as global variables.

Can i make this inside the rule as well?

If i put val and var to the top of my rules-file, it works. But if i put it inside the rule, nothing happens…

working:

var Timer md1Timer = null
val int timeoutMinutes = 5

// KG Garage Licht aus nach 5 Minuten

rule "KG Garage Licht auto off"
when
    Item Licht_KG_Garage_1 changed
then
	if (Licht_KG_Garage_1.state == ON) {
			md1Timer = createTimer(now.plusMinutes(timeoutMinutes), [|
				Licht_KG_Garage_1.sendCommand(OFF)
				md1Timer = null
			]
	}
    else {
			md1Timer = null
    }
end

not working:

// KG Garage Licht aus nach 5 Minuten

rule "KG Garage Licht auto off"
when
    Item Licht_KG_Garage_1 changed
then
        var Timer md1Timer = null
        val int timeoutMinutes = 5
	if (Licht_KG_Garage_1.state == ON) {
			md1Timer = createTimer(now.plusMinutes(timeoutMinutes), [|
				Licht_KG_Garage_1.sendCommand(OFF)
				md1Timer = null
			]
	}
    else {
			md1Timer = null
    }
end

By doing this you are defining local vs. global variables. Local in this case means (as in all programming languages) the scope and the existence of this variable is limited to one execution of the code block, being your rule. The Timer variable will be deleted when reaching end and recreated when executing the rule again.

Yes, i know this. I want to have this variable “var Timer md1Timer = null” only inside my rule. The rule will not reach “end” while the timer is running.

And when the timer is “null”, i don´t need it anymore.

But this doesn´t work with “var timer…” inside the rule. No error-message in the log-file. I made some logging and the rule will not get triggered, if i put var inside it.

Do you know the reasen, why it doenst´t work?

I don’t think you understand how Timers work. They do not block the rest of the execution of your Rule. Once created the timer runs in the background while the rest of your Rule continues to execute. So in the above your rule immediately exits once the Timer is created instead of waiting around for the timeout.

This is why you must store the timer variable as a global.

See the following:

Your rule above, “KG Garage Licht auto off”, could be removed and replaced with adding the expire binding to the item:

Switch Licht_KG_Garage_1 "KG Garage Licht" { yourbinding="..." or channel="...", expire="5m,command=OFF" }

I found this very useful, it allows you to have different timeout values for items as well which is a benefit.

HI @rlkoshak, I try to run your code but with error, I don’t know how to make it work,
do you mind gave a hand on it?

10:24:45.871 [ERROR] [.script.engine.ScriptExecutionThread] - Rule ‘A Motion Detector triggered’: org.eclipse.smarthome.core.library.items.SwitchItem

import java.util.Map

val Map<String, Timer> timers = newHashMap

rule "A Motion Detector triggered"
when
    Item MS_LivingRoom_MotionSensor1 received update MOTION or
    Item MS_LivingRoom_MotionSensor2 received update MOTION
then
    Thread::sleep(100) // give persistence time to save the update
    val sw =  gMotionSensor.members.sortBy[lastUpdate].last as SwitchItem
    logInfo("Motion.Sensor", "Motion Sensor Counter start")

    val timeoutMinutes = 3 // use an appropriate value

    if(timers.get(sw.name) == null){
        timers.put(sw.name, createTimer(now.plusMinutes(timeoutMinutes), [|
        	logInfo("Motion.Sensor", "Motion Sensor Counter works")
            sw.sendCommand(OFF)
            timers.put(sw.name, null)
        ]))
    }
    else {
        timers.get(sw.name).reschedule(now.plusMinutes(timeoutMinutes))
        logInfo("Motion.Sensor", "Motion Sensor Counter timer extend")
    }
    logInfo("Motion.Sensor", "Motion Sensor Counter finished")
end

What item types are you motion detectors. I’ve never seen an Item with a MOTION state. In the rule you cast then to a SwitchItem which only has the states of ON and OFF.

Do you get the first log statement in your logs?

You may need to filter out the items that have yet to be saved to persistence.

val sw =  gMotionSensor.members.filter.[s.lastUpdate != null].sortBy[lastUpdate].last as SwitchItem

I use mihome, which motion sensor announce MOTION when detect motion,
I set String type at items,

yesterday I have setup mapdb and have * : strategy = everyChange, restoreOnStartup in mapdb.persist
I remove restoreOnStartup from influx and I see item state restored after system startup,
so I assume value should have saved.

I really don’t know how to do coding, according to your last reply, does your code only able to apply to item type is switch?

My code as written expects the motion sensors to be SwitchItems. That is what the as SwitchItem means when you get the latest updated item.

Change that to StringItem and it might work.

Also note the was a bug that was preventing the use of Strings in the updated or changed to rule triggers. Perhaps this has been fixed…

Thanks, I will try again once I am back home,
you mention OH2 prevent update or changed to rule triggers on Strings type,
does that mean if item type is string will not get trigger in rules if it’s not been fixed?
if it is than no wonder why I can’t set a test rules which will light up when door open,
I tried it last night without success, I thought I was using wrong code in rules.

Item MyStringItem changed works.

Item MyStringItem changed to "Some String" currently broken (unless I missed an update with a fix).

Item MySwitxhItem changed to ON works.

gerat info, Thanks for let me know, it will save me lot of time on trying,
I will continue try on the code see which part goes wrong

if you don’t mind, would you just a short question,
will if(receivedCommand==OPEN){ works? or it should only works when ==ON?

If the item that triggers the rule is a Contact, OPENED. If it is a Switch, ON.

Each Item type has different state types. You have to use what is appropriate for the Item type.