Good point. I didn’t double check that line.
For notPresent you want it to be true only if all the Items are OFF so the line should be
val notPresent = GRoom22.allMembers.filter[s | s.state == ON].size > 0
Good point. I didn’t double check that line.
For notPresent you want it to be true only if all the Items are OFF so the line should be
val notPresent = GRoom22.allMembers.filter[s | s.state == ON].size > 0
I’ve been experimenting with it and it appears to be working. The only problem I see is when I return home and open the door the log shows, after the 5 minute delay, “No one is home” and then 2 seconds later it shows “Someone is home”. It appears that the “Presence not Detected” rule is still be activated even though after 5 minutes I’m sure that our phones have already connected to the router and we’ve past by motion detectors. Do you see anything wrong that would cause this? This should be my last post on this subject so I definitely want to tell you thanks for all your help!
rule "Presence not Detected"
when
Item GRoom21 received update
then
logInfo("Presence", "Verifying if anyone is home")
if(presenceTimer != null) {
presenceTimer.cancel
}
presenceTimer = createTimer(now.plusMinutes(5), [|
val notPresent = GRoom22.allMembers.filter[s | s.state == OFF].size > 0
if(notPresent && homeoraway.state != OFF){
logInfo("Presence", "No one is home")
homeoraway.sendCommand(OFF)
sendMail("xxxx@xxxx.net", "Occupancy", "No One's Home")}
])
end
You still have notPresent being set to true if one or more sensor is OFF. What you want to know is if ALL are off. You can check that by seeing if none are ON.
GRoom22.allMembers.filter[s | s.state == ON].size == 0
I see that my example above was in error.
Rich, I’ve implemented your presence rule it’s working well except for one part. It wont cancel the timer if I go from OFF to ON within the 5 minutes. The weird part is the timmer does seem to go to null. I also don’t know why it logs everything twice… Any help you or anyone else can provide will be greatly appreciated!
Logs:
2016-02-20 12:17:13.863 [INFO ] [.openhab.model.script.Presence] - No one is home, setting timer
2016-02-20 12:17:13.865 [INFO ] [.openhab.model.script.Presence] - No one is home, setting timer
2016-02-20 12:21:54.469 [INFO ] [.openhab.model.script.Presence] - Someone is home
2016-02-20 12:21:54.471 [INFO ] [.openhab.model.script.Presence] - Someone is home
2016-02-20 12:22:13.888 [INFO ] [.openhab.model.script.Presence] - No one is home after five minutes
2016-02-20 12:21:40.729 [INFO ] [rg.openhab.model.script.mydata] - #false#
2016-02-20 12:21:40.730 [INFO ] [rg.openhab.model.script.mydata] - #false#
2016-02-20 12:21:54.468 [INFO ] [rg.openhab.model.script.mydata] - #true#
2016-02-20 12:21:54.469 [INFO ] [rg.openhab.model.script.mydata] - #true#
2016-02-20 12:21:54.470 [INFO ] [rg.openhab.model.script.mydata] - #org.openhab.model.script.internal.actions.TimerImpl@2d18a36e#
2016-02-20 12:21:54.471 [INFO ] [rg.openhab.model.script.mydata] - #null#
2016-02-20 12:21:54.472 [INFO ] [rg.openhab.model.script.mydata] - #null#
Items:
Group gPresent "Present group" <present>
Group gPresence "Presence group" <present>
Switch Present "Someone is Present" <present> (gPresence) // turns off 5 minutes
e leaves
Switch WifePhoneMac <network> (gWifePresent, gPresent, gPresence)
Switch ShawnsPhoneMac <network> (gShawnPresent, gPresent, gPresence)
Rule:
import org.openhab.model.script.actions.*
var Timer presenceTimer = null
rule "gPresent changed"
when
Item gPresent received update
then
val isPresent = gPresent.allMembers.filter(s | s.state == ON).size > 0
logInfo("mydata", "#" + isPresent + "#") // log variable
// Someone came home
if(isPresent != false && Present.state != OFF) {
logInfo("Presence", "Someone is home")
logInfo("mydata", "#" + presenceTimer + "#") // log variable
if(presenceTimer != null && !presenceTimer.hasTerminated) {
presenceTimer.cancel
presenceTimer = null
logInfo("mydata", "#" + presenceTimer + "#") // log variable
}
Present.sendCommand(ON)
}
// Everyone is gone
else if(!isPresent != true && Present.state != ON) {
logInfo("Presence", "No one is home, setting timer")
if(presenceTimer != null && !presenceTimer.hasTerminated) {
presenceTimer.cancel
}
presenceTimer = createTimer(now.plusMinutes(5), [|
logInfo("Presence", "No one is home after five minutes")
if(Present.state != OFF) Present.sendCommand(OFF)
])
}
end
I thought I had it working but I’m still not getting the timer canceled. This is what I have so far
Logs:
2016-02-20 13:50:28.889 [INFO ] [.openhab.model.script.Presence] - No one is home, setting timer
2016-02-20 13:50:28.890 [INFO ] [.openhab.model.script.Presence] - No one is home, setting timer
2016-02-20 13:50:39.228 [INFO ] [rg.openhab.model.script.mydata] - #true#
2016-02-20 13:50:39.229 [INFO ] [rg.openhab.model.script.mydata] - #ON#
2016-02-20 13:50:39.232 [INFO ] [rg.openhab.model.script.mydata] - #true#
2016-02-20 13:50:39.233 [INFO ] [rg.openhab.model.script.mydata] - #ON#
2016-02-20 13:50:39.239 [INFO ] [.openhab.model.script.Presence] - Someone is home, timmer canceled
2016-02-20 13:50:39.240 [INFO ] [.openhab.model.script.Presence] - Someone is home, timmer canceled
2016-02-20 13:50:39.249 [INFO ] [rg.openhab.model.script.mydata] - #org.openhab.model.script.internal.actions.TimerImpl@1c39039f#
2016-02-20 13:50:39.250 [INFO ] [rg.openhab.model.script.mydata] - #org.openhab.model.script.internal.actions.TimerImpl@1c39039f#
2016-02-20 13:50:39.267 [INFO ] [rg.openhab.model.script.mydata] - #null#
2016-02-20 13:50:39.270 [INFO ] [rg.openhab.model.script.mydata] - #null#
2016-02-20 13:55:28.892 [INFO ] [.openhab.model.script.Presence] - No one is home after five minutes
Rule:
import org.openhab.model.script.actions.*
var Timer presenceTimer = null
rule "gPresent changed"
when
Item gPresent received update
then
val isPresent = gPresent.allMembers.filter(s | s.state == ON).size > 0
logInfo("mydata", "#" + isPresent + "#") // log variable
logInfo("mydata", "#" + Present.state + "#") // log variable
// Someone came home
if(isPresent != false && Present.state != ON) {
logInfo("Presence", "Someone is home")
logInfo("mydata", "#" + presenceTimer + "#") // log variable
Present.sendCommand(ON)
}
if(presenceTimer != null && !presenceTimer.hasTerminated) {
logInfo("Presence", "Someone is home, timmer canceled")
logInfo("mydata", "#" + presenceTimer + "#") // log variable
presenceTimer.cancel
presenceTimer = null
logInfo("mydata", "#" + presenceTimer + "#") // log variable
}
// Everyone is gone
if(isPresent != true && Present.state != OFF) {
logInfo("Presence", "No one is home, setting timer")
if(presenceTimer != null && !presenceTimer.hasTerminated) {
presenceTimer.cancel
}
presenceTimer = createTimer(now.plusMinutes(5), [|
logInfo("Presence", "No one is home after five minutes")
if(Present.state != OFF) Present.sendCommand(OFF)
])
}
end
It looks like it might be a timing issue. The rule is being triggered twice for each update and it looks like the code it running with just the right timing that it is creating two timers but losing the reference to the first timer when it creates the second one. Probably the easiest way around this would be to wrap the rule in an reentrant lock so only one instance of the rule can run at a time. This will prevent this sort of timing problem.
// other imports
import java.util.concurrent.locks.ReentrantLock
// other globals
val ReentrantLock lock = new ReentrantLock
// other rules
rule "gPresent changed"
when
Item gPresent received update
then
try {
lock.lock
// your code goes here
}
catch(Throwable t){
logError("Presence", "Error processing BTPresence update: " + t)
}
finally {
lock.unlock
}
end
That worked! It never accured to me that it was creating two timers! It should have been obvious with the double log especially since I was seeing the timer being canceled. Thanks for the help, I’m new to openhab but so far this is a great community! Now I know how to create locks too!
Since were on the subject any idea why it would be running twice in the first place, wondering if there’s something wrong with the code itself cause it to loop?
It is a side effect of how the state of a Group Item gets calculated. The short of it is that your rule will triggered n times per item update where n is the number of items they are a member of the group.
You are not doing anything wrong. I should have had the lock there in the first place.
I am trying to use your code. However, i noticed that the “gPresent changed” is never trigged. Any idea?
openhab.log does not show the logInfo “Here”
2017-08-22 19:10:07.074 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'default.items'
events.log shows
2017-08-22 19:10:39.364 [ItemStateChangedEvent ] - S_V_JohnPhoneIP changed from OFF to ON
Item file
Group gPresent "Present group" <present>
Group gJohnPresent "John Present" <present>
Switch Present "Someone is Present" <present> // turns off 5 minutes after everyone leaves
Switch S_V_JohnPhoneIP <network> (gJohnPresent,gPresent) {channel="network:device:d12435eb:online"}
Rules
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import java.util.concurrent.locks.ReentrantLock
val ReentrantLock lock = new ReentrantLock ()
var Timer presenceTimer = null
rule "gPresent changed"
when
Item gPresent received update
then
logInfo("mydata", "HERE") // log variable
try {
lock.lock
// your code goes here
val isPresent = gPresent.allMembers.filter(s | s.state == ON).size > 0
logInfo("mydata", "#" + isPresent + "#") // log variable
// Someone came home
if(isPresent && Present.state != ON) {
logInfo("Presence", "Someone is home")
if(presenceTimer != null) {
presenceTimer.cancel
presenceTimer = null
}
Present.sendCommand(ON)
}
// Everyone is gone
else if(!isPresent && Present.state != OFF) {
logInfo("Presence", "No one is home, setting timer")
if(presenceTimer != null) {
presenceTimer.cancel
}
presenceTimer = createTimer(now.plusMinutes(5), [|
logInfo("Presence", "No one is home after five minutes")
if(Present.state != OFF) Present.sendCommand(OFF)
])
}
}
catch(Throwable t){
logError("Presence", "Error processing BTPresence update: " + t)
}
finally {
lock.unlock
}
end
I think that this is due to the new way that Groups work now:
Try to use a type (and optionally a function) for your Group definition.
Something like:
Group:Switch gPresent "Present group" <present>
http://docs.openhab.org/configuration/items.html#group-types
Group:Switch gPresent "Present group" <present>
is also not triggering the rule. I wonder if the following is required
Group:Switch:OR(ON,OFF) gPresent "Present group" <present>
I have updated the above code that I payed above which you can find here:
My understanding is adding a type to the group should be sufficient to get update events on the Group but since I use gPresent’s state elsewhere I’ve always had a function.
I noticed that there is no ReentrantLock in the new code. Is that not required anymore?
That is correct. The ReentrantLock is no longer needed. I’ve been running this code for many months now and I honestly don’t remember why there ReentrantLock lock was in there in the first place.
I have a DateTime associated to each Sensor as Rich Koshak as explained in another post.
i want to display the max of all those dates
How can i proceed ?
Group:DateTime:MAX SensorUpdates
Something like that should work.
If not you will need a Rule and a Proxy Item.
@rlkoshak Thanks i will try but i have seen another issue
The Sensor_lastUpdate is update at each reboot or restart service
Rule:
rule "ZWaveNode2_DoorWindowSensor"
when
Item ZWaveNode2_DoorWindowSensor changed
then
ZWaveNode2_DoorWindowSensor_LastUpdate.postUpdate(new DateTimeType())
end
log at the boot
018-06-06 10:06:42.821 [vent.ItemStateChangedEvent] - ZWaveNode2_DoorWindowSensor_LastUpdate changed from NULL to 2018-06-06T09:41:32.001+0300
influxdb.persist:
Strategies{
everyMinute : "0 * * * * ?"
every5Minutes : "0 */5 * * * ?"
everyHour : "0 0 * * * ?"
everyDay : "0 0 0 * * ?"
default = everyChange
}
Items{
* : strategy = everyChange, everyDay, restoreOnStartup
}
i have tried to check if the value is not NULL in my rule (boot time) but it do not work
rule "ZWaveNode2_DoorWindowSensor"
when
Item ZWaveNode2_DoorWindowSensor changed
then
if (ZWaveNode2_DoorWindowSensor.previousState().state.toString!==NULL){
ZWaveNode2_DoorWindowSensor_LastUpdate.postUpdate(new DateTimeType())
}
end
it do not work
Could you advise?
Try that.
Don’t forget the ;
after return
rule "ZWaveNode2_DoorWindowSensor"
when
Item ZWaveNode2_DoorWindowSensor changed
then
if (previousState === null) return; # Don't run the rule if the previous state is null
ZWaveNode2_DoorWindowSensor_LastUpdate.postUpdate(new DateTimeType())
end
Use
if(previousState != NULL) {
.previousState will give you the most recent value in the database. So what is probably happening is the changed state (i.e. the change that triggered the Rule) is already saved in the DB before the Rule executes giving you the current state of the Item when you call previousState. This is why the implicit variable previousState exists.
Also, only use !== or === when on of the operands is null
which is not the same thing as NULL
.
THanks a lot