Webhook Tutorial - replacement for openweathermap.org

Tyler Hutchison asked me to post this question here:

Though I have been teaching the IoT class using the Argon for 4 years, my background is more electrical engineering than coding. So, I occasionally run up against things that I am less proficient at.

I like to show my students a webhook example in the class. In the DeviceOS documentation there is an openweathermaps.org example that I had been using. However, openweathermaps.org changed their registration and so now in order to get an API key that works, you need to give them a credit card (even if you say within the free usage limit). This doesn’t work in our environment with students.

So, I have changed to using TomTom where I can still get a totally free / no credit card API and doing a query to find the closed Pizza to my classroom. This works:

However, the JSON that I get back is an outer object of an Array with the GPS coordinates in the inner object(s). I am, at best, a total novice at JSON. I was wondering if there is anyone in your education support team that could

  1. Show me how to setup the “response template” in the webhook. I think I know how to modify the “query” given the above. I think my issue is I don’t know how to deal with the other array.
  2. Show me how to then parse the json on the Argon to extract just lat and lon.

Here is what gets returned from the query (it is obviously easier to read in a JSONViewer)

{"summary":{"query":"pizza","queryType":"NON_NEAR","queryTime":6,
"numResults":1,"offset":0,"totalResults":94,"fuzzyLevel":1,
"geoBias":{"lat":35.08425,"lon":-106.64924},"queryIntent":[],
"geobiasCountry":"US"},"results":[{"type":"POI",
"id":"W8FPHJMG6uTenwbelVPptg","score":0.9930446148,
"dist":70.421687,"info":"search:ta:840351000124089-US","poi":
{"name":"JC's New York Pizza Department","phone":"+1 505-766-6973",
"categorySet":[{"id":7315036}],"url":"jcnypd.com",
"categories":["pizza","restaurant"],
"classifications":[{"code":"RESTAURANT",
"names":[{"nameLocale":"en-US","name":"restaurant"},
{"nameLocale":"en-US","name":"pizza"}]}]},
"address":{"streetNumber":"215","streetName":"Central Avenue Northwest",
"municipalitySubdivision":"Downtown Albuquerque",
"municipality":"Albuquerque",
"countrySecondarySubdivision":"Bernalillo","countrySubdivision":"NM",
"countrySubdivisionName":"New Mexico",
"postalCode":"87102","extendedPostalCode":"87102-3317",
"countryCode":"US","country":"United States","countryCodeISO3":"USA",
"freeformAddress":"215 Central Avenue Northwest,
 Albuquerque, NM 87102","localName":"Albuquerque"},
"position":{"lat":35.084629,"lon":-106.64986},
"viewport":{"topLeftPoint":{"lat":35.08553,"lon":-106.65096},
"btmRightPoint":{"lat":35.08373,"lon":-106.64876}},
"entryPoints":[{"type":"main",
"position":{"lat":35.08433,"lon":-106.6499}},
{"type":"main","position":{"lat":35.08433,"lon":-106.64991}}]}]}

@Rashap, the JSON you provided is not working for me in a viewer. Something is amiss!

@peekay123 I just copied it from my post into https://jsonviewer.stack.hu/ and was able to see it without issue. Is there a different way I can send it to you.

Actually. Maybe I take that back. https://jsonviewer.stack.hu/ formats the json to be more readable, but it also gives me an error in “viewer” mode. I can send you the actual query so you can see what exactly gets returned, but as it has my TomTom API key in it, I would rather not post it publicly.

@Rashap, DM me the “raw” response you get from your query in your browser. The JSON should not contain any weird characters.

@Rashap, ChatGPT tells me the quotation marks are incorrect. With those fixed, the JSON looks like this:

{
  "summary": {
    "query": "pizza",
    "queryType": "NON_NEAR",
    "queryTime": 6,
    "numResults": 1,
    "offset": 0,
    "totalResults": 94,
    "fuzzyLevel": 1,
    "geoBias": {
      "lat": 35.08425,
      "lon": -106.64924
    },
    "queryIntent": null,
    "geobiasCountry": "US"
  },
  "results": [
    {
      "type": "POI",
      "id": "W8FPHJMG6uTenwbelVPptg",
      "score": 0.9930446148,
      "dist": 70.421687,
      "info": "search:ta:840351000124089-US",
      "poi": {
        "name": "JC's New York Pizza Department",
        "phone": "+1 505-766-6973",
        "categorySet": [
          {
            "id": 7315036
          }
        ],
        "url": "jcnypd.com",
        "categories": [
          "pizza",
          "restaurant"
        ],
        "classifications": [
          {
            "code": "RESTAURANT",
            "names": [
              {
                "nameLocale": "en-US",
                "name": "restaurant"
              },
              {
                "nameLocale": "en-US",
                "name": "pizza"
              }
            ]
          }
        ]
      },
      "address": {
        "streetNumber": "215",
        "streetName": "Central Avenue Northwest",
        "municipalitySubdivision": "Downtown Albuquerque",
        "municipality": "Albuquerque",
        "countrySecondarySubdivision": "Bernalillo",
        "countrySubdivision": "NM",
        "countrySubdivisionName": "New Mexico",
        "postalCode": "87102",
        "extendedPostalCode": "87102-3317",
        "countryCode": "US",
        "country": "United States",
        "countryCodeISO3": "USA",
        "freeformAddress": "215 Central Avenue Northwest, Albuquerque, NM 87102",
        "localName": "Albuquerque"
      },
      "position": {
        "lat": 35.084629,
        "lon": -106.64986
      },
      "viewport": {
        "topLeftPoint": {
          "lat": 35.08553,
          "lon": -106.65096
        },
        "btmRightPoint": {
          "lat": 35.08373,
          "lon": -106.64876
        }
      },
      "entryPoints": [
        {
          "type": "main",
          "position": {
            "lat": 35.08433,
            "lon": -106.6499
          }
        },
        {
          "type": "main",
          "position": {
            "lat": 35.08433,
            "lon": -106.64991
          }
        }
      ]
    }
  ]
}

1 Like

@Rashap, what would you like to parse from the response? I suggest using the built-in “mustache” response template to do the heavy lifting. @rickkas7, has a great online tool for building and testing the mustache response template for your needs.

Thank you for finding the error. I was able to get it to work as well. All I need is three things:
“name” from under “poi” and the “lat” and “lon” from “position”

I can try to play with the mustache response template. I assume that gives me the response template that I enter into the webhook. Correct?

@peekay123

So, the below response template pulls the data that my class needs.

Can you point me in the direction of setting up the webhook with this response template and then parsing the resulting data on the particle device?

{{{results.0.poi.name}}}
{{{results.0.position.lat}}}
{{{results.0.position.lon}}}

@Rashap, have you looked here?

@peekay123 I was able to get the webhook to work and return the pizza location. Thank you for the lead on the Mustache response template.

I have one more question. Right now, I am hard coding lat and lon into the webhook. However, I would rather have my students send lat and lon from their particle devices.

I sent lat and lon in the particle.publish() and that didn't work. I deleted the hard coded lat and lon and resent in particle.publish. Is there something I need to do in the way I setup the webhook to have it read the lat and lon I send?

Particle.publish(EVENT_NAME, String::format("{"lat":%0.5f,"lon":%0.5f}", lat, lon), PRIVATE);

@Rashap, I use the "Query Parameters" section of the webhook definition. This section appended to the webhook URL. For my weather call to openweatherapi, here is what I do:

In the webhook Query Parameters section I have the following to build the remainder of the request

{
  "q": "{{mycity}}",
  "units": "{{units}}",
  "cnt": "{{count}}",
  "appid": "{{apikey}}"
}

I use #define for the parameters I may want to change:

#define DEFAULT_CITY	"\"mycity\":\"Ottawa,CA\""	// Change to desired default city,state
#define API_KEY			"\"apikey\":\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\""
#define UNITS			"\"units\":\"metric\""		// Change to "imperial" for farenheit units
#define CNT             "\"count\":\"24\""

Finally, in setup(), I assemble the JSON string to publish to trigger the webhook:

 	// Build json string for webhook (where qry is defined as char qry[100])
	strcpy(qry, "{");
	strcat(qry, DEFAULT_CITY);
	strcat(qry, ",");
	strcat(qry, UNITS);
	strcat(qry, ",");
	strcat(qry, CNT);
	strcat(qry, ",");
	strcat(qry, API_KEY);
	strcat(qry, "}");

You can use this to adapt for "lat" and "lon" instead of "mycity". Hope that helps!

2 Likes

@peekay123 This helps a lot. Thank you.

1 Like