Cumulative rainfall per hour and day. Hour does not work

Community. I try to document the amount of rain per hour, day, week, month and year. All values work perfectly, except the recording per hour; it remains “0 mm / h”.
So far I have looked for the mistake without success.
I use the same approach for all items. One of them doesn’t work.
No error is shown in the log.
How can I keep looking?
Am I having a wrong approach or an obvious flaw?
Thanks for any tip.

My Items:

	Number:Length           Weather_davis_rain_rate 	    "Weather Davis - Rain - Rate (actual) [%.1f mm]"
        <rain>
        { channel="xyz" }
	Number:Length           Weather_davis_rain_total 	    "Weather Davis - Rain - Total Year [%.1f mm]"
        <rain>
        { channel="xyz" }
	Number:Length           Weather_davis_rain_p1h 	        "Weather Davis - Rain - per Hour [%.1f mm]"
        <rain>
	Number:Length           Weather_davis_rain_p1d 	        "Weather Davis - Rain - per Day [%.1f mm]"
        <rain>
	Number:Length           Weather_davis_rain_p7d 	        "Weather Davis - Rain - per Week [%.1f mm]"
        <rain>
	Number:Length           Weather_davis_rain_p1M 	        "Weather Davis - Rain - per Month [%.1f mm]"
        <rain>
	Number:Length           Weather_davis_rain_p1Y 	        "Weather Davis - Rain - per Year [%.1f mm]"
        <rain>

My rrd4j.persist:

	Weather_davis_rain_total, Weather_davis_rain_rate, Weather_davis_rain_p1h, Weather_davis_rain_p1d,
      Weather_davis_rain_p7d, Weather_davis_rain_p1M, Weather_davis_rain_p1Y : strategy = everyChange, everyMinute

My Rules:

