Issue with a rule (DateTime to String)

Hi folks,

I receive this error:

2018-09-06 17:39:46.851 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'Daylight concat': H != java.lang.String

Here my rule:

rule "Daylight concat"
when
  Item Astro_Sun_Daylight_Start changed or
  Item Astro_Sun_Daylight_End changed or
  System started
then
    val String moonrise_start_string  = Astro_Sun_Daylight_Start.state.format("%1$tH:%1$tM")
    val String moonrise_end_string  = Astro_Sun_Daylight_End.state.format("%1$tH:%1$tM")
    if(moonrise_start_string = moonrise_end_string) {
        Converted_Astro_Sun_Daylight.postUpdate("at " + moonrise_start_string.toString)
    }
    else {
        Converted_Astro_Sun_Daylight.postUpdate("from " + moonrise_start_string.toString + " to " + moonrise_end_string.toString)
    }
end

Here my items:

DateTime Astro_Sun_Daylight_Start "Daylight Start [%1$tH:%1$tM]" (gOutdoor)
DateTime Astro_Sun_Daylight_End "Daylight End [%1$tH:%1$tM]" (gOutdoor)
String Converted_Astro_Sun_Daylight "Daylight [%s]" (gOutdoor)

Where is my mistake?

Can you please help me?

thanks
Andrea

I’ve never seen the format method used inside Rules so I don’t know for sure whether that works or not.

But I was able to get the calls to format to work in my Rule.

So the question is Astro_Sun_Daylight_Start not NULL when the Rule starts? If it is NULL the call to format will fail because NULL is not a DateTimeType so the formatting String makes no sense.

if(Astro_Sun_Daylight_Start.state == NULL || Astro_Sun_Daylight_End.state == NULL) {
    logWarn("daylight", "One of daylight start or daylight end are NULL, cannot calculate Converted times")
    return;
}
val String moonrise_start_string ...

You should change the names of your variables. daylight start and end have nothing to do with moonrise.

Well actually it seems I’m not understanding how Persistence works, as I have mapdb as default persistence but I see a lot of NULL items in my logs at the starting point.

My Persistence strategy is like this:

  • mapdb as default (in PaperUI > Configuration > System > Persistence)
Strategies {
        default = everyUpdate
}

Items {
        // persist all items on every change and restore them from the db at startup
        * : strategy = everyChange, restoreOnStartup
}
  • rrd4j for some items (gChart group)
// Configuration file for "rrd4j" persistence module
Strategies {
	// for rrd charts, we need a cron strategy
	everyMinute	: "0 * * * * ?"
	everyHour	: "0 0 * * * ?"
	everyDay	: "0 0 0 * * ?"
	
	// if no strategy is specified for an item entry below, the default list will be used
	default = everyChange
}


Items {
	
	gChart* : strategy =  everyMinute
	
}
  • influxdb for analysis (+ grafana) -> gHistory and gPresence group
Strategies {
    everyMinute : "0 * * * * ?"
    everyHour   : "0 0 * * * ?"
    everyDay    : "0 0 0 * * ?"
}

Items {
    gHistory*	: strategy = everyChange, everyHour
    gPresence*	: strategy = everyChange
}

Influxdb and grafana work 
 rrd4j not as expected (troubleshooting in progress) 
 but I expect mapdb should work for all items, right? What is missing?

Andrea

Using persistence doesn’t prevent Items from starring up as NULL. It just populates those Items with the most recent value in the database as soon as Persistence starts up. Based on timing, this can potentially occur after Rules start running so there is no guarantee that all Items will never be null even when using restoreOnStartup.

Also, the Item actually has to have a non-NULL value to save in the database in the first place. This means that all your Items will have had to change (since your strategy is everyChange) after you installed and enabled MapDB. Otherwise there is nothing in the database to restore to your Items.

Do these explain the NULLs you are seeing?

Makes sense 
 so in my rules better to cover also the possibility some items can start with NULL value. Right?

Yes. I take it one step further and actually use NULL in a meaningful way. For example, if for some reason a sensor has not reported back in an amount of time where the current value of the Item is no longer useful, I use the Expire binding to set that Item to NULL and my Rules are written to look for those NULLs and behave accordingly (e.g. use a different sensor value, do nothing at all, generate an alert, etc.).

Therefore I avoid situations like controlling my heating based on a temperature reading from an hour ago.

While you can use these NULL states to indicate that the sensor has gone off line, I do not automatically assume that. I just assume that the Item simply doesn’t have a usable Item and I have other Rules and Items to monitor and report when a sensor goes offline.

1 Like

Yes 
 and if you have NULL some web items (from http binding), just because you simply didn’t receive yet the values, 
 better to do nothing and wait next change :slight_smile:

