Doing a PUT command in LUA?

Is there any way to do a PUT command in LUA? I’m looking to send a command to my Hunter Douglas shade hub to execute a calibration command.

I’ve found the command at github, but it is not in LUA code. Here it is -

PUT /api/shades/<SHADE_ID>

// PUT body is raw JSON
{"shade":{"motion":"jog"}}

thanks

You could use the http module, but it’s probably easier to just use curl.

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

The trick here is getting the quotes right (and the escaping of quotes, where needed).

Hi,

You can many things in Lua, but needs some work if it is networking. Some thing like this could be a start for you:

local http = require("socket.http")
local ltn12 = require("ltn12")

        local result = {}
       -- Set headers as needed
	local headers = {
		['accept'] = 'application/json',
		['content-type'] = 'application/x-www-form-urlencoded',
	}	
        -- Set you request post data
	local request_body = "deviceId=" .. SMD
	headers["content-length"] = string.len(request_body)

	local retCode,HttpCode = http.request{
		url=URL, 
		method='POST',
		sink=ltn12.sink.table(result),
		source = ltn12.source.string(request_body),
		headers = headers
	}
	if (retCode ~= nil and HttpCode == 200) then
             -- process results
        end

Or indeed use curl if all you need is to send that PUT command.

Cheers Rene

Once again the community has come to my aid!

Yes, apparently all I need to do is send the PUT command, so it looks like the suggestion from rigpapa is the way I’d like to go.

Where would I define the IP address of the HD Hub? With my other communication with the HD Hub I would use the following format

Timeout=5
local status, result = luup.inet.wget(“http://xxx.xxx.x.xxx/api/scenes?sceneId=43542”, timeout)

(all this does is activate a scene (43542) on the HD Hub)

Are you sure that you need a PUT or is it POST command?

@reneboer’s suggestion is a POST. @rigpapa‘s is a PUT. You can just replace the url you need into @rigpapa’s code suggestion if it is a PUT. Look at the very end of the command where the url is shown.

If you look at my example, the end of the os.execute string begins “https://…”… that’s where you put the URL where the PUT needs to be sent. So, for example, if the HD Hub is at 192.168.0.111, then something like http://192.168.0.111/api/shades/SHADE_ID is likely to be the URL, based on the other information you’ve posted (put the shade ID where shown). It also may be http or https–you need to figure that out, too (but since it’s local, try plain http first).

rigpapa,

I didn’t notice that the line of code you sent has a scroll bar underneath it and there was more to the line!

The http vs the https made all the difference. IT WORKED!

Thank you very much!

1 Like
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.