Is there a way to distinguish the source of an event?

Hi guys,
I used this beautiful design pattern from @rlkoshak Design Pattern: Motion Sensor Timer to implement a logic for my bathrooms.
I used the pattern to implement the rule for handling timed turn off of the fan, i.e. if you want to go to toilet you click the button and it turns the fan ON, after period of time it turns it OFF.
Then I use another ruled logic which is very simple but operates the same item: when humidity level goes above certain treshold, turn ON the fan, when it goes bellow another treshold turn it Off.
The problem is that I cannot distinguish the source of the event in order to disable the timer, i.e. when the humidity treshold goes up the event bus fires item changed event and when this event is ON I create timer.
Is there a solution to this? I’m thinking of reading the event source if there is such support

Here is the code:

rule "Item that needs to be switched off after 30 minutes" 
when Member of TriggerOffAfterThirdyMinutes changed  
then
    val timeOutMinutes = 30; 
    if(triggeringItem.state == ON) {
        logDebug("Bathrooms", "Received command ON for item " + triggeringItem.name )
        if(fansTimers.get(triggeringItem.name) === null || fansTimers.get(triggeringItem.name).hasTerminated()) {
            logDebug("Bathrooms", "Timer of " + triggeringItem.name + " is null or finished. Will create new one." )
            fansTimers.put(triggeringItem.name, createTimer(now.plusMinutes(timeOutMinutes), [|
                logDebug("Bathrooms", "Timer of " + triggeringItem.name + " finished. Executing command OFF." )
                triggeringItem.sendCommand(OFF)
                fansTimers.put(triggeringItem.name, null)
            ]))
        } else {
            logDebug("Bathrooms", "Rescheduling timer for " +  triggeringItem.name + " ...")
            fansTimers.get(triggeringItem.name).reschedule(now.plusMinutes(timeOutMinutes))
        }
    } else {
        val timer = fansTimers.get(triggeringItem.name);
        if (timer !== null && !timer.hasTerminated()) {
            logDebug("Bathrooms", "Canceling timer + " +timer.toString())
            timer.cancel();
        }
    }
end

rule "Bedroom Bath Humidity events" 
when Item floor2BedBathroomHumidity received update or Item floor2BedBathroomDesiredHumidity changed 
then
    val measuredHumidity = floor2BedBathroomHumidity.state as DecimalType
    val desiredHumidity  = floor2BedBathroomDesiredHumidity.state as DecimalType
    val timer = fansTimers.get(floor2BedroomBathFanPower.name);
    val tolerance = 5;
    if (measuredHumidity <= desiredHumidity - tolerance && floor2BedroomBathFanPower.state == ON ) {
        logDebug("Bathrooms", floor2BedBathroomHumidity.label + " value=" + measuredHumidity + "%.")
        if (timer === null || timer.hasTerminated()) {
            logDebug("Bathrooms", "Will stop Fan.")
            floor2BedroomBathFanPower.sendCommand(OFF)
        } else {
            logDebug("Bathrooms", "Timer is not null or finished. Cannot stop it until timer finishes...")
        }
    } else if (measuredHumidity > desiredHumidity + tolerance && floor2BedroomBathFanPower.state == OFF) {
        logDebug("Bathrooms", "Will start Fan.")
        floor2BedroomBathFanPower.sendCommand(ON)
    }
end

You mean, you want your timer to check if the humidity is above threshold when it comes time to turn off? (and if it is, do nothing)

That could be one way to approach it, the other I’m thinking about is to not create a timer at all if the source is rule trigger due to humidity and not a z-wave switch.
I’m not sure if rule engine supports creating more complex objects, for example:

MyCheckObject {
    key=TheItemId
    timer=timerObject
    dependsOn=humidityObject
}

The new rule engine with scripted automation does. You can create and store objects within a module or ScriptExtension that can be accessed from other rules or when the rule triggers again. You also have access to Item metadata.

You’re already dealing with a fairly “complex object” in the form of an “array” of independent timers. I wouldn’t worry about that capability.

That still leaves you the case of when a humidity uhh “demand” occurs while a manually triggered timer is already running.

Another variation for consideration; just run (or reschedule) the same timer every time the fan starts, regardless of cause.
Your humidity trigger rule need only worry about issuing ON.
At the end of timer run, check humidity and reschedule itself if still too high.

What do you want to happen if user exiting clicks the button, expecting fan-OFF?
What do you want to happen if the user walks into room with fan already running due humidity, and clicks the button anyway? What does the user expect to happen?

What I’m saying is, I think you are focusing on the wrong part - why the fan started. Who cares really. The important part is why and when you want it to stop.

1 Like

Thanks for making me think about this from a different angle !
It seems that sometimes our train of thought tries to reach solution through the most complex way. :smiley:

At the end I will fix it with the following approach:

  • when receive ON - start timer, if timer exists - reschedule it.
  • when receive OFF - remove/nullify timer
    It’s not very ellegant but it will work. :smiley:

Thank you !

Our natural way of thinking is quite unlike dimwitted computers, especially when it comes to decision making.