rule "Weather: Rain per Hour, Day, Week, Mounth and Year"
	when
		Time cron "1 0/10 * * * ?"
	then
		val String	StrDateTimeNow = now.toLocalDateTime().toString()
		val String	StrHour		= StrDateTimeNow.substring(11,13)
		val int 	DayOfWeek	= now.getDayOfWeek.getValue
		val String	StrDay 		= StrDateTimeNow.substring(8,10)
		val String	StrMonth 	= StrDateTimeNow.substring(5,7)
		val String	StrYear 	= StrDateTimeNow.substring(0,4)
		var String StrMyZoneDateTime = StrYear + "-" + StrMonth + "-" + StrDay + "T" + StrHour + ":00:00.000Z"
		var MyZoneDateTime = ZonedDateTime.parse(StrMyZoneDateTime).withZoneSameInstant(ZoneId.systemDefault())
	 	logInfo("*** Rain ***", "Now: " + now.toString + ", StrDateTimeNow: " + StrDateTimeNow + ", String day: " + StrDay + ", String hour: " + StrHour)

		// Rain this hour
		StrMyZoneDateTime = StrYear + "-" + StrMonth + "-" + StrDay + "T" + StrHour + ":00:00.000Z"
		MyZoneDateTime = ZonedDateTime.parse(StrMyZoneDateTime).withZoneSameInstant(ZoneId.systemDefault())
		if (Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j")  != NULL) {
			Weather_davis_rain_p1h.postUpdate(Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j") as Number)
		} else {
			Weather_davis_rain_p1h.postUpdate(0)
		}
	 	logInfo("*** Rain ***", "Now = " + now.toString + ", DateTime String: " + StrMyZoneDateTime + ", String per hour: " + MyZoneDateTime + ", Rain: " + Weather_davis_rain_p1h.state)

		// Rain in this day
		StrMyZoneDateTime = StrYear + "-" + StrMonth + "-" + StrDay + "T00:00:00.000Z"
		MyZoneDateTime = ZonedDateTime.parse(StrMyZoneDateTime).withZoneSameInstant(ZoneId.systemDefault())
		if (Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j")  != NULL) {
			Weather_davis_rain_p1d.postUpdate(Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j") as Number)
		} else {
			Weather_davis_rain_p1d.postUpdate(0)
		}
	  	logInfo("*** Rain ***", "Now = " + now.toString + ", DateTime String: " + StrMyZoneDateTime + ", String per day: " + MyZoneDateTime + ", Rain: " + Weather_davis_rain_p1d.state)

// the same principle for week, month, year...

	end

My Openhab.log:

2021-08-04 12:22:01.508 [INFO ] [enhab.core.model.script.*** Rain ***] - Now: 2021-08-04T12:22:01.508409+02:00[Europe/Zurich], StrDateTimeNow: 2021-08-04T12:22:01.506810, String day: 04, String hour: 12
2021-08-04 12:22:01.510 [INFO ] [enhab.core.model.script.*** Rain ***] - Now = 2021-08-04T12:22:01.510299+02:00[Europe/Zurich], DateTime String: 2021-08-04T12:00:00.000Z, String per hour: 2021-08-04T14:00+02:00[Europe/Zurich], Rain: 0 mm
2021-08-04 12:22:01.511 [INFO ] [enhab.core.model.script.*** Rain ***] - Now = 2021-08-04T12:22:01.511077+02:00[Europe/Zurich], DateTime String: 2021-08-04T00:00:00.000Z, String per day: 2021-08-04T02:00+02:00[Europe/Zurich], Rain: 0.6 mm

Only the value for the amount of rain [mm / h] does not work …
(I use openHAB 3.2.0.M1 on Ubuntu)
Many thanks!

This is doomed to failure.
Item states are not simple variables.
postUpdate() is asynchronous, the instruction to perform the update is sent away on openHABs events bus, triggering any rules or bindings that are interested. Eventually, it also updates the Item’s state - but it takes time. Just a few milliseconds.

Your rule does not stop and wait.
So when you read back the state in the same rule, you will almost certainly get the “old” state. Or you might be lucky, it’s indeterminate.

That’s okay, you already know what you posted to the Item. Capture that in a real variable and use that instead for display or further calculations.

Running a rule every minute to calculate yearly rainfall isn’t very efficient, you might consider restructuring here.
If this minutes rainfall hasn’t changed, neither has the yearly etc.

Thanks for the quick reply, even if I don’t fully understand this technical statement.
I am now trying to read in using variables and process them with a delay.
Furthermore, I read a “null” for the item (in this hour) and the correct value in the retrieval of the item (in this day).
my customized rules:

		// Rain this hour
		StrMyZoneDateTime = StrYear + "-" + StrMonth + "-" + StrDay + "T" + StrHour + ":00:00.000Z"
		MyZoneDateTime = ZonedDateTime.parse(StrMyZoneDateTime).withZoneSameInstant(ZoneId.systemDefault())
		if (Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j")  != NULL) {
			var Number Weather_davis_rain = Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j") as Number
	  		logInfo("*** Rain ***", "Rain1 mm/h: " + Weather_davis_rain)
			Thread::sleep(300)
	  		logInfo("*** Rain ***", "Rain2 mm/h: " + Weather_davis_rain)
			Weather_davis_rain_p1h.postUpdate(Weather_davis_rain)
		} else {
			Weather_davis_rain_p1h.postUpdate(0)
		}
	 	logInfo("*** Rain ***", "Now: " + now.toString + ", DateTime String: " + StrMyZoneDateTime + ", String per hour: " + MyZoneDateTime + ", Rain: " + Weather_davis_rain_p1h.state)

		// Rain in this day
		StrMyZoneDateTime = StrYear + "-" + StrMonth + "-" + StrDay + "T00:00:00.000Z"
		MyZoneDateTime = ZonedDateTime.parse(StrMyZoneDateTime).withZoneSameInstant(ZoneId.systemDefault())
		if (Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j")  != NULL) {
			var Number Weather_davis_rain = Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j") as Number
	  		logInfo("*** Rain ***", "Rain3 mm/h: " + Weather_davis_rain)
			Thread::sleep(300)
	  		logInfo("*** Rain ***", "Rain4 mm/h: " + Weather_davis_rain)
			Weather_davis_rain_p1d.postUpdate(Weather_davis_rain)
		} else {
			Weather_davis_rain_p1d.postUpdate(0)
		}
	  	logInfo("*** Rain ***", "Now: " + now.toString + ", DateTime String: " + StrMyZoneDateTime + ", String per day: " + MyZoneDateTime + ", Rain: " + Weather_davis_rain_p1d.state)

I recognize the “null” in the log:

2021-08-04 15:14:01.128 [INFO ] [enhab.core.model.script.*** Rain ***] - Rain1 mm/h: null
2021-08-04 15:14:01.428 [INFO ] [enhab.core.model.script.*** Rain ***] - Rain2 mm/h: null
2021-08-04 15:14:01.430 [INFO ] [enhab.core.model.script.*** Rain ***] - Now: 2021-08-04T15:14:01.429652+02:00[Europe/Zurich], DateTime String: 2021-08-04T15:00:00.000Z, String per hour: 2021-08-04T17:00+02:00[Europe/Zurich], Rain: 0 mm
2021-08-04 15:14:01.432 [INFO ] [enhab.core.model.script.*** Rain ***] - Rain3 mm/h: 2.4
2021-08-04 15:14:01.733 [INFO ] [enhab.core.model.script.*** Rain ***] - Rain4 mm/h: 2.4
2021-08-04 15:14:01.735 [INFO ] [enhab.core.model.script.*** Rain ***] - Now: 2021-08-04T15:14:01.734607+02:00[Europe/Zurich], DateTime String: 2021-08-04T00:00:00.000Z, String per day: 2021-08-04T02:00+02:00[Europe/Zurich], Rain: 2.4 mm

is this part of the query wrong in principle?

		StrMyZoneDateTime = StrYear + "-" + StrMonth + "-" + StrDay + "T" + StrHour + ":00:00.000Z"
		MyZoneDateTime = ZonedDateTime.parse(StrMyZoneDateTime).withZoneSameInstant(ZoneId.systemDefault())
		if (Weather_davis_rain_total.deltaSince(MyZoneDateTime, "rrd4j")  != NULL) {

on her note…

Running a rule every minute to calculate yearly rainfall isn’t very efficient, you might consider restructuring here.
If this minutes rainfall hasn’t changed, neither has the yearly etc.

I will split it up again. Thanks for the tip.

Let me try again -

This isn’t going to work.
postUpdate() does not have immediate effect.
But the rule does not stop and wait for it.
So when the logInfo() fetches and displays Item state it will be the old wrong value.

If you do any further calculations based on that, they’ll be wrong too.

A workaround

var x = 0
if ( blah) {
   calculations, put result in x
} else {
   calculations, put result in x
}
yourItem.postUpdate(x)
logInfo("test", "My Item will have value " + x + " very soon")

Meanwhile, do more calculations with x if you like

Here, we don’t care how long the postUpdate() takes and can press on without delay.
Even if you’re only logging out the value, you can’t debug your rule with wrong info.


Unrelated -

This’ll never happen, deltaSince() returns a number, or null if no data available.
NULL is a special state seen only in uninitialized Items, and not the same thing as the general null of rules and variables land.


Unrelated, this one’s more about style than results

Database queries are a relatively big job, and here you’ve run the same query twice unnecessarily.
Technically you could even get different results each time.
Better to query once.

var x = yourItem.deltaSince(MyZoneDateTime, "rrd4j")
if ( x !== null) {
     calculate with x
} else {
    logInfo("test", "My query came up empty")
}

As a result, I’ve always done this wrong for a long time.
I have adjusted it and am now waiting for rain …
Thanks for the support.
Greetings, Urs

I did not get the processing per hour to run. Davis delivered his input too late …
As feared by rossko57, my approach via the rrd4j.persist was not the best idea.
I’ve changed my approach and now everything is going right.

However, there is still one problem that I cannot elegantly solve.
I set my rain items as [mm/h]:

	Number:Length           Weather_davis_rain_p1h 	        "Weather Davis - Rain - per Hour [%.1f mm]"

How do I subtract two components with ([mm] as total sum) - ([mm] as subtotal).
For me, only the solution via the base unit [m] works.
This seems like a tinkered solution to me …

Weather_davis_rain_p1M.postUpdate((Weather_davis_rain_total.state as Number - Weather_davis_rain_p0M.state as Number) * 1000)

Overall, the following solution now works:
My Items:

	Number:Length           Weather_davis_rain_total 	    "Weather Davis - Rain - Total Year [%.1f mm]"
        <rain>        { channel="xyz" }
	Number:Length           Weather_davis_rain_p0h 	        "Weather Davis - Rain - per xx:00 Hour [%.1f mm]"
	Number:Length           Weather_davis_rain_p0D 	        "Weather Davis - Rain - per 00:00 Day [%.1f mm]"
	Number:Length           Weather_davis_rain_p0W 	        "Weather Davis - Rain - per Monday/Week [%.1f mm]"
	Number:Length           Weather_davis_rain_p0M 	        "Weather Davis - Rain - per 01.Month [%.1f mm]"
	Number:Length           Weather_davis_rain_p0Y 	        "Weather Davis - Rain - per 01.01.Year [%.1f mm]"
	Number:Length           Weather_davis_rain_p1h 	        "Weather Davis - Rain - per Hour [%.1f mm]"
	Number:Length           Weather_davis_rain_p1D 	        "Weather Davis - Rain - per Day [%.1f mm]"
	Number:Length           Weather_davis_rain_p1W 	        "Weather Davis - Rain - per Week [%.1f mm]"
	Number:Length           Weather_davis_rain_p1M 	        "Weather Davis - Rain - per Month [%.1f mm]"
	Number:Length           Weather_davis_rain_p1Y 	        "Weather Davis - Rain - per Year [%.1f mm]"

My rrd4j.persist:

   Weather_davis_rain_p0h, Weather_davis_rain_p0D, Weather_davis_rain_p0W, Weather_davis_rain_p0M, Weather_davis_rain_p0Y : strategy = everyChange, everyMinute, restoreOnStartup
	Weather_davis_rain_p1h, Weather_davis_rain_p1D, Weather_davis_rain_p1W, Weather_davis_rain_p1M, Weather_davis_rain_p1Y,
      Weather_davis_rain_total, Weather_davis_rain_rate : strategy = everyChange, everyMinute

My Rules:

rule "Weather14: Rain every new Hour"
	when
		Time cron "1 0 * * * ?"		// everyHour
	then
		if (Weather_davis_rain_total.state instanceof Number ) {
			if (Weather_davis_rain_total.state >= 0|mm && Weather_davis_rain_total.state <= 10000|mm) {
				Weather_davis_rain_p0h.postUpdate(Weather_davis_rain_total.state)		// Basis für Regen pro Stunde

				// Rain in this month
				if (Weather_davis_rain_p0M.state instanceof Number) {
					Weather_davis_rain_p1M.postUpdate((Weather_davis_rain_total.state as Number - Weather_davis_rain_p0M.state as Number) * 1000)
				} 
			}
		}
	end
//------------------------------------
rule "Weather15: Rain every new Day"
	when
		Time cron "3 0 0 * * ?"		// everyDay
	then
		if (Weather_davis_rain_total.state instanceof Number ) {
			if (Weather_davis_rain_total.state >= 0|mm && Weather_davis_rain_total.state <= 10000|mm) {
				Weather_davis_rain_p0D.postUpdate(Weather_davis_rain_total.state)		// Basis für Regen pro Tag

				// Rain in this year
				if (Weather_davis_rain_p0Y.state instanceof Number) {
					Weather_davis_rain_p1Y.postUpdate((Weather_davis_rain_total.state as Number - Weather_davis_rain_p0Y.state as Number) * 1000)
				} 
			}
		}
	end
//------------------------------------
rule "Weather16: Rain every new Week"
	when
		Time cron "4 0 0 ? * 1"		// At 00:00:00am, on every Monday, every month
	then
		if (Weather_davis_rain_total.state instanceof Number ) {
			if (Weather_davis_rain_total.state >= 0|mm && Weather_davis_rain_total.state <= 10000|mm) {
				Weather_davis_rain_p0W.postUpdate(Weather_davis_rain_total.state)		// Basis für Regen pro Woche
			}
		}
	end
//------------------------------------
rule "Weather17: Rain every new Month"
	when
		Time cron "5 0 0 1 * ?"		// everyMonth
	then
		if (Weather_davis_rain_total.state instanceof Number ) {
			if (Weather_davis_rain_total.state >= 0|mm && Weather_davis_rain_total.state <= 10000|mm) {
				Weather_davis_rain_p0M.postUpdate(Weather_davis_rain_total.state)		// Basis für Regen pro Monat
			}
		}
	end
//------------------------------------
rule "Weather18: Rain every new Year"
	when
		Time cron "6 0 0 1 1 ?" 	// every New Year
	then
		if (Weather_davis_rain_total.state instanceof Number ) {
			if (Weather_davis_rain_total.state >= 0|mm && Weather_davis_rain_total.state <= 10000|mm) {
				Weather_davis_rain_p0Y.postUpdate(Weather_davis_rain_total.state)		// Basis für Regen pro Jahr
			}
		}
	end
//------------------------------------
rule "Weather19: Rain per Hour, Day, Week"
	when
		Time cron "2 0/10 * * * ?"
	then

		// Rain this hour
		if (Weather_davis_rain_total.state instanceof Number && Weather_davis_rain_p0h.state instanceof Number) {
			Weather_davis_rain_p1h.postUpdate((Weather_davis_rain_total.state as Number - Weather_davis_rain_p0h.state as Number) * 1000)
		} 

		// Rain in this day
		if (Weather_davis_rain_total.state instanceof Number && Weather_davis_rain_p0D.state instanceof Number) {
			Weather_davis_rain_p1D.postUpdate((Weather_davis_rain_total.state as Number - Weather_davis_rain_p0D.state as Number) * 1000)
		} 

		// Rain in this week
		if (Weather_davis_rain_total.state instanceof Number && Weather_davis_rain_p0W.state instanceof Number) {
			Weather_davis_rain_p1W.postUpdate((Weather_davis_rain_total.state as Number - Weather_davis_rain_p0W.state as Number) * 1000)
		} 

	end

Thank you rossko57 for the valuable tips, which I was able to correct in various applications.
Greetings, Urs

Depends if both are Quantities, for example the state of Number:Length Items, ar a constant defined like 3 | mm.
If both are quantities of the same type, just subtract, the framework sorts it all out including units conversions.
You can force the (quantity type) result into units of your choice.

If they’re mixed quantity / plain number types, you have to take action to force similar typing.

Note that
myQuantityItem.state as Number
may not give you what you expect.

Thanks rossko57 for the answer.
I can’t get this subtraction going.
All three items are defined as Number: Length and all three show units in [mm].
Nevertheless, Openhab complains with:

Ambiguous feature call.
The extension methods
	postUpdate(Item, State) in BusEvent and
	postUpdate(Item, Number) in BusEvent
both match.(org.eclipse.xtext.xbase.validation.IssueCodes.ambiguous_feature_call)

Section Rules:

Weather_davis_rain_p1M.postUpdate((Weather_davis_rain_total.state as QuantityType<Length>) - (Weather_davis_rain_p0M.state as QuantityType<Length>))

or:

Weather_davis_rain_p1M.postUpdate((Weather_davis_rain_total.state as QuantityType<Length>) - (Weather_davis_rain_p0M.state as QuantityType<Length>)).toUnit("mm")

Items used:

	Number:Length           Weather_davis_rain_total 	    "Weather Davis - Rain - Total Year [%.1f mm]"
        { channel="http:url:meteobridge:weather_davis_rain_total" }
	Number:Length           Weather_davis_rain_p0M 	        "Weather Davis - Rain - per 01.Month  [%.1f mm]"
	Number:Length           Weather_davis_rain_p1M 	        "Weather Davis - Rain - per Month [%.1f mm]"

Maybe I defined [%.1f mm] wrong?

However, this is just a flaw in my openhab.
I can handle it well.
Thanks and regards, Urs

DSL rules interpreter has to guess what kind of Item state is involved (number, string, blah) and isn’t always very good at it before runtime.

This looks good, give a predictable result, just needs the brackets jiggling.

Weather_davis_rain_p1M.postUpdate( ( (Weather_davis_rain_total.state as QuantityType<Length>) - (Weather_davis_rain_p0M.state as QuantityType<Length>) ).toUnit("mm") )

It’s the maths result that wants to be .toUnits-ed, not the postUpdate.

If DSL still doesn’t know what’s going on, giving the postUpdate a string version to digest is pretty foolproof.

Weather_davis_rain_p1M.postUpdate( ( (Weather_davis_rain_total.state as QuantityType<Length>) - (Weather_davis_rain_p0M.state as QuantityType<Length>) ).toUnit("mm").toString )
1 Like

Mathematics 1st semester: The bracket in the right place helps over many problems …
The version with a conversion via string works perfectly.

All done! Thank you rossko57 for the valuable support.
Greetings, Urs