[SOLVED] Error: Rule '<rulename>': null

NULL

null != NULL

NULL is a special state indicating the Item has an uninitialized state. And you would get the null error above if you tried to actitem.state as Number if actitem.state == NULL.

Scanning throught the Rule… (not everything below is relevant to the error).

Why the Thread::sleep?

val NumberItem actitem = triggeringItem as NumberItem

Are all the members of gActTemp of type NumerItem?

if (actitem !== null)

This will never happen. triggeringItem will always be set to the Item that triggered the Rule. Since this Rule can only be triggered by a change to an Item, triggeringItem will never be null.

val NumberItem setitem = gSetTemp.members.filter[ i | i.name==String::format("%s_SETTEMP",actitem.name.split("_").get(0)) ].last

This is more a matter of style, but it would be shorter to use findFirst and string concatination:

val NumberItem setitem = gSetTemp.members.findFirst[ i | i.name = actitem.name.split("_").get(0) + "_SETTEMP" ]

if(actitem.state !== null && setitem.state !== null) {

This also can never be the case. If you have a not null Item, the Item will always have a State. But that State may be NULL which cannot be cast to a DecimalType.

Again, NULL != null. They are not the same thing.

Over all, this Rule could be simplified using Design Pattern: How to Structure a Rule.

rule "calc diff temp"
when
    Member of gActTemp changed
then
    val actitem = triggeringItem // we don't really need to cast this to a NumberItem
    val setitem = gSetTemp.members.findFirst[ i | i.name == actitem.name.split("_").get(0) + "_SETTEMP" ]
    val diffitem = gDiffTemp.members.findFirst[ i | i.name == actitem.name.split("_").get(0) + "_DIFFTEMP"]

    // skip the rule if the actitem changed to NULL
    if(actitem.state == NULL){
        logInfo(logger, "%s's state is NULL!", actitem.name)
        return;
    }

    // skip the rule if setitem or diffitem were not found in their respective Groups
    if(setitem === null || diffitem == null){
        logInfo(logger, "Could not find setitem and/or diffitem")
        return;
    }

    // skip the rule if either the associated setitem and diffitem's states are NULL
    if(setitem.state == NULL || diffitem.state == NULL){
        logInfo(logger, "One of setitem or diffitem's state is NULL:\n  setitem = %s\n  diffitem = %s", setitem.state, diffitem.state)
        return;
    }

    // at this point we have all three relevant Items and know their states are not NULL

    var Number diff = actitem.state - setitem.state // it should be able to do this without casting
    diffitem.postUpdate(diff)

end