How to access JSON data to populate widget/Watch face

Hello everyone,

I am very new to the Garmin IQ environment and am working my way through various content to try to put together a widget able to extract weather data from a website and display it on a widget and/or watch face. this website has publicly available data in various formats:

After doing some research, it seems like using the JSON capabilities might be the easiest/neatest way to achieve this. I have set the basic architecture based on example I found and trial/errors which got me to Error -1001 , ended up having to disable HTTPS requirements which now gets me to error -402 which seems to indicate an issue with the parameters (which I haven't defined). According to the following page, it could mean that the data requested it too large: https://developer.here.com/blog/build-your-own-sightseeing-app-with-garmin-connect-iq-and-the-here-location-apis

Apologies for the very basic question but I have very limited experience with JSON requests and could find any simple example showing the syntax needed for the calls. I am looking for a basic example which could work and extra anything from the page, at least to show that it can be done (such as the first "wind_spd_kt" of the first "data" set) so I can understand the syntax and I'll take it from there hopefully. 

If anyone could point me in the right direction that would be much appreciated! if we can get the JSON request work, I would also be very interested in the syntax to extract the values from the response!

See code extracts below:

using Toybox.WatchUi;
using Toybox.Graphics;
using Toybox.System;
using Toybox.Lang;
using Toybox.Communications;

   // set up the response callback function
   function onReceive(responseCode, data) {
       if (responseCode == 200) {
           System.println("Request Successful");                   // print success
       }
       else {
           System.println("Response: " + responseCode.toString());            // print response code
       }
   }

   function makeRequest() {
       var url = "http://www.bom.gov.au/fwo/IDW60801/IDW60801.95620.json"; // set the url
       var params = {
       "sort_order" =>  0,
       "data" => "wind_spd_kt",
       }; //Not sure what to include here???
       var options = {
       	//:methods => Communications.HTTP_REQUEST_METHOD_GET,
		//:headers => {"Content-Type" => Communications.REQUEST_CONTENT_TYPE_URL_ENCODED},
		:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
       };
       var callable = new Lang.Method($, :onReceive);                  // set responseCallback to
                                                                   // onReceive() method
       // Make the Communications.makeWebRequest() call
       Communications.makeWebRequest(url, params, options, callable);
  }
  

class WinWatchWidgetView extends WatchUi.View {

    function initialize() {
        View.initialize();
    }

    // Load your resources here
    function onLayout(dc) {
        setLayout(Rez.Layouts.MainLayout(dc));
    }

    // Called when this View is brought to the foreground. Restore
    // the state of this View and prepare it to be shown. This includes
    // loading resources into memory.
    function onShow() {

    }

    // Update the view
    function onUpdate(dc) {
    	//removed Settings->Use device HTTPS requirements in Simulator
        var test = makeRequest();
    	System.println("Test Response: " + test);
        // Call the parent onUpdate function to redraw the layout
        View.onUpdate(dc);
    }

    // Called when this View is removed from the screen. Save the
    // state of this View here. This includes freeing resources from
    // memory.
    function onHide() {
    }

}

  • What you see is a dictionary with the data.

    So in you're onReceive(), lets say you get

    data={observations=>{uv=>null, humidity=>34.000000}}

    then, to get humidity, you would use

    humidity=data["observations"]["humidity"];

    You what to use HTTPS and not just HTTP.  That's why you got the -1001.

    You can see all the error codes here: https://developer.garmin.com/connect-iq/api-docs/Toybox/Communications.html

    the -402 means the response is too large/too complex.

  • Hello Jim and thank you for the quick reply! I now understand that I can process the data within the onReceive function and the syntax shown above works fine when trying on smaller pages.

    (I'll park the HTTP Vs HTTPS issue as the page I'm trying to focus on extracting a number from this page from now but will certainly get back to it).

    I looked at the HTTP Traffic within the simulator and got confirmation that the request is downloading the whole page which ends up at 130kB which is way too large:

     

    I have managed to output the results of another smaller page for a test and I was getting a string into the data variable even with a 400 error code. However, when running it with the link above, the data variable returns "null" so I can't even process it partially. If it is not possible to filter the request, is it possible to ignore the Error and get the data anyway?

    I can also see what is the address queried and now understand that the parameters are fed into the address with some formatting although this doesn't seem to be doing anything to its content in my case... Basically, this confirms that I'm not getting anything as the code is currently downloading the full page which is too large. Is there a way for the server queried to filter the JSON request in order to only receive what is needed rather than the whole page and then apply filters to it? This seems to be the point of JSON, otherwise, all it is is a pre-formatted web page.

    I can see that the http://www.bom.gov.au/fwo/IDW60801/IDW60801.95620.json?sort_order=0&data=wind_spd_kt page is being requested but the full page is still being downloaded. Did I miss a parameter? Is a way to filter/crop the page by including parameters and/or dowmloading only a specific row?


       function onReceive(responseCode, data) {
           if (responseCode == 200) {
               System.println("Request Successful");                   // print success
           }
           else {
               System.println("ResponseCode: " + responseCode.toString());            // print response code
           }
            var info = data;
            //var info = data["metaInfo"]["mapVersion"];
       		//var info = data["observations"]["data"]["wind_spd_kt"];
       		System.println("Results: " + info);
       }
       
    	function makeRequest() {
    		//var url = "https://route.api.here.com/routing/7.2/calculateroute.json";
    		var url = "http://www.bom.gov.au/fwo/IDW60801/IDW60801.95620.json";
    		var parameters = {
    			"sort_order" =>  0,
    			"data" => "wind_spd_kt"
    		};
    		var options = {
    		:methods => Communications.HTTP_REQUEST_METHOD_GET,
    	    :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
    	    };        
    		var method = new Lang.Method($, :onReceive);
    		Communications.makeWebRequest(url, parameters, options, method);
    	}  

  • The response needs to be in the 8k range.  To accomplish that, there could be parameters in the request will cut down the size of the response.  Or you need to build a proxy built with something like Google or Azure that strips the data you don't need.

  • Thanks for that. Is there a specific way to find out what the parameters might be or would I need to contact the website owner to try to find out?

    For the Google/Azure solution, would you be able to point me to some examples by any chance or some more details on the specific tools I should look for?

  • Many APIs have that API documented on line.

    As far as google/Azure, I've not done it myself, as I found data sources that didn't need it - Open Weather Maps for weather data for example.