Get item previous state in a rule

I’d like to execute a rule based on the items state before it received the command

here is how i set my rule up

rule "Cinema scene Level selection"
    when
        Item Scene_Cinema received command
    then
        //logInfo("TEST ", "Cinema scene previous state WAS  " + Scene_Cinema.previousState)
        switch(receivedCommand) {
            case 0 : return false                             //no selection
            case 1 : {                                        //Level I
                        sendCommand(D2, DOWN)
                        }
            case 2 : {                                        //Level II
                        sendCommand(D0, DOWN)
                        }
            case 3 : {                                        //Level III
                        sendCommand(D0, DOWN) 
                        sendCommand(K1, DOWN)
                        }
            case 4 : {                                        //Level IV
                        sendCommand(D0, DOWN) 
                        sendCommand(K0, DOWN)
                        }
            case 5 :     if (Scene_Cinema.previousState == 1) {        
                            sendCommand(D2, UP)
                            postUpdate(Scene_Cinema, 0)
                        }
                        else if (Scene_Cinema.previousState == 2) {        
                            sendCommand(D0, UP)
                            postUpdate(Scene_Cinema, 0)
                        }
                        else if (Scene_Cinema.previousState == 3) {        
                            sendCommand(D0, UP)
                            sendCommand(K1, UP)
                            postUpdate(Scene_Cinema, 0)
                        }
                        else if (Scene_Cinema.previousState == 4) {       
                            sendCommand(D0, UP)
                            sendCommand(K0, UP)
                            postUpdate(Scene_Cinema, 0)
                        }
                        else {        
                            sendCommand(D0, UP)
                            sendCommand(K0, UP)
                            postUpdate(Scene_Cinema, 0)
                        }

        }
