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?