previousState().state returns current state (=first entry) in MariaDB and not previous entry (=second entry)

Hi,

I read several posts regarding the previousState() issue but couldn’t find one which solves my issue.
In my case previousState() return everytime the current state and not the previous state.

rlkoshak wrote in one of his posts:

MyItem.previousState // most recent value in the persisted database not counting the current state
MyItem.previousState(true) // most recent value in the persisted database that is different from the current state

My understanding is that previousState() returns the second value in the DB table.
Let me give you an example:

Item: Heating_Bedroom_Mode

1. Step

Inital DB entries.

2020-05-18   14:14:14.000     1.0
2020-05-18   15:15:15.000     2.0

Return values:

Heating_Bedroom_Mode.state —> “2.0”
Heating_Bedroom_Mode.previousState().state —> returns “2.0” (expected “1.0”!)

2. Step

sendCommand(Heating_Bedroom_Mode, "3.0)

2020-05-18   14:14:14.000     1.0
2020-05-18   15:15:15.000     2.0
2020-05-18   16:16:15.000     3.0

Return values:

Heating_Bedroom_Mode.state —> “3.0”
Heating_Bedroom_Mode.previousState().state —> “3.0” (expected “2.0”)

Do I have another understanding of the function previousState() and is this the normal behavior or is it a bug?

Here is my setup where I’m facing the issue:

Items

Number Heating_Bedroom_Mode  "Mode" <fire>	(gHeatingMode, Bedroom) {channel="knx:device:bridge:actuatorAKH080002:channelDMode"}

Script:

rule "Heating: Deactivate during airing"
    when
        Member of gContactsWindowsAndDoorsWithoutEntrance changed
    then
        try {
            if(Setting_Heating_DeactivateInRoomWhileAiring.state == ON) {
                val room = transform("REGEX", "Homematic_Sensor_([^_]*).*", triggeringItem.name )
                val item = gHeatingMode.members.filter[ i | i.name.contains(room) ].last
                if(triggeringItem.state == OPEN) {
                    sendTelegram('bot1', "Window in room: "+room+" has been opened.");
                    sendCommand(item, "4.0")
                } else if(triggeringItem.state == CLOSED) {
                    sendTelegram('bot1', "Window: "+room+" has beend closed. Current state: "+item.state.toString+". Restore previous state: "+item.previousState().state.toString);
                    sendCommand(item, item.previousState().state.toString)
                }
            }
        }
        catch(Throwable t) {
            sendTelegram('bot1', "Error during execution rule: 'Heating: Deactivate during airing'. \n\r\Error:\n\r\n" + t.toString);
        }
    end

Platform information:

  • Hardware: Synology Diskstation DS918+
  • Java Runtime Environment: 1.8.0_202
  • openHAB version: 2.5.1
  • Persistence: MariaDB (JDBC)
1 Like

I would expect that; the most recent stored state, whether or not it is different from the current state

Have you yet tried
Heating_Bedroom_Mode.previousState(true).state
which both Rich and the docs tell you will reach back in the database to a different state?

Hi @rossko57, thx for your quick reply. I already tried previousState(true). This defintley return the previous value which isn’t the current one but then I’m facing another issue.

2020-05-18   14:14:14.000     1.0
2020-05-18   15:15:15.000     2.0
2020-05-18   16:16:15.000     2.0

Heating_Bedroom_Mode.previousState(true).state --> “1.0” (all fine!)

But in case of my rule this can lead to the following issue.

  1. Start with one DB entry - heating is in comfort mode = 1.0
2020-05-18   14:14:14.000     1.0
  1. You manually set the heating in the room to “frost protection” = 4.0, e.g. by using a KNX panel
2020-05-18   14:14:14.000     1.0
2020-05-18   15:15:15.000     4.0
  1. You open the window which should set the heating to “frost protection”

No DB change happens

2020-05-18   14:14:14.000     1.0
2020-05-18   15:15:15.000     4.0
  1. You close the window and the previous state is recovered
2020-05-18   14:14:14.000     1.0
2020-05-18   15:15:15.000     4.0

Heating_Bedroom_Mode.previousState(true).state —> “1.0”

Suddenly, your heating is turned on automatically although you turned it off manually.

In this case I have to rewrite the rule and store the values in a temp item but I thought I could avoid it by using the persistence.

previousState() and previousState(true) are working exactly as designed.

That is your problem. Data that is not persisted cannot be recovered by the persistence service, obviously.

In your case, you changed something but did not persist the change.
Have you considered reviewing your persist strategy settings for this Item?
EDIT - oh right, forget that, there was no change.
I should rephrase as “something happened” (window open) but you have no record of that in what you have shown.

You’l need special coding. as you have, because you have no other way to recognize why the previous ‘frost’ mode was set.
If you had a timestamp of window opening, you could use historicState() to retrieve the old mode for that instant.
It’s just as easy to temporarily store in another variable though.

My persistance is an every change one.

Items {

	gLights* : strategy = everyChange, restoreOnStartup

	gSettings* : strategy = everyChange, restoreOnStartup

	gTemperature* : strategy = everyChange, every10Minutes

	gHeatingSetValue* : strategy = everyChange, every10Minutes

	gHumidity* : strategy = everyChange, every10Minutes

	gHumidity_Room_Index* : strategy = everyChange, every10Minutes

	gDB_everyChange* : strategy = everyChange

	gHeatingMode* : strategy = everyChange
}

Correct, because there was no change the DB isn’t updated.
Would it help if I add

Heating_Bedroom_Mode.persist()

to my rule in the opening statement for the cases that the mode won’t change by opening the window?

Typing against each other.

You might get that to work; but I would worry if you have any other reason to insert a persist though (or might do at some later date), like a 10minute or something initialzing. This would mess up the sequence you rely on.

If you have the window-open timestamp (which you might for some other reason) then historicState() is the best bet using persistence.

If you use a temporary variable though, that is open to editing (“correcting”) if someone twiddles buttons while the window is open.

I tried

Heating_Bedroom_Mode.persist()

but this doesn’t solve it. So I’ll go ahead with the historicState() function.

Nevertheless, I checked some perseistence entries of other items and it still doesn’t make sense to me.
In case of a window contact I have the following behavior:

Window is OPEN → previousState() returns OPEN
Window is CLOSED → previousState() returns CLOSED

You wrote in one of your posts:

previousState() and previousState(true) are working exactly as designed.

But with the behavior above previousState() works like something à la “currentState()” because it always returns the last/newest entry in the DB and not the previous one.

What the “latest” entry in the persistence database actually is, depends on your persist strategy. It may or may not be the same state as the “live” current state of the Item.

Yes, that is what it is supposed to do.
Every record is a historical snapshot.
Getting the most recent record is getting the historic state just before the current live state… “previous state”
It’s a method in the Item, myItem.previousState(), and the “previous” is with reference to the Item.
Notably, there is no other method to get “most recent entry”.
“most recent entry” is not the same thing as current state.
Remember these are generalized methods and will give different results depending on your persist strategy.

You really haven’t discovered a bug after ten years of it working this way. It might not do what you expected in this situation, but it does what it is supposed to.

EDIT - if you must have the record-before-most-recent, you’d have to construct your own means. Fetch previousState(), get the timestamp, subtract a few milliseconds, then perform historicState() with the adjusted timestamp. This will reach back to the most recent record before the given adjusted timestamp.

Hmmm, but then it must be an issue in my persistence file.

Let’s explain my setup:

  1. I have two window contacts which are both assigned to a group
Group:Contact:OR(OPEN, CLOSED) gContacts  "[MAP(windowContact.map):%s]" <door>
Contact Homematic_Sensor_Office_RightWindow_State "Office" <door> (gContacts)
Contact Homematic_Sensor_Bedroom_State "Bedroom" <door> (gContacts)
  1. The persistence stores every change
Items {
	gContacts* : strategy = everyChange
}
  1. I open the window in my office, can see in the log
Homematic_Sensor_Office_RightWindow_State changed from CLOSED to OPEN
gContacts changed from CLOSED to OPEN through Homematic_Sensor_Office_RightWindow_State

and the value OPEN is stored in DB

  1. I close the window in my office, can see in the log
gContacts changed from OPEN to CLOSED through Homematic_Sensor_Office_RightWindow_State
Homematic_Sensor_Office_RightWindow_State changed from OPEN to CLOSED

and the value CLOSED is stored in DB

This looks complettly fine for me. But if I now call Homematic_Sensor_Office_State.previousState() I’ll get the value CLOSED.

Honestly, this is completty weird for me! Or maybe I’m totally blind and can’t see the error above :sweat_smile:

Okay, so apart from a few milliseconds while the machinery chunters, the most recent record here (of the change) will be same as current Item (which will still be whatever it last changed to).
Other Items may be recorded with different strategies.

Yes. previousState goes and fetches the most recent record. Because you have chosen everyChange strategy, the most recent record will be from the last change to CLOSED.

The window is CLOSED.
The current state of the Item is CLOSED.
The previous state of the Item is CLOSED, because you chose to record it at the time of change.
The window was CLOSED at that point in time before now.

If you don’t open the window ever again, in a year’s time the previous state is still CLOSED which perhaps makes more sense?

The “previous” word is in relation to the Item. Things can previously have been in the same state as they are now.

If you want to know the most recent time that it was different to now, this is what previousState(true) is about.

Hi @rossko57,

Thanks a lot for your time and explanation! Makes sense how you described it. I don’t know whether I’m the only guy who has problems of understanding with the documentation saying
Gets the previous State of a persisted Item (returns HistoricItem) . This really sounds for me like the 2nd DB entry and not the recent one :wink:

Hi

sorry for replying to an old thread. I’m in the same situation.
Thanks to your explanations I understand the behaviour (I’m also using “every change” persistence)

But,
What if previousState(true) also don’t get the most recent different value? With some Items it seems to work with some not.

There are reports that this isn’t very reliable with all databases. I don’t think anyone has ever quantified this or provided evidence, much less open an issue for it.

I think I will go for a workaround as you also mentioned.
Maybe something like this:

EG_Eingang_Helligkeit.historicState((EG_Eingang_Helligkeit.previousState().timestamp.minusSeconds(2))).state