Strange ArrayList problem

I’m currently working on a system that will let me use “profiles” to set things like lights and thermostats throughout a 24 hour day by assigning them values for every one hour block. To do so, I’ve created a profile Item which holds all the data in it formatted as a long JSON string.

my.items:

String Profilestore_json_1_input "Input Channel" (Timecontrol,Profilestore_input {expire='1s,command="{}"'}

String Profilestore_json_1 "Profile: [JSONPATH($.label):%s]" (Timecontrol,Profilestore_store)

The input item takes a JSON-formatted input from a custom HabPanel widget and expects either:

{"reset":""}
{"rename":"<new name>"}
{"retype":"<percentage, temperature, or boolean>"}
{"interval":"<hour>","value":"<value>"}

A properly formatted Profilestore_json_1 looks like this:

{"label":"Super cool name","type":"percentage","intervals": {"0":11,"1":9,"2":6,"3":0,"4":0,"5":0,"6":0,"7":0,"8":18,"9":18,"10":18,"11":8,"12":17,"13":0,"14":20.4,"15":20,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":8}}

Here’s the rule I’m using to handle the input from the input Item:

//Input handler
rule "Profile JSON input handler"
when
    Member of Profilestore_input changed
then
    var intervallist = newArrayList(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
    //Find the associated profilestore_store item
    var profilenumber = triggeringItem.name.split("_").get(2)
    var profilestoreitem = Profilestore_store.members.findFirst [ i | i.getName.equals("Profilestore_json_" + profilenumber)]

    var profilecommand    = triggeringItem.state.toString
    var profilestoreddata = profilestoreitem.state.toString

    //Check to see if the associated profile storage item needs to be initialized. If so, do so
    if(profilestoreitem.state == NULL || triggeringItem.state.toString.contains('"reset":')){
        var emptyProfileJson = new StringBuilder
        emptyProfileJson.append('{')
            emptyProfileJson.append('"label":"unnamed",')
            emptyProfileJson.append('"type":"unknown",')
            emptyProfileJson.append('"intervals": {')
            var counter = 0
            while(counter < 23){
                emptyProfileJson.append('"' + counter + '":0,')
                counter = counter + 1
            }
            emptyProfileJson.append('"23":0')
            emptyProfileJson.append('}')
        emptyProfileJson.append('}')
        logInfo("ThermoProfileCalc", "Profile updated to " + emptyProfileJson)
        profilestoreitem.sendCommand("" + emptyProfileJson)
    }

    //Deconstruct the stored JSON string
    var profileLabel = transform("JSONPATH", "$.label", profilestoreddata)
    var profileType  = transform("JSONPATH", "$.type", profilestoreddata)
    var counter = 0
    while(counter <= 23){
        var String fetchlabel = "$.intervals." + counter
        intervallist.set(counter, transform("JSONPATH", fetchlabel, profilestoreddata))
        counter = counter + 1
    }

    

    //Perform retype command        FORMAT: {"retype":"<type here>"}
    if(profilecommand.contains('"retype":')){
        var tempProfileType = transform("JSONPATH", "$.retype", profilecommand)
        if(tempProfileType.equals("temperature") || tempProfileType.equals("percentage") || tempProfileType.equals("boolean")){
            logInfo("ThermoProfileCalc", "Profile #" + profilenumber + " retypeed from " + profileType + " to " + tempProfileType)
            profileType = tempProfileType
        }else{
            logError("ThermoProfileCalc", "Profile #" + profilenumber + " experienced invalid retyping attempt to \"" + tempProfileType + '"')
        }        
    }

    //Perform rename command        FORMAT: {"rename":"<name here>"}
    if(profilecommand.contains('"rename":')){
        var tempProfileLabel = transform("JSONPATH", "$.rename", profilecommand)
        logInfo("ThermoProfileCalc", "Profile #" + profilenumber + " renamed from " + profileLabel + " to " + tempProfileLabel)
        profileLabel = tempProfileLabel      
    }

    //Perform interval adjustment   FORMAT: {"interval":"<interval number>", "value":<value here>}
    if(profilecommand.contains('"interval":')){
        var tempProfileInterval = Integer.parseInt(transform("JSONPATH", "$.interval", profilecommand)) as Number
        var tempProfileIntervalValue = DecimalType.valueOf(transform("JSONPATH", "$.value", profilecommand)) as Number
        
        //value range checking
        if(profileType.equals("temperature")){
            if(tempProfileIntervalValue >= 0.0){
                logInfo("ThermoProfileCalc", "Profile #" + profilenumber + " interval " + tempProfileInterval + " changed to " + tempProfileIntervalValue)
                intervallist.set(tempProfileInterval, tempProfileIntervalValue)
            }else{
                logError("ThermoProfileCalc", "Submitted number " + tempProfileIntervalValue + " is not a valid temperature!")
            }
        }
        if(profileType.equals("percentage")){
            if(tempProfileIntervalValue >= 0.0 && tempProfileIntervalValue <= 100.0){
                logInfo("ThermoProfileCalc", "Profile #" + profilenumber + " interval " + tempProfileInterval + " changed to " + tempProfileIntervalValue)
                intervallist.set(tempProfileInterval, tempProfileIntervalValue)            
            }else{
                logError("ThermoProfileCalc", "Submitted number " + tempProfileIntervalValue + " is not a valid percentage!")
            }
        }
        if(profileType.equals("boolean")){
            if(tempProfileIntervalValue == 0.0 || tempProfileIntervalValue == 1.0){
                logInfo("ThermoProfileCalc", "Profile #" + profilenumber + " interval " + tempProfileInterval + " changed to " + tempProfileIntervalValue)
                intervallist.set(tempProfileInterval, tempProfileIntervalValue)
            }else{
                logError("ThermoProfileCalc", "Submitted number " + tempProfileIntervalValue + " is not a valid boolean (1 or 0)!")
            }
        }
    }
    logInfo("loggerName", "ddddd")

    //Reconstruct the stored JSON string (with changes)
    var reconstructProfileJson = new StringBuilder
    reconstructProfileJson.append('{')
    reconstructProfileJson.append('"label":"' + profileLabel + '",')
    reconstructProfileJson.append('"type":"' + profileType + '",')
    reconstructProfileJson.append('"intervals": {')
    counter = 0
    while(counter < 23){
        reconstructProfileJson.append('"' + counter + '":' + intervallist.get(counter) +',')
        counter = counter + 1
    }
    reconstructProfileJson.append('"23":' + intervallist.get(23) + '}}')

    profilestoreitem.sendCommand("" + reconstructProfileJson)
end

Now, unfortunately, it almost works. Changing label and type isn’t a problem but when I attempt to change the value in a specific interval, I get this in the log:

[ome.event.ItemCommandEvent] - Item 'Profilestore_json_1_input' received command {"interval":"4","value":"16"}
[vent.ItemStateChangedEvent] - Profilestore_json_1_input changed from "{}" to {"interval":"4","value":"16"}
[INFO ] [thome.model.script.ThermoProfileCalc] - Profile #1 interval 4 changed to 16
[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Profile JSON input handler': An error occurred during the script execution: null

Using logInfo to debug, I’ve narrowed down the problem to being in intervallist.set(tempProfileInterval, tempProfileIntervalValue) but I’ve got no clue why it isn’t working. Are my variables somehow the wrong type? Neither tempProfileInterval nor tempProfileIntervalValue are null and intervallist seems fine too when I read it out.

I’ve got a feeling that I’m missing something very basic. Any ideas?

The first step when debugging a complicated DSL rule such as this it to add some logging to narrow down with line of code it’s failing on. Without that we’re just shooting in the dark. There like 150 lines of code there, any one of which could be failing.