Escaping json in http post body in reactor activity

This should be simple but apparently so am I so I don’t know how to escape basic json being passed via Reactor in an http POST body - in a Reactor activity. Simple stuff like:

{
"user":"myuser",
"command":"The garage door was opened",
"broadcast":true
}

Tried various methods of escaping characters and Reactor barfs on each attempt. Have searched to no avail. Can someone tell me the proper way of handling this? Trying to eliminate a bunch of repetitive lua used to send broadcast messages to Google Home devices and using Reactor’s native http post ability instead.

There’s nothing in that JSON string that needs to be escaped. What are you trying to escape, and why?

You don’t describe the actual errors you were getting, nor did you post a Logic Summary of the ReactorSensor including your activity, so there’s not much to go on here. But one of the key things you might check is that you’re passing a correct content-type header (should be application/json if your body is JSON).

1 Like

Well, I guess I should have been more explicit - I thought my implication was clear: that I was getting an error in the logic summary report and assumed that escaping the brackets was necessary because of that error.

Here is the error from the logic summary report:

2021-07-25 00:46:22: TROUBLE: Evaluation error in “\“user\”: \“mwalker\”,\“command\”: \“The garage door has opened\”,\“broadcast\”: true”: [luaxp]Invalid operator at 7
2021-07-25 00:46:22: Request action: POST http://192.168.1.133:3000/assistant
2021-07-25 00:46:22: TROUBLE: Request failed, response status 400

It posts fine if I use another method.

And here is the screen:

The first log line is an expression that contains an error, which seems unrelated to the HTTP request, at least as presented.

The request action is being executed, the server is being contacted, and the server is returning an HTTP 400 return status code. This usually means your parameters are incorrect for the request. Either something is missing, or you’re otherwise not giving that endpoint what it expects.

The code below works correctly, so I don’t understand why it doesn’t in the form post, unless it is being changed along the way - which is why I assumed some characters needed to be escaped.

local http=require(“socket.http”)
local siteurl = “http://192.168.1.133:3000/assistant
local payload = [[ {“user”: “mwalker”,“command”: “The garage door has opened.”,“broadcast”: true} ]]
local respback = { }
local res, code, response_headers, status = http.request
{
url = siteurl,
method = “POST”,
headers =
{
[“Content-Type”] = “application/json”,
[“Content-Length”] = payload:len()
},
source = ltn12.source.string(payload),
sink = ltn12.sink.table(respback)
}
return true;

Post the full Logic Summary, following the instructions for posting in the summary header.

The error is at the bottom from when I’m testing the post method as opposed to the lua code I’m trying to eliminate.

*************************************************** REACTOR LOGIC SUMMARY REPORT ***************************************************
   Version: 3.8-20262 config 20190 cdata 20045 ui 20190 pluginDevice 96 LuaXP 1.0.2enh
    System: Vera version 1.7.5186 (7.31) on Sercomm G450 ID 36 (Vera Plus); loadtime 1627178888/1627178909; systemReady 1627178914
       Env: Lua 5.1; JSON dkjson 1.2; UnsafeLua=1/true
Local time: 2021-07-25T00:46:36-0600; DST=1; Denver, Colorado United States; formats %d/%m/%Y %H:%M:%S
House mode: plugin 1; system 1; tracking off
  Sun data: { "source": "int", "civdawn": 1627212120, "nautdawn": 1627209832, "sunset": 1627265990, "nautdusk": 1627270125, "stamp": 2021206, "latitude": 39.7392, "astrodusk": 1627272667, "longitude": -104.985, "civdusk": 1627267837, "astrodawn": 1627207289, "sunrise": 1627213967 }
  Geofence: not running
        RS: 1627157610,1627157693,1627158412,1627158444,1627158479,1627158582,1627158719,1627161660,1627163837,1627178909
        NS: 1617604260:D,1617604980:U,1619301360:D,1619302200:U,1621185360:D,1621185720:U,1623708600:D,1623708840:U,1623711665:D,1623716940:U
