Cannot identify null value in rule

awesome! making great progress! getting a value now that i can work with. HUGE.

still have this as an info item. not sure if its to worry:

Previous state result is [FAILED toString()]

Latest code:

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
	val previousState = main_heater_power.previousState(true, "influxdb") 
	logInfo("heater runtime", "Previous state result is {}", previousState);
	logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
	val newDuration = Duration.between(previousState.timestamp, now)
	logInfo("heater runtime", "Duration is {}",  newDuration.toString);
	HeaterTotalRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterTotalRuntime: {}", HeaterTotalRuntime.state)
end

next step is to do some addition to start tracking the runtime over time. i will take a crack at that and see what i can come up with.

It’s info because it’s coming from

It’s attempting to call previousState.toString() and failing. I’m not sure why that would be the case but I don’t use persistence like this very often so maybe HistoricItem doesn’t provide working toString(). Do you get a more meaningful log statement using

logInfo(“heater runtime”, “Previous state result is {}”, previousState.state)

That worked. Just tells that its on


So adding in the daily. Not sure this the best approach but this is what I did. It does throw up and error though:

Could not cast NULL to java.lang.Number; line 20, column 27, length 34

Here is the rule:

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
    val previousState = main_heater_power.previousState(true, "influxdb")
    logInfo("Heater Runtime", "main_heater_power changed to OFF: Previous state result is {}", previousState.state)
    //logInfo("Heater Runtime", "main_heater_power changed to OFF: Previous state timestamp is {}", previousState.timestamp.toString)

    val newDuration = Duration.between(previousState.timestamp, now)
    logInfo("Heater Runtime", "main_heater_power changed to OFF: Duration is {}",  newDuration.toString)

    HeaterTotalRuntime.postUpdate(newDuration.toMinutes + ' min')
    logInfo("Heater Runtime", "main_heater_power changed to OFF: HeaterTotalRuntime updated to {}", HeaterTotalRuntime.state)

    // Get the current date
    val currentDate = new DateTimeType()
    logInfo("Heater Runtime", "main_heater_power changed to OFF: Current date is {}", currentDate.format("%1$tY-%1$tm-%1$td"))

    // Get the previous runtime for today
    val previousRuntime = HeaterDailyRuntime.state as Number
    logInfo("Heater Runtime", "main_heater_power changed to OFF: Previous runtime for today is {}", previousRuntime)

    // Calculate the new runtime for today
    val newRuntime = previousRuntime.intValue() + newDuration.toMinutes
    logInfo("Heater Runtime", "main_heater_power changed to OFF: New runtime for today is {}", newRuntime)

    // Update the HeaterDailyRuntime item
    HeaterDailyRuntime.postUpdate(newRuntime.toString() + ' min')
    logInfo("Heater Runtime", "main_heater_power changed to OFF: HeaterDailyRuntime updated to {}", HeaterDailyRuntime.state)
end


I added this item:

Don’t know why it chokes on that. Seems like it is exactly like what we were doing before.

That said, I tried something else to try to use Duration. I went to the site that was sent over but really could not find anything that seemed to work for day. Did some googling and not luck so just thought I would try it and it didn’t work. But maybe there is something that would. I thought about replacing “now” but that doesnt look like it would work.

The whole idea of this is to see if previousState was null. Changing it to .state would cause an error when it’s null. But moot point when the next line accesses the timestamp anyway. They were just a debugging aid.

Agreed but what’s weird is when it’s not null an error is logged instead of the Item’s toString which is weird.

It’s probably this line:

    val previousRuntime = HeaterDailyRuntime.state as Number

If HeaterDailyRuntime’s state is NULL you can’t cast it to a Number. NULL isn’t a Number.

ok, i took a whole different approach. i think this will work. easier for me to understand.

one question i have though, can can i do the postUpdate to 0 for time? i just want to reset the time values.

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
	val previousState = main_heater_power.previousState(true, "influxdb") 
	logInfo("heater runtime", "Previous state result is {}", previousState);
	logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
	
	val newDuration = Duration.between(previousState.timestamp, now)
	logInfo("heater runtime", "Duration is {}",  newDuration.toString);
	
	HeaterTotalRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterTotalRuntime: {}", HeaterTotalRuntime.state)
	
	HeaterDailyRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterDailyRuntime: {}", HeaterDailyRuntime.state)
	
	HeaterWeeklyRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterWeeklyRuntime: {}", HeaterWeeklyRuntime.state)
	
	HeaterMonthlyRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterMonthlyRuntime: {}", HeaterMonthlyRuntime.state)
