updating view content from background event with GPS and OWM API

Hi,

I am wondering whether my approach to using GPS coordinates with the Open Weather Map API to make changes to the watch face layout is 1) correct (it is a bit difficult to test on a watch compared to the simulator), and 2) efficient. I want to fire the background event once every hour, but "instantly" (as soon as possible with the 5 min background event request limit) when the user checks their watch after visiting another screen.

This is the logic in my onTemporalEvent function in the background.mc file. I register it once every 60 mins, and it is meant to pick up the current location from the GPS; if it fails to do so, it gets the last lat/long data from the last successful API request. If those are unavailable then it uses a set of constant lat/long coordinates I've defined.

if (Activity.getActivityInfo().currentLocation == null)
    		{
    			Sys.println("current loc null");
    			lat = Application.getApp().getProperty("lat");
    			lon = Application.getApp().getProperty("lon");
    			
    			if (lat == null || lon == null)
    			{
    				//read fixed home coordinates
    				lat = home_lat;
    				lon = home_lon;
    				Sys.println("home coords");
    			}
    		}

    		else
    		{
    			Sys.println("got coords!");
				lat = Activity.getActivityInfo().currentLocation.toDegrees()[0];
				lon = Activity.getActivityInfo().currentLocation.toDegrees()[1];
    		}

Then I launch a OWM API request (using the code here), and this is my receive function:


dict = {};

...

    function receiveWeather(responseCode, data) {
    	if (responseCode == 200)// && data instanceof Dictionary)
    	{ 	
	    	var weather = data.get("weather")[0].get("icon");
	    	var temperature = data.get("main").get("temp");
	    	
	    	dict["temp"] = temperature;
	    	dict["code"] = weatherCodes.get(weather);
	    	
//	    	Sys.println(dict);	
	    	
		Background.exit(dict);

		}
		else
		{
		//	Sys.println("ERROR SIMULATED!");
			Background.exit(400); // send some number to indicate an error
		}
	}
}

And then with my onBackgroundData in the app class:

    function onBackgroundData(data) {
    
    	if (data != 400) 
    	{
    		app_dict = data; // pass data to foreground
    		
    		var now = Time.now().value();
    		
    		//save coordinates if we got them
    		if (Activity.getActivityInfo().currentLocation != null)
    		{
    			Sys.println(Activity.getActivityInfo().currentLocation);
	    		var lat = Activity.getActivityInfo().currentLocation.toDegrees()[0];
				var lon = Activity.getActivityInfo().currentLocation.toDegrees()[1];
		    	
				lat = Application.getApp().setProperty("lat", lat);
				lon = Application.getApp().setProperty("lon", lon);
			}
				
	        App.getApp().setProperty("weatherCode", app_dict["code"]);
	        App.getApp().setProperty("temperature", app_dict["temp"]);
	        App.getApp().setProperty("timestamp", now);
	        Ui.requestUpdate();
    	}    
    	else
    	//we got some error
    	{
    		//see if we can retrieve the previously stored value, otherwise fill in an empty string
	        var prevCode = App.getApp().getProperty("weatherCode");
	        app_dict["code"] = prevCode == null ? prevCode : null;
	        
	        var prevTemp = App.getApp().getProperty("temperature");   
	        app_dict["temp"] = prevTemp == null ? prevTemp : null; 		
    	}
    }

Lastly, I check in my view initialize() code (which I assume is run each time the user checks the watch face app after closing another screen on their watch) whether the latest timestamp is "too new" for a background event request. If not, 

	const MIN_TIME = 900; // 15 min
	
    function initialize() {
        WatchFace.initialize();
        
        var now = Time.now().value();
        var timeLastGPS = App.getApp().getProperty("timestamp"); // get timestamp of the last time we saved
        
        if (timeLastGPS != null)
        {        
        	if (now - timeLastGPS < MIN_TIME) // load recent GPS data if it hasn't been more than X min
        	{
		        if (App.getApp().getProperty("temperature") != null)
		        {
		        	app_dict["temp"] = App.getApp().getProperty("temperature");
		        	app_dict["code"] = App.getApp().getProperty("weatherCode");
		        }     
		    }
		    else
		    {
		    	// fire up GPS event
				var FIVE_MINUTES = new Time.Duration(5 * 60);
				var lastTime = Background.getLastTemporalEventTime();
				if (lastTime != null) {
				    // Events scheduled for a time in the past trigger immediately
				    var nextTime = lastTime.add(FIVE_MINUTES);
				    Background.registerForTemporalEvent(nextTime);
				} else {
				    Background.registerForTemporalEvent(Time.now());
				}    
		    }  
	    }    
    }

And I use check if app_dict["code"] != null to make changes in the onUpdate(dc) which seems to work with no problems. Also, I thought the watch needs to be connected to WiFi in order to make a request to the API, but it seems like a watch-phone connection also works.

  • Couple of things I noticed is that you have

    else
    {
    // Sys.println("ERROR SIMULATED!");
    Background.exit(400); // send some number to indicate an error
    }

    were you may want to return responseCode instead of 400.  That way the main app can see the acutal error, be it no phone, vs another error and take action based on that.

    Then in onBackgroundData, do
    if(data instanceof Number) {    //got an error

    ...

    } else {     //got data

    ...

    }

    to see if you have an error vs data and you can see what error it was.  You may want to add a few error codes of your own, such as no API Key, no location, etc.  Always using 400 gives the same error you'd see with no API Key from OWM.

    Also, with OWN, you may want to use app_dict["dt"] to get the time of the reading by OWM instead of using the current time on the watch or the last time the background was run, as the reading could be older than current time,  Using that with Time.now().value() will give you how old that data is in seconds, which could be, for example, an hour before the background service got the data.  That's where only requesting the data every hour could result in old data.  Maybe make the time of the temporal event configurable, so it can get the data save every 15 minutes if the data you see every hour is old?

    for lat/lon you may what to check currentLocation when the watchface starts, and if that's not null, save it to storage, and then just use that in the background service.  If its null in storage, the background could return an error that indicated "no location" or use the home location, or you could just show the error in the watch face itself if currentLocation is null and there's nothing in storage.  What if the home lat/lon was never set by the user?

  • BTW, the watch generally won't use Wifi as it's rarely connected, but will generally use BT to the phone.