JSON Rule Help, Transform?

Hello All.

I’m currently working on a controlled dimmer using JSON from a Kodi plugin. The Kodi plugin allows me to send JSON data at certain points basically creating a Theater atmosphere. Here is my rule. At the top of the Rule file there is the default three items found on the wiki JSON Transformations · openhab/openhab1-addons Wiki · GitHub

rule "Home Theater Dimmer"
	when
		Item HT_Dimmer changed
	then
		var Number curstate = 0
		if(HT_Dimmer.state instanceof PercentType) curstate = HT_Dimmer.state as PercentType
		
		var String json = (HT_Dimmer.state as StringType).toString
		// {"_type": "dimDown", "DimDownPercent": "70", "Increments": "5",
	    //    "Speed": "400"}
	    var String type = transform("JSONPATH", "$._type", json)
	    if (type == "dimDown") {
	      var Number dimto = transform("JSONPATH", "$.DimDownPercent", json)
	      var Number inc = transform("JSONPATH", "$.Increments", json)
	      var long speed = transform("JSONPATH", "$.Speed", json)
		  
		  while(curstate<dimto)

			{
		        curstate=curstate - inc
		        sendCommand(Light_LivRoom_Steps_Dim,curstate)
		        Thread::sleep(speed)
			}
			
		}
		
end

My issue is openHAB Designer keeps showing transform as though it is not valid. It is telling me this: Couldn’t resolve reference to JvmIdentifiableElement ‘transform’.

I tried to run this anyway using POSTMAN in Chrome to test. I sent the header as application/json and the body of {“_type”: “dimDown”, “DimDownPercent”: 70, “Increments”: 5, “Speed”: 400} and i get a created and I see the variable gets set. However the logfile shows this.

Error during the execution of rule ‘Home Theater Dimmer’: Could not invoke method: org.openhab.model.script.lib.NumberExtensions.operator_lessThan(java.lang.Number,java.lang.Number) on instance: null

So first, why does transform show underlined red and second how can I send this information to my item to trigger my rule with the variables. Or can I send my JSON directly to the rule and fire it that way? Thanks for the help!

I suspect the automatic datatype conversion didn’t work.

transform returns a String, so you may need to explicitly convert the result to your expected datatype before using its value.

run? My guess is it didn’t convert either. Am I not using JSONPATH right? I can see the string does get set to the value string that I send it cause I displayed it on my sitemap to verify its getting passed, it seems that its not able to use transform to pull the information out or parse the data I’d say. Which would be why my values are returning null hence the error log entry and the rule not triggering. Is there something that I possibly didn’t install on my vm or should it of installed when installing OpenHAB? Please let me know.

Hi Robert

Few questions:

  1. What is the HT_Dimmer item? In the first ‘if’ statement, we are checking for a percentage type. However, in the later json assignment/transforms, you are expecting a json string. Which is correct?

  2. Assuming that the HT_Dimer is actually getting a json string and is failing on the transforms, do you have the correct imports at the top of your rule file?

  3. Do you know which line the null error is on? Given that it says operator_lessThan I assume it is failing on the while(curstate<dimto) line. This being the case, I would check the values of curstate first, and then the value of dimto. You can do this easily by printing to the log, or using e.g. logInfo etc. Again the question that comes to mind is what is HT_Dimmer.state, as this is being assigned to curstate.

I think once you get the above resolved, the triggering of rules with your variables should hopefully be straight forward.

Hello @smar - now that you are saying that I guess this is definately an issue. I’m sending the json string to HT_Dimmer item which I have as a string.state so there for curstate is more then likely getting the NULL value due to this. I’ve updated my rule to the following. It seems I no longer get the error to show in the log, however the state of the light does not change.

