Parrot Flower Power - Web API

Posted on
Thu May 07, 2015 7:41 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Parrot Flower Power - Web API

OK After days of googling and trying to find a way to put the result of a JSON GET (please forgive me if my terminology is not right, im not a propper scripter and in no way a programer) into a dictionary so that i can display the value of "instruction_key" using indigo.server.log(data, "testoutput"), i am having to ask for help.

Here is what i am doing (python): http://pastebin.com/wsxy7WFH (from the action in a schedule within indigo)

I am getting "string indices must be integers, not str" on the line "data = ..." in the indigo log.

Now this is my utterly first atempt at python so im probably doing this completly wrong but giving it a go.

I am able to write the entire JSON output to the log fine (by writing "dump" to the log in the same way albiet moving the server.indigo.log(dump) into the def) but plucking out the value of an element is what i cant seem do. If i can do that, i can write it to the varible and use it which i know how to do also.

Can some one help me pluck out the value of "instruction_key"? If i can do that then I "should" be able to get any value for use in Indigo.

Here is the returned JSON: http://pastebin.com/hp23jbQY
Last edited by AndyVirus on Thu May 07, 2015 10:05 am, edited 1 time in total.

Posted on
Thu May 07, 2015 8:22 am
RogueProeliator offline
User avatar
Posts: 2501
Joined: Nov 13, 2012
Location: Baton Rouge, LA

Re: Parrot Flower Power - Web API - Help needed

The locations looks to be an array, meaning essentially that you have to tell it you want the first element (location) in there even though there is only one. I don't have a JSON parser on me to verify, but try:

Code: Select all
dump["locations"][0]['air_temperature']['instruction_key']
Note the [0] added in that line...

Adam

Posted on
Thu May 07, 2015 8:36 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API - Help needed

Thanks Adam. I still get "TypeError: string indices must be integers, not str".
Im using the line:
Code: Select all
indigo.server.log('"instruction_key":',dump['locations'][0]['air_temperature']['instruction_key'])

Its probably a simple syntax error/typo on my part but no idea where im going wrong on this one. I think i have stipped down the script and re done it 4 or 5 times in different ways but this one aspect is stumping me.

Posted on
Thu May 07, 2015 9:09 am
RogueProeliator offline
User avatar
Posts: 2501
Joined: Nov 13, 2012
Location: Baton Rouge, LA

Re: Parrot Flower Power - Web API - Help needed

My fault, you aren't going to want to do a dump on that, I assume you just want the value? You can get it straight from the JSON data:

Code: Select all
json_data['locations'][0]['air_temperature']['instruction_key']
I haven't validated/looked at the rest of the code, but briefly, it looks like json_data is ultimately returning from a json.loads which brings it into Python dictionaries and arrays, you access those directly.

Posted on
Thu May 07, 2015 9:34 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API - Help needed

Perfect! That did it. Thanks! I have a whole bunch of these being delivered so now to figure out an elegant way to differentiate them for unique temp/light/moisture/fertiliser values (i started with the easy it, overall garden health).

Thanks again!
Last edited by AndyVirus on Thu May 07, 2015 10:10 am, edited 1 time in total.

Posted on
Thu May 07, 2015 10:05 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API

Parrot Flower Power Garden Status Script

OK in the spirit of community, below is what i have working so far. The script will connect to the web api and update 4 variables with the returned values of "air_temperature","fertilizer","light" and soil_moisture". Fill in the client_id,client_secret,username and password fields with your Parrot Flower Power details. Sign up here: https://apiflowerpower.parrot.com/api_access/signup

Code: Select all
import datetime
import urllib, urllib2, os, pwd
import simplejson as json

basename = "https://apiflowerpower.parrot.com"

client_id = "username"
client_secret = "secret"
username = "username"
password = "pasword"
oauth_bearer = {}

def api_json(url, params, method='GET', headers={}):
   request = URLRequest(basename+url, params, method, headers=headers)
   request.add_header('Accept-Language', 'en_us')
   result = urllib2.urlopen(request)

   result_string = result.read()
   return json.loads(result_string)
 
   
def URLRequest(url, params, method="GET", headers={}):
   if method == "POST":
      return urllib2.Request(url, headers=headers, data=urllib.urlencode(params))
   else:
      return urllib2.Request(url + "?" + urllib.urlencode(params),headers=headers)

def garden_summary(key, url):
   json_data = api_json(url, {}, headers=oauth_bearer, method='GET')
   dump = json.dumps(json_data, indent=4, sort_keys=True)

