Unable to talk to tinker firmware w/ Python via REST

I must be missing something pretty simple. I’ve got pretty good experience with Arduino, web/ip standards and python, but I just cannot make this work.

I’ve got the latest tinker firmware installed on my core (sourced from the github page). The tinker iPhone app seems to work fine (turns on D7 when requested, etc). However, for the life of me, I cannot get my own REST queries to make the core do anything.

I’m using the following bit of code:

from hammock import Hammock
from pprint import pprint

spark = Hammock('https://api.spark.io/v1')
access_token='TOKEN'
device_id='ID'

me = spark.devices(device_id).digitalwrite.POST(params={'access_token':access_token,'args': 'D7,HIGH'})

Now, for those of you not familiar with the Hammock library, its just an easy way to use RESTful APIs. The resulting URL request that comes out of that is:

POST https://api.spark.io/v1/devices/DEVICEID/digitalwrite?access_token=TOKEN&args=D7%2CHIGH'

That seems pretty reasonable to me given the API documentation, but I always end up with a -1 return value:

{u'connected': True,
 u'id': u'DEVICEID',
 u'last_app': None,
 u'name': u'cowgerhome',
 u'return_value': -1}

I’ve gone through the digital write() function in the tinker firmware and change it such that each failure condition returns a different negative value, and continue to end up with ‘-1’, indicating that this snippet is somehow failing:

int pinNumber = command.charAt(1) - '0';	
if (pinNumber< 0 || pinNumber >7) return -1;

I’ve even gone so far as to make that function statically return ‘42’ as the only statement. THAT seems to work just fine, so something is going on with the snippet above - but its the stock code, so there must be something wrong in my request. Ideas?

What is this line supposed to do?

I honestly don’t know. Its in the tinker firmware: https://github.com/spark/core-firmware/blob/master/src/application.cpp (line 94)

Ah, ok. It converts to an integer relying on the fact that the numeric values of ascii strings are sequential.

I think the issue is that you are using “args” instead of “params” in your query string ;).

{'access_token':access_token,'args': 'D7,HIGH'})

args should be params :wink:

oh whoops, @amanfredi you beat me to it!

I will give that a shot whenI get home, but the documentation is inconsistent. For example, here is says args:

http://docs.spark.io/#/api

EXAMPLE REQUEST

curl https://api.spark.io/v1/devices/0123456789abcdef01234567/brew \
     -d access_token=1234123412341234123412341234123412341234 \
     -d "args=202,230"

but here it says params:
http://docs.spark.io/#/start/tinkering-with-tinker-the-tinker-api

curl https://api.spark.io/v1/devices/0123456789abcdef01234567/analogwrite \
  -d access_token=1234123412341234123412341234123412341234 -d params=A0,215

The url gets encoded and the comma between D7 and HIGH becomes %2C. Have you tried manually preparing the URL?

thats standard URL encoding - even if you paste it into a browser, the browser will do the same thing under the hood. I would expect the spark.io cloud do handle that intelligently (if they don’t, I’d consider it a bug).

As it happens, I have tried forcing it with a direct http call, to the same result.

Ok I just did some playing with Tinker and the API for functions.

Seems like you can call the extra parameter whatever you want… it works with all of these:

params=D7,LOW
args=D7,LOW
umno=D7,LOW
wtf=D7,LOW

You are getting a -1 return value though, which is a good sign. Must be getting there, just being interpreted as a failure, but which one? There are three places it returns -1. I was thinking these types of routines should return different negative values for each type of failure so we will know EXACTLY what’s failing… -1, -2, -3 @zach @zachary does that seem appropriate?

I tried exactly the method you suggest of multiple return values, and thats how I narrowed it down to that specific set off statements.

I had trouble getting python to talk REST to the Spark. Got it working though. The following code example shows what I found out. Please note that when calling a Spark function the parameters have to be passed in the POST body (not as a URL parameter as in GET calls)! The name of the arguments do not matter. The docs could definitely improve there.

Please note that I used requests to do http REST calls.

    import requests

DEVICE_ID = 'change this'
ACCESS_TOKEN = 'change this'

API_URL = 'https://api.spark.io/v1/devices'
DEVICE_URL = API_URL+'/'+DEVICE_ID

print 'Get devices'
r = requests.get(API_URL, params={'access_token':ACCESS_TOKEN})
print r
print r.json()


print 'Get basic info'
r = requests.get(DEVICE_URL, params={'access_token':ACCESS_TOKEN})
print r
print r.json()

print 'Get a variable'
r = requests.get(DEVICE_URL+'/temperature', params={'access_token':ACCESS_TOKEN})
print r
j = r.json()
print j
print j['result']

print 'Call a function'
# Note use of data (=POST body) instead of params
# Note the the name of the argument to the function is irrelevant
r = requests.post(DEVICE_URL+'/led', data={'access_token':ACCESS_TOKEN, 'bla':20})
print r.url
print r
print r.json()
1 Like

Interesting. Will give this a try - mapping it to Hammock will be trivial.

Thanks…seems like the placement of the access_token is inconsistent between HTTP methods.

Great idea. Just updated tinker error return values:

And yes, the name of the parameter does not matter. If anyone sees a place to fix it in the docs, please submit a pull request, or at least bring it to our specific attention. Thanks!

Thanks everyone for the help - correcting the POST request to place the access token and arguments in the data rather than parameters field corrected my issue.

1 Like

@mcowger good to know you have your problems resolved… and I learned a little bit with python today. I had it set up and making requests, getting the -1 just like you. But because the Hammock docs were so lacking, I didn’t know what options I had to change things.

Looks like using the ‘requests’ library is just as easy really with better docs.

I did just change params to data and it worked though so I’ve got new tools! Thanks.

@zachary great! Thank you :slight_smile:

Hammock is actually just a wrapper to requests that uses some clever polymorphism to make REST easier. It perhaps should be better documented that it is, in fact, just a wrapper to requests and that users of Hammock should know requests too :smile:, as it returns all the same objects, etc.

Yeah, without knowing that Request treats params and data differently, I would have assumed that the POST wrapper automatically put the params in the body and not the url.