/* CinemaVision Slow Dimmer */
rule "Home Theater Dimmer"
	when
		Item HT_Dimmer changed 
	then
		var Number curstate = 0
		if(Light_MasterBed_Lamp_Dim.state instanceof PercentType) curstate = Light_MasterBed_Lamp_Dim.state as PercentType
		
		var String json = (HT_Dimmer.state as StringType).toString
		// {"_type": "dimDown", "DimDownPercent": "70", "Increments": "5",
	    //    "speed": "400"}
	    var String type = transform("JSONPATH", "$._type", json)
	    if (type == "dimDown") {
	      var Number dimto = transform("JSONPATH", "$.DimDownPercent", json)
	      var Number inc = transform("JSONPATH", "$.increments", json)
	      var long speed = transform("JSONPATH", "$.speed", json)
	      
	      logInfo("Rules", "Dimmer set", dimto)
      logInfo("Rules", "Dimmer set", inc)
      logInfo("Rules", "Dimmer set", speed)
		  
		  while(curstate>dimto)

			{
		        curstate=curstate - inc
		        sendCommand(Light_MasterBed_Lamp_Dim, curstate)
		        Thread::sleep(speed)
			}
			
		}
		
end

Currently I have this at the top of my rules file.

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*

When resending the json after placing the logInfo I am not getting anything in the log at all, granted I’m not sure if I did that correctly

So here is an update and maybe someone can tell me why. In the while loop, I can not place ANY of the variables that I have declared. I verified that the value gets set by posting it to a variable and displaying the information with a sitemap. also another thing I was wondering that i have found is that every time I use post to trigger the rule I have to change the name of the type in order for it to reupdate. I can see this causing issues with how I am trying to use this. Though I could clear the HT_Dimmer State so each post is new, I can test this and I’m sure thats how I’ll do it. Another thing is I can’t seem to use the if statement. If I place my rule as this

/* CinemaVision Slow Dimmer */
rule "Home Theater Dimmer"
	when
		Item HT_Dimmer changed 
	then
		var Number curstate = 0
		if(Light_MasterBed_Lamp_Dim.state instanceof PercentType) curstate = Light_MasterBed_Lamp_Dim.state as PercentType
		
		var String json = (HT_Dimmer.state as StringType).toString
		// {"_type": "dimDown", "DimDownPercent": "70", "Increments": "5",
	    //    "speed": "400"}
	    var String type = transform("JSONPATH", "$._type", json)
	    
	      var Number dimto = transform("JSONPATH", "$.DimDownPercent", json)
	      var Number inc = transform("JSONPATH", "$.increments", json)
	      var long speed = transform("JSONPATH", "$.speed", json)
		  
		  while(curstate>50)

			{
		        curstate=curstate - 5
		        sendCommand(Light_MasterBed_Lamp_Dim, curstate)
		        Thread::sleep(500)
			}
		
end

If I use this rule I can get a slow dim from 100% or whatever I have the light as down to 50% slowly. Is the system limited to where this is something I can not do? It seems everything I have written within the above rule should work but it does not and when I break it down it comes down to variables not allowed to be used.

In JSON, capitalization is significant so this:

var Number inc = transform("JSONPATH", "$.increments", json)

needs to be this:

var Number inc = transform("JSONPATH", "$.Increments", json)

Then your extraction phase will be correct, at least in the way you’ve expressed it if the incoming data looks like your sample.

You can validate this by adding the following just after json is assigned.

logInfo('myrule', json)

Mark,

I figured that was the case, I’ve actually made sure that when I updated it they are 100% case correct. I noticed my comment was wrong but didn’t bother with it. I know the extraction works because if I did say this in my rule after the

var String type = transform(“JSONPATH”, “$._type”, json) with

postUpdate(Test_var, type) 

I created a string on my items file like so

String Test_var "Test Data output [%s]" 

Added to sitemap like so

Text Item=Test_var 

