Power usage estimate from kWh with rules and mysql

Hi!

I’m using a mysensors LED pulse counter to measure energy consumption. I have my sensor battery powered and because of this I’ve decided to not measure Watts with the Arduino. This would require keeping track of time and thus the battery life would be quite poor. But why not calculate the power at the controller instead? I guess this would lead to a quite poor precision in the power values but I would get a better indication of when during the day I spent the most energy.

I’m really not that experienced in writing rules with the Openhab syntax and sadly also my sql knowledge is limited. I should mention that I’m using mysgl persistance storing kWh and count values. My mysql data should have everything needed to accomplish this (ratio of time and count differences) but I guess doing the math on the fly using rules could be one way of doing this which also eliminates an additional interface towards Python etc. .

Could someone point me in the right direction so I could get started on this. Or perhaps someone would advice me to go about this in a different way?

Thanks!

I do something similar with mine. I send the power in watts (you could do pulses if you like, but then multiply the result by watts per pulse) every 10 seconds via mqtt (helps with the timing and doesn’t flood mqtt). So that’s 6 updates every minute, or 3600 every hour. I then use sumSince.withTimeAtStartOfDay to give me a usage since the start of day. This value can be persisted as well.

rule "update power usage"
when
Item power changed
then
powerTotalDaily.postUpdate(power.sumSince(now.withTimeAtStartOfDay,"mysql")/(3600.0*1000.0))  //mulitply the divisor by 1000 to turn watts to kilowatts
end

It is important to persist only changes and not persist every minute as well.

Note: since I started this thread I’ve abandoned mysql in favor for Influxdb (and rrd4j for charts for the phone).

Hello again!

I’m trying to calculate the time between now and the latest change of an item. This time is then to be used to calculate the power from an energy reading, as explained in the first post some time ago. I finally got some time to try to implement this.

But for some reason which I can’t explain the timestamp for the previousState is not what’s expected. It appears as though an additional hidden change has occurred which adds a more recent timestamp to the persistence of the item. My feeling is that also reading from the persistence somehow creates new timestamps or changes to the item. From the log its clear that no additional changes are being made to the item.

The problem doesn’t always occur however but quite often, perhaps in 80 % of the cases. The middle part of the log shows a successful update. Also, the “divide by zero” ERROR has already been dealt with by doing the calculation in a different way.

I would very much appreciate some assistance with this problem. Please see the attached setup and hopefully valuable log of what’s happening.

Items file:

Number kWhCorr	"Energy Corr [%.1f kWh]"	<energy>
Number kWhPower	"Power [%.1f W]"		<energy>

Persistance (default service is set to Influxdb):

RRD4J: every minute
Influxdb: every change, restoreOnStartup

Rule:

rule "Energy counter - power calculation"
when
  Item kWhCorr changed
then 
  var lastkWh = kWhCorr.previousState(false,"influxdb").timestamp.time
  Thread::sleep(500) // sleep some time to make certain that the timestamp is retrieved in time?!
  logInfo("kWhPower", "now.millis: " + now.millis + " lastkWh: " + lastkWh)
  
  // Calculate time base
  var duration = (now.millis - lastkWh)
  var duration2 = Math::round(duration.floatValue()) // rounding is probably not necessary
  logInfo("kWhPower", "duration i ms: " + duration2)
  
  // Calculate energy base
  var Number energyDiff = ((kWhCorr.state as DecimalType) - (previousState as DecimalType)) * 1000 // 1000: kWh -> Wh
  var energyDiff2 = Math::round(energyDiff.floatValue()) // rounding is probably not necessary
  logInfo("kWhPower", "energyDiff i Wh: " + energyDiff2)
  
  // Calculate power
  var Number powerCalc = energyDiff2 * 3600 / (duration2 / 1000) // "* 3600": h -> s and "/ 1000": ms -> s 
  postUpdate(kWhPower, powerCalc)  
  
end

Log file:

10:04:35.816 [INFO ] [marthome.event.ItemStateChangedEvent] - kWh changed from 89.2840 to 89.4900
10:04:35.856 [INFO ] [marthome.event.ItemStateChangedEvent] - kWhCorr changed from 89.2840 to 89.4900
10:04:36.459 [INFO ] [ipse.smarthome.model.script.kWhPower] - now.millis: 1480842276453 lastkWh: 1480842275851
10:04:36.480 [INFO ] [ipse.smarthome.model.script.kWhPower] - duration in ms: 612
10:04:36.499 [INFO ] [ipse.smarthome.model.script.kWhPower] - energyDiff in Wh: 206
10:04:36.517 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Energy counter - power calculation': / by zero
/---/
10:25:34.986 [INFO ] [marthome.event.ItemStateChangedEvent] - kWh changed from 89.4900 to 89.6550
10:25:35.039 [INFO ] [marthome.event.ItemStateChangedEvent] - kWhCorr changed from 89.4900 to 89.6550
10:25:35.624 [INFO ] [ipse.smarthome.model.script.kWhPower] - now.millis: 1480843535616 lastkWh: 1480842275851
10:25:35.655 [INFO ] [ipse.smarthome.model.script.kWhPower] - duration in ms: 1259791 // success! Roughly 21 minutes
10:25:35.701 [INFO ] [ipse.smarthome.model.script.kWhPower] - energyDiff in Wh: 165
10:25:35.743 [INFO ] [marthome.event.ItemStateChangedEvent] - kWhPower changed from 400 to 471
/---/
10:52:57.003 [INFO ] [marthome.event.ItemStateChangedEvent] - kWh changed from 89.6550 to 89.8490
10:52:57.075 [INFO ] [marthome.event.ItemStateChangedEvent] - kWhCorr changed from 89.6550 to 89.8490
10:52:57.667 [INFO ] [ipse.smarthome.model.script.kWhPower] - now.millis: 1480845177661 lastkWh: 1480845177055 // darn it! 27 min between the "nows" are correct but lastkWh is evidently wrong! Why?
10:52:57.709 [INFO ] [ipse.smarthome.model.script.kWhPower] - duration in ms: 640
10:52:57.740 [INFO ] [ipse.smarthome.model.script.kWhPower] - energyDiff in Wh: 194
10:52:57.765 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Energy counter - power calculation': / by zero

