Doing a PUT command in LUA?

base_url = 'http://192.168.1.??/api/<yourAPIkeyhere>'

io = require('io')
http = require('socket.http')
ltn12 = require('ltn12')

function hueit(device, operation)
   -- Sends commands to Philips Hue bridge
   --  device is the device specific url i.e. /lights/1/state
   --  operation is the message body i.e. {"on":true, "sat":255, "bri":255,"hue":10000}
   --    for details see http://www.developers.meethue.com/documentation/getting-started
  local url = base_url .. '/lights/1/state'
  print(url)
  local req_body = '{"on":true}'
  local headers = {
    ["Content-Type"] = "application/x-www-form-urlencoded";
    ["Content-Length"] = #req_body;
  }
  client, code, headers, status = http.request{url=url, headers=headers, source=ltn12.source.string(req_body), method='PUT'}
  print(status)
  return
end
hueit()

This has worked well for me when it comes to the Philips Hue API.

1 Like

That, IMHO, is a good way to do it.

Caution, though, that the code fragment sends the wrong MIME type for the data being passed, which may not bother some targets, and would fail on others. For the type of data shown, it should be application/json.

1 Like

This is true, some servers don’t mind it and some do. Good call, but if you are going to use:
os.execute("curl -s -X PUT -H 'Content-Type: application/json' -d '{\"shade\":{\"motion\":\"jog\"}}' https://api-location-url/api/shades/shadeid")
I would consider using a timeout, otherwise you could end up causing Vera to restart.

Fryswatter, please don’t leave me hanging! How would I code a “timeout” in this instance?

Just add the timeout option to the command like below. I used -m for max time and the value of 5 seconds. 5 seconds should be plenty but you can adjust as needed. Safer to use the max timeout option rather than just a connection timeout as the max timeout covers the whole job timeout and not just the connection phase.