# Update Indigo Variables
   indigo.variable.updateValue(1857995394,json_data['locations'][0]['air_temperature']['instruction_key'])
   indigo.variable.updateValue(1539841434,json_data['locations'][0]['fertilizer']['instruction_key'])
   indigo.variable.updateValue(202743283,json_data['locations'][0]['light']['instruction_key'])
   indigo.variable.updateValue(1190192908,json_data['locations'][0]['soil_moisture']['instruction_key'])

def garden_images(key, url):
   json_data = api_json(url, {}, headers=oauth_bearer, method='GET')
   dump = json.dumps(json_data, indent=4, sort_keys=True)

# Update Indigo Variables
   indigo.variable.updateValue(120879240,json_data['images'][0]['url'])

# first get OAuth token
result = api_json('/user/v1/authenticate', {'grant_type':'password','client_id':client_id,'client_secret':client_secret,'username':username, 'password':password}, method='POST')
access_token = result['access_token']
oauth_bearer = {'Authorization':'Bearer '+access_token}


# user data : status (garden_locations and sensors) and statuses.
for key,url in (
   ('api-1.28-status','/sensor_data/v1/garden_locations_status'),
   ):
   garden_summary(key,url)

# user data : images (garden_locations and sensors) and statuses.
for key,url in (
   ('api-3.05-images','/image/v2/location/user_images'),
   ):
   garden_images(key,url)




Started with the easiest first. Only have 1 device currently but should have 5 more tomorrow so will start grabbing individual temp,light,moisture and fertiliser figures for each device once i have more data.

Updated: Plant Image URL for Control Pages

Posted on
Mon May 11, 2015 7:42 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API

OK so I am trying to come up with a method, maybe somone can help. My terminoligy may be a bit off so please correct me as i am learning (by doing so its as i go)

I have an array which within holds each value found in ['locations'][0]['location_identifier']
I have another array which holds each value found in ['locations'][0]['plant_nickname'] (maybe irrelivent for what i want to do but was trying to spark and idea)

What i want to do is match the correct location_identifier to the coresponding plant_nickname so i can create 4 varibles in indigo for each plant_nickname.
The 4 varibles per plant being something like, plant_nickname+light,moisture,fertilizer,temp.
I then want to popultate each of the varbiles created with the coresponding values.

I cant think how to say location_identifier=plant_nickname reliably.
Once i have that I need to then somehow say, for each location_identifier, pluck the correct vlaue of ['locations'][0]['air_tempreture'][instruction_key] due to how the JSON is formated (see here: https://flowerpowerdev.parrot.com/projects/flower-power-web-service-api/wiki/Obtain_garden_location_statuses)

Ill keep playing but if anyone can point me in the right direction it would be appriciated. :)

Oh, the code i have so far which prints both arrays to the log currently and updates 4 static variables (which is nowkind of redundant as i have more than 1 plant, i have 6 now)

Code: Select all
import datetime
import urllib, urllib2
import simplejson as json

basename = "https://apiflowerpower.parrot.com"

client_id = "user"
client_secret = "secret"
username = "user"
password = "pass"
oauth_bearer = {}

def api_json(url, params, method='GET', headers={}):
   request = URLRequest(basename+url, params, method, headers=headers)
   request.add_header('Accept-Language', 'en_us')
   result = urllib2.urlopen(request)

   result_string = result.read()
   return json.loads(result_string)
 
   
def URLRequest(url, params, method="GET", headers={}):
   if method == "POST":
      return urllib2.Request(url, headers=headers, data=urllib.urlencode(params))
   else:
      return urllib2.Request(url + "?" + urllib.urlencode(params),headers=headers)

def garden_summary(key, url):
   json_data = api_json(url, {}, headers=oauth_bearer, method='GET')
   dump = json.dumps(json_data, indent=4, sort_keys=True)

# Update Indigo Variables
   indigo.variable.updateValue(1857995394,json_data['locations'][0]['air_temperature']['instruction_key'])
   indigo.variable.updateValue(1539841434,json_data['locations'][0]['fertilizer']['instruction_key'])
   indigo.variable.updateValue(202743283,json_data['locations'][0]['light']['instruction_key'])
   indigo.variable.updateValue(1190192908,json_data['locations'][0]['soil_moisture']['instruction_key'])


   

def garden_images(key, url):
   json_data = api_json(url, {}, headers=oauth_bearer, method='GET')
   dump = json.dumps(json_data, indent=4, sort_keys=True)

# Update Indigo Variables
   indigo.variable.updateValue(120879240,json_data['images'][0]['url'])

def create_plant_variable(key, url):
   json_data = api_json(url, {}, headers=oauth_bearer, method='GET')
   dump = json.dumps(json_data, indent=4, sort_keys=True)
   locations = json_data['locations'][0]['location_identifier']
   locations1 = [l['location_identifier'] for l in json_data['locations']]
   nicename = json_data['locations'][0]['plant_nickname']
   plant_nickname = [nickname['plant_nickname'] for nickname in json_data['locations']]
   
# Update Indigo Variables
   for eachlocation in locations1:
    indigo.server.log(eachlocation)
   for eachnickname in plant_nickname:
    indigo.server.log(eachnickname)
   


# first get OAuth token
result = api_json('/user/v1/authenticate', {'grant_type':'password','client_id':client_id,'client_secret':client_secret,'username':username, 'password':password}, method='POST')
access_token = result['access_token']
oauth_bearer = {'Authorization':'Bearer '+access_token}


# user data : status (garden_locations and sensors) and statuses.
for key,url in (
   ('api-1.28-status','/sensor_data/v1/garden_locations_status'),
   ):
   garden_summary(key,url)

# user data : images (garden_locations and sensors) and statuses.
for key,url in (
   ('api-3.05-images','/image/v2/location/user_images'),
   ):
   garden_images(key,url)

# user data : sync (garden_locations and sensors) and statuses.
for key,url in (
   ('api-1.25-sync','/sensor_data/v2/sync?include_s3_urls=1'),
   ):
   create_plant_variable(key,url)


and prints:

Code: Select all
  Script                          TkZeaiwxsH1431033160579
  Script                          8ZVzsz17sx1431032791557
  Script                          FztWqlz34H1431068956616
  Script                          xfJ5A6ZjoC1431033867913
  Script                          BO19Bad6Oj1430424822897
  Script                          u9IG5iNi9m1431115442473
  Script                          Blue Grass
  Script                          Japanese Acer Red
  Script                          Dianthus
  Script                          Bamboo
  Script                          Japanese Acer Green
  Script                          Prunus

Posted on
Mon May 18, 2015 9:55 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API

OK maybe a simpleier question then:

Code: Select all
# Update Indigo Variables
   for m in variablenames:
    if not (m in indigo.variables):
     indigo.variable.create(m, "default value")
   for airdata in air_temp_data:
     indigo.variable.updateValue(m, value=airdata)


The above created the variables in indigo for each device, this works fine.

What it does not do is populate the data for every device, it populates just the last one with actual data and the rest just get "default value"

How do i write/loop "airdata" to each "m" not just one of them?

Posted on
Mon May 18, 2015 3:43 pm
matt (support) offline
Site Admin
User avatar
Posts: 21417
Joined: Jan 27, 2003
Location: Texas

Re: Parrot Flower Power - Web API

Try indenting the last 2 lines over (by it looks like 2 spaces?).

Image

Posted on
Tue May 19, 2015 3:51 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Parrot Flower Power - Web API

If I move the second 'for" statement over then it loops too much so i get

Code: Select all
19 May 2015 12:38:47
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good


If i leave it where it is currently i get:

Code: Select all
19 May 2015 12:38:47
  Script                          fertilizer_too_low
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
  Script                          fertilizer_good
 


Which looks like what i want but what is written to the 6 varables is "default value" and then only one gets the value "fertilizer_good" which is the last one in the json, so i am guessing that it loops the whole thing 6 times overwriting the variables with "default value" on every loop until it can not loop anymore which leaves the value "fertilizer_good" in the very last object.

It seems like this should be simple, im sure im just doing something stupid and it is just somethng lile indentation but i have tried indenting and outdenting till the cows come home and i either get all 6 values 6 times but only the last value in the JSON is written to ALL varibles or 1 value writen to the last variable that is the last object in the JSON.
Am i missing another loop?

Sent from my iPhone using Tapatalk

Posted on
Wed May 20, 2015 8:08 am
matt (support) offline
Site Admin
User avatar
Posts: 21417
Joined: Jan 27, 2003
Location: Texas

Re: Parrot Flower Power - Web API

I'm not following exactly what is going on here. Try logging the values of your lists which will give you (and me) a better idea as to what is occurring:

Code: Select all
   indigo.server.log("variablenames: " + unicode(variablenames))
   indigo.server.log("air_temp_data: " + unicode(air_temp_data))
   for m in variablenames:
    if not (m in indigo.variables):
     indigo.variable.create(m, "default value")
   for airdata in air_temp_data:
     indigo.variable.updateValue(m, value=airdata)

Image

Posted on
Thu May 21, 2015 10:32 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API

matt (support) wrote:
I'm not following exactly what is going on here. Try logging the values of your lists which will give you (and me) a better idea as to what is occurring:

Code: Select all
   indigo.server.log("variablenames: " + unicode(variablenames))
   indigo.server.log("air_temp_data: " + unicode(air_temp_data))
   for m in variablenames:
    if not (m in indigo.variables):
     indigo.variable.create(m, "default value")
   for airdata in air_temp_data:
     indigo.variable.updateValue(m, value=airdata)


I have taken a different approach as for a beginer this was too complex. My new "working" script follows:

Posted on
Thu May 21, 2015 10:39 am
AndyVirus offline
Posts: 257
Joined: Mar 18, 2014
Location: Newport Pagnell, UK

Re: Parrot Flower Power - Web API

OK I gave up on the way I was doing this. Instead I have made this simpler and more linea (for me anyway).

You need to install the python libary "requests". I did this by:

Code: Select all
curl -O https://bootstrap.pypa.io/get-pip.py
sudo python2.6 get-pip.py
sudo pip requests


To the script add the usual API credentials and in the variable
Code: Select all
variable folder =
put the ID of a folder in Variables. I called mine "Flower Power".

Code: Select all
import requests
from pprint import pformat

# First we set our credentials
username = 'user'
password = 'pass'
# Get your client credentials here:
# https://apiflowerpower.parrot.com/api_access/signup
client_id = 'user'
client_secret = 'secret'

req = requests.get('https://apiflowerpower.parrot.com/user/v1/authenticate',
                   data={'grant_type': 'password',
                         'username': username,
                         'password': password,
                         'client_id': client_id,
                         'client_secret': client_secret,
                         })
response = req.json()
print('Server response: {0}'.format(pformat(response)))

variablefolder = 801757239

# Get authorization token from response
access_token = response['access_token']
auth_header = {'Authorization': 'Bearer {token}'.format(token=access_token)}

# From now on, we won't need initial credentials: access_token and auth_header
# will be enough.

# Get sync data
req = requests.get('https://apiflowerpower.parrot.com/sensor_data/v3/sync',
                   headers=auth_header)
response = req.json()
print('Server response: {0}'.format(pformat(response)))

sync_locations = response['locations']

# Build a dict to link location_identifiers to plant name
loc2name = dict((l['location_identifier'], l['plant_nickname']) for l in sync_locations)

# Get locations status
req = requests.get('https://apiflowerpower.parrot.com/sensor_data/v3/' +
                   'garden_locations_status',
                   headers=auth_header)
response = req.json()
status_locations = response['locations']

for loc in status_locations:
    plantname = ('{plant}:'.format(plant=loc2name[loc['location_identifier']]))
    plantnamenor = plantname.replace(" ", "_")
    for metric in ['light', 'soil_moisture', 'air_temperature', 'fertilizer']:
        data = loc[metric]
        plantmetric = ('{metric}:'.format(metric=metric))
        plantdata = ('{0}'.format(data['instruction_key']))
        for plantvariables in status_locations:
          if not ((plantnamenor+plantmetric) in indigo.variables):
           indigo.variable.create((plantnamenor+plantmetric), "default value", folder=variablefolder)
        for plantvariabledata in status_locations:
           indigo.variable.updateValue((plantnamenor+plantmetric), value=plantdata)

I sceduled this to run every 4 hours.

This creates a variable for each of your plants and each of their values for light, soil_moisture, air_temperature and fertilizer. For myself i did not want the specific data such as temp in C or F as the data does not get uploaded to the flower power website in real time as it needs to be uploaded by the app on a phone so all I care about is if something is too low or too high. There is a Bluetooth API to read the lie data, but thats a project for another time. For now, enjoy!
Attachments
2015-05-21_17-42-32.jpg
What the output looks like
2015-05-21_17-42-32.jpg (136.37 KiB) Viewed 5101 times

Posted on
Sat Apr 30, 2016 5:33 am
Mirko offline
Posts: 79
Joined: Dec 04, 2014

Re: Parrot Flower Power - Web API

Hi,

I get the following error:

Schedule Flower Power 1
Script Error embedded script: No module named requests
Script Error Exception Traceback (most recent call shown last):

embedded script, line 1, at top level
ImportError: No module named requests


I run the latest Indigo on my server MacOs 10.6.8

Could anyone help me out here?

Thanks,

Mirko

Posted on
Sat Apr 30, 2016 11:08 am
matt (support) offline
Site Admin
User avatar
Posts: 21417
Joined: Jan 27, 2003
Location: Texas

Re: Parrot Flower Power - Web API

Hi Mirko,

It looks like you didn't install (or it didn't install correctly) the requests module. Did you follow Andy's instructions at the top of this post?

Image

Who is online

Users browsing this forum: No registered users and 7 guests