My sitemap shows that infact dimDown gets set to that variable and thats what displays. I can do that for each of the variables I pull out of the JSON string. The issue comes when trying to use those variables is where the issue now is. Nothing will trigger unless the value after curstate in the while loop start, the 5 and the 400. Unless those values are hardcoded nothing happens. Thats the issue I am currently having. I know the variables are there and set its just wont let me use them.

Here is what I did to the rule to verify the variables are saved and here is the logout put

Logfile

2015-09-16 00:51:42.665 [INFO ] [g.openhab.model.script.HT Rule] - JSON Information dimDown
2015-09-16 00:51:42.682 [INFO ] [g.openhab.model.script.HT Rule] - Entered Loop
2015-09-16 00:51:42.754 [INFO ] [g.openhab.model.script.HT Rule] - dimto variable = 50
2015-09-16 00:51:42.849 [INFO ] [g.openhab.model.script.HT Rule] - inc variable = 10
2015-09-16 00:51:42.920 [INFO ] [g.openhab.model.script.HT Rule] - speed variable = 400

2015-09-16 00:51:42.987 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule ‘Home Theater Dimmer’: Could not invoke method: org.openhab.model.script.lib.NumberExtensions.operator_greaterThan(java.lang.Number,java.lang.Number) on instance: null

Rule

/* CinemaVision Slow Dimmer */
rule "Home Theater Dimmer"
	when
		Item HT_Dimmer changed 
	then
		var Number curstate = 0
		if(Light_MasterBed_Lamp_Dim.state instanceof PercentType) curstate = Light_MasterBed_Lamp_Dim.state as PercentType
		
		var String json = (HT_Dimmer.state as StringType).toString
		// {"_type": "dimDown", "DimDownPercent": "70", "Increments": "5",
	    //    "speed": "400"}
	    var String type = transform("JSONPATH", "$._type", json)
	    logInfo("HT Rule", "JSON Information " + type)
	    if (type == "dimDown") {
	    logInfo("HT Rule", "Entered Loop")	
	      var Number dimto = transform("JSONPATH", "$.DimDownPercent", json)
	      logInfo("HT Rule", "dimto variable = " + dimto)	
	      var Number inc = transform("JSONPATH", "$.increments", json)
	      logInfo("HT Rule", "inc variable = " + inc)
	      var long speed = transform("JSONPATH", "$.speed", json)
	      logInfo("HT Rule", "speed variable = " + speed)
		  
		  while(curstate>dimto)

			{
		        curstate=curstate - inc
		        sendCommand(Light_MasterBed_Lamp_Dim, curstate)
		        Thread::sleep(speed)
			}
			
		}
		
end

The rule triggers, variable set however the while loop does not trigger. So I’m getting the null issue still but as you can see the log shows all of these variables were set.

I used the above as sample data, and then ran this block:

    var String type = transform("JSONPATH", "$._type", json)
    
      var Number dimto = transform("JSONPATH", "$.DimDownPercent", json)
      var Number inc = transform("JSONPATH", "$.increments", json)
      var long speed = transform("JSONPATH", "$.speed", json)

with a bunch of logInfo’s so I could see where it was failing… so I’m guessing your sample data-snippet is wrong (incorrect case) and the real data from the service has the correct case?

Either way, if Light_MasterBed_Lamp_Dim isn’t a PercentType, then it’ll bypass the code and curstate will be 0, so the latter dimming code will also be bypassed.

	if(Light_MasterBed_Lamp_Dim.state instanceof PercentType) curstate = Light_MasterBed_Lamp_Dim.state as PercentType

Put this code in a {} block, and add in the logInfo("HT Rule", "... setting curstate to " ...) calls so you can see what’s really going on.

posted again with some log outputs just above. Here is the line I am sending from postman to test

{"_type": “dimDown”, “DimDownPercent”: 50, “increments”: 10, “speed”: 400}

log file