os.execute("curl -s -X PUT -H 'Content-Type: application/json' -m 5 -d '{\"shade\": 
{\"motion\":\"jog\"}}' https://api-location-url/api/shades/shadeid")

Thanks Fryswatter.
Couple more questions if you don’t mind.

When using the timeout, will that delay the code, ie, the code will not proceed until the timeout expires? I need to pause the code for a few moments, it seems that if the shade receives commands too quickly, they lock up and need to be recalibrated. (these are older shades, don’t know if the newer ones have this issue)

Inside the OS.COMMAND structure, at the end, there is the shadeid position. I’ve tried passing a named variable in this spot and it does not seem to respond. I have to “hard” code a number at that point in order to get it to work. Is there a way to pass a variable?

thanks again

Yes, os.execute is a system thread and should be considered blocking unless you specifically run it in the background. As for the “shadeid”, it would be more helpful to look at your code. Keep in mind to mask sensitive info such as your API key.

Here ya go, thanks for looking at this. Please bear in mind, I’m stumbling along in the dark as far as coding.

I’ve changed the IP address for this paste.
When I place the variable into the OS command, the code does nothing. If I replace the variable with the actual number (as I’ve done in the pasted code), it works fine.

code follows:

– 11/5/19
– 11/5 put timeout feature into os.command structure
– New Check Shade
– gets position of shade, moves shade, compares position
– if position is the same, then shade didn’t move, and needs to be re-calibrated.
– At the end of the scene return shade to closed position

local shadeid = 42654

local timeout = 5
local status, result0 = luup.inet.wget(“http://xxx.xxx.xxx.xxx/api/shades/” … shadeid, timeout)
– gets original status (result0)
– print (result0)

os.execute(“curl -s -X PUT -H ‘Content-Type: application/json’ -m 5 -d ‘{"shade":{"id":42654,"positions":{"position1":7943,"posKind1":1}}}’ http://xxx.xxx.xxx.xxx/api/shades/42654”)

– moves shade to position 7943
– waits 5 seconds

 local status, result1 = luup.inet.wget("http://xxx.xxx.xxx.xxx/api/shades/" .. shadeid, timeout)

– get updated status (result1)
– print (result1)

 if (result1 == result0)
 then

– shade did not move, run calibration

os.execute(“curl -s -X PUT -H ‘Content-Type: application/json’ -m 5 -d ‘{"shade":{"motion":"calibrate"}}’ http://xxx.xxx.xxx.xxx/api/shades/42654”)

 end

– return shade counter to zero (closed position)

os.execute(“curl -s -X PUT -H ‘Content-Type: application/json’ -m 5 -d ‘{"shade":{"id":42654,"positions":{"position1":0,"posKind1":1}}}’ http://xxx.xxx.xxx.xxx/api/shades/42654”)

Try putting a $ in front of the referenced variable within your data set. Not when you define the variable but only when referencing it in the command.

To put the variable timeout from your Lua code into the command line (all on one line):

os.execute("curl -s -X PUT -H 'Content-Type: application/json' -m " .. timeout .. " -d '{\"shade\":{\"id\":42654,\"positions\":{\"position1\":7943,\"posKind1\":1}}}' http://xxx.xxx.xxx.xxx/api/shades/42654")

You have to use proper escaping in these strings, too, because you are using both single and double quotes (for different reasons).

Also, when pasting a code block into these forums, it’s really a necessity that you precede the block with three backticks (```) on a line by itself, and follow it with the three backticks again to mark the end of the block, or the forum’s formatting will jack up the quotes and make the code invalid for you or anyone else attempting to copy/paste, or may hide certain special characters that we need to see…

Fryswatter, no matter how I tried, using the $ in front of the named variable (shadeNUM) in the pasted below code, I couldn’t get it to work. But the silver lining was that when I tried using $shadeNUM, it caused the shade to fail. This is a good thing strangely enough. The root cause of why I’m trying to do this scene/code is that some of my older shades fail, apparently randomly. This made working with the code very slow and frustrating. I would have to wait for a shade to fail before I could test my code in a real-life situation. But now, thanks to your suggestion, I can now cause the shade to fail on command and I am able to “proof” my code quickly! Seriously, thanks.

rigpapa, the inclusion of the variable … timeout … worked fine. In fact, I saw a similarity in structure with the double periods and was able to use the shadeNUM variable at the end of the OS statement.

So now, here’s the code that works.

local timeout = 5
local shadeNUM = 42654     

os.execute("curl -s -X PUT -H 'Content-Type: application/json' -m" .. timeout .. " -d '{\"shade\":{\"id\":42654,\"positions\":{\"position1\":7943,\"posKind1\":1}}}' http://xxx.xxx.xxx.xxx/api/shades/" .. shadeNUM)

I’m 2/3rds of the way there. I can use a variable at the end of the statement and as part of the command line.

What I need to do is pass the variable shadeNUM inside the data structure. In the updated code, where you see the number 42654, I need to be able to put the variable shadeNUM. $ didn’t work, two periods before and after didn’t work. Is there some punctuation that needs to be placed? Double quotes, etc, etc??

thanks

Good instincts there. You’re oh so close with that last step. I’m adding some line breaks here to make the code a little more manageable, and that may help make it more clear to see how this hangs together. I’ve also made the shade position a variable.

local timeout = 5
local shadeNUM = 42654     
local position = 7943

os.execute("curl -s -X PUT" ..
  " -H 'Content-Type: application/json'" ..
  " -m " .. timeout .. 
  " -d '{\"shade\":{\"id\":" .. shadeNUM .. ",\"positions\":{\"position1\":" .. position .. ",\"posKind1\":1}}}'" .. 
  " http://192.168.1.28/api/shades/" .. shadeNUM)

So now we’re using the .. (string concatenation) operator to build up the command line string from a series of smaller, easier to read strings.

You could also use the JSON library to create the data string from a Lua table, which for data much more complicated than this would probably be a good idea, but isn’t necessary for this rather simple query.

rigpapa,

I cut and pasted what you did and got the error message -

“[string “ALTUI - LuaRunHandler”]:7: ‘)’ expected (to close ‘(’ at line 5) near '” -m ‘… timeout …’“'”

But that’s OK. Breaking it apart into separate lines made the issue clear. I understood what you did (those damn quotes!), plugged the correct form into the original one line of code and it worked like a charm!

thanks again for all the help, I certainly couldn’t have gotten to this point without your help!

Think you missed something at the end of this line.
" -H 'Content-Type: application/json'" ..

Indeed! Corrected in situ.

Try that copy-paste again.

1 Like

Yup, the correction did the trick. Thanks akbooer.

The human “readability” of the string concatenation approach is much better.

And BTW, it’s heartening to see that even the “pros” can make errors in this stuff! I won’t feel quite as dumb the next time I spend hours troubleshooting a bit of code that doesn’t work, only to find out I misplaced a comma, or misspelled a variable!

If you want readability…

local timeout = 5
local shadeNUM = 42654     
local position = 7943
local url = "192.168.1.28"

local json = require "dkjson"
local Lua = {shade = {id = shadeNUM, positions = {position1 = position, posKind1 = 1}}}
local JSON = json.encode (Lua)

local command = "curl -s -X PUT -H 'Content-Type: application/json' -m %d -d '%s' http://%s/api/shades/%d"

os.execute (command: format (timeout, JSON, url, shadeNUM))

…well, I think it’s readable!

They are, in the end, only human. :grinning:

1 Like

Agreed. At some point, when the JSON payload is complex enough, it’s worth bringing in the JSON library letting it do some heavy lifting.

A post was split to a new topic: Controlling Smith & Noble Shades?