Rule / Persistence: Shut off Power after 5min of low Power-Consumption

I had to remove it and add it again, and now: Yes! Thank you!

But now of course it becomes clear, that my rule is a desaster. :slight_smile:

If the power consumption changes and in the same moment I want to calculate the average power consumption over the last ten minutes of course this number is below any threshold because the media player just started.

Is it possible to use:
if(Verbrauch_Kodi.averageSince(now.plusMinutes(10)) > 2)
Instead of minusMinutes in this situation? I guess no because persistence has no numbers for the future, only for the past.

So what would be the best attempt here? Put a timer to wait for 10 minutes after a change in powerconsumption?

Well yes, persistence is not prediction :wink:

The usual approach, which you will find talked about here in the context of monitoring e.g. washing machine cycle, would be to detect when power has reduced to below a threshold.
By implication, the interest in the past when power changes is therefore “was it just working?” and so maybe it’s now stopped.
At that point you might start a timer, or count some more below-threshold reports, before deciding that yes it really has stopped.

That’s what I want to achieve. How do I count more reports for let’s say 10 more minutes, before deciding to switch off the plug?

As mentioned I want to make sure the Mediaplayer had really shut down and not only performed e restart or had a fluctuation in Power Consumption. Therefore, the trigger should be a reduced power consumption as in my rule example. But instead of immediately shutting it down if consumption is below the threshold of 3 Watts, I want the rule to check for 10 minutes after the initial loss in power consumption (trigger) if the consumption stayed below the threshold. How can I achieve this?

One approach, using a timer.

Have a rule triggered by power update

If power below threshold -
If your timer already exists, do nothing
Otherwise, make a new timer.
The timer is scheduled for ten minutes time to do your “off” actions.

If power above threshold, cancel any existing timer.

Thank you for your advise. Please find below my proposal and feel free to comment. :slight_smile:

rule "Kodi Stromzufuhr abschalten wenn ungenutzt"

when
Item Verbrauch_Kodi changed
then
// Add persistence to Verbrauch_Kodi item
if(Verbrauch_Kodi. < 2 ){
if (timer===null) {
timer = createTimer(now.plusMinutes (10))
if(Verbrauch_Kodi.averageSince(now.minusMinutes(10)) < 2){
Kodi_Energiezufuhr.sendCommand(OFF)
}
}
}
else if(Verbrauch_Kodi > 2){
timer.cancel
timer = null // cancel any timer if power consumption raises
}
end

