Sending RESTful Commands using Racket

382 views
Skip to first unread message

bru...@gmail.com

unread,
Jun 17, 2015, 10:57:15 AM6/17/15
to racket...@googlegroups.com
Hello,

I'm new to programming, so patience is appreciated. I'm writing a simple program in Racket to control Phillip Hue Bulbs in a performance environment. Phillips has a simple RESTful API and I'm looking for the Racket commands or library to send the commands. Previously I've used AppleScript to launch bash curl commands, like:

curl -x PUT -d '{"on":true,"bri":170,"ct":500}' http://192.168.1.20/api/<username>/lights/8/state

Is there an easy way to send a similar message in Racket?

Thank you,
Bruce

Alexis King

unread,
Jun 17, 2015, 11:35:32 AM6/17/15
to bru...@gmail.com, racket...@googlegroups.com
You probably want to use the net/http-client library, specifically the http-sendrecv function. I’m not 100% sure, but I’d guess that the equivalent Racket code for your curl command would look something like this.

(require net/http-client
net/uri-codec)

(http-sendrecv
"192.168.1.20" "/api/<username>/lights/8/state"
#:method 'PUT
#:data
(alist->form-urlencoded
'((on #t)
(bri 170)
(ct 500)))
#:headers
'("Content-Type: application/x-www-form-urlencoded"))

See http://docs.racket-lang.org/net/http-client.html#%28def._%28%28lib._net%2Fhttp-client..rkt%29._http-sendrecv%29%29

John Clements

unread,
Jun 17, 2015, 1:48:53 PM6/17/15
to Alexis King, bru...@gmail.com, racket...@googlegroups.com

> On Jun 17, 2015, at 8:35 AM, Alexis King <lexi....@gmail.com> wrote:
>
> You probably want to use the net/http-client library, specifically the http-sendrecv function. I’m not 100% sure, but I’d guess that the equivalent Racket code for your curl command would look something like this.
>
> (require net/http-client
> net/uri-codec)
>
> (http-sendrecv
> "192.168.1.20" "/api/<username>/lights/8/state"
> #:method 'PUT
> #:data
> (alist->form-urlencoded
> '((on #t)
> (bri 170)
> (ct 500)))
> #:headers
> '("Content-Type: application/x-www-form-urlencoded"))
>
> See http://docs.racket-lang.org/net/http-client.html#%28def._%28%28lib._net%2Fhttp-client..rkt%29._http-sendrecv%29%29

Wait… http-client? That’s *way* better than all of the post-pure-port stuff I’ve been doing. How long has this been around? Okay, I probably don’t want to know.

Many thanks!

John Clements

bru...@gmail.com

unread,
Jun 17, 2015, 2:38:16 PM6/17/15
to racket...@googlegroups.com, bru...@gmail.com
Thanks so much; however, I'm still having trouble getting the lights to respond. I had to alter your example somewhat, because Racket was complaining about an "in-string: contract violation". The following seems to work:

