Can't figure out calculation with "previousState()"

Hello!
I have an item the gives me the power consumptionTotal (accumulated consumption since power loss on Shelly Device) and i’d like to store power consumption on every update of this item.

approach: Shelly_EM3_consumption = Shelly_EM3_accTotal - Shelly_EM3_accTotal.previousState

Here’s some info on my setup

  • Platform information:
    • Hardware: Rpi 4/4GB
    • OS: raspian (buster) / openhabianpi
    • openHAB version: 2.5-10

and here’s my code:

Number      Shelly_EM3_accTotal     "Shelly em3 Gesamtverbrauch"  (gShelly)   {channel="shelly:shellyem3:xxx:device#accumulatedWTotal"}
Number      Shelly_EM3_consumption                                (gShelly)
rule "Power Consumption EM3"
when
    Item Shelly_EM3_accTotal changed
then
/// some logs to check the previous state
    logInfo("NEW", Shelly_EM3_accTotal.state.toString)
    logInfo("PREVIOUS MAPDB", Shelly_EM3_accTotal.previousState(false,"mapdb").state.toString)
    logInfo("PREVIOUS INFLUXDB", Shelly_EM3_accTotal.previousState(false,"influxdb").state.toString)
        
    Shelly_EM3_consumption.postUpdate((Shelly_EM3_accTotal.state as Number) - (Shelly_EM3_accTotal.previousState(false,"mapdb").state as Number))

end

apparently there’s something wrong with my code because:

2020-12-23 15:18:01.081 [vent.ItemStateChangedEvent] - Shelly_EM3_accTotal changed from 225.478 to 225.501
2020-12-23 15:18:01.659 [INFO ] [g.eclipse.smarthome.model.script.NEW] - 225.501
2020-12-23 15:18:01.665 [INFO ] [marthome.model.script.PREVIOUS MAPDB] - 225.501
2020-12-23 15:18:01.684 [INFO ] [thome.model.script.PREVIOUS INFLUXDB] - 225.501

shouldn’t previousstate be 225.478?
tried different presistence services… but mapdb should be ok, right?
mapdb: * : strategy = everyChange,restoreOnStartup
influxdb: gShelly* : strategy = everyChange, everyMinute

later, unchanged config:

2020-12-23 15:20:30.880 [vent.ItemStateChangedEvent] - Shelly_EM3_accTotal changed from 225.524 to 225.547
2020-12-23 15:20:30.890 [INFO ] [g.eclipse.smarthome.model.script.NEW] - 225.547
2020-12-23 15:20:30.901 [INFO ] [marthome.model.script.PREVIOUS MAPDB] - 225.547
2020-12-23 15:20:30.927 [INFO ] [thome.model.script.PREVIOUS INFLUXDB] - 225.524

influxdb this time gives back the previous state, mapdb doesn’t?
Shelly_EM3_consumption is not calculated as it should be according to my rule.

later again, same config:

2020-12-23 15:22:03.045 [vent.ItemStateChangedEvent] - Shelly_EM3_accTotal changed from 225.569 to 225.592
2020-12-23 15:22:03.064 [INFO ] [g.eclipse.smarthome.model.script.NEW] - 225.592
2020-12-23 15:22:03.073 [INFO ] [marthome.model.script.PREVIOUS MAPDB] - 225.592
2020-12-23 15:22:03.094 [INFO ] [thome.model.script.PREVIOUS INFLUXDB] - 225.569
2020-12-23 15:22:03.131 [vent.ItemStateChangedEvent] - Shelly_EM3_consumption changed from 0.000 to 0.023

influxdb has the right “previous” state (mapdb still doesn’t) and the calculation is done according to the rule…

now i don’t even know where to begin as there seem to be 2 problems:

  1. i’m not able to get the correct previousstate of my persisted item.
  2. there’s something wrong with my calculation.

any help would be appriciated because i’ve already lost (invested?) 3 hours for this (apparently not so) simple math problem…

best regards
Peter

Only if somehow your rule ran before MapDB managed to save the current state. Remember, MapDB only stores one value per Item. When your Item changed, it stored that state. That means in a rule you will almost always getting the current state of the Item when requesting any data from MapDB in the rule.

You don’t need persistence at all for this. When a rule triggers from a changed trigger, there is a previousState implicit variable that lets you see what the Item changed from.

ok, so previousState() (with persistence) doesn’t really make sense in rules, right?

i’ve tried with previousState, first i got i wrong:
logInfo("POWER CONSUMPTION", Shelly_EM3_accTotal.previousState.state.toString)

it’s much easier :slightly_smiling_face:
logInfo("POWER CONSUMPTION", previousState)

thank you rich!

just to be sure, this rule should do the trick (?):

rule "Power Consumption new"
when
    Item Shelly_EM3_accTotal changed
then
    Shelly_EM3_consumption.postUpdate((Shelly_EM3_accTotal.state as Number) - (previousState as Number))
end

with influxdb.persist
Shelly_EM3_consumption : strategy = everyUpdate

so everytime the value of Shelly_EM3_accTotal gets bigger presistence should write the delta to influxdb, (even if the delta is the same as before).

If using a database other than MapDB, when you pass true in the call to lastUpdate it will retrieve the last update for when the Item was different from it’s current state which could be useful in many circumstances.

Try it and find out. Looks reasonable.

1 Like

I took a copy of the above rule and I’m not sure if something is wrong but the value I’m getting from subtraction seems a little out.

rule "Shelly Power Consumption"
when
    Item Power_mains_Total_Energy_Consumption changed