************************************************************************************************************************************
Google Voice Alerts (#186)
    Version 20045.20 07/25/21 00:46:09
    Message/status: Not tripped
    Condition group "Google Voice Alerts Overall" (AND)  false as of 14:19:38 <root>
      &-F-group "Garage Door" (AND)  false as of 20:22:20 <grp18yf6g45>
      |     &-F-service Garage Door (24) urn:micasaverde-com:serviceId:DoorLock1/Status isfalse  [0 => 1 at 20:22:20; F/F as of 20:22:20/20:22:20] <cond18yf6itp>
      &-F-group "Porch Motion" (AND)  false as of 17:08:53 <grp18yf989x>
      |     &-F-service Porch Motion Sensor (113) urn:micasaverde-com:serviceId:SecuritySensor1/Tripped istrue  [1 => 0 at 17:08:53; F/F as of 17:08:53/17:08:53] <cond18yfc6at>
      &-F-group "Trailer Motion" (AND)  false as of 14:33:04 <grp18yfquq5>
      |     &-F-service Trailer Motion Sensor (185) urn:micasaverde-com:serviceId:SecuritySensor1/Tripped istrue  [0 at 14:33:04; F/F as of 14:33:04/14:33:04] <cond18yfqxdp>
    Activity grp18yf6g45.false
        Run Lua:
             1: 
             2: local http=require("socket.http")
             3: local siteurl = "http://192.168.1.133:3000/assistant"
             4: local payload = [[ {"user": "mwalker","command": "The garage door was closed","broadcast": true} ]]
             5: local respback = { }
             6: local res, code, response_headers, status = http.request
             7: {
             8: url = siteurl,
             9: method = "POST",
            10: headers =
            11: {
            12: ["Content-Type"] = "application/json",
            13: ["Content-Length"] = payload:len()
            14: },
            15: source = ltn12.source.string(payload),
            16: sink = ltn12.sink.table(respback)
            17: }
            18: return true;
    Activity grp18yfquq5.true
        Run Lua:
             1: local http=require("socket.http")
             2: local siteurl = "http://192.168.1.133:3000/assistant"
             3: local payload = [[ {"user": "mwalker","command": "Motion near the travel trailer was detected","broadcast": true} ]]
             4: local respback = { }
             5: local res, code, response_headers, status = http.request
             6: {
             7: url = siteurl,
             8: method = "POST",
             9: headers =
            10: {
            11: ["Content-Type"] = "application/json",
            12: ["Content-Length"] = payload:len()
            13: },
            14: source = ltn12.source.string(payload),
            15: sink = ltn12.sink.table(respback)
            16: }
            17: return true;
    Activity grp18yf989x.true
        Run Lua:
             1: local http=require("socket.http")
             2: local siteurl = "http://192.168.1.133:3000/assistant"
             3: local payload = [[ {"user": "mwalker","command": "Porch motion was detected","broadcast": true} ]]
             4: local respback = { }
             5: local res, code, response_headers, status = http.request
             6: {
             7: url = siteurl,
             8: method = "POST",
             9: headers =
            10: {
            11: ["Content-Type"] = "application/json",
            12: ["Content-Length"] = payload:len()
            13: },
            14: source = ltn12.source.string(payload),
            15: sink = ltn12.sink.table(respback)
            16: }
            17: return true;
    Activity grp18yf6g45.true
        Action type request? method=POST, index=1, target=, url=http://192.168.1.133:3000/assistant, data={"user": "mwalker","command": "The garage door has opened","broadcast": true}, headers=table: 0x257c148
    Events
        2021-07-24 20:08:29: Reactor startup (Luup reload)
        2021-07-24 20:08:29: Starting (Luup Startup/Reload)
        2021-07-24 20:08:30: Sensor update starting
        2021-07-24 20:08:30: Sensor update completed; 0.013s
        2021-07-24 20:21:35: Device Garage Door (#24) urn:micasaverde-com:serviceId:DoorLock1/Status changed from "1" to "0"
        2021-07-24 20:21:35: Sensor update starting
        2021-07-24 20:21:35: Condition cond18yf6itp test state changed from false to true
        2021-07-24 20:21:35: Condition cond18yf6itp evaluation state changed from false to true
        2021-07-24 20:21:35: Group Garage Door test state changed from false to true
        2021-07-24 20:21:35: Group Garage Door evaluation state changed from false to true
        2021-07-24 20:21:35: Preparing Garage Door.true (grp18yf6g45.true) activity
        2021-07-24 20:21:35: Launching scene/activity grp18yf6g45.true
        2021-07-24 20:21:35: Starting "grp18yf6g45.true" group 1
        2021-07-24 20:21:37: Activity "grp18yf6g45.true" finished
        2021-07-24 20:21:37: Stopping activity "grp18yf6g45.true"
        2021-07-24 20:21:37: Sensor update completed; 2.067s
        2021-07-24 20:22:06: Device Garage Door (#24) urn:micasaverde-com:serviceId:DoorLock1/Status changed from "0" to "1"
        2021-07-24 20:22:06: Sensor update starting
        2021-07-24 20:22:06: Condition cond18yf6itp test state changed from true to false
        2021-07-24 20:22:06: Condition cond18yf6itp evaluation state changed from true to false
        2021-07-24 20:22:06: Group Garage Door test state changed from true to false
        2021-07-24 20:22:06: Group Garage Door evaluation state changed from true to false
        2021-07-24 20:22:06: Preparing Garage Door.false (grp18yf6g45.false) activity
        2021-07-24 20:22:06: Launching scene/activity grp18yf6g45.false
        2021-07-24 20:22:06: Starting "grp18yf6g45.false" group 1
        2021-07-24 20:22:08: Activity "grp18yf6g45.false" finished
        2021-07-24 20:22:08: Stopping activity "grp18yf6g45.false"
        2021-07-24 20:22:08: Sensor update completed; 2.180s
        2021-07-24 20:22:08: Device Garage Door (#24) urn:micasaverde-com:serviceId:DoorLock1/Status changed from "1" to "0"
        2021-07-24 20:22:08: Sensor update starting
        2021-07-24 20:22:08: Condition cond18yf6itp test state changed from false to true
        2021-07-24 20:22:08: Condition cond18yf6itp evaluation state changed from false to true
        2021-07-24 20:22:08: Group Garage Door test state changed from false to true
        2021-07-24 20:22:08: Group Garage Door evaluation state changed from false to true
        2021-07-24 20:22:08: Preparing Garage Door.true (grp18yf6g45.true) activity
        2021-07-24 20:22:08: Launching scene/activity grp18yf6g45.true
        2021-07-24 20:22:08: Starting "grp18yf6g45.true" group 1
        2021-07-24 20:22:10: Activity "grp18yf6g45.true" finished
        2021-07-24 20:22:10: Stopping activity "grp18yf6g45.true"
        2021-07-24 20:22:10: Sensor update completed; 1.968s
        2021-07-24 20:22:20: Device Garage Door (#24) urn:micasaverde-com:serviceId:DoorLock1/Status changed from "0" to "1"
        2021-07-24 20:22:20: Sensor update starting
        2021-07-24 20:22:20: Condition cond18yf6itp test state changed from true to false
        2021-07-24 20:22:20: Condition cond18yf6itp evaluation state changed from true to false
        2021-07-24 20:22:20: Group Garage Door test state changed from true to false
        2021-07-24 20:22:20: Group Garage Door evaluation state changed from true to false
        2021-07-24 20:22:20: Preparing Garage Door.false (grp18yf6g45.false) activity
        2021-07-24 20:22:20: Launching scene/activity grp18yf6g45.false
        2021-07-24 20:22:20: Starting "grp18yf6g45.false" group 1
        2021-07-24 20:22:22: Activity "grp18yf6g45.false" finished
        2021-07-24 20:22:22: Stopping activity "grp18yf6g45.false"
        2021-07-24 20:22:22: Sensor update completed; 1.924s
        2021-07-25 00:46:09: Configuration changed!
        2021-07-25 00:46:09: Sensor update starting
        2021-07-25 00:46:09: Sensor update completed; 0.013s
        2021-07-25 00:46:22: { dev=186, action="RunScene", options={ stopRunningScenes=true, forceReactorScenes=true, contextDevice=186, stopPriorScenes=false }, sceneId="grp18yf6g45.true", event="action", scene="grp18yf6g45.true" }
        2021-07-25 00:46:22: Launching scene/activity grp18yf6g45.true
        2021-07-25 00:46:22: Starting "grp18yf6g45.true" group 1
        2021-07-25 00:46:22: TROUBLE: Evaluation error in "\"user\": \"mwalker\",\"command\": \"The garage door has opened\",\"broadcast\": true": [luaxp]Invalid operator at 7
        2021-07-25 00:46:22: Request action: POST http://192.168.1.133:3000/assistant
        2021-07-25 00:46:22: TROUBLE: Request failed, response status 400
        2021-07-25 00:46:22: Activity "grp18yf6g45.true" finished
        2021-07-25 00:46:22: Stopping activity "grp18yf6g45.true"
    Devices
        Trailer Motion Sensor (185) urn:schemas-micasaverde-com:device:MotionSensor:1 (4/3); parent 183; plugin -; mfg  model ; dev D_MotionSensor1.xml impl 
        Garage Door (24) urn:schemas-micasaverde-com:device:DoorLock:1 (7/0); parent 0; plugin 2998; mfg  model ; dev D_GarageDoorLock.xml impl I_GarageDoorLock.xml
        Porch (111) urn:schemas-upnp-org:device:DigitalSecurityCamera:2 (6/-1); parent 0; plugin -; mfg  model ; dev D_DigitalSecurityCamera2.xml impl I_BlueIris.xml
        Trailer (183) urn:schemas-upnp-org:device:DigitalSecurityCamera:2 (6/-1); parent 0; plugin -; mfg  model ; dev D_DigitalSecurityCamera2.xml impl I_BlueIris.xml
        Porch Motion Sensor (113) urn:schemas-micasaverde-com:device:MotionSensor:1 (4/3); parent 111; plugin -; mfg  model ; dev D_MotionSensor1.xml impl 
    Watches
        Device #186 Google Voice Alerts service urn:toggledbits-com:serviceId:ReactorSensor variable cdata
        Device #185 Trailer Motion Sensor service urn:micasaverde-com:serviceId:SecuritySensor1 variable Tripped
        Device #186 Google Voice Alerts service urn:toggledbits-com:serviceId:ReactorSensor variable TestTime
        Device #24 Garage Door service urn:micasaverde-com:serviceId:DoorLock1 variable Status
        Device #113 Porch Motion Sensor service urn:micasaverde-com:serviceId:SecuritySensor1 variable Tripped
        Device #186 Google Voice Alerts service urn:toggledbits-com:serviceId:ReactorSensor variable TestHouseMode

OK. You’ll need to update to 3.10 stable branch release from Github:

  1. Open this page: GitHub - toggledbits/Reactor at stable
  2. Click the green “Code” button, and choose “Download ZIP”
  3. Once the ZIP file has downloaded, uncompress the downloaded archive. Remember what folder you did this in.
  4. Open Apps > Develop apps > Luup files in the Vera UI.
  5. Grab, as a group, all of the uncompressed files (not the folder, the files themselves) and drag them as a group to the “Upload” button in the Vera UI.
  6. When the upload completes, your Vera will reload.
  7. Important last step: hard refresh your browser

Done. No visible change in behavior. This one shows a timeout rather than the 400 error, but I checked the lua version and it still works fine.

*************************************************** REACTOR LOGIC SUMMARY REPORT ***************************************************
   Version: 3.10develop (21206) config 21129 cdata 20045 ui 21170 pluginDevice 96 LuaXP 1.0.2enh
    System: Vera version 1.7.5186 (7.31) on Sercomm G450 ID 36 (Vera Plus); loadtime 1627231500/1627231523; systemReady 1627231528
       Env: Lua 5.1; JSON dkjson 1.2; UnsafeLua=1/true
Local time: 2021-07-25T10:51:18-0600; DST=1; Denver, Colorado United States; formats %d/%m/%Y %H:%M:%S
House mode: plugin 1; system 1; tracking off
  Sun data: { "source": "int", "civdawn": 1627212120, "nautdawn": 1627209832, "sunset": 1627265990, "nautdusk": 1627270125, "stamp": 2021206, "latitude": 39.7392, "astrodusk": 1627272667, "longitude": -104.985, "civdusk": 1627267837, "astrodawn": 1627207289, "sunrise": 1627213967 }
  Geofence: not running
        RS: 1627158412,1627158444,1627158479,1627158582,1627158719,1627161660,1627163837,1627178909,1627230163,1627231523
        NS: 1617604260:D,1617604980:U,1619301360:D,1619302200:U,1621185360:D,1621185720:U,1623708600:D,1623708840:U,1623711665:D,1623716940:U
************************************************************************************************************************************
Google Voice Alerts (#186)
    Version 20045.23 2021-07-25 10:50:13
    Message/status: Not tripped
    Condition group "Google Voice Alerts Overall" (AND)  false as of 14:19:38 <root>
      &-T-group "Garage Door" (AND)  TRUE as of 10:16:49 <grp18yf6g45>
      |     &-T-service Garage Door (24) urn:micasaverde-com:serviceId:DoorLock1/Status isfalse  [1 => 0 at 10:16:49; T/T as of 10:16:49/10:16:49] <cond18yf6itp>
      &-F-group "Porch Motion" (AND)  false as of 17:08:53 <grp18yf989x>
      |     &-F-service Porch Motion Sensor (113) urn:micasaverde-com:serviceId:SecuritySensor1/Tripped istrue  [1 => 0 at 17:08:53; F/F as of 17:08:53/17:08:53] <cond18yfc6at>
      &-F-group "Trailer Motion" (AND)  false as of 14:33:04 <grp18yfquq5>
      |     &-F-service Trailer Motion Sensor (185) urn:micasaverde-com:serviceId:SecuritySensor1/Tripped istrue  [0 at 14:33:04; F/F as of 14:33:04/14:33:04] <cond18yfqxdp>
    Activity grp18yf6g45.false
        Action type request? method=POST, index=1, target=, url=http://192.168.1.133:3000/assistant, data= {"user": "mwalker","command": "The garage door was closed","broadcast": true}, headers=table: 0x1b514c0
    Activity grp18yfquq5.true
        Run Lua:
             1: local http=require("socket.http")
             2: local siteurl = "http://192.168.1.133:3000/assistant"
             3: local payload = [[ {"user": "mwalker","command": "Motion near the travel trailer was detected","broadcast": true} ]]
             4: local respback = { }
             5: local res, code, response_headers, status = http.request
             6: {
             7: url = siteurl,
             8: method = "POST",
             9: headers =
            10: {
            11: ["Content-Type"] = "application/json",
            12: ["Content-Length"] = payload:len()
            13: },
            14: source = ltn12.source.string(payload),
            15: sink = ltn12.sink.table(respback)
            16: }
            17: return true;
    Activity grp18yf989x.true
        Run Lua:
             1: local http=require("socket.http")
             2: local siteurl = "http://192.168.1.133:3000/assistant"
             3: local payload = [[ {"user": "mwalker","command": "Porch motion was detected","broadcast": true} ]]
             4: local respback = { }
             5: local res, code, response_headers, status = http.request
             6: {
             7: url = siteurl,
             8: method = "POST",
             9: headers =
            10: {
            11: ["Content-Type"] = "application/json",
            12: ["Content-Length"] = payload:len()
            13: },
            14: source = ltn12.source.string(payload),
            15: sink = ltn12.sink.table(respback)
            16: }
            17: return true;
    Activity grp18yf6g45.true
        Run Lua:
             1: local http=require("socket.http")
             2: local siteurl = "http://192.168.1.133:3000/assistant"
             3: local payload = [[ {"user": "mwalker","command": "The garage door has opened.","broadcast": true} ]]
             4: local respback = { }
             5: local res, code, response_headers, status = http.request
             6: {
             7: url = siteurl,
             8: method = "POST",
             9: headers =
            10: {
            11: ["Content-Type"] = "application/json",
            12: ["Content-Length"] = payload:len()
            13: },
            14: source = ltn12.source.string(payload),
            15: sink = ltn12.sink.table(respback)
            16: }
            17: return true;
    Events
        2021-07-25 10:45:23.413: Reactor startup (Luup reload)
        2021-07-25 10:45:23.673: Starting (Luup Startup/Reload)
        2021-07-25 10:45:24.312: Sensor update starting
        2021-07-25 10:45:24.329: Sensor update completed; 0.016s
        2021-07-25 10:50:13.678: Configuration changed!
        2021-07-25 10:50:13.709: Sensor update starting
        2021-07-25 10:50:13.829: Sensor update completed; 0.120s
        2021-07-25 10:50:24.323: { dev=186, action="RunScene", options={ stopRunningScenes=true, forceReactorScenes=true, contextDevice=186, stopPriorScenes=false }, sceneId="grp18yf6g45.false", event="action", scene="grp18yf6g45.false" }
        2021-07-25 10:50:24.324: Launching scene/activity "grp18yf6g45.false"
        2021-07-25 10:50:24.327: Starting "grp18yf6g45.false" group 1 at step 1
        2021-07-25 10:50:24.357: TROUBLE: Evaluation error in "\"user\": \"mwalker\",\"command\": \"The garage door was closed\",\"broadcast\": true": [luaxp]Invalid operator at 7
        2021-07-25 10:50:24.359: Request action: POST http://192.168.1.133:3000/assistant
        2021-07-25 10:50:39.376: TROUBLE: Request failed, response status timeout
        2021-07-25 10:50:39.379: Activity "grp18yf6g45.false" finished in 15.054000139236s
        2021-07-25 10:50:39.380: Stopping activity "grp18yf6g45.false"
        2021-07-25 10:50:56.301: { dev=186, action="RunScene", options={ stopRunningScenes=true, forceReactorScenes=true, contextDevice=186, stopPriorScenes=false }, sceneId="grp18yf6g45.true", event="action", scene="grp18yf6g45.true" }
        2021-07-25 10:50:56.301: Launching scene/activity "grp18yf6g45.true"
        2021-07-25 10:50:56.304: Starting "grp18yf6g45.true" group 1 at step 1
        2021-07-25 10:50:58.249: Activity "grp18yf6g45.true" finished in 1.9470000267029s
        2021-07-25 10:50:58.250: Stopping activity "grp18yf6g45.true"
    Devices
        Trailer Motion Sensor (185) urn:schemas-micasaverde-com:device:MotionSensor:1 (4/3); parent 183; plugin -; mfg  model ; dev D_MotionSensor1.xml impl 
        Garage Door (24) urn:schemas-micasaverde-com:device:DoorLock:1 (7/0); parent 0; plugin 2998; mfg  model ; dev D_GarageDoorLock.xml impl I_GarageDoorLock.xml
        Porch (111) urn:schemas-upnp-org:device:DigitalSecurityCamera:2 (6/-1); parent 0; plugin -; mfg  model ; dev D_DigitalSecurityCamera2.xml impl I_BlueIris.xml
        Trailer (183) urn:schemas-upnp-org:device:DigitalSecurityCamera:2 (6/-1); parent 0; plugin -; mfg  model ; dev D_DigitalSecurityCamera2.xml impl I_BlueIris.xml
        Porch Motion Sensor (113) urn:schemas-micasaverde-com:device:MotionSensor:1 (4/3); parent 111; plugin -; mfg  model ; dev D_MotionSensor1.xml impl 
    Watches
        Device #186 Google Voice Alerts service urn:toggledbits-com:serviceId:ReactorSensor variable cdata
        Device #185 Trailer Motion Sensor service urn:micasaverde-com:serviceId:SecuritySensor1 variable Tripped
        Device #186 Google Voice Alerts service urn:toggledbits-com:serviceId:ReactorSensor variable TestTime
        Device #24 Garage Door service urn:micasaverde-com:serviceId:DoorLock1 variable Status
        Device #113 Porch Motion Sensor service urn:micasaverde-com:serviceId:SecuritySensor1 variable Tripped
        Device #186 Google Voice Alerts service urn:toggledbits-com:serviceId:ReactorSensor variable TestHouseMode

Also tested it with an online tool and it was successful:

1 Like

It looks like you have a stray space at the front of the body string. Just take that out and it should work (that is, the first character in the string should be the opening curly brace). I’ll make it more tolerant for a future build.

1 Like

That fixed it - now works fine. Thanks!

1 Like