Solved it. Its now working as expected. If others are interested in how I got it working, see the following change:

Old line:

var lastkWh = kWhCorr.previousState(false,"influxdb").timestamp.time

New line:

var lastkWh = kWhCorr.previousState(true).timestamp.time

The change from false to true is probably not what got it working but my guess is rather that it was the deletion of the persistence string. As I set Influxdb to be my default provider in runtime.cfg the string argument is not neccesary and this might in some way have optimized the fetching of values from the Influxdb database.

For some reason, after a restart of Openhab, the previousState is not working. Or I guess its working in some way but not as expected by me. For some reason the fetching of the previous timestamp receives a much more recent “millis” then I would like.

I guess it would be possible to work with a DateTime object instead of the timestamp used in this case. Could someone please try to assist me in determining the duration between changes using a DateTime object? I guess I would need to persist this “last change” item and then fetch the value rather then its timestamp.

Thanks!

Ok, solved it again. But I had to abandon the previousState method for calculating the elapsed time. I’m using the following code instead which uses a datetime item which is used as the reference time for calculating the elapsed time between power calculations.

rule "Energy counter - power calculation"
when
  Item kWhCorr changed
then 
  var lastChangeMilli = (kWhPowerDT.state as DateTimeType).calendar.timeInMillis
  var elapsedTime = now.millis - lastChangeMilli
  
  logInfo("kWhPower", "now.millis: " + now.millis + " lastChangeMilli: " + lastChangeMilli)
    
  // Calculate time base
  var duration = (now.millis - lastChangeMilli)
  var duration2 = Math::round(duration.floatValue())
  logInfo("kWhPower", "duration in ms: " + elapsedTime)
  
  // Calculate energy base
  var Number energyDiff = ((kWhCorr.state as DecimalType) - (previousState as DecimalType)) * 1000
  var energyDiff2 = Math::round(energyDiff.floatValue())
  logInfo("kWhPower", "energyDiff in Wh: " + energyDiff2)
  
  // Calculate power
  var Number powerCalc = energyDiff2 * 3600 * 1000 / elapsedTime
  postUpdate(kWhPower, powerCalc)
  postUpdate(kWhPowerDT, new DateTimeType())  
  
end

The approach is quite simple really, the only downside is that I have to use this additional datetime item kWhPowerDT which which is also being persisted. I can live with this I guess.

1 Like

Actually, the change from false to true is what likely did it. The true will return the most recent state that is different from the current state. This is likely skipping over the more recent one.

I recommend keeping the persistence name in the call. If you decide at a later date to change your default persistence engine you will avoid being forced to change all of your rules to stay on InfluxDB.

The everyChange strategy might be persisting the Item when it changes from NULL to whatever state it is restored to in restoreOnStartup. Without access to logs and the DB itself I’ve nothing better than this guess.

Hi Guys

I’m trying to use @Frasier rule, I modified some settings as they were deprecated but it seems I’ve got an issue populate the kWhPowerDT with the Date/Time.

I assume this item needs to be a DateTime type.


06:20:29.344 [ERROR] [untime.internal.engine.RuleEngineImpl] - Rule 'Energy counter - power calculation': Could not cast NULL to org.eclipse.smarthome.core.library.types.DateTimeType; line 5, column 26, length 32

Rule


rule "Energy counter - power calculation"
when
  Item gPowerUsage changed
then
  var lastChangeMilli = (kWhPowerDT.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli
  var elapsedTime = now.millis - lastChangeMilli

  logInfo("kWhPower", "now.millis: " + now.millis + " lastChangeMilli: " + lastChangeMilli)

  // Calculate time base
  var duration = (now.millis - lastChangeMilli)
  var duration2 = Math::round(duration.floatValue())
  logInfo("kWhPower", "duration in ms: " + elapsedTime)

  // Calculate energy base
  var Number energyDiff = ((gPowerUsage.state as DecimalType) - (previousState as DecimalType)) * 1000
  var energyDiff2 = Math::round(energyDiff.floatValue())
  logInfo("kWhPower", "energyDiff in Wh: " + energyDiff2)

  // Calculate power
  var Number powerCalc = energyDiff2 * 3600 * 1000 / elapsedTime
  postUpdate(kWhPower, powerCalc)
  postUpdate(kWhPowerDT, new DateTimeType())

end

If kWhPowerDT is not initialized (i.e. NULL) then you can’t cast it to DateTimeType. So you really need to check for NULL and UNDEF and handle that error case in the Rule.

Hi Rich,

I changed the item to this, which removed the error:

DateTime kWhPowerDT                                { channel="astro:sun:home:rise#start" }