Help MAP transformation with HTTP Binding OH3

Just slowly migrating things from an almost purely text based OH2 (2.5.10) setup to OH3 (currently M3)

I have been stuck with the http binding for several hours and can’t work out how to apply mappings.

When I create and link the item as a string, I get the desired result but when I set it up as a switch I always get UNDEF (which probably makes sense). Furthermore when I try to apply a MAP transformation, and whenever I add that to the configuration, I get Error, even for the string item. In the logs, no matter what I try, I get the following:

23:21:10.357 [WARN ] [rofiles.JSonPathTransformationProfile] - Could not transform state 'Error' with function '$.data.last_data.DA.amOn' and format '%s'

In OH2, this is my setup:
http service:

actron.url=http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy{Referrer=http://actron.ninja.is/}
actron.updateInterval=10000

Item:

String	hvac_webserver_power	"The System is: [MAP(actron.map):%s]"				                                        	{http="<[actron:4000:JSONPATH($.data.last_data.DA.amOn	)]"}

Via rules I sent commands to the particular endpoint such as:

sendHttpPutRequest("http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy", "application/json", '{"DA":{"amOn":"true"}}',3000)

I thought it would be possible and great to roll everything together using the new GUI and use the opportunity to get rid of a few rules. I was hoping to bundle the on and off commands (true and false) into a switch rather than relying on rules.

My OH3 setup:
Thing:

Base URL: http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy
Refresh Time: 30
Timeout: 3000
Authentication Mode: Basic
Command Method: GET
Content Type: application/json

Channel Setup

Item Setup

MAP file (actron.map)

false=Off
true=On

In that last screenshot there are parentheses around the actron.map but I have tried MAP:actron.map and with it removed.

Is it possible to do what I am doing and if so if I could get a bit of guidance on how to make it would I would be very appreciative.

It’s most useful to us to click on the code tab and paste in the YAML shown there instead of screenshots.

Basic stuff first. The Map transform is installed?

Why do you have a JSONPATH Profile set up on the link? That’s the root of the error. The Map transformation on the Thing is returning “Error” which of course is not valid JSON so it cannot extract $.data.last_data.DA.amOn from “Error”.

Do you mean extract the value from the JSON first and then do a Map? If so you need to use the JSONPATH at the Thing and then the Map profile at the Link. Though I don’t know if the changes have been merged yet to allow Profiles to work with Items other than just String Items. Give it a try and see.

Thanks for your advice @rlkoshak

It seems the ability to map items other than strings was merged:

In any event, your advice helped somewhat. I swapped the State Transformation on the Channel to the JSONPATH transform and then on the item to the MAP transform and it works. Sort of. However, even though the ON and OFF states are correctly reflecting the state of the device, there continues to be an error in the logs:

19:46:31.018 [WARN ] [rofiles.JSonPathTransformationProfile] - Could not transform state 'ON' with function '$.data.last_data.DA.amOn' and format 'MAP(actron.map):%s'

If it helps, the webserver returns the following:

{"result":1,"error":null,"id":0,"data":{"vid":2,"did":4,"device_type":"airconditioner","default_name":"Air Conditioner Settings","tags":"aircon","is_sensor":1,"is_actuator":1,"is_silent":0,"has_time_series":0,"has_subdevice_count":0,"has_state":0,"gid":"0","node":"xxx","shortName":"Air Conditioner Settings","meta":{},"subDevices":{},"last_data":{"DA":{"amOn":true,"tempTarget":25.5,"mode":2,"fanSpeed":0,"enabledZones":[1,1,0,0,0,0,0,0]},"timestamp":1606812545658}}}

The bit I am interested in is the last_data part.

The code for the thing is below:

UID: http:url:actron
label: HTTP Actron Webserver
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy
  refresh: 30
  commandMethod: PUT
  contentType: application/json
  timeout: 3000
channels:
  - id: power
    channelTypeUID: http:switch
    label: Power
    description: ""
    configuration:
      onValue: "true"
      mode: READONLY
      offValue: "false"
      stateTransformation: JSONPATH:$.data.last_data.DA.amOn

At this point I’m obviously just trying to read the data and then I’ll want to focus on sending commands.

(edit: yep, MAP and JSONPATH transforms are installed)

I don’t see any problem with the Thing and channel (but I haven’t moved to OH3 yet, and don’t use the http binding). From the log it seems like the problem is in the Item definition, and specifically the link where it seems like you’re using the Jsonpath transformation profile, which shouldn’t be needed since you have that configured on the Thing.

Anders is right. The problem is with the Link and the profile. It looks like the JSONPATH and onValue parameters are working correctly because the Item state is ON. And of course “ON” isn’t JSON so that JSONPATH fails.

Another interesting bit is the format. That’s for an Item label and never appropriate for a transformation or the transform profile. So there is likely other stuff wrong with the Item definition as well.

So it works now. I kept on making changes to the item including stripping everything back to the Thing level but the logs kept on complaining about the map:

9:46:31.018 [WARN ] [rofiles.JSonPathTransformationProfile] - Could not transform state 'ON' with function '$.data.last_data.DA.amOn' and format 'MAP(actron.map):%s

Even after stripping everything back to the Thing level and recreating them I kept on getting the same error, so I did the clean-cache, recreated the channel and the item and BAM! it works with no errors. I didn’t think I’d need to resort to clearing the cache given this is mostly UI based so not sure if that was ignorance on my part or a bug.

The final setup for the channel was to put my JSONPATH transformation in the State Transformation section and in the item I left the Profile as Default.

I have now been trying unsuccessfully to use the switch to send true or false to the webserver. In OH2 I do it this way through a rule and a virtual switch:

sendHttpPutRequest("http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy", "application/json", '{"DA":{"amOn":"true"}}',3000)

or

curl -v -H "Accept: application/json" -H "Content-Type: application/json" -X PUT -d '{"DA":{"amOn":true}}' http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy

Is this still the best way to do it going forwards or can I use the binding?

I’ve tried adding various combinations to the Command URL Extension but I’m not really sure what I’m doing. I’m not sure how to convert the senHttpPutRequest command into something accepted by the binding.

The API for this device isn’t published and I mostly reverse-engineered it through trial and error (was actually my first device in OH2).

Any ideas are appreciated

You can use the binding. Unfortunately I don’t have any experience with the new HTTP binding so I can’t say what you need to do, but I believe you can set all the header stuff as you need it and you’ve use the ON value and OFF value parameters to convert the commands to the JSON I think. Or you can use an outgoing transform and use the Map Transformation to do the same.

Thanks for the help so far. I was able to make the item display the correct state but I just can’t successfully send a command. I’ve reviewed the documentation and the entirety of this post but there must be something I am just not understanding:

I’m not sure what I’m doing wrong but would very much appreciate help from anyone knowledgeable with the HTTP binding. If I need to add extra information to help others help me I am happy to do that - just let me know.

This is how the channel is configured currently:

UID: http:url:actron
label: HTTP Actron Webserver
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  headers:
    - Accept=application/json
  baseURL: http://actron.ninja.is
  password: xxx
  refresh: 10
  commandMethod: PUT
  contentType: application/json
  timeout: 3000
  username: xxx
channels:
  - id: power
    channelTypeUID: http:switch
    label: Power
    description: ""
    configuration:
      onValue: "true"
      commandTransformation: JSONPATH:$.data.last_data.DA.amOn
      offValue: "false"
      stateExtension: /rest/v0/device/xxxx?user_access_token=yyyy
      commandExtension: /rest/v0/device/xxxx?user_access_token=yyyy
      stateTransformation: JSONPATH:$.data.last_data.DA.amOn

And this is the error in the log when I flick the switch:

23:17:03.919 [WARN ] [l.transform.SingleValueTransformation] - Executing transformation ChannelStateTransformation{pattern='$.data.last_data.DA.amOn', serviceName='JSONPATH'} failed: Invalid path '$.data.last_data.DA.amOn' in 'false'

The thing is if I change the onValue or offValue the Item goes into UNDEF.

With regards to the way the item is set up it’s just a switch with no profile currently, but I’ve tried lots of various combinations so there’s not much point in posting it - i’m just down to trying random stuff now hoping to see some different behaviour.

What is the expected output?

Hi Jan

Thanks for replying. I’m so sorry, I’m not sure what you mean by the expected output. If these comments don’t answer the question please let me know.

This is currently working in OH 2.5:

sendHttpPutRequest("http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy", "application/json", '{"DA":{"amOn":"true"}}',3000)

and this works as well:

curl -v -H "Accept: application/json" -H "Content-Type: application/json" -X PUT -d '{"DA":{"amOn":true}}' http://actron.ninja.is/rest/v0/device/xxx?user_access_token=yyy

Basically this PUT command sends the JSON payload to the cloud server which is then queried by my air-conditioner and the webserver returns this:

{"result":1,"error":null,"id":0,"data":null}

The GET which is actually working as configured in the post several up returns this:

{"result":1,"error":null,"id":0,"data":{"vid":2,"did":4,"device_type":"airconditioner","default_name":"Air Conditioner Settings","tags":"aircon","is_sensor":1,"is_actuator":1,"is_silent":0,"has_time_series":0,"has_subdevice_count":0,"has_state":0,"gid":"0","node":"xxx","shortName":"Air Conditioner Settings","meta":{},"subDevices":{},"last_data":{"DA":{"amOn":true,"tempTarget":25.5,"mode":2,"fanSpeed":0,"enabledZones":[1,1,0,0,0,0,0,0]},"timestamp":1606812545658}}}

If I have not answered your question, please let me know and I’ll get that information

    configuration:
      onValue: "true"
      commandTransformation: JSONPATH:$.data.last_data.DA.amOn
      offValue: "false"

Is this supposed to be a transformation for the outgoing value? In that case I don’t think JSONPATH can be used to encode json, just decode it.

Invalid path '$.data.last_data.DA.amOn' in 'false'

What this means is that jsonpath is trying to find the key $.data.last_data.DA.amOn in the string false which of course won’t work.

I believe you would have to set onValue to ‘{“DA”:{“amOn”:true}}’, and offValue to ‘{“DA”:{“amOn”:false}}’. Not sure how to set the incoming transformation though.

1 Like

I thought the onValue and offValue was used for both the incoming and outgoing commands? But now I’m not sure. In testing, with the onValue set “true” and the offValue set “false”, the item state is correctly represented in MainUI.

If I change this to anything else, the item becomes UNDEF. This includes onValue = ‘{“DA”:{“amOn”:true}}’ and offValue = ‘{“DA”:{“amOn”:true}}’

I went ahead and set the on and off values as suggested:

22:26:34.359 [WARN ] [l.transform.SingleValueTransformation] - Executing transformation ChannelStateTransformation{pattern='$.data.last_data.DA.amOn', serviceName='JSONPATH'} failed: Invalid path '$.data.last_data.DA.amOn' in ''{"DA":{"amOn":true}}''
22:26:35.328 [WARN ] [l.transform.SingleValueTransformation] - Executing transformation ChannelStateTransformation{pattern='$.data.last_data.DA.amOn', serviceName='JSONPATH'} failed: Invalid path '$.data.last_data.DA.amOn' in ''{"DA":{"amOn":false}}''

I also removed the ’ character and got this:

22:27:17.837 [WARN ] [l.transform.SingleValueTransformation] - Executing transformation ChannelStateTransformation{pattern='$.data.last_data.DA.amOn', serviceName='JSONPATH'} failed: Invalid path '$.data.last_data.DA.amOn' in '{"DA":{"amOn":true}}'
22:27:18.324 [WARN ] [l.transform.SingleValueTransformation] - Executing transformation ChannelStateTransformation{pattern='$.data.last_data.DA.amOn', serviceName='JSONPATH'} failed: Invalid path '$.data.last_data.DA.amOn' in '{"DA":{"amOn":false}}'

I’m not sure if I am meant to apply a mapping in the item - possibly I am but I am not sure how to do this correctly. At the moment the profile is set to default, however i have tried various iterations of JSONPATH and MAP.

I also found sometimes even though I had changed a profile in MainUI, I kept on getting an error for a mapping i had removed. In this example, I get this error even though the profile was set to Default:

22:16:10.125 [WARN ] [nal.profiles.MapTransformationProfile] - Could not transform state 'UNDEF' with function 'MAP:onoff.map' and format '%s'

Only a clean-cache would fix this so sometimes I’m not even confident that the changes I am making are actually being reflected in the channel/item.

The problem is that you have specified a jsonpath transformation on the command, i.e. it sends the onValue/offValue to jsonpath before the request is sent. The jsonpath transformation then tries to find the specified key: $.data.last_data.DA.amOn, which doesn’t exist, so it fails. I believe it then returns the original string which is then sent in the request.

What might work is if you change that to a MAP transform with a .map file like this:

true = {"DA":{"amOn":true}}
false = {"DA":{"amOn":false}}

and keep onValue=‘true’ and offValue=‘false’

Yes!! That’s done it. Thank you for that suggestion. I didn’t need to do anything with the item - all the configuration was on the channel.

UID: http:url:actron
label: HTTP Actron Webserver
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  headers:
    - Accept=application/json
  baseURL: http://actron.ninja.is
  password: xxxx
  refresh: 10
  commandMethod: PUT
  contentType: application/json
  timeout: 3000
  username: xxxx
channels:
  - id: power
    channelTypeUID: http:switch
    label: Power
    description: ""
    configuration:
      onValue: "true"
      commandTransformation: MAP:onoff.map
      stateExtension: /rest/v0/device/xxxx?user_access_token=yyyy
      offValue: "false"
      commandExtension: /rest/v0/device/xxxx?user_access_token=yyyy
      stateTransformation: JSONPATH:$.data.last_data.DA.amOn

onoff.map is like this:

true={"DA":{"amOn":true}}
false={"DA":{"amOn":false}}

This is exactly how you said. It was also stated in the other thread I posted and I admit I did try it, but I was doing it wrong. The things I was missing was a) no config on the item and b) I was trying to do ON = {“DA”… rather than true = {“DA”.

Thank you - I’ve never been as stuck with something as I was with this. Thank you and thank you again.

1 Like

I’m sorry for coming back again so quickly, but I am stuck again and I’m not sure whether what I am trying to do is possible.

I have converted most of my HVAC items from OH2 into OH3 without needing any rules so far, which is really great, except for this next one. In OH2 I have this rule:

rule "Set HVAC SetPoint"
	when
		Item hvac_setpoint_webserver received command
	then
		sendHttpPutRequest("url", "application/json", '{"DA":{"tempTarget":'+receivedCommand+'}}',4000)
		logInfo("Crib","Temperature changed to: "+receivedCommand)
end

With the approach I have been following so far, in the MAP file, I would need a separate line for each temperature value, in 0.5 increments. With a proposed temperature range of 20-30, this would require 20 entries and doesn’t seem very efficient. Is there a way to use a variable instead (as per ‘receivedCommand’ above)?

I tried both receivedCommand and %2$s in the mapping file (just from reading the forums) but that got me the error below, which seems to indicate that it is looking for the value 18 in the mapping file:

20:46:26.705 [WARN ] [orm.AbstractFileTransformationService] - Could not transform '18' with the file 'actron_tempTarget.map' : Target value not found in map for '18'

If it’s like in your previous case that the outgoing and incoming data have different formats, I would suggest looking into the javascript transformation, where you could easily encode json.

As per @pacive suggestion I was able to use a javascript transformation to encode the json. cf

Just updating here in case it helps someone else

For now, I didn´t find the solution for me. Maybe someone can help me.
Recieving the state is working as well, but the sending command doesn´t work.
For ON = set/tr-064.6.states.reboot?value=true
for OFF = set/tr-064.6.states.reboot?value=false
The baseURL is fine.

Type switch : FritzRestartRepeater600Bad "Fritz 600Bad Neustart .7" [
			mode = "READWRITE",
			stateExtension = "get/tr-064.6.states.reboot" ,
			onValue = "true", 
			commandTransformation = "MAP:123.map",
			commandExtension = "set/tr-064.6.states.reboot?value=%2$s",
			offValue = "false",
			stateTransformation ="JSONPATH:$.val"  
		]

The 123.map:

ON=true
OFF=false
true=ON
false=OFF

Someone is seen, what is wrong in my config?
Thx Joe