Design Pattern: Group Based Persistence

It would probably be best to open a new thread

I donā€™t use rrd4j either but maybe this thread can help you troubleshoot if you have a data issue or just a charting issue.

@rlkoshak, I noticed that you persist all items on startup, and Iā€™ve done the same. However, the Persistence section of the Configuration Guide says:

It is usually not necessary to restore all Items since there is a good chance that they are no longer accurate (switches may have been toggled, sensor values are likely to have changed), and the restoration may result in unwanted rule actions.

It seems that persisting all items is fairly common in the OH community. Couldnā€™t persistence be limited to items that canā€™t be updated when OH starts up (e.g. unbound items)? It would theoretically be more accurate and have a very minor side benefit of reducing writes to the storage medium.

This is all moot if OH doesnā€™t actually update items on startup. I thought I might test it later by turning off persistence and seeing which items become NULL or UNDEF after I restart, but I wonder if Iā€™m missing the bigger picture.

You mean restore all Items at startup?

Persistence would have no way of knowing. And there are cases where you may want to restore Items even if they are bound/linked to something. And if you know what you are doing and what to expect, there is nothing inherently wrong with restoring all Items at startup. It is a dangerous option, yes, but not an unuseful option.

When OH starts up or loads a .items file, all items get initialized to NULL. If you have persistence set up and configured to restoreOnStartup, OH will then update the Item with whatever the most recent value stored in the database happens to be. If your Rules happen to already be running these updates and changes might cause Rules to trigger and depending on what your assumptions are in those Rules and what those Rules do that may be undesirable behavior. Or it may not matter at all. Or you may have checks in those Rules to handle this case.

Itā€™s impossible to preemptively detect and understand the difference between all of these cases so the docs warn the user to be deliberate instead of lazy. The correct solution (i.e. restore all or just some) is entirely dependent on your specific context.

For this design pattern I did the lazy thing so I wouldnā€™t have to go into all of this which is really not the main focus of the DP. I should also note that this DP was written before that sentence was added to the docs. :wink:

UNDEF is a different state entirely and is used by bindings or Rules to indicate that we donā€™t know what state the Item happens to be in. For example, if the MQTT Broker Thing goes offline, the MQTT binding will set all the Items linked to Channels on Things attached to that Broker will get set to UNDEF. By default the Expire binding will set an Item to UNDEF if you donā€™t specify the state yourself.

Yep, I should have been referring to restoring items, not persisting them. Thanks for picking up on that.

This is really what I was trying to ask. But now that I think of it, the more direct question is: when OH starts up and initializes items, does it ask them for their current states? If so, restoring seems unnecessary for items that can respond to requests, or for items that post frequent updates to OH.

That depends entirely on the binding-device combination. There is no built-in OH mechanism to ā€˜refreshā€™ all item states because that wouldnā€™t work for many device types.
So, for items that receive very frequent updates of the status from linked devices, there really isnā€™t a need for RestoreOnStartup, but if the device only sends its status when it changes, it would certainly help to persist its status.
The main purpose of RestoreOnStartup is for OH not to lose state in case of a restart for instance.

1 Like

No, it does not. The Item will remain NULL until such time something changes that. That something can be restoreOnStartup, the binding, a Rule, or manual interaction from a UI.

You might be surprised to learn that technologies that support having their current state reported on demand is in the minority. But indeed, for Items linked to devices that do get immediately updated you would not want to use restoreOnStartup in the off chance that the binding updates the Items before persistence gets a chance to.

1 Like

I am a little surprised by that. Guess Iā€™ll find out which of my devices do it later tonight, with the expectation that the answer will be ā€œfew to noneā€. Thanks!

rlkoshak thanks so much for this contribution and all your others!

One point to note that caught me up for a while. If you are using more than 1 persistence service and you want to call a specific one to use it has to be the last string in the parentheses!
e.g. item.previousState(false,ā€œrrd4jā€) NOT item.previousState(ā€œrrd4jā€)
(note item.previousState() works and uses the default persistence service and I believe it assumes False input. Specifying True returns last state different to current state).
Just noting on this thread as this seems the go to thread for setting up persistence apart from the official docs.

