How to access Cayenne API using Insomnia, Node-red and Thunkable

In this topic we will learn how to access the cayenne API from 3 different platforms:

  1. Insomnia: is a cross-platform GraphQL and REST client, available for Mac, Windows, and Linux
  2. Node-red: Is a programming tool for wiring together hardware devices, APIs and online services in new and interesting ways.
  3. Thunkable: Is a platform where anyone can build their own mobile apps. Available for iOS and Android.

Get The Keys.

you need to get the APP KEY and APP SECRET from cayenne dashboard by clicking on Create app and Generate API Keys.

image

image

Where APP key == client_id and APP secret == client secret.

Get front end token

You need to get the base64 string of your username and password. Go to Base64 encode and base64 decode online and enter your MQTT username:password and encode it.

image

Get the JWT access token

1) Insomnia.

    curl --request POST \
  --url https://accounts.mydevices.com/auth/realms/cayenne/protocol/openid-connect/token \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=password&client_id=APP key&client_secret=APP Secret&username=email_id&password=password’

In the above curl you need to add the following: app key, app secret, your cayenne username and password.

2) Node-red.

[{"id":"54410621.96d1f8","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"8a3392ed.acac1","type":"inject","z":"54410621.96d1f8","name":"Get Access Token.","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":232,"wires":[["a8014528.b74688"]]},{"id":"54c4d950.77bc58","type":"debug","z":"54410621.96d1f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":823.5,"y":222,"wires":[]},{"id":"baad932f.35aec","type":"change","z":"54410621.96d1f8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.access_token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":640.5,"y":222,"wires":[["54c4d950.77bc58"]]},{"id":"a8e98562.d7ca58","type":"http request","z":"54410621.96d1f8","name":"","method":"POST","ret":"obj","url":"https://accounts.mydevices.com/auth/realms/cayenne/protocol/openid-connect/token","tls":"","x":464,"y":223,"wires":[["baad932f.35aec"]]},{"id":"a8014528.b74688","type":"function","z":"54410621.96d1f8","name":"SET","func":"msg.payload = {};\nmsg.payload['grant_type'] = 'password';\nmsg.payload['client_id'] = '<app key>';\nmsg.payload['client_secret'] = '<app secret>';\nmsg.payload['username'] = '<email>';\nmsg.payload['password'] = '<password>';\nmsg.headers = {};\nmsg.headers['content-type'] = 'application/x-www-form-urlencoded';\nreturn msg;","outputs":1,"noerr":0,"x":302.5,"y":233,"wires":[["a8e98562.d7ca58"]]}]

You need to edit the set node and add your app key, app secret, your cayenne username and password.

3) Thunkable

image
image
image

Get the refresh token.

  1. Insomnia

    curl --request POST
    –url https://accounts.mydevices.com/auth/realms/cayenne/protocol/openid-connect/token
    –header ‘content-type: application/x-www-form-urlencoded’
    –data 'grant_type=refresh_token&client_id= &client_secret=&refresh_token=<refresh_token>’

2) Node-red

[{"id":"54410621.96d1f8","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"8a3392ed.acac1","type":"inject","z":"54410621.96d1f8","name":"Get Access Token.","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":232,"wires":[["a8014528.b74688"]]},{"id":"54c4d950.77bc58","type":"debug","z":"54410621.96d1f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":823.5,"y":222,"wires":[]},{"id":"baad932f.35aec","type":"change","z":"54410621.96d1f8","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.access_token","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":640.5,"y":222,"wires":[["54c4d950.77bc58"]]},{"id":"a8e98562.d7ca58","type":"http request","z":"54410621.96d1f8","name":"","method":"POST","ret":"obj","url":"https://accounts.mydevices.com/auth/realms/cayenne/protocol/openid-connect/token","tls":"","x":464,"y":223,"wires":[["baad932f.35aec"]]},{"id":"a8014528.b74688","type":"function","z":"54410621.96d1f8","name":"SET","func":"msg.payload = {};\nmsg.payload['grant_type'] = 'refresh_token';\nmsg.payload['client_id'] = '&lt;app key&gt;';\nmsg.payload['client_secret'] = '&lt;app secret&gt;';\nmsg.payload['refresh_token'] = '&lt;refresh token&gt;';\nmsg.headers = {};\nmsg.headers['content-type'] = 'application/x-www-form-urlencoded';\nreturn msg;”,"outputs":1,"noerr":0,"x":302.5,"y":233,"wires":[["a8e98562.d7ca58"]]}]

3) Thunkable

image

Publish data.

  1. Insomnia

    curl --request POST \
     --url https://api.mydevices.com/things/clientid \
     --header 'authorization: Basic token' \
     --header 'content-type: application/json' \
     --data '[{
     "value": 12,
     "channel": 11,
     "unit": "c",
     "type": "temp"
     }]'
    

