Rounding/stabilize changing numbers?

I’ve got an item that’s a bit too sensitive. :blush:
And this resuls in not so nice graphs (high detail). See below for an example.

I’m trying to find a way that my rule isn’t been triggerd so quickly. So it’s result in a more stable graph.
The involved values lay between 0.33 and 0.77. So in the end, I want to calculate a (stable) indication percentage.

  1. Is there a way where I can easily complete the numbers with fe 0.05 steps instead of 0.01 steps?
    Something like:

    var Number Meter3b = Meter3.RoundingWith0,5steps :blush:

  2. Or can I use something where the value must change with more then 0.05 from the last step(s).
    But of course, in this case it should work if the number rises slowly (fe 0.33 > 0.35 > 0.37 > 0.39 > 0.40 …)

  3. Other suggestions?

2020-07-03 12:07:21.588 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:13:21.586 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:14:21.594 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:26:21.598 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:27:21.599 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:29:21.604 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:30:21.613 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:31:21.602 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:32:21.620 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:42:21.620 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:43:21.624 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:48:21.632 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:49:21.631 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:51:21.634 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:52:21.638 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36
2020-07-03 12:58:21.645 [vent.ItemStateChangedEvent] - Meter3 changed from 0.36 to 0.33
2020-07-03 12:59:21.652 [vent.ItemStateChangedEvent] - Meter3 changed from 0.33 to 0.36

Day chart:
chart

Week chart:
chart (1)

You will need to create a proxy item. The proxy item is what gets saved to the database. Create a rule that triggers when the sensor item changes. Only update the proxy item if the sensor reading is different enough from the current value of the proxy item.

When using Grafana to chart, there are all sorts of ways to smooth out a noisy graph without changing the data.

2 Likes

You can create a JS transformation to do the rounding and then use it as a profile. BTW, I am working on a Jython transformation service!

Unfortunately, transform profiles do not (yet) work with Number type Items, which defeats the object here.

Depending on the binding, it might be possible to transform at the Thing level; but that probably won’t reduce update rate, if that is an objective too. That’s limited to rounding - you could not do averaging as there is no way to “store” or access old/last values.

1 Like

This is what I cooked:

rule "test"                                                                                      // Rule to check if difference is more then 0,03
when
        Item Meter3 changed
then
        // Calculation of things
              val Number valueNow  = Meter3.state as Number                                      // Value right now
              val Number valueJust = Meter3.previousState.state as Number                        // Previous value
              val Number valueOld  = Meter3.historicState(now.minusMinutes(30)).state as Number  // Value long time ago
              var valueDiff        = valueNow - valueJust
              val valuePosi        = if (valueDiff < 0)  ( valueDiff * -1 ) else valueDiff       // Change negative to positive
              var valueDiff2       = valueNow - valueOld
              val valuePosi2       = if (valueDiff2 < 0)  ( valueDiff2 * -1 ) else valueDiff2    // Change negative to positive
        if ( valuePosi <= 0.03 && valuePosi2 <= 0.03 )  {                                        // Differences (short & long) are small
            logInfo("TEST", "Value remains" )
            }
        else {
            var valueRounded = valueNow
            logInfo("TEST", "New Value becomes " + valueRounded )
            }
end

If feels a bit much for what I need?
So if somebody can simplify it, I’m curious. :wink:

Use a the proxy Item as the previous value. I wouldn’t bother with the 30 minute old value. You really only care how different the current reading is from the last one you accepted.

rule "test"
when
    Item Meter3 changed
then

    // If we don't have a good previous value, record the current one and return
    if(Meter3Proxy.state == NULL || Meter3Proxy.state == UNDEF) {
        Meter3Proxy.postUpdate(Meter3.state)
        return;
    }

    // Calculate the difference between this reading and the last accepted reading.   
    val valueNow = Meter3.state as Number
    val prevValue = Meter3Proxy.state as Number
    val delta = Math:abs((prevValue - valueNow).floatValue)

    // If the delta is large enough, update the proxy Item
    if(delta > 0.03) {
        logInfo("TEST", "Change is big enough, recording new value")
        Meter3Proxy.postUpdate(valueNow)
    }
    else {
        logInfo("TEST", "Change is too small, ignoring the change")
    }
end

Meter3Proxy will only be updates when Meter3 changes more than 0.03 from Meter3Proxy’s current state and ignores smaller changes.

1 Like

Thanks for your input !!!

  • You won’t bother about the 30 minute old value?
    How will it act in case that the input slow rise (with fe steps 0.1 > 0.2 > 0.3 > 0.4 > 0.5 > 0.6 > …)?

  • And for my understanding, why do you create a proxyitem instead of using the previousState?

Let’s say we will only allow it to be saved to the DB if its a raise of 0.2 or more. Given the above:

Meter3 Meter3Proxy Conditional What happens
0.1 NULL Meter3Proxy.state == NULL Meter3Proxy.postUpdate(Meter3.state)
0.2 0.1 ` 0.2 - 0.1
0.3 0.1 ` 0.3 - 0.1
0.4 0.3 ` 0.4 - 0.3
0.5 0.3 ` 0.5 - 0.3
0.6 0.5 ` 0.6 - 0.5
0.7 0.5 ` 0.7 - 0.5

The `Meter3Proxy column is what gets saved to Persistence.
Who cares what the value was 30 minutes ago? What we care about is the most recent value accepted and saved in the database and that is the state of the Proxy Item. And as you can see, because we compare the current reading to the Proxy instead of the previous value, it doesn’t matter how slowly the reading changes, once it changes enough the value is recorded. If you only look at the previousState than those gradual changes will be missed. If you look back 30 minutes, all you know is the new reading is different from what it was half an hour ago but there could have been drastic between then and now that would be completely missed.

Because we care about the most recent value saved in the database, not the most recent value received. Therefore there needs to be a way to separate the changing of the Item with the storing of the values to the database. The easiest way to describe how to do that is to use a Proxy Item (there are alternatives but it’s more complex to explain).

You create a Proxy Item to store the sensor reading. This Item is the one configured with Persistence and it’s this Item that gets used by your other Rules and on your sitemap and such. The Item that is linked to the sensor is not configured with Persistence. Then, with the above Rule, the sensor reading has to change more than 0.03 from the state of the Proxy Item before the Rule updates the Proxy Item, and therefore the value gets saved to Persistence.

If you do use previousState than you very much do run into the problem where the values very slowly change. And you don’t care how much the reading is different from the previous reading, you care how different it is from the most recent reading that was actually saved to the database.

1 Like

I really appreciate the time you putted in!
And thank you very much for your excellent explanation !!!

We don’t say it enough, but thank you!