end

rule "Reset Daily Values"
when
    Time cron "0 0 12 1/1 * ? *" // at noon. heater usually doesnt run during the day.
then
    HeaterDailyRuntime.postUpdate(0)
    
end

rule "Reset Weekly Values"
when
    Time cron "0 0 12 ? * SUN *" // at noon. heater usually doesnt run during the day.
then
    HeaterWeeklyRuntime.postUpdate(0)
    
end

rule "Reset Monthly Values"
when
    Time cron "0 1 0 1 1/1 ? *" // first day of every month as early as it can run. will be close enough for government work
then
    HeaterMonthlyRuntime.postUpdate(0)
    
end



I’m not sure what you mean by the question. Do you mean update the Item to 0 minutes? Yes you can do that "0 min". If you mean a DateTime Item, then yes, you can but 0 means 00:00:00 Jan 1, 1970

thanks @rlkoshak doesn’t like that though:

extraneous input 'min' expecting ')'
HeaterDailyRuntime.postUpdate (0 min)

This isn’t working as planned. It adds the HeaterTotalRuntime to 1 and thats it. I have had the heater on for much more than a minute but it’s stuck at a minute.

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
	val previousState = main_heater_power.previousState(true, "influxdb") 
	logInfo("heater runtime", "Previous state result is {}", previousState);
	logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
	
	val newDuration = Duration.between(previousState.timestamp, now)
	logInfo("heater runtime", "Duration is {}",  newDuration.toString);
	
	HeaterTotalRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterTotalRuntime: {}", HeaterTotalRuntime.state)
	
	HeaterDailyRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterDailyRuntime: {}", HeaterDailyRuntime.state)
	
	HeaterWeeklyRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterWeeklyRuntime: {}", HeaterWeeklyRuntime.state)
	
	HeaterMonthlyRuntime.postUpdate(newDuration.toMinutes + ' min')
	logInfo("Heater Runtime", "HeaterMonthlyRuntime: {}", HeaterMonthlyRuntime.state)
end

Your script is wrong. You’re setting all those runtime values to just the last runtime duration.

You need to accumulate them, i.e. add the new duration + the previous duration for the corresponding items.

Well, that’s kinda what I thought. But wasn’t sure how to actually code that out.

Assume it needs to change in this line or create a new one?

HeaterTotalRuntime.postUpdate(newDuration.toMinutes + ' min')

Assuming that minutes needs to be something to do with the current HeaterTotalRuntime. So how to express that


HeaterTotalRuntime.postUpdate(newDuration.toMinutes + HeaterTotalRuntime.state)

Or is that the wrong direction. That code doesn’t work so I am certain that does not work. That would give me the ongoing total of heater runtime. I really don’t care to log the just that each time that the heater ran but rather the count of the time.

So for daily it would be something like:

HeaterDailyRuntime.postUpdate(newDuration.toMinutes + HeaterDailyRuntime.state)

so something like that for each of the other values. then should reset to zero each night and start over.

EDIT: fixed the wrong case for midNight

There are many ways to do this. Here’s one version:

// Number:Time HeaterRuntime "Heater's Last Runtime" 
// Set it to persist on every UPDATE, not every change

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
  val previousState = main_heater_power.previousState(true, "influxdb") 
  logInfo("heater runtime", "Previous state result is {}", previousState);
  logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
  
  val runtime = Duration.between(previousState.timestamp, now)

  HeaterRuntime.postUpdate(String.valueOf(runtime.toMillis / 1000) + " s")
  logInfo("heater runtime", "Last Runtime was {}",  HeaterRuntime.state);
    
  val midNight = now.with(LocalTime::MIDNIGHT)

  HeaterDailyRuntime.postUpdate(HeaterRuntime.sumSince(midNight, "influxdb"))
  logInfo("Heater Runtime", "HeaterDailyRuntime: {}", HeaterDailyRuntime.state)
  
  HeaterWeeklyRuntime.postUpdate(HeaterRuntime.sumSince(midNight.with(java.time.temporal.ChronoField.DAY_OF_WEEK, 1), "influxdb"))
  logInfo("Heater Runtime", "HeaterWeeklyRuntime: {}", HeaterWeeklyRuntime.state)
  
  HeaterMonthlyRuntime.postUpdate(HeaterRuntime.sumSince(midNight.with(java.time.temporal.ChronoField.DAY_OF_MONTH, 1), "influxdb"))
  logInfo("Heater Runtime", "HeaterMonthlyRuntime: {}", HeaterMonthlyRuntime.state)
