Jython rule to get time of last update for boiler gas consumption

How can I get the number of seconds since the last update of an item in Jython?

I’m working on some code to track the gas consumption of my boiler. I have a boiler interface that shows the percentage that the burner is running at. Together with the maximum heating output of the boiler (30.6kw) and it’s efficiency (92%) this should enable the gas consumption to be calculated. For example, if the burner starting point is off, then it’s at 50% power for 60 seconds and then 40% power for 300 seconds and then off we can determine that:

For 60 seconds the consumption in kwh was (50 * 30.6 * 60) / 0.92 / 3600
For 300 seconds the consumption in kwh was (40 * 30.6 * 300) / 0.92 / 3600

Where the formula is:

(burner power in %) * (max heating output in kw) * (duration in seconds) / efficiency / (seconds per hour)

The burner power in % is an item value, the max heating output in kw, efficiency and seconds per hour are constants. The duration in seconds is the number of seconds since the last update of the burner power item which is the value I don’t know how to get at the moment.

Something like this should work. I do something similar for car charging metrics.

from core.actions import PersistenceExtensions
from core.date import seconds_between

from org.joda.time import DateTime

time_of_last_update = DateTime(PersistenceExtensions.previousState(ir.getItem("Burner_Power"),True).timestamp.time)
seconds_since_last_update = seconds_between(time_of_last_update, DateTime.now())

[Edit: had args for seconds_between backwards]

1 Like

@5iver comes to the rescue once more! :slight_smile:

I get the following error when saving the rule:

Error during evaluation of script 'file:/etc/openhab2/automation/jsr223/personal/hea
ting.py': ImportError: cannot import name seconds_between in <script> at line number 6

Line 6 is:

from core.date import seconds_between

Some googling leads me to think the error is possibly caused by a missing compat1.x bundle. My system is running 2.4 (and I’d rather stick with the latest release version rather than upgrade to a milestone if possible) so do I need to install the compat1.x bundle? If so, where do I find it? I’ve logged in to the karaf console and searched for it but it’s eluding me.

Does the /etc/openhab2/automation/lib/python/core/date.py file (I’m guessing the path but it should be somewhere below lib and in the core directory) contain the function seconds_between? If not, then you must update the openhab-helper-libraries.

I’m thinking the same… check to see if the function exists in your core.date library. If not, update your libraries

The date module is meant to be used with or without compat1.x it will ignore the legacy types if they are not importable.

I’ve just checked and date.py doesn’t contain seconds_between. Thanks to you both for pointing me in the right direction, I downloaded the latest version of the helper libraries and now have the seconds_between. Excellent! :slight_smile:


One more question which is slightly off-topic…

I have created an item to represent the gas consumption in kw:

Number BoilerGasConsumptionkw "Boiler Gas Consumption in kw" (gPersist_History, gGraphing)

Since this item has never been set to a value before it’s state appears to be NULL which causes an issue when a rule tries to perform any sort of mathematical function on it. To resolve this I’ve tried to add some code to check if it’s in a NULL state and set to zero if necessary:

if ir.getItem("BoilerGasConsumptionkw") == UnDefType.NULL:
    events.sendCommand(ir.getItem("BoilerGasConsumptionkw"), 0)
    previousBoilerGasConsumptionkw = 0
    previousBoilerGasConsumptionkw = float(str(ir.getItem("BoilerGasConsumptionkw")))

The problem I’ve found is that I can’t find the correct comparison value to use to check for NULL. I’ve tried NULL, “NULL” and UnDefType.NULL but the code in the else: block is always executed and of course, you can’t convert the text “NULL” to a float.

What’s the best way to perform this check? Ideally, I’d move this check to a system startup event to keep this particular rule a little bit tidier.

I suggest not doing a check and using persistence with restoreOnStartup in the startegy, which would resolve the whole problem. If you want to continue down the path you are on though, take a look here…


#if ir.getItem("BoilerGasConsumptionkw") == UnDefType.NULL:
if isinstance(items["BoilerGasConsumptionkw"], UnDefType):
    #events.sendCommand(ir.getItem("BoilerGasConsumptionkw"), 0)
    events.sendCommand("BoilerGasConsumptionkw", "0")
    previousBoilerGasConsumptionkw = 0
    #previousBoilerGasConsumptionkw = float(str(ir.getItem("BoilerGasConsumptionkw")))
    previousBoilerGasConsumptionkw = items["BoilerGasConsumptionkw"].floatValue()

There were a couple other things I cleaned up in there too :wink:! Another thing… energy is measured in kWh and power is measured in kW, so your Item and variable names are not accurate.

Most helpful person ever! Thanks!

1 Like

Thanks for reminding me about restoreOnStartup, I’d forgotten to add that to the item definition. I do have a gRestoreOnStartup group that I use for that.

I’m not sure I understand how restoreOnStartup would help in this instance. If I’m reading the situation correctly the above problem in which the item state is NULL only ever occurs the very first time the item state is ever used as an argument in a mathematical expression in this rule. As soon as it has had a value assigned it will cease to cause an error because it will have changed from NULL to a valid value.

So, now that you have very kindly fixed my shoddy code the else branch in the above if statement will be executed forever more.

Once you provide an initial state, restoreOnStartup will make sure that the state is never NULL. So, you could take out the conditional. restart OH, and the rule would run without errors.