hi i am looking for the way to use my db to learn haw we use the lights and then replay them when we are not home. say we are away for longer then a week. so i can turn on leave mode. and it will run it as if we where there. ?

Bit of a random thread to post to. Are people shy of making new threads?

2 Likes

Rich, could I ask why you donā€™t use rrd4j any more - is there a better way for charting?

It doesnā€™t support Switches and Contacts and doesnā€™t work with external graphing packages.

Though I believe HABPanel has pretty good charting and OH 3ā€™s UI will come with some better charting options than the defaultsold openHAB 1/2 charting.

1 Like

Hi Iā€™m struggling to receive the average temperature of a room using groups.

if (myActualTemp !== null) {
logInfo (myRuleName,"Item: " + myActualTemp.name.toString)
var Number n_AvgTempDay=myActualTemp.averageSince(now.withTimeAtStartOfDay,ā€œmysqlā€)
logInfo (myRuleName,"Status: " + myActualTemp.state.toString)
logInfo (myRuleName,"Durchschnitt: " + n_AvgTempDay.toString) }

I assume that I cannot cast the average temperature using the myActualTemp item. The return value of n_AvgTempDay=myActualTemp.avgā€¦ is null.
How to solve?

Martin

Bear in mind that we donā€™t know what kind of thing myActualTemp is.
You mention that is an Item, perhaps it is a Group type Item?

The obvious candidate for a no-data return would be that you did not persist any data. You can persist the value of Group Items, if that is what we are dealing with here. First you must give the Group a state, by defining a type and aggregation function for it. Then you must explicitly include the Group Item in your xxx.persist file. No ā€œwildcardsā€ * because they are not wildcards and mean something special to persistence.

as an aside -

this is not really a sensible test for an Item

Thanks, I missed to provide some more information about this.
I do have for each room an item storing the actual temperature. This value is persisted at every change.
All of them are assigned to a group called gRoomActualTemp.

Number    Wohnzimmer_IstTemp    "Wohnzimmertemp [%.1f Ā°C]"          <temperature>   (gRoomActualTemp)   
Number    Wohnzimmer_IstTemp_DailyAvg       "Durchschnittliche Zimmertemperatur" <temperature>  (gRoomDailyAvgTemp)

In a rule, once a day, I want to calculate the average temperature for the room. In order to ease the coding I use the following code:

try{
	gRoomActualTemp.members.forEach[myActualTemp | 
	if (myActualTemp !== null) {
		var Number n_AvgTempDay=myActualTemp.averageSince(now.withTimeAtStartOfDay,"mysql")
		logInfo (myRuleName,"Durchschnittliche Temperatur im Zimmer " + myActualTemp.label.toString + " ist " + n_AvgTempDay.toString)
		val myDailyAvgTemp = gRoomDailyAvgTemp.members.findFirst[ myTempItem | myTempItem.name == myActualTemp.name.toString + "_DailyAvg"] as NumberItem
		logInfo(myRuleName,"durchschnittliche " + myActualTemp.label + " gesetzt auf " + n_AvgTempDay.toString)
		if (myDailyAvgTemp !== null) {myDailyAvgTemp.postUpdate(n_AvgTempDay)}
	}]

But I think that this is not working, but not sure how to solve using a pattern instead of considering each item separate in the rule.

Martin

Youā€™ve placed helpful logInfo, but not shown us the result. Maybe thereā€™s error messages? We cannot see over your shoulder.
for-eaching a Group can be a bit funny about the objects that you get, but letā€™s see the detail. I think the generic Item returned does support persistence functions.

logInfo (myRuleName,"Durchschnittliche Temperatur im Zimmer " + myActualTemp.label.toString + " ist " + n_AvgTempDay.toString)

This lead to the following error in the logger:

Error: cannot invoke method public java.lang.String java.lang.Object.toString() on null

I found out that the error is cause by the variable n_AvgTempDay as it seems this is null. The item myActualTemp is working, I can access the state, the label and the name without problems.

Martin

Okeydoke, as that is derived by
var Number n_AvgTempDay=myActualTemp.averageSince(now.withTimeAtStartOfDay,"mysql")
the next logical step is to find out if there is any data for that to operate on.

Use REST API to see if the Item has any persistence records. Youā€™ll need to log out the name of your Item, like you did in your earlier post, to make the same comparison.

If it does, check the timestamps to see if there is any data for the given timespan, midnight to now.

I just check via MySQL in the database and via Rest, data is available

{
    "name": "Wohnzimmer_IstTemp",
    "datapoints": "47",
    "data": [
        {
            "time": 1597010501000,
            "state": "25.48"
        },
        {
            "time": 1597010671000,
            "state": "25.49"
        },
        {
            "time": 1597013810000,
            "state": "25.39"
        },
        {
            "time": 1597016828000,
            "state": "25.42"
        },
        {
            "time": 1597020137000,
            "state": "25.41"
        },
        {
            "time": 1597020649000,
            "state": "25.43"
        },
        {
            "time": 1597023577000,
            "state": "25.45"
        },
        {
            "time": 1597026866000,
            "state": "25.48"
        },
        {
            "time": 1597035359000,
            "state": "25.51"
        },
        {
            "time": 1597036883000,
            "state": "25.48"
        },
        {
            "time": 1597037325000,
            "state": "25.52"
        },
        {
            "time": 1597037756000,
            "state": "25.45"
        },
        {
            "time": 1597040864000,
            "state": "25.22"
        },
        {
            "time": 1597043422000,
            "state": "24.94"
        },
        {
            "time": 1597043933000,
            "state": "25.05"
        },
        {
            "time": 1597047072000,
            "state": "25.33"
        },
        {
            "time": 1597047243000,
            "state": "25.38"
        },
        {
            "time": 1597049549000,
            "state": "25.89"
        },
        {
            "time": 1597050401000,
            "state": "26.06"
        },
        {
            "time": 1597051284000,
            "state": "26.4"
        },
        {
            "time": 1597053199000,
            "state": "26.91"
        },
        {
            "time": 1597053661000,
            "state": "27.1"
        },
        {
            "time": 1597054173000,
            "state": "27.35"
        },
        {
            "time": 1597056799000,
            "state": "26.84"
        },
        {
            "time": 1597057150000,
            "state": "26.77"
        },
        {
            "time": 1597059637000,
            "state": "26.53"
        },
        {
            "time": 1597060249000,
            "state": "26.43"
        },
        {
            "time": 1597063267000,
            "state": "26.39"
        },
        {
            "time": 1597064711000,
            "state": "26.34"
        },
        {
            "time": 1597066456000,
            "state": "26.47"
        },
        {
            "time": 1597066717000,
            "state": "26.68"
        },
        {
            "time": 1597067660000,
            "state": "27.22"
        },
        {
            "time": 1597070026000,
            "state": "27.35"
        },
        {
            "time": 1597070217000,
            "state": "27.34"
        },
        {
            "time": 1597072383000,
            "state": "26.83"
        },
        {
            "time": 1597073185000,
            "state": "27.0"
        },
        {
            "time": 1597076283000,
            "state": "27.14"
        },
        {
            "time": 1597079733000,
            "state": "27.04"
        },
        {
            "time": 1597081488000,
            "state": "27.02"
        },
        {
            "time": 1597081608000,
            "state": "27.17"
        },
        {
            "time": 1597081618000,
            "state": "27.15"
        },
        {
            "time": 1597081669000,
            "state": "27.21"
        },
        {
            "time": 1597081769000,
            "state": "27.12"
        },
        {
            "time": 1597082381000,
            "state": "27.05"
        },
        {
            "time": 1597083273000,
            "state": "26.88"
        },
        {
            "time": 1597084617000,
            "state": "26.6"
        },
        {
            "time": 1597086322000,
            "state": "26.46"
        }
    ]
}