Use three ticks ``` before and after code or logs…

```
code or logs
```

I guess that is your instantaneous power measurement Item?
It’s the .state you are interested in.

Be careful about what you compare it with, if your Item has UoM units.

You don’t need the persistence/average; if the reading rises above threshold it cancels the timer.
If it hasn’t done that in ten minutes, it commands OFF.

Use a unique name for your timer handle variable, so that you don’t accidentally introduce a clash when you write some other timer-using rule later.
Don’t forget to declare it in global namespace.

Yes. Thanks will change accordingly.

Yes makes sense. More simple is better.

How would I do that?

And how is that achieved?

Are the following modifications right? Can I use it like this?

var Timer timerKodiabschalten = null

rule “Kodi Stromzufuhr abschalten wenn ungenutzt”

when
Item Verbrauch_Kodi changed
then
if(Verbrauch_Kodi.state < 2 ){
if (timerKodiabschalten===null) {
timerKodiabschalten = createTimer(now.plusMinutes (10))
if(Verbrauch_Kodi.state < 2){
Kodi_Energiezufuhr.sendCommand(OFF)
}
}
}
else if(Verbrauch_Kodi.state > 2){
timerKodiabschalten.cancel
timerKodiabschalten = null // cancel timer if power consumption raises
}
end

Maybe it got lost in copy/paste, but your createTimer is missing vital delimeters for its code block, | [ ]

Your cancel section should test to see if the timer is non-null before trying to cancel, else that will error.
There is a shortcut for that -

Ok. I added it and the shortcut, hopefully in the right way, as I did not fully understand how to use both

var Timer timerKodiabschalten = null

rule “Kodi Stromzufuhr abschalten wenn ungenutzt”

when
Item Verbrauch_Kodi changed
then
if(Verbrauch_Kodi.state < 2 ){
if (timerKodiabschalten===null) {
timerKodiabschalten = createTimer(now.plusMinutes (10) [ |
if(Verbrauch_Kodi.state < 2){
Kodi_Energiezufuhr.sendCommand(OFF)
}
}
])}
else if(Verbrauch_Kodi.state > 2){
timerKodiabschalten?.cancel
timerKodiabschalten = null // cancel timer if power consumption raises
}
end

I tested it with no luck. It seems the command is not triggered.

May I ask somebody to help me check my rule and to correct it?

I made some adjustments, especially with the [|] and () and {}, as VisualStudio reported some errors. For now it seems to be working, but I am not sure with the timer still:

var Timer timerKodiabschalten = null

rule “Kodi Stromzufuhr abschalten wenn ungenutzt”

when
Item Verbrauch_Kodi changed
then
if(Verbrauch_Kodi.state < 2){
if (timerKodiabschalten===null) {
timerKodiabschalten = createTimer(now.plusMinutes (10), [ |
if(Verbrauch_Kodi.state < 2){
Kodi_Energiezufuhr.sendCommand(OFF)
}
])}
}
else if(Verbrauch_Kodi.state > 2){
timerKodiabschalten?.cancel
timerKodiabschalten = null // cancel timer if power consumption raises
}
end

Add some diagnostics temporarily

rule "Kodi Stromzufuhr abschalten wenn ungenutzt"
when
    Item Verbrauch_Kodi changed
then
    logInfo("test", "My rule begins")
    logInfo("test", "key value " + Verbrauch_Kodi.state.toString)
    if(Verbrauch_Kodi.state < 2){
        if (timerKodiabschalten===null) {
            logInfo("test", "starting timer")
            timerKodiabschalten = createTimer(now.plusMinutes (10), [ |
                logInfo("test", "timer begins")
                logInfo("test", "key value " + Verbrauch_Kodi.state.toString)
                if(Verbrauch_Kodi.state < 2){
                    Kodi_Energiezufuhr.sendCommand(OFF)
                }
        ])}
    }
    else if(Verbrauch_Kodi.state > 2){
        logInfo("test", "timer aborting")
        timerKodiabschalten?.cancel
        timerKodiabschalten = null // cancel timer if power consumption raises
    } else {
        logInfo("test", "unexpectedly here")
    }
end

Have a think about edge cases.
What happens if Item state is exactly 2? I think you can do away with else-if() and just use else for cancelling.

What happens after 10 minutes is up the first time? Can you ever create a new timer later? Hint - you need the timer code block to set its own handle to null before exiting, to clear the way for a later run.

Thanks. I’ll try with some logging.
For the unlikely case of consumption being exactly 2 I added >= to the cancel condition.

One last question regarding:

Isn’t the timerKodiabschalten = null line meant to do so? Or do you mean I should include this line also in the first condition where the timer actually gets used? If so, where would I have to add it?

I tested the as in my last post for several on and off cycles and it seems it is working. But I’ll improve it with your tips. :wink:

Yes. You will see this technique used in most of the timer examples.
Generally at the end of the timer’s execution block, set the timerKodiabschalten handle back to null, so that it becomes easy for subsequent runs of the rule to detect “there is no timer now”.

My rule now looks like below. If you agree I have not to fear major errors comming up, I could mark this as solves


rule "Kodi Stromzufuhr abschalten wenn ungenutzt"

when
	Item Verbrauch_Kodi changed
then
    if(Verbrauch_Kodi.state < 2){ 
        if (timerKodiabschalten===null) {  
	        timerKodiabschalten = createTimer(now.plusMinutes (10), [ |
                if(Verbrauch_Kodi.state < 2){     
	            Kodi_Energiezufuhr.sendCommand(OFF)
                timerKodiabschalten = null
            }
            ])}
    }
    if(Verbrauch_Kodi.state >= 2){
        timerKodiabschalten?.cancel
        timerKodiabschalten = null   		// cancel timer if power consumption raises
    }
end

That would allow a timer execution to finish without doing anything, if power over threshold, but also not nulling the handle.
When the timer finishes, whatever the outcome, you’ll want to clear the way for the next time.

	        timerKodiabschalten = createTimer(now.plusMinutes (10), [ |
                if(Verbrauch_Kodi.state < 2){     
	               Kodi_Energiezufuhr.sendCommand(OFF)
                }
                timerKodiabschalten = null
            ])}

Thanks. Sometimes the logic still strikes me. :smiley:

It happens. I spent an hour last night looking at a rule where a simple
if ( X or Y ) didn’t do what I expected because of an oversight.