Sunday, July 19, 2015

LittleBits CloudBit with Python Requests - Part 2 Input and Output

In part 1 of the blog, I gave an example to get started on the LittleBits CloudBit and query the device status. In part 2, let's look at how to get input and output from the CloudBit. In its current iteration, there is only one physical input and one physical output.

In my setup I have an 'i3 button' as the input and 'o9 bargraph' as the output:



In the v2 documentation, there was no specific documentation on input, just output. 


After opening a case with LittleBits, I was given the pre-release v3 API documentation (Thanks! Theodore from LittleBits): 


Following the example on the page, the input and output sample are below (note the different v3 endpoint than v2): 
  • Output
$ curl -i -XPOST -H "Authorization: Bearer <token>" https://api-http.littlebitscloud.cc/v3/devices/<id>/output -d percent=50 -d duration_ms=5000
HTTP/1.1 200 OK
access-control-allow-headers: Authorization, Content-Type, If-None-Match
access-control-allow-methods: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
access-control-allow-origin: *
access-control-expose-headers: WWW-Authenticate, Server-Authorization
access-control-max-age: 86400
cache-control: no-cache
content-type: application/json; charset=utf-8
Date: Mon, 20 Jul 2015 04:47:37 GMT
Content-Length: 16
Connection: keep-alive

{"success":true}


  • Input (stream)
$ curl -i -H "Authorization: Bearer <token>" https://api-http.littlebitscloud.cc/v3/devices/<id>/input
HTTP/1.1 200 OK
access-control-allow-headers: Authorization, Content-Type, If-None-Match
access-control-allow-methods: GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS
access-control-allow-origin: *
access-control-expose-headers: WWW-Authenticate, Server-Authorization
access-control-max-age: 86400
cache-control: no-cache
content-encoding: identity
content-type: text/event-stream; charset=utf-8
Date: Mon, 20 Jul 2015 04:49:44 GMT
transfer-encoding: chunked
Connection: keep-alive

data:{"type":"input","timestamp":1437367785035,"from":{"socket":{"remotePort":<port>,"remoteAddress":"<id>","localAddress":"<addr>","localPort":<port>,"id":"<id>"},"server":{"id":"<id>"},"user":{"id":<id>},"device":{"id":"<id>","device":"littlebits-module-cloud","setup_version":"1.0.0","protocol_version":"1.1.0","firmware_version":"1.0.140820b","mac":"<mad>","hash":"<hash>","ap":{"ssid":"<ssid>","mac":"<mac>","strength":<strength>},"settings":{"label":"<label>","input_interval_ms":750}},"is_identified":true},"percent":0,"absolute":0,"name":"amplitude","payload":{"percent":0,"absolute":0}}

Of which, when press the button, the payload percent and absolute value changes accordingly. 



Converting the two Input and Output Curl to Python were not too difficult, thanks again to the awesome Request package: 

  • Output Script

#!/usr/env/bin python

import json
import requests

deviceId = "<id>"
littleBitsOutputUrl = "https://api-http.littlebitscloud.cc/v3/devices/" + deviceId + "/output"
authToken = "<token>"

headers = {"Authorization": "Bearer "+authToken, "Content-type": "application/x-www-form-urlencoded"}

body = "percent=50&duration_ms=5000"

r = requests.post(littleBitsOutputUrl, headers=headers, data=body)
print r.status_code

- Execution

$ python CloudbitOputput.py
200

  • Input Script
#!/usr/env/bin python

import json
import requests, time

deviceId = "<id>"
littleBitsInputUrl = "https://api-http.littlebitscloud.cc/v3/devices/" + deviceId + "/input"
authToken = "<token>"

headers = {"Authorization": "Bearer "+authToken}

r = requests.get(littleBitsInputUrl, headers=headers, stream=True)

for line in r.iter_lines():
    if line:
        result = json.loads(line.split('data:')[1])
        print result['payload']


- Execution

$ python CloudbitInput.py
{u'percent': 0, u'absolute': 0}
{u'percent': 0, u'absolute': 0}
{u'percent': 0, u'absolute': 0}
{u'percent': 100, u'absolute': 1023}
{u'percent': 100, u'absolute': 1023}
{u'percent': 100, u'absolute': 1023}
{u'percent': 100, u'absolute': 1023}
{u'percent': 100, u'absolute': 1023}
{u'percent': 100, u'absolute': 1023}
{u'percent': 0, u'absolute': 0}
{u'percent': 0, u'absolute': 0}
{u'percent': 0, u'absolute': 0}


Happy Coding!





4 comments:

  1. Thanks! It worked just as advertised when I ran the script in python 2 but I wanted to alert you and your readers that when I tried it in python3 I got the following error reading the input :

    ---------------------------------------------------------------------------
    TypeError Traceback (most recent call last)
    in ()
    6 for line in r.iter_lines():
    7 if line:
    ----> 8 result = json.loads(line.split('data:')[1])
    9 print(result['payload'])

    TypeError: 'str' does not support the buffer interface

    According to [this stackoverflow answer](http://stackoverflow.com/questions/26945613/str-does-not-support-the-buffer-interface-python3-from-python2) it is because "Python 2, bare literal strings (e.g. 'string') are bytes, whereas in Python 3 they are unicode.". I haven't figured out how to fix that problem quite yet but I'll stick to python2 in the meantime..

    One more question. Suppose you don't want a constant stream but you just want to poll the device to just find out the value of the current input (say a temperature, or light sensor reading at just 4pm daily). I can think of ways to adapt your code to break out of the for loop, but is there a more elegant way? On the request call you set the parameter stream=True. Do you know if there is a 'non-stream' options?

    Thanks again!

    ReplyDelete
  2. Unfortunately my cloudBit is not working at this time, just open a case with LittleBits, so I can't test this out. But I think you just need to specify the encoding for line, i.e. line.encode("utf-8") then Python 3 should work as well. Like a lot of people, I have not transition to Python 3 yet.

    For the second question about current input, since the data is not stored anywhere, the easiest way I would do is just to cron (set execution of the script at specific time) if you are no Mac or Linux or the equivalent on Windows to execute the script at 4pm daily. Or you can continue to stream the data and write the output to a simple text file (along with timestamp) or simple database.

    Hope it helps.

    ReplyDelete
  3. That helps me understand better. Thanks again.
    Re my question about a single (non-streaming) poll response on the current input value, Turns out the issue has been discussed on github with the littlebits team
    https://github.com/littlebits/cloud-platform/issues/1
    but from what I can follow there was, as of that time, still no easy non-streaming option (at least one that works with available python wrappers).

    ReplyDelete
  4. Hi Jonathan, the nice folks at LittleBits is RMA'ing the old cloudBit. The new one should arrive soon and I will try it out with some IoT streaming and display solutions like Xively or Push.

    ReplyDelete