end`

What i’d like is to execute a rule in case 5 according to what the previous state of the item was, before the command was issued. However i can’t seem to get the .previousState working.

Can anyone suggest a solution?

Did you setup a persistence service for Scene_Cinema?

Is it the default persistence service? If not, you have to use Scene_Cinema.previousState(false,"mysql") e.g. when mysql is the persistence service. The boolean false instructs the persistence service not to omit identical states.

1 Like

I’m using rrd4j persistence and it is set to
* : strategy = everyChange, restoreOnStartup
This should cover it right?

Should i change syntax to item.previousState.state

I also tried to add import org.openhab.core.persistence.* at the begining of the rules file

Yes, I should have seen this, .previousState includes the timestamp, so previousState.state would be the state. I’m not sure if it is necessary to cast the state to a DecimalType
if (Scene_Cinema.previousState.state as DecimalType == n)
because the number could be interpreted as a number but not a state.

1 Like

Anotherquick question:
do i need to:
import org.openhab.core.persistence.*
for the item.previousState.state to work?

1 Like

l[quote=“Udo_Hartmann, post:4, topic:9368, full:true”]
I’m not sure if it is necessary to cast the state to a DecimalType
if (Scene_Cinema.previousState.state as DecimalType == n)
because the number could be interpreted as a number but not a state.
[/quote]
Could you please give me an example on what you meen by this?
Also what should i import for this to work?

As the states 1, 2, 3 and 4 are digits, it’s possible that the state has to be casted to a numerical value. Simply set n to 1, 2, 3 or 4 in the example above. There should be no need for importing anything. Also, this should work as well:

case 5 : switch(Scene_Cinema.previousState.state as DecimalType) {        
    case 1: {
                sendCommand(D2, UP)
                postUpdate(Scene_Cinema, 0)
                }
    case  2: {        
                sendCommand(D0, UP)
                postUpdate(Scene_Cinema, 0)
                }
    case  3: {        
                sendCommand(D0, UP)
                sendCommand(K1, UP)
                postUpdate(Scene_Cinema, 0)
                }
    case  4: {       
                sendCommand(D0, UP)
                sendCommand(K0, UP)
                postUpdate(Scene_Cinema, 0)
                }
    default: {        
                sendCommand(D0, UP)
                sendCommand(K0, UP)
                postUpdate(Scene_Cinema, 0)
                }
}
end
1 Like

thanks will try and report back :slight_smile:

Unfortunately it’s not working…

item.previousState.state doesn’t return the previous value …

Here is the code (unfortunately i can’t get it to convert to code):
// ###########Cinema
rule "Cinema scene Level selection"
when
Item Scene_Cinema received command
then
logInfo("TEST ", "Cinema scene previous state WAS " + Scene_Cinema.previousState.state)
switch(receivedCommand) {
case 0 : return false //no selection
case 1 : { //Level I
//sendCommand(D2, DOWN)
//postUpdate(Scene_Cinema_prev, 1)
logInfo("TEST ", “Cinema scene state now 1”)
}
case 2 : { //Level II
//sendCommand(D0, DOWN)
//postUpdate(Scene_Cinema_prev, 2)
logInfo("TEST ", “Cinema scene state now 2”)
}
case 3 : { //Level III
//sendCommand(D0, DOWN)
//sendCommand(K1, DOWN)
//postUpdate(Scene_Cinema_prev, 3)
logInfo("TEST ", “Cinema scene state now 3”)
}
case 4 : { //Level IV
//sendCommand(D0, DOWN)
//sendCommand(K0, DOWN)
//postUpdate(Scene_Cinema_prev, 4)
logInfo("TEST ", “Cinema scene state now 4”)
}
case 8 : { //STOP
//sendCommand(D0, STOP)
//sendCommand(K0, STOP)
//postUpdate(Scene_Cinema, Scene_Cinema_prev.state)
postUpdate(Scene_Cinema, Scene_Cinema.previousState.state)
}
case 9 : //raise shutters- exit Cinema mode
logInfo(“TEST OFF”, "Cinema scene state was "+Scene_Cinema.previousState.state)
switch(Scene_Cinema.previousState.state) {
//switch(Scene_Cinema_prev.state) {
case 1 : {
//sendCommand(D2, UP)
//postUpdate(Scene_Cinema_prev, 0)
//postUpdate(Scene_Cinema, 0)
logInfo("TEST OFF ", "1Cinema scene state "+Scene_Cinema.previousState.state)
}
case 2 : {
//sendCommand(D0, UP)
//postUpdate(Scene_Cinema_prev, 0)
//postUpdate(Scene_Cinema, 0)
logInfo("TEST OFF ", "2Cinema scene state "+Scene_Cinema.previousState.state)
}
case 3 : {
//sendCommand(D0, UP)
//sendCommand(K1, UP)
//postUpdate(Scene_Cinema_prev, 0)
//postUpdate(Scene_Cinema, 0)
logInfo("TEST OFF ", "3Cinema scene state "+Scene_Cinema.previousState.state)
}
case 4 : {
//sendCommand(D0, UP)
//sendCommand(K0, UP)
//postUpdate(Scene_Cinema_prev, 0)
//postUpdate(Scene_Cinema, 0)
logInfo("TEST OFF ", "4Cinema scene state "+Scene_Cinema.previousState.state)
}
default : {
//sendCommand(D0, UP)
//sendCommand(K0, UP)
//postUpdate(Scene_Cinema_prev, 0)
//postUpdate(Scene_Cinema, 0)
logInfo("TEST OFF ", "0Cinema scene state "+Scene_Cinema.previousState.state)
}
}
}
end

Here is the log output:

==> /var/log/openhab/events.log <==
2016-04-04 18:53:55 - Scene_Cinema received command 1

==> /var/log/openhab/openhab.log <==
2016-04-04 18:53:55.599 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene previous state WAS  1
2016-04-04 18:53:55.605 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene  state now 1
2016-04-04 18:53:55.625 [INFO ] [openhab.model.script.TEST OFF ] - 1Cinema scene  state 1

==> /var/log/openhab/events.log <==
2016-04-04 18:53:58 - Scene_Cinema received command 2

==> /var/log/openhab/openhab.log <==
2016-04-04 18:53:58.853 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene previous state WAS  2

This should say that it was 1

2016-04-04 18:53:59.060 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene  state now 2
2016-04-04 18:53:59.584 [INFO ] [openhab.model.script.TEST OFF ] - 2Cinema scene  state 2

==> /var/log/openhab/events.log <==
2016-04-04 18:54:01 - Scene_Cinema received command 9

==> /var/log/openhab/openhab.log <==
2016-04-04 18:54:01.414 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene previous state WAS  9

This should say that previous state was 2 not 9

2016-04-04 18:54:01.436 [INFO ] [.openhab.model.script.TEST OFF] - Cinema scene  state was 9
2016-04-04 18:54:01.454 [INFO ] [openhab.model.script.TEST OFF ] - 0Cinema scene  state 9

To post code as code, just use 3 backticks (```) before and after the code (each in a separate line)