then
	logInfo("Energy total", Power_mains_Total_Energy_Consumption.state.toString)
	logInfo("Energy prevvalue", (previousState as Number).toString)
    logInfo("Energy diff1", ((Power_mains_Total_Energy_Consumption.state as Number) - (previousState as Number)).toString)
end

2023-05-23 20:01:01.519 [INFO ] [enhab.core.model.script.Energy total] - 111.289 kWh
2023-05-23 20:01:01.521 [INFO ] [b.core.model.script.Energy prevvalue] - 111.278 kWh
2023-05-23 20:01:01.522 [INFO ] [enhab.core.model.script.Energy diff1] - 39600

This is what happens when you go out of your way to try to strip of the units. Keep everything with units and it should make more sense. In this case

(Power_mains_Total_Energy_Consumption.state as QuantityType).minus(previousState as QuantityType)

That will give you a result with units.

Also, there is no guarantee that Power_mains_Total_Energy_Consumption hasn’t changed state between triggering of the rule and when that calculation occurred. Use the implicit variables instead.

    val total = newState as QuantityType
    val prev = previousState as QuantityType
    val diff = newState.minus(previousState)

Note, in the other languages handling QuantityTypes is often easier. For example, in JS Scripting there’s quantityState on the Item Object which casts/converts the value for you.

thanks for the reply I’m now getting some errors:

val total = newState as QuantityType
val prev = previousState as QuantityType
val diff = total.minus(previousState)
QuantityType is a raw type. References to generic type QuantityType<T> should be parameterized
QuantityType is a raw type. References to generic type QuantityType<T> should be parameterized

2023-05-24 20:48:06.349 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'shelly-1' failed: 'minus' is not a member of 'org.openhab.core.library.types.QuantityType'; line 7, column 16, length 26 in shelly

Not sure if this better but I’m getting close just getting some issues with overflow of digits

rule "Shelly Power Consumption"
when
    Item Power_mains_Total_Energy_Consumption changed
then
	val curr_total = (newState as Number).floatValue //QuantityType<Power>
    val prev_total = (previousState as Number).floatValue // QuantityType<Power>
    val diff = curr_total - prev_total
	logInfo("Energy total","current= "+curr_total.toString + ", prev= "+ prev_total.toString + ", delta:"+ diff.toString)

2023-05-24 21:21:00.998 [INFO ] [enhab.core.model.script.Energy total] - current= 154.664, prev= 154.593, delta:0.070999146
2023-05-24 21:22:03.371 [INFO ] [enhab.core.model.script.Energy total] - current= 154.737, prev= 154.664, delta:0.07299805

If you want less digits, you can just use

String.format("%.3f", diff)

or if you want rounded values you could use Math.round on curr_total and prev_total so the diff should be only 3 digits too:

Math.round((newState as Number).floatValue*1000)/1000

Rules DSL can be such a pain sometimes.

Try QuantityType<?>.

I’ve got it working now.
for reference below
however I’m wondering what units to save as kWh or Wh and how do I set that in the item?
ideally I would like to save in Watt Hours

rule "Shelly Power Consumption"
when
    Item Power_mains_Total_Energy_Consumption changed
then
	val curr_total = (newState as Number).floatValue 
    val prev_total = (previousState as Number).floatValue 
    val power_interval = curr_total - prev_total
	//val power_interval = Math.round((curr_total - prev_total).floatValue*1000) 
	logInfo("Energy total","current= "+curr_total.toString + " , prev= "+ prev_total.toString + " , delta:"+  String.format("%.3f", power_interval) + " kWh")
	
	Power_mains_energy_total.postUpdate(String.format("%.3f", power_interval));
	   
end

ok I think I will take the plunge into JS now… how do I get Quantity value for these in the trigger

    var curr_total = event.newState   // 51.724 kWh
    var prev_total = event.oldState  // 51.788 kWh

I think it depends on whether you are in the UI or file based.

In the UI, this is one of the places where you have the raw Java objects. But I think you can just do the following:

    var curr_total = Quantity(event.newState);   // 51.724 kWh
    var prev_total = Quantity(event.oldState);  // 51.788 kWh

If that doesn’t work, it should and we should for an issue. in the mean time this will work.

    var curr_total = Quantity(event.newState.toString());  // 51.724 kWh
    var prev_total = Quantity(event.oldState.toString());  // 51.788 kWh

Thanks, but just checking if I need to include any libraries for that function in oh 3.4.4

You might need to install openhab-js manually. I can’t remember whether Quantity was added before it after 3.4 release.

it looks like its installed but that function isn’t defined in my version

023-05-30 17:07:59.884 [ERROR] [hab.automation.script.file.mytest.js] - Failed to execute rule test-Energy-Consumption-05f196b0-9352-471a-8408-87b940d90967: ReferenceError: "Quantity" is not defined: ReferenceError: "Quantity" is not defined
        at <program> (mytest.js:17)
        at _run (webpack://openhab/./node_modules/openhab/rules/rule-builder.js?:47)
        at execute (webpack://openhab/./node_modules/openhab/rules/rule-builder.js?:91)
        at doExecute (webpack://openhab/./node_modules/openhab/rules/rules.js?:242)

installed updated version using
cd $OPENHAB_CONF/automation/js
npm i openhab

code:

	var curr_total = Quantity(event.newState);
	console.log("new="+event.newState +" q="+curr_total.float)

Also make sure to enable OH to configure JS Scripting to not use the built in library under Settings → JavaScript Scripting