(http-sendrecv
"192.168.1.95" "/api/<username>/lights/1/state"
#:method 'PUT
#:data
(alist->form-urlencoded
(list (cons 'bri "1")
(cons 'ct "500")))
#:headers
'("Content-Type: application/x-www-form-urlencoded"))

However, instead of affecting the light, I just get the following on the REPL:

#"HTTP/1.1 200 OK"
'(#"Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0"
#"Pragma: no-cache"
#"Expires: Mon, 1 Aug 2011 09:00:00 GMT"
#"Connection: close"
#"Access-Control-Max-Age: 3600"
#"Access-Control-Allow-Origin: *"
#"Access-Control-Allow-Credentials: true"
#"Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, HEAD"
#"Access-Control-Allow-Headers: Content-Type"
#"Content-type: application/json")
#<input-port:pipe>

I've also tried sending the message using:

#:data
(form-urlencoded-encode "\"bri\": 1")

and changing the #:headers to '("Content-Type: application/json")

Any thoughts?

My best,
Bruce

On Wednesday, June 17, 2015 at 11:35:32 AM UTC-4, Alexis King wrote:
> You probably want to use the net/http-client library, specifically the http-sendrecv function. I’m not 100% sure, but I’d guess that the equivalent Racket code for your curl command would look something like this.
>
> (require net/http-client
> net/uri-codec)
>
> (http-sendrecv
> "192.168.1.20" "/api/<username>/lights/8/state"
> #:method 'PUT
> #:data
> (alist->form-urlencoded
> '((on #t)
> (bri 170)
> (ct 500)))
> #:headers
> '("Content-Type: application/x-www-form-urlencoded"))
>
> See http://docs.racket-lang.org/net/http-client.html#%28def._%28%28lib._net%2Fhttp-client..rkt%29._http-sendrecv%29%29
>

John Clements

unread,
Jun 17, 2015, 2:50:36 PM6/17/15
to bru...@gmail.com, racket...@googlegroups.com

> On Jun 17, 2015, at 11:38 AM, bru...@gmail.com wrote:
>
> Thanks so much; however, I'm still having trouble getting the lights to respond. I had to alter your example somewhat, because Racket was complaining about an "in-string: contract violation". The following seems to work:

Oops forgot to cc: group:

Going back to your original message, it appears that the data was encoded as JSON, not using a urlencoding (that would make sense for a GET, but not for a PUT or POST).

Try using this code to generate the data:

#lang racket
(require json)

(jsexpr->string
(hash 'on #t
'bri 170
'ct 500))

Let me know if you want me to stuff it into the http-sendrecv call.

John
> --
> You received this message because you are subscribed to the Google Groups "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

bru...@gmail.com

unread,
Jun 17, 2015, 5:14:21 PM6/17/15
to racket...@googlegroups.com, bru...@gmail.com
John,

Thank you so much. That solved the problem of controlling the lights. However, I still can't figure out how to get at the response from the Hue Bridge. I should be receiving:

[
{"success":{"/lights/1/state/on":true}},
{"success":{"/lights/1/state/bri":170}},
{"success":{"/lights/1/state/ct":500}}
]

I'm having the same problem when I use GET to inquire about the state of the lights.

My best,
Bruce

On Wednesday, June 17, 2015 at 2:50:36 PM UTC-4, johnbclements wrote:

Jay McCarthy

unread,
Jun 17, 2015, 5:24:09 PM6/17/15
to bru...@gmail.com, racket...@googlegroups.com
The http-sendrecv function returns three values:

status : bytes?
header : (listof bytes?)
response : input-port?

You need to look at the third one for the response. You could pass it
to read-json or you could do port->string or anything else that you
can do with an input port.

Jay
--
Jay McCarthy
http://jeapostrophe.github.io

"Wherefore, be not weary in well-doing,
for ye are laying the foundation of a great work.
And out of small things proceedeth that which is great."
- D&C 64:33

Michael Wilber

unread,
Jun 17, 2015, 5:33:55 PM6/17/15
to bru...@gmail.com, racket...@googlegroups.com, bru...@gmail.com
If you're on linux, one dirty trick you could try is to start up a local
"web server" like netcat to just listen on the HTTP port and show you
the request that's happening:

nc -l -p 80

Then, point Curl and your Racket script to localhost and compare the
request sent by each.

bru...@gmail.com

unread,
Jun 19, 2015, 3:48:19 PM6/19/15
to racket...@googlegroups.com, bru...@gmail.com
Hi Jay,

Sorry for this newbie question, but how do I grab just the third value. Everything I try gets me an arity mismatch:

result arity mismatch;
expected number of values not received
expected: 1
received: 3
values...:
#"HTTP/1.1 200 OK"
'(#"Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-che...
#<input-port:pipe>

Thanks!

-Bruce

antoine

unread,
Jun 19, 2015, 4:08:38 PM6/19/15
to racket...@googlegroups.com
Hello,

Use (define-values (a b c) (http-send-recv ...)) as a remplacement for define when you receive multiple values.
Or (let-values ([(a b c) (http-send-recv ...)]) ...) the same idea but for let.

You can search for binding ending with '-values'.

see http://docs.racket-lang.org/reference/eval-model.html?q=values#%28part._values-model%29
and http://docs.racket-lang.org/reference/values.html?q=values#%28def._%28%28quote._~23~25kernel%29._values%29%29

example:

(define-values (a b c) (values 1 2 3))
(define a (values 1 2 3)) ;; error expect one returned value get 3.
Reply all
Reply to author
Forward
0 new messages