You could try to use Scene_Cinema.previousState(true).state to ensure that the previous state is another than the actual state (evil hack :slight_smile: ).

As logInfo writes Strings, you better should use Scene_Cinema.previousState.state.toString as part of the logging.

Still no joy … however now i got a new problem:

==> /var/log/openhab/events.log <==
2016-04-05 18:53:30 - Scene_Cinema received command 1

==> /var/log/openhab/openhab.log <==
2016-04-05 18:53:30.019 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Cinema scene Level selection': rrd4j does not allow querys without a begin date, unless order is descending and a single value is requested

==> /var/log/openhab/events.log <==
2016-04-05 18:53:32 - Scene_Cinema received command 2

==> /var/log/openhab/openhab.log <==
2016-04-05 18:53:32.520 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Cinema scene Level selection': rrd4j does not allow querys without a begin date, unless order is descending and a single value is requested

==> /var/log/openhab/events.log <==
2016-04-05 18:53:38 - Scene_Cinema received command 9

==> /var/log/openhab/openhab.log <==
2016-04-05 18:53:38.385 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Cinema scene Level selection': rrd4j does not allow querys without a begin date, unless order is descending and a single value is requested
Code i used was 
rule "Cinema scene Level selection"
    when
        Item Scene_Cinema received command
    then
        logInfo("TEST ", "Cinema scene previous state WAS  " + Scene_Cinema.previousState(true).state )
        switch(receivedCommand) {
            case 0 : return false                             //no selection
            case 1 : {                                        //Level I
                //sendCommand(D2, DOWN)
                //postUpdate(Scene_Cinema_prev, 1)
                logInfo("TEST ", "Cinema scene  state now 1") 
            }
            case 2 : {                                        //Level II
                //sendCommand(D0, DOWN)
                //postUpdate(Scene_Cinema_prev, 2)
                logInfo("TEST ", "Cinema scene  state now 2")
            }
            case 3 : {                                        //Level III
                //sendCommand(D0, DOWN) 
                //sendCommand(K1, DOWN)
                //postUpdate(Scene_Cinema_prev, 3)
                logInfo("TEST ", "Cinema scene  state now 3")
            }
            case 4 : {                                        //Level IV
                //sendCommand(D0, DOWN) 
                //sendCommand(K0, DOWN)
                //postUpdate(Scene_Cinema_prev, 4)
                logInfo("TEST ", "Cinema scene  state now 4")
            }
            case 8 : {                                        //STOP
                //sendCommand(D0, STOP)
                //sendCommand(K0, STOP)
                //postUpdate(Scene_Cinema, Scene_Cinema_prev.state)
                postUpdate(Scene_Cinema, Scene_Cinema.previousState(true).state)
            }
            case 9 :                                         //dvigni rolete - exit Kino mode .previousState
                logInfo("TEST OFF", "Cinema scene  state was "+Scene_Cinema.previousState(true).state) 
                switch(Scene_Cinema.previousState(true).state) {
                //switch(Scene_Cinema_prev.state) {
                    case 1 : {
                        //sendCommand(D2, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        //postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "1Cinema scene  state "+Scene_Cinema.previousState(true).state)
                    }
                    case 2 : {
                        //sendCommand(D0, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        //postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "2Cinema scene  state "+Scene_Cinema.previousState(true).state)
                    }
                    case 3 : {
                        //sendCommand(D0, UP)
                        //sendCommand(K1, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        //postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "3Cinema scene  state "+Scene_Cinema.previousState(true).state)
                    }
                    case 4 : {
                        //sendCommand(D0, UP)
                        //sendCommand(K0, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        //postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "4Cinema scene  state "+Scene_Cinema.previousState(true).state)
                    }
                    default : {                 //use instead of else statement
                        //sendCommand(D0, UP)
                        //sendCommand(K0, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        //postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "0Cinema scene  state "+Scene_Cinema.previousState(true).state)
                    }
                }    
            }
end

After reading about this error it seems that it is mostly a problem with rrd4j when you don’t use strategy every minute
so i added this line in rrd4j.persist file
Scene_Cinema : strategy = everyMinute
as opposed to
* : strategy = everyChange, restoreOnStartup

any ideas?
(also @Udo_Hartmann thank you very much for your effort so far)

Have you tried previousState (no XXX. in front) ? I believe it refers to the item that triggered rule execution which in your case is what you’re looking for. Works for me.