Interesting your view of “meaningful way”. In facts, you should have NULL because the sensor is down/broken 
 an alert should make sense. Or you should implement some default strategies in case you are doing something when the temperature in the kitchen is X, but if this is NULL better to have anyway a default strategy than nothing.

thanks for your view. As usual, much appreciated :slight_smile:
Andrea

To provide a concrete example


I don’t have an air condition but do have a forced air furnace located in the basement. During the summer the basement is nearly 10 degrees F cooler than the top floor of the house. So I placed a sensor in the master bedroom and my son’s bedroom. On the main floor I have the thermostat that controls the heater so I have that sensor for that floor. Finally I have a sensor in the basement.

In the decision to turn on the fan to cool the upper floors by circulating the cooler basement air I use the following strategy.

If the main floor temp is NULL it means the thermostat itself is offline so I couldn’t turn on the fan if I wanted to.

I use the maximum temp from the top floor. If both are NULL I use the main floor’s temp for the upper temp to compare against.

I use the basement temp for the lower value. If basement is NULL but one of the top floor temps are not NULL I use the main floor temp. If the basement temp and both of the top floor temps are NULL I use the main floor temp for the lower value and the outside temp for the upper value.

Then I check if the lower is a degree cooler than the upper and that the upper is higher than the target and if so turn on the fan for 15 minutes.

As you can see, I can still get a reasonable behavior from the fan even if only one of the temp sensors are working (and the weather is being updated). And the NULLs are key that allows this to happen. I essentially use the best information I have available and gracefully degrade when the best information isn’t available. And it doesn’t take much code to accomplish.

1 Like

Thank you. Interesting :slight_smile:

May I ask your help to understand better those messages?

2018-09-06 23:55:47.206 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'manipulation.rules', using it anyway:
The value of the local variable astrodawn_start is not used
The value of the local variable astrodawn_end is not used

items are not NULL

here the rule:

rule "Astro Dawn concat"
when
  Item Astro_Sun_AstroDawn_Start changed or
  Item Astro_Sun_AstroDawn_End changed or
  System started
then
    if(Astro_Sun_AstroDawn_Start.state == NULL || Astro_Sun_AstroDawn_End.state == NULL) {
        return;
    }
    val String astrodawn_start  = Astro_Sun_AstroDawn_Start.state.format("%1$tH:%1$tM")
    val String astrodawn_end  = Astro_Sun_AstroDawn_End.state.format("%1$tH:%1$tM")
    if(astrodawn_start == astrodawn_end) {
        Converted_Astro_Sun_AstroDawn.postUpdate("at " + astrodawn_start.toString)
    }
    else {
        Converted_Astro_Sun_AstroDawn.postUpdate("from " + astrodawn_start.toString + " to " + astrodawn_end.toString)
    }
end

and here the second log:

2018-09-06 23:56:14.418 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Moonset concat': The name 'NULL' cannot be resolved to an item or type; line 113, column 42, length 4
2018-09-06 23:56:14.421 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Moonrise concat': The name 'NULL' cannot be resolved to an item or type; line 94, column 43, length 4
2018-09-06 23:56:14.429 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Moonset concat': The name 'NULL' cannot be resolved to an item or type; line 113, column 42, length 4

here the rule:

rule "Moonset concat"
when
  Item Astro_Moon_Moonset_Start changed or
  Item Astro_Moon_Moonset_End changed or
  System started
then
    if(Astro_Moon_Moonset_Start.state == NULL || Astro_Moon_Moonset_End.state == NULL) {
        return;
    }
    val String moonset_start  = Astro_Moon_Moonset_Start.state.format("%1$tH:%1$tM")
    val String moonset_end  = Astro_Moon_Moonset_End.state.format("%1$tH:%1$tM")
    if(moonset_start == moonset_end) {
        Converted_Astro_Moon_Moonset.postUpdate("at " + moonset_start.toString)
    }
    else {
        Converted_Astro_Moon_Moonset.postUpdate("from " + moonset_start.toString + " to " + moonset_end.toString)
    }
end

line 113 is in this rule:

if(Astro_Moon_Moonset_Start.state == NULL || Astro_Moon_Moonset_End.state == NULL) {

line 94 in facts is the same in another rule:

if(Astro_Moon_Moonrise_Start.state == NULL || Astro_Moon_Moonrise_End.state == NULL) {

thanks
Andrea

Hmmmm. That warning is in error.

You will see that error when you create a variable (val/var) and never use it. It is just a warning, not an error and it is just providing a heads up to you that you may have forgotten to code something or the like. The code will still run.

But in this case you actually are using both of those variables so there shouldn’t be any error.

This is a known issue. What is happening is your Rules start executing before they are fully loaded and parsed. The above warnings may be related to this. You should only see these errors when OH first starts running. After that the errors should go away and your Rules should start working again.

Unfortunately this puts you in a bad position because you need this Rule to run when the system starts.

Do these errors appear every time you restart OH? Or do they only appear some times?