Shelly - Power Meter resets total consumption on power loss

Hello there
:slight_smile:

OH 2.5.7
shelly binding 2.5.8
LInux system
persistence service is RRD4J

i implemented the above mentioned rule:

rule "ShellyEM rotex LWP PowerLoss"
when
    Item RotexHPSULwpTotalKWH changed
then

//    logInfo("ShellyEM rotex LWP PowerLoss", "RotexHPSULwpTotalKWH changed")
//  neu  0 kWh   :  alt 55.432 kWH

if (RotexHPSULwpTotalKWH_persist.state == NULL)  // persistent neu
{
    var Number newTotal = (RotexHPSULwpTotalKWH.state as DecimalType)
    // store the current value in "persist"
    RotexHPSULwpTotalKWH_persist.postUpdate( newTotal )
    logInfo("ShellyEM rotex LWP PowerLoss", "persistent missing totalKWH  " + (newTotal.toString) )
}
else
{
   // calc the delta and add it in "persist"

if ( RotexHPSULwpTotalKWH.previousState() != NULL)   // is there persistence to compare old and new?
{

logInfo("ShellyEM rotex LWP PowerLoss", "get delta prev LwpTotalKWH" )
logInfo("ShellyEM rotex LWP PowerLoss", "current "
   + String.format("%.4f",  (RotexHPSULwpTotalKWH.state as                 DecimalType).floatValue ) + " prev: "
   + String.format("%.4f",  (RotexHPSULwpTotalKWH.previousState().state as DecimalType).floatValue ) )
var float delta = (RotexHPSULwpTotalKWH.state as DecimalType).floatValue - (RotexHPSULwpTotalKWH.previousState().state as DecimalType).floatValue
logInfo("ShellyEM rotex LWP PowerLoss", "delta: " +    String.format("%.4f",  delta ) )
if ( delta < 0.0 ) {
    logInfo("ShellyEM rotex LWP PowerLoss", "power lost ! New RotexHPSULwpTotalKWH_persist is base : " + (RotexHPSULwpTotalKWH_persist.state as N
    delta = (RotexHPSULwpTotalKWH.state as DecimalType).floatValue
} else {
    logInfo("ShellyEM rotex LWP PowerLoss", "normal RotexHPSULwp-totalKWH  " + (RotexHPSULwpTotalKWH.state as DecimalType).toString + " persist o
}

RotexHPSULwpTotalKWH_persist.postUpdate( (RotexHPSULwpTotalKWH_persist.state as DecimalType).floatValue + delta)
    logInfo("ShellyEM rotex LWP PowerLoss", "persist new: " + (RotexHPSULwpTotalKWH_persist.state as Number).toString)

}  // RotexHPSULwpTotalKWH previous  NULL
else
{
    logInfo("ShellyEM rotex LWP PowerLoss ",  "RotexHPSULwpTotalKWH.previousState is NULL - no delta by now")
}
}

end

in general it is working:

  • if persistence of RotexHPSULwpTotalKWH_persist deleted and OH restarted then RotexHPSULwpTotalKWH_persist will be set to current value
  • if RotexHPSULwpTotalKWH reset in SHELLY device to zero then this will be noted

however, calculation of “delta” of current state to previousState().state is sometimes wrong by one unit “Wh”

delta is “0.000” instead of “0.001” or “0.004” instead of “0.003” e.g.

2020-08-27 14:27:01.276 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH changed from 0.013 to 0.014
2020-08-27 14:27:01.286 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH_persist changed from NULL to 0.014

2020-08-27 14:27:01.283 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - persistent fehlte totalKWH  0.014
2020-08-27 14:30:01.058 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH changed from 0.014 to 0.015
2020-08-27 14:30:01.063 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - get delta prev LwpTotalKWH
2020-08-27 14:30:01.066 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - delta: 0.001
2020-08-27 14:30:01.071 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH_persist changed from 0.014 to 0.015
2020-08-27 14:30:01.072 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - normal RotexHPSULwp-totalKWH  0.015 persist: 0.015
 
--> SAME 


2020-08-27 14:31:00.412 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - get delta prev LwpTotalKWH
2020-08-27 14:31:00.413 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH changed from 0.015 to 0.018
2020-08-27 14:31:00.416 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - delta: 0.003999999
2020-08-27 14:31:00.421 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - normal RotexHPSULwp-totalKWH  0.018 persist: 0.015
2020-08-27 14:31:00.423 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH_persist changed from 0.015 to 0.018999998

--> STRANGE 0.004 instead of 0.003

2020-08-27 14:31:59.428 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH changed from 0.018 to 0.020
2020-08-27 14:31:59.439 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH_persist changed from 0.018999998 to 0.020999998
2020-08-27 14:31:59.432 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - get delta prev LwpTotalKWH
2020-08-27 14:31:59.435 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - delta: 0.0020000003
2020-08-27 14:31:59.439 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - normal RotexHPSULwp-totalKWH  0.020 persist: 0.020999998

--> DELTA .001 ?!?


2020-08-27 15:34:00.621 [vent.ItemStateChangedEvent] - RotexHPSULwpTotalKWH changed from 0.097 to 0.098
2020-08-27 15:34:00.792 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - delta: 0.0
--> STRANGE 0.000 delta shall be 0.001

2020-08-27 15:34:00.794 [INFO ] [.script.ShellyEM rotex LWP PowerLoss] - normal RotexHPSULwp-totalKWH  0.098 persist: 0.100999996

noting that the first time and many time in between it is working - i.e. proxy persistence item value and orignal value are same (if started from zero)

but why not working ? effect directly visible after 30mins
concurrency?
using wrong previousState( false/true)???

using RRD by now - wanting to use MySQL

thanks in advance,

BR/
Peter

chart-persist

There are a few things that I can think of:

  • When there is a power loss, you essentially throw away the delta since the power loss. I think you want to add the absolute value of delta to RotexHPSULwpTotalKWH when the delta is negative.

  • You are working with small numbers. Computers are generally pretty bad and dealing with small floating point numbers. There are simple some numbers that are impossible to represent given the way programming languages typically represent floating point numbers. I wouldn’t be surprised if 0.021 is one of them, which is why you see 0.020999998 This will introduce errors which build up over time. I think BigDecimal is able to avoid some of this so instead of forcing stuff to be a primitive float, keep them as BigDecimal. Every place you have .floatValue cast the value to Number instead. This will keep it as BigDecimal and potentially avoid this growing loss of precision. In scientific computing, the values will actually be converted to integers instead of relying on unreliable floating point values. Some languages also support fixed precision numbers which are reliable and could be used. I don’t think any of these options are available for you in this case though.

  • You are using rrd4j which decimates the data as it ages. For example, it will take ten values and replace them with one value that is the average of the ten. I don’t think this is the case for this example as I think it doesn’t start doing that until the data gets over a day old (@opus can confirm). This too will introduce errors as the data ages.

I don’t know if any of these are relevant to what you are seeing or not but they are worth exploring.

The default setup will start to give averages as soon as you are asking for period longer then 8 houres starting backwards from now.

1 Like

Hello Rich,

i do like always your great and comprehensive answers and explanations… :slight_smile:

if the power meter resets to ZERO and old value would be e.g. 6 than the Delta would be -6
if i directly adding the absolute value of delta it would double the current value (==12)

if new power meter set to 4 (before 6) than delta is -2: if delta < 0.0 then i’m using the new value of RotexHPSULwpTotalKWH as delta ( perhaps still 0.0, perhaps start increasing already)

-> so the algorithm is working in general

Can you pls send me the whole working rule because I think there are some parts missing?

BR
Daniel

1 Like