And possibly a silly question, but do you have rrd4j persistence activated (persistence:default=rrd4j) and configured in openhab.cfg ?

I would recommend using another persistence service e.g. db4o instead of rrd4j. This implies a growing persistence file, though, but just to rule errors out…

With Strategy everyMinute, previousState would deliver the state one minute ago.

If you could trigger your rule by a state change

Item SceneCinema changed

you would have the implicit variable previousState in the rule body available.

1 Like

Thanks for suggestion but no joy …
Item Scene_Cinema changed with Scene_Cinema.previousState(true).state)
didn’t work…
I even tried with the historic.State(now.minusSeconds(1)).state), however i got some unreliable results so i took another approach at the problem and solved it …
i introduced another item Scene_Cinema_prev whose state i change to reflect the previous state:

// ###########Cinema
rule "Cinema scene Level selection"
    when
        Item Scene_Cinema received command
    then
        switch(receivedCommand) {
            case 0 : return false                             //no selection
            case 1 : {                                        //Level I
                sendCommand(D2, DOWN)
                postUpdate(Scene_Cinema_prev, 1)
            }
            case 2 : {                                        //Level II
                sendCommand(D0, DOWN)
                postUpdate(Scene_Cinema_prev, 2)
            }
            case 3 : {                                        //Level III
                sendCommand(D0, DOWN) 
                sendCommand(K1, DOWN)
                postUpdate(Scene_Cinema_prev, 3)
            }
            case 4 : {                                        //Level IV
                sendCommand(D0, DOWN) 
                sendCommand(K0, DOWN)
                postUpdate(Scene_Cinema_prev, 4)
            }
            case 8 : {                                        //STOP
                sendCommand(D0, STOP)
                sendCommand(K0, STOP)
                postUpdate(Scene_Cinema, Scene_Cinema_prev.state)
            }
            case 9 :                                         //dvigni rolete - exit Kino mode
                switch(Scene_Cinema_prev.state) {
                    case 1 : {
                        sendCommand(D2, UP)
                        postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                    }
                    case 2 : {
                        sendCommand(D0, UP)
                        postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                    }
                    case 3 : {
                        sendCommand(D0, UP)
                        sendCommand(K1, UP)
                        postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                    }
                    case 4 : {
                        sendCommand(D0, UP)
                        sendCommand(K0, UP)
                        postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                    }
                    default : {                 //use instead of else statement
                        sendCommand(D0, UP)
                        sendCommand(K0, UP)
                        postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                    }
                }    
        }
end

Can’ t leave this one alone … :slight_smile:
@thkrebs and @mstormi
Tried your suggestion with the previousState and it works … however now i have a new issue:

When i send command 8 to send a partial stop, i want to return the scene status to previous state, however that puts me in a loop because the rule looks for when Item changed instead of received command and it effectively trigers a rule even when i only update the status of the switch:

 case 8 : {                                        //STOP
                //sendCommand(D0, STOP)
                //sendCommand(K0, STOP)
                //postUpdate(Scene_Cinema, Scene_Cinema_prev.state)
                postUpdate(Scene_Cinema, previousState)
            }

log file:

2016-04-10 22:09:33.205 [INFO ] [c.internal.ModelRepositoryImpl] - Refreshing model 'scene_events.rules'
"send command 2"
2016-04-10 22:09:42.598 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene previous state WAS:  1
2016-04-10 22:09:43.056 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene  state now 2 
"send command 8"
2016-04-10 22:09:52.709 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene previous state WAS:  2
"rule executes and completes with "postUpdate(Scene_Cinema, previousState)""
"rule is triggered by "postUpdate(Scene_Cinema, previousState)" and entire case 2 is executed "
2016-04-10 22:09:53.482 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene previous state WAS:  8
2016-04-10 22:09:53.491 [INFO ] [org.openhab.model.script.TEST ] - Cinema scene  state now 2

but this is unwanted.

Any ideas?

The entire code:

// ###########Cinema
rule "Cinema scene Level selection"
    when
        Item Scene_Cinema changed
    then
        logInfo("TEST ", "Cinema scene previous state WAS:  " + previousState )
        switch(Scene_Cinema.state) {
            case 0 : return false                             //no selection
            case 1 : {                                        //Level I
                //sendCommand(D2, DOWN)
                //postUpdate(Scene_Cinema_prev, 1)
                logInfo("TEST ", "Cinema scene  state now 1") 
            }
            case 2 : {                                        //Level II
                //sendCommand(D0, DOWN)
                //postUpdate(Scene_Cinema_prev, 2)
                logInfo("TEST ", "Cinema scene  state now 2")
            }
            case 3 : {                                        //Level III
                //sendCommand(D0, DOWN) 
                //sendCommand(K1, DOWN)
                //postUpdate(Scene_Cinema_prev, 3)
                logInfo("TEST ", "Cinema scene  state now 3")
            }
            case 4 : {                                        //Level IV
                //sendCommand(D0, DOWN) 
                //sendCommand(K0, DOWN)
                //postUpdate(Scene_Cinema_prev, 4)
                logInfo("TEST ", "Cinema scene  state now 4")
            }
            case 8 : {                                        //STOP
                //sendCommand(D0, STOP)
                //sendCommand(K0, STOP)
                //postUpdate(Scene_Cinema, Scene_Cinema_prev.state)
                postUpdate(Scene_Cinema, previousState)
            }
            case 9 :                                         //dvigni rolete - exit Kino mode .previousState
                //logInfo("TEST OFF", "Cinema scene  state was "+Scene_Cinema.previousState(true).state) 
                switch(previousState) {
                //switch(Scene_Cinema_prev.state) {
                    case 1 : {
                        //sendCommand(D2, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "1Cinema scene  state: "+ previousState)
                    }
                    case 2 : {
                        //sendCommand(D0, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "2Cinema scene  state: "+ previousState)
                    }
                    case 3 : {
                        //sendCommand(D0, UP)
                        //sendCommand(K1, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "3Cinema scene  state: "+ previousState)
                    }
                    case 4 : {
                        //sendCommand(D0, UP)
                        //sendCommand(K0, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "4Cinema scene  state: "+ previousState)
                    }
                    default : {                 //use instead of else statement
                        //sendCommand(D0, UP)
                        //sendCommand(K0, UP)
                        //postUpdate(Scene_Cinema_prev, 0)
                        postUpdate(Scene_Cinema, 0)
                        logInfo("TEST OFF ", "0Cinema scene  state: ")
                    }
                }    
            }
end

You can add “from XXX” and/or “to YYY” after “changed”. Should help breaking the loop.

Does it also work with not from so that i could only exclude state 8?

Perhaps a simple if (previousState == 8) return false?

Not sure about ‘not’ in condition line (check the Wiki ‘rules’ pages), but yes, you can use previousState as a variable, so that should work, too.

Solved:

// ###########Cinema v2
rule "Cinema scene Level selection"
    when
        Item Scene_Cinema changed
    then
        switch(Scene_Cinema.state) { 
            case 0 : return false                             //no selection
            case 1 : {                                        //Level I
                if (previousState == 8) return false
                //logInfo("TEST ", "sendCommand(D2, DOWN)"
                sendCommand(D2, DOWN)
            }
            case 2 : {                                        //Level II
                if (previousState == 8) return false
                sendCommand(D0, DOWN)
            }
            case 3 : {                                        //Level III
                if (previousState == 8) return false
                sendCommand(D0, DOWN) 
                sendCommand(K1, DOWN)
            }
            case 4 : {                                        //Level IV
                if (previousState == 8) return false
                sendCommand(D0, DOWN) 
                sendCommand(K0, DOWN)
            }
            case 8 : {                                        //STOP
                if (previousState == 8) return false
                sendCommand(D0, STOP)
                sendCommand(K0, STOP)
                postUpdate(Scene_Cinema, previousState)
            }
            case 9 :                                         //exit Cinema mode
                switch(previousState) {
                    case 1 : {
                        sendCommand(D2, UP)
                        postUpdate(Scene_Cinema, 0)
                    }
                    case 2 : {
                        sendCommand(D0, UP)
                        postUpdate(Scene_Cinema, 0)
                    }
                    case 3 : {
                        sendCommand(D0, UP)
                        sendCommand(K1, UP)
                        postUpdate(Scene_Cinema, 0)
                    }
                    case 4 : {
                        sendCommand(D0, UP)
                        sendCommand(K0, UP)
                        postUpdate(Scene_Cinema, 0)
                    }
                    default : {                 //use instead of else statement
                        sendCommand(D0, UP)
                        sendCommand(K0, UP)
                        postUpdate(Scene_Cinema, 0)
                    }
                }
        }
end

Thank you all!

1 Like