end

The quotes are required.

"0 min"

thanks @JimT ! very elegant! this is where I am with it though


i wanted to change the time from midnight to noon. the heater doesnt run much at all during the day over the season. so really the time i need is how much it ran overnight. so noon makes more sense. so i changed the code and changed MIDNIGHT to NOON and then the val from midNight to noon. Did not like that. So i just changed MIDNIGHT to NOON and left the midNight. that works. sorta. now i have funky numbers.

Last Runtime was 19 s
HeaterDailyRuntime: 0 s
HeaterWeeklyRuntime: 312 s
HeaterMonthlyRuntime: 293 s

So that caused the daily runtime to stop counting. and the weekly and monthly dont have the same numbers even though they should. thought about setting them back to zero and see if they just had something stored in there already.

// Number:Time HeaterRuntime "Heater's Last Runtime" 
// Set it to persist on every UPDATE, not every change

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
  val previousState = main_heater_power.previousState(true, "influxdb") 
  logInfo("heater runtime", "Previous state result is {}", previousState);
  logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
  
  val runtime = Duration.between(previousState.timestamp, now)

  HeaterRuntime.postUpdate(String.valueOf(runtime.toMillis / 1000) + " s")
  logInfo("heater runtime", "Last Runtime was {}",  HeaterRuntime.state);
    
  val midNight = now.with(LocalTime::NOON)

  HeaterDailyRuntime.postUpdate(HeaterRuntime.sumSince(midNight, "influxdb"))
  logInfo("Heater Runtime", "HeaterDailyRuntime: {}", HeaterDailyRuntime.state)
  
  HeaterWeeklyRuntime.postUpdate(HeaterRuntime.sumSince(midNight.with(java.time.temporal.ChronoField.DAY_OF_WEEK, 1), "influxdb"))
  logInfo("Heater Runtime", "HeaterWeeklyRuntime: {}", HeaterWeeklyRuntime.state)
  
  HeaterMonthlyRuntime.postUpdate(HeaterRuntime.sumSince(midNight.with(java.time.temporal.ChronoField.DAY_OF_MONTH, 1), "influxdb"))
  logInfo("Heater Runtime", "HeaterMonthlyRuntime: {}", HeaterMonthlyRuntime.state)
end



Error when I change midNight to noon.

no viable alternative at input 'noon'
[19,56]: no viable alternative at input 'noon'
[19,73]: mismatched input ')' expecting 'end'
// Number:Time HeaterRuntime "Heater's Last Runtime" 
// Set it to persist on every UPDATE, not every change

rule 'Heater runtime'
when
    Item main_heater_power changed to OFF
then
  val previousState = main_heater_power.previousState(true, "influxdb") 
  logInfo("heater runtime", "Previous state result is {}", previousState);
  logInfo("heater runtime", "Previous state timestamp is {}", previousState.timestamp.toString)
  
  val runtime = Duration.between(previousState.timestamp, now)

  HeaterRuntime.postUpdate(String.valueOf(runtime.toMillis / 1000) + " s")
  logInfo("heater runtime", "Last Runtime was {}",  HeaterRuntime.state);
    
  val noon = now.with(LocalTime::NOON)

  HeaterDailyRuntime.postUpdate(HeaterRuntime.sumSince(noon, "influxdb"))
  logInfo("Heater Runtime", "HeaterDailyRuntime: {}", HeaterDailyRuntime.state)
  
  HeaterWeeklyRuntime.postUpdate(HeaterRuntime.sumSince(noon.with(java.time.temporal.ChronoField.DAY_OF_WEEK, 1), "influxdb"))
  logInfo("Heater Runtime", "HeaterWeeklyRuntime: {}", HeaterWeeklyRuntime.state)
  
  HeaterMonthlyRuntime.postUpdate(HeaterRuntime.sumSince(noon.with(java.time.temporal.ChronoField.DAY_OF_MONTH, 1), "influxdb"))
  logInfo("Heater Runtime", "HeaterMonthlyRuntime: {}", HeaterMonthlyRuntime.state)
end

My guess is you need noon of the previous day, so do a .minusDays(1) or something

Oh yes you ran into the same issue I encountered with midnight, hence why I changed it to midNight. I haven’t looked into why midnight, and noon, don’t work. Try nooN for giggles, I bet it works.

1 Like

Hey @JimT just wanted to let you know that this all worked, and more! Have not had a chance to be online and am traveling the rest of the week. As soon as I can I will post the updated code along with some extra that I got to work. Thanks so much for you help!

2 Likes