2) Node-red

[{"id":"54410621.96d1f8","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"a7c1b2f7.8aa7e","type":"function","z":"54410621.96d1f8","name":"","func":"var access = msg.payload;\nmsg.payload = [{\n \"value\": 12,\n \"channel\": 11,\n \"unit\": \"c\",\n \"type\": \"temp\"\n}];\nmsg.headers = {};\nmsg.headers['Authorization'] = ‘Basic &lt;api_token&gt;’;\nmsg.headers['content-type'] = 'application/json'\nreturn msg;","outputs":1,"noerr":0,"x":311,"y":379,"wires":[["a56542d5.6cba9"]]},{"id":"a56542d5.6cba9","type":"http request”,"z":"54410621.96d1f8","name":"","method":"POST","ret":"txt","url":"https://api.mydevices.com/things/&lt;client_id&gt;/data","tls":"","x":481,"y":381,"wires":[["30f3f465.7d167c"]]},{"id":"30f3f465.7d167c","type":"debug","z":"54410621.96d1f8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":666,"y":384,"wires":[]},{"id":"61891662.991c48","type":"inject","z":"54410621.96d1f8","name":"","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":125.5,"y":372,"wires":[["a7c1b2f7.8aa7e"]]}]

You need to edit the function node:

[{
"value": 12,
"channel": 11,
"unit": "c",
"type": "temp"
}]
msg.headers = {};
msg.headers['Authorization'] = 'Bearer &lt;front_end_token&gt;’;
return msg;

3) Thunkable

image

Control Actuator

  1. Insomnia

    curl --request POST \
     --url https://platform.mydevices.com/v1.1/things/device_id/cmd \
     --header 'authorization: Bearer JWT token' \
     --header 'content-type: application/json' \
     --data '{
     "channel": 1,
     "value": 1
    }’
    
  2. Node-red

[{"id":"65f7a535.a68acc","type":"function","z":"1c439d4.cbaba63","name":"","func":"var access = msg.payload;\nmsg.payload = {\n \"channel\": \"1\",\n \"value\": 1\n};\nmsg.headers = {};\nmsg.headers['Authorization'] = 'Bearer JWT token';\nmsg.headers['Content-Type'] = 'application/json';\nreturn msg;","outputs":1,"noerr":0,"x":302,"y":175,"wires":[["456f8bbd.161344"]]},{"id":"456f8bbd.161344","type":"http request”,”z":"1c439d4.cbaba63","name":"","method":"POST","ret":"txt","paytoqs":false,"url":"https://platform.mydevices.com/v1.1/things/device_id/cmd","tls":"","proxy":"","x":472,"y":177,"wires":[["5b6dfaf8.901a44"]]},{"id":"5b6dfaf8.901a44","type":"debug","z":"1c439d4.cbaba63","name":"","active":true,"console":false,"complete":"payload","x":677,"y":180,"wires":[]},{"id":"b082d35d.50d73","type":"inject","z":"1c439d4.cbaba63","name":"","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"x":116.5,"y":168,"wires":[["65f7a535.a68acc"]]}]

  1. Thunkable

image

Get History.

  1. Insomnia

    curl --request GET \
    --url ‘https://platform.mydevices.com/v1.1/telemetry/clientid/sensors/thingid/summaries?type=latest' \
    --header 'authorization: Bearer FE Token'
    
  2. Node-red

[{"id":"a18cabde.5b2b38","type":"function","z":"23dcad07.045b32","name":"","func":"var access = msg.payload;\nmsg.headers = {};\nmsg.headers['Authorization'] = ‘Bearer token';\nreturn msg;","outputs":1,"noerr":0,"x":323,"y":121,"wires":[["a1fc2447.b6cb08"]]},{"id":"a1fc2447.b6cb08","type":"http request","z":"23dcad07.045b32","name":"","method":"GET","ret":"txt","url":"https://platform.mydevices.com/v1.1/telemetry/deviceId/sensors/sensorIDsummaries?type=latest","tls":"","x":493,"y":123,"wires":[["e5d991cb.6c62f"]]},{"id":"e5d991cb.6c62f","type":"debug","z":"23dcad07.045b32","name":"","active":true,"console":false,"complete":"payload","x":698,"y":126,"wires":[]},{"id":"9323b1ae.79286","type":"inject","z":"23dcad07.045b32","name":"","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"x":137.5,"y":114,"wires":[["a18cabde.5b2b38"]]}]

  1. Thunkable

image

3 Likes

Here is a little program to demonstrate fetching data from Cayenne using REST-API on a ESP 8266 – similar to the Insomnia, Node-Red, Thunkable above.

This gets a current value, and a history series.

It might be used to find the state of a machine after a 8266 reboots, or is replaced, and it does not have the state of the system in EEPROM, or to get current values from other devices at cayenne or non-cayenne REST databases.

This example fetches the value of a slider, and the changes to that slider over the last 24 hours – it is part of my internet quickness monitor:
- Cayenne

Note 1: I see I am using ArduinoJSON version 5, rather then the current version 6.
Note 2: Obviously there is a tiny amount of memory in an 8266, so you cannot fetch 1000’s of samples of history without slowly consuming and discarding them. I use this to find “events” that only trigger a couple times a day, rather then every 15 seconds, so the json buffers and memory used for Strings does not kill the 8266.

/*
 * cayenne_rest_demo
 * 
 * James Zahary Nov 15, 2019
 * 
 * This shows how to access current values and history from the mydevices.com database using REST API on a esp8266
 * 
 */

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <CayenneMQTTESP8266.h>
#include <ArduinoJson.h>

/*
Using library ESP8266WiFi at version 1.0 in folder: C:\Users\James\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.0\libraries\ESP8266WiFi 
Using library ESP8266HTTPClient at version 1.2 in folder: C:\Users\James\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.6.0\libraries\ESP8266HTTPClient 
Using library CayenneMQTT at version 1.3.0 in folder: C:\Users\James\Documents\Arduino\libraries\CayenneMQTT 
Using library ArduinoJson at version 5.13.5 in folder: C:\Users\James\Documents\Arduino\libraries\ArduinoJson 

*/

// your wifi name and password
const  char ssid[] = "your_wifi";           
const  char wifipassword[] = "wifi_pass"; 

// cayenne username and password for MQTT
const char cayusername[] = "97XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXce";
const char caypassword[] = "257XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX94";

// cayenne device name and sensor name 
const  char cayclientID[] = "ea767930-5b0f-11e8-9907-5defa4aa8de2";
const  char caycycles[] =   "a59b1650-6810-11e8-a25e-d5e347246797";

// cayenne server names
const  char cayaddr[] =        "mqtt.mydevices.com";
const  char cayserver[] =      "platform.mydevices.com";
const  char cayauthserver[] =  "accounts.mydevices.com";

// cayenne username and password to access REST API
const char client_id[] =     "1XXXXXX5";
const char client_secret[] = "d1XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX10";
const char username[] =      "mrpeanut@gmail.com";
const char password[] =      "peanutpassword";

// the access token JWT for actual access
char access_token[1200] = "";
char refresh_token[700]  = "";

// global variables to return the data
int channel = 0;
double v = 0;
String ts = "";
String unit = "";
String device_type = "";

// functions below

void do_access_token();     // get access token using REST API credentials above

// get currect value of device and sensor, returned in global variables v, ts, unit, device_type
int rest_query_cayenne(const char caydev[], const char caysen[]);   

// get a day of history of the device and sensor -- just print it out
int rest_query_cayenne_day(const char caydev[], const char caysen[]);


void setup() {
  Serial.begin(115200);
  Serial.println("");
  Serial.println("");

  Serial.println("\n>>>>> cayenne_rest_demo  Nov 13, 2019 -jz");

  Serial.println("\n>>>>> Starting Cayenne ...");
  Cayenne.begin( cayusername, caypassword, cayclientID, ssid, wifipassword);

  Serial.println("\n>>>>> Get access token for REST API access ...");
  
  do_access_token();

  Serial.println("\n>>>>> Query cayenne for state of slider ");

  if ( rest_query_cayenne( cayclientID,  caycycles) == 1) {   // return in global variables v, ts, device_type, unit

    Serial.println("");
    Serial.println("   Device: " + String(cayclientID));
    Serial.println("   Sensor: " + String(caycycles));
    Serial.println("");
    Serial.println("Results from cayenne ");
    Serial.println("      v = " + String(v));
    Serial.println("     ts = " + String(ts));
    Serial.println(" device = " + String(device_type));
    Serial.println("   unit = " + String(unit));
    Serial.println("");

  } else {
    Serial.println("Couldn't access REST API");
  }

  Serial.println("\n>>>>> Getting last 24 hours history of cycle slider changes ");

  rest_query_cayenne_day(  cayclientID,  caycycles );

  Serial.println("\n>>>>> Only this, and nothing more. ");

}

void loop() {

  Cayenne.loop();

}

void do_access_token() {

  WiFiClientSecure cayclient;
  HTTPClient cayhttp;

  cayclient.setInsecure();

  if (cayclient.connect(cayauthserver, 443)) {

    if (cayhttp.begin(cayclient, "https://accounts.mydevices.com/auth/realms/cayenne/protocol/openid-connect/token")) {

      cayhttp.addHeader("Content-Type", "application/x-www-form-urlencoded");
      cayhttp.addHeader("cache-control", "no-cache");

      String body = "grant_type=password&client_id=" + String(client_id) + "&client_secret=" + String(client_secret) + "&username=" + String(username) + "&password=" + String(password) ;

      int httpCode = cayhttp.POST(body);

      if (httpCode > 0) {

        // HTTP header has been send and Server response header has been handled
        //Serial.printf("[HTTPS] POST... code: %d\n", httpCode);

        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {

          String payload = cayhttp.getString();
          //Serial.println(payload);

          DynamicJsonBuffer  jsonBuffer(2000);
          JsonObject& root = jsonBuffer.parseObject(payload);

          if (!root.success()) {
            Serial.println("parseObject failed");
            return ;

          } else {

            String at = root["access_token"];
            String rt = root["refresh_token"];

            Serial.println("access token =" + at);
            //Serial.println("");
            //Serial.println("refresh token =" + rt);

            at.toCharArray(access_token, 1200);
            rt.toCharArray(refresh_token, 700);

          }
        } else {
          Serial.printf("[HTTPS] POST... failed, error: %s\n", cayhttp.errorToString(httpCode).c_str());
        }
      }

      cayhttp.end();

    } else {
      Serial.printf("[HTTPS] Unable to connect\n");
    }
  } else {
    Serial.println("Cay Auth Server failed to connect");
  }
  cayclient.stop();
}


int rest_query_cayenne(const char caydev[], const char caysen[]) {

  // results   v, ts, device, unit

  WiFiClientSecure cayclient;
  HTTPClient cayhttp;

  cayclient.setInsecure();

  int ret = 0;

  if (!cayclient.connect(cayserver, 443)) {
    Serial.println("Cayserver failed to connect");
  } else {

    String get = "https://platform.mydevices.com/v1.1/telemetry/" + String(caydev) + "/sensors/" + String(caysen) + "/summaries?endDateLatest=true&type=latest";
    //Serial.println(get);

    if (cayhttp.begin(cayclient, get)) {

      cayhttp.addHeader("Authorization" , "Bearer " + String(access_token));

      int httpCode = cayhttp.GET();

      if (httpCode > 0) {

        //Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {

          String payload = cayhttp.getString();
          //Serial.println(payload);

          int payload_length = payload.length();
          Serial.print("Payload length is "); Serial.println(payload_length);

          //  [{"v":"1526700604","ts":"2018-05-19T03:30:05.283Z","unit":"seconds","device_type":"analog"}]

          DynamicJsonBuffer  arrayBuffer(100);
          DynamicJsonBuffer  jsonBuffer(100);

          JsonArray& array = arrayBuffer.parseArray(payload, 2);

          int size = array.size();
          Serial.print("Array Size is "); Serial.println(size);

          String onejson = array[0];
          JsonObject& root = jsonBuffer.parseObject(onejson);

          if (!root.success()) {
            Serial.println("parseObject failed");

          } else {
            double xv  = root["v"];
            String xts = root["ts"];
            String xdevice_type = root["device_type"];
            String xunit = root["unit"];

            v = xv;
            ts = xts;
            device_type = xdevice_type;
            unit = xunit;

            return 1;
          }
          jsonBuffer.clear();
        }

      } else {
        String payload = cayhttp.getString();
        Serial.println(httpCode);
        Serial.println(payload);
      }
    }
  }

  cayhttp.end();
  cayclient.stop();
}

int rest_query_cayenne_day(const char caydev[], const char caysen[]) {

  // results   v, ts, device, unit

  WiFiClientSecure cayclient;
  HTTPClient cayhttp;

  cayclient.setInsecure();

  int ret = 0;

  if (!cayclient.connect(cayserver, 443)) {
    Serial.println("Cayserver failed to connect");
  } else {

    String get = "https://platform.mydevices.com/v1.1/telemetry/" + String(caydev) + "/sensors/" + String(caysen) + "/summaries?endDateLatest=true&type=day";
    //Serial.println(get);

    if (cayhttp.begin(cayclient, get)) {

      cayhttp.addHeader("Authorization" , "Bearer " + String(access_token));

      int httpCode = cayhttp.GET();

      if (httpCode > 0) {

        //Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {

          String payload = cayhttp.getString();
          //Serial.println(payload);

          int payload_length = payload.length();
          Serial.print("Payload length is "); Serial.println(payload_length);

          //  [{"v":"1526700604","ts":"2018-05-19T03:30:05.283Z"},{"v":"1526700604","ts":"2018-05-19T03:30:05.283Z"}]     

          DynamicJsonBuffer  arrayBuffer(500);
          DynamicJsonBuffer  jsonBuffer(100);

          JsonArray& array = arrayBuffer.parseArray(payload, 2);

          int size = array.size();
          Serial.print("Array Size is "); Serial.println(size);

          for (int ii = 0; ii < size; ii++ ) {

            String onejson = array[ii];
            JsonObject& root = jsonBuffer.parseObject(onejson);

            //Serial.print(ii); Serial.print(" >");  Serial.print(onejson); Serial.println("<");

            if (!root.success()) {
              Serial.println("parseObject failed");

            } else {
              double xv  = root["v"];
              String xts = root["ts"];

              Serial.print("index  "); Serial.print(ii);
              Serial.print("\t v      "); Serial.print(xv);
              Serial.print("\t ts     "); Serial.print(xts);
              Serial.println("");

              v = xv;
              ts = xts;

            }
          }
        }

      } else {
        String payload = cayhttp.getString();
        Serial.println(httpCode);
        Serial.println(payload);
      }
    }
  }

  cayhttp.end();
  cayclient.stop();

}

CAYENNE_OUT_DEFAULT() {
}

CAYENNE_IN_DEFAULT()
{
}
2 Likes

excellent work @jameszahary and thanks for sharing it.

Hi, can you help me. When I find const char cayclientID and const char caycycles ?

here

I do it, but it is for // cayenne username and password to access REST API
const char client_id = “675782”;
const char client_secret = “a0d1b3b0-d730-4319-a05e-7b0b92b4051e”;when I understand well.
But I don’t know what I have to fill in // cayenne device name and sensor name
const char cayclientID = “ea767930-5b0f-11e8-9907-5defa4aa8de2”;
const char caycycles = “a59b1650-6810-11e8-a25e-d5e347246797”;

I try code from jameszahary and I get status >>>>> Query cayenne for state of slider
Payload length is 0
Array Size is 0
parseObject failed
Couldn’t access REST API

to get cayclientID open the device setting on the cayenne dashboard and you will get the MQTT credentials. where the MQTT client id is cayclientID
or you can both of them together:
open the setting of the sensor widget you want to get data from. you will get an url like:

https://cayenne.mydevices.com/cayenne/dashboard/arduino/1a306ad0-f6f2-xxxxxx/settings/bfb1b0d0-f6f3-xxxxxx

where : 1a306ad0-f6f2-xxxxxx is the cayclientID and bfb1b0d0-f6f3-xxxxxx is the caycycles

1 Like

Thanks, now it works.

1 Like