2015-09-16 01:12:39.131 [INFO ] [g.openhab.model.script.HT Rule] - JSON Information dimDown1
2015-09-16 01:12:43.797 [INFO ] [g.openhab.model.script.HT Rule] - JSON Information dimDown
2015-09-16 01:12:43.810 [INFO ] [g.openhab.model.script.HT Rule] - Entered Loop
2015-09-16 01:12:43.886 [INFO ] [g.openhab.model.script.HT Rule] - dimto variable = 50
2015-09-16 01:12:43.987 [INFO ] [g.openhab.model.script.HT Rule] - inc variable = 10
2015-09-16 01:12:44.048 [INFO ] [g.openhab.model.script.HT Rule] - speed variable = 400
2015-09-16 01:12:44.072 [INFO ] [g.openhab.model.script.HT Rule] - curstate = 100
2015-09-16 01:12:44.092 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Home Theater Dimmer': Could not invoke method: org.openhab.model.script.lib.NumberExtensions.operator_greaterThan(java.lang.Number,java.lang.Number) on instance: null

rule

var String type = transform("JSONPATH", "$._type", json)
	    logInfo("HT Rule", "JSON Information " + type)
	    if (type == "dimDown") {
	    logInfo("HT Rule", "Entered Loop")	
	      var Number dimto = transform("JSONPATH", "$.DimDownPercent", json)
	      logInfo("HT Rule", "dimto variable = " + dimto)	
	      var Number inc = transform("JSONPATH", "$.increments", json)
	      logInfo("HT Rule", "inc variable = " + inc)
	      var long speed = transform("JSONPATH", "$.speed", json)
	      logInfo("HT Rule", "speed variable = " + speed)
		  
		  logInfo("HT Rule", "curstate = " + curstate)
		  
		  while(curstate > dimto)

			{
				logInfo("HT Rule", "While Loop Triggered")
		        curstate=curstate - inc
		        sendCommand(Light_LivRoom_AllLights_Dim, curstate)
		        Thread::sleep(speed)
			}
			
		}

Not sure why specifically, but using explicit type conversions works.

Here’s my sample block:

import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import java.lang.Integer

rule "JSON Test"
  when
    Time cron "0/10 * * * * ?"
  then
        var String json = '{"_type": "dimDown", "DimDownPercent": "70", "increments": "5", "speed": "400"}'

        var String typ = transform("JSONPATH", "$._type", json)
        var curstate = 100 
        var dimto = new Integer(transform("JSONPATH", "$.DimDownPercent", json))
        var inc = new Integer(transform("JSONPATH", "$.increments", json))
        var speed = new Integer(transform("JSONPATH", "$.speed", json))
        logInfo("jsont", "typ:" + typ)
        logInfo("jsond", "dim:" + dimto)
        logInfo("jsoni", "inc:" + inc)
        logInfo("jsons", "spd:" + speed)

        while (curstate > dimto) {
           logInfo('jsonw', 'Looping:' + curstate)
           curstate=curstate - inc
        }
end

Roll on Python Rule scripting, this will be a lot easier there… :wink:

Whats interesting is when I place this in designer its giving me this error and underlining it in red.

Missig error message for org.eclipse.xtext.xbase.validation.IssueCodes.invalid_generic_argument_types

HOWEVER, this works, soon as I placed that new Integer line in there it works like a charm. THANK YOU THANK YOU THANK YOU. This is going to make my Home Theater setup that much closer to an actual theater!

Great to hear that it’s working for you.

I tend to do it all cmdline, and have never gotten the Designer to work. It’s faster to rev-test-rev on the cmdline anyhow (and thick desktop clients are soooo 90’s :wink: )

1 Like

@guessed just a question, if I am using authentication how can I still put a get command?

It would depend upon how the auth was implemented in the target device. In most cases, it’s done as one-or-more HTTP Headers.

If it’s done that way, then the HTTP Binding supports a syntax for sending headers:

I was looking to see how I would send it to openhab if security was enabled. I got it to work but i decided I didnt need security internally just have it for external only. Thanks for the tip if I wanna send to securted devices though.