Wednesday, September 23, 2015

Navigation ETA Information Revisited

The display of ETA information that I talked about in my last entry about the Digital Dash project was (I thought) pretty neat, since it expanded on the philosophy of bringing as much useful information to one screen as possible.  It was also, unfortunately, very short-lived.

I got to use the feature on exactly one trip.  Then, after we returned home, I made the mistake of upgrading my tablet to Lollipop.  I figured it had been out awhile and had had a couple of point releases, so it was probably safe.  Wrong.

In the first place, it completely messed up my GPS.  After upgrading it took more than five minutes to get a lock, and even when it did, it had an error of up to 900 feet and would drop out every couple of minutes.  I was on the verge of flashing back to KitKat when I saw a post where someone mentioned the "GPS Status Test & Fix - No Ads" app as a possible solution.  Fortunately, installing it did the trick and my GPS is working normally again.  At the same time, the original "GPS Status" app that I was using wasn't working at all, so I've uninstalled that one.

The other thing that Lollipop broke was Google Maps notifications.  Well, maybe not broke, exactly, but it did change things to the point where AutoNotification can't return the ETA data any more.  The information is still there, and AN can actually respond to it, but it can't report it back to Tasker as a variable.  I've reached out to the AN developer and he's investigating, but I haven't heard anything back from him yet.

There's no guarantee, of course, that he'll be able to fix it or that it won't break again, so rather than waiting, I decided to see if there was any other way to get the information I wanted.

Turns out, there is.

I originally started out looking to see if there was an intent that I could call or monitor for this information.  I didn't find one, but I did find plenty of discussions from developers wanting to create different types of navigation apps, particularly for geocaching.  It seemed like they were always directed to check out something called the "Google Maps DistanceMatrix API".

A little more research turned up that this was a web-based service that can be called via a simple HTTP Get command.  You simply create a query string with a starting point, a destination point, and few settings and ship it off to the server.  You get back a data block (either xml or json) that contains, among other things, the distance to the target and the estimated travel time.  Just what I needed.

I played around with it a bit and came up with the code I needed.  I added a couple of lines to my V3_Compass routine to save the current latitude and longitude, added one line to the V3_StartNavigation task that saved my chosen destination to a new global variable, and added one more line to the V3_TimeAndTorque task to call my new routine so that it gets executed about every 30 seconds.

Here's that new task: 

APIQuery (113)
A1: Variable Set [ Name:%dayhalf To:AM Do Maths:Off Append:Off ] 

We start off by assuming that we will be arriving at our destination during the morning hours.  Later, we'll test this assumption and modify this variable if necessary.

A2: Variable Set [ Name:%uri To:/maps/api/distancematrix/xml?origins=%V3_Lat+%V3_Long&destinations=%V3_CurrentDestination&mode=driving&units=imperial&key=My Google Key Do Maths:Off Append:Off ] 
A3: HTTP Get [ Server:Port:https://maps.googleapis.com Path:%uri Attributes: Cookies: User Agent: Timeout:10 Mime Type: Output File: None Trust Any Certificate:Off Continue Task After Error:On ] 

This is the heart of the routine.  We use variables from other parts of the system to construct a properly-formatted query string and ship it off to the Google Maps DistanceMatrix API.  Although anyone can call the non-secure version of the API, I signed up for a free Google Developer Account and obtained a Key so that I could use a secure connection and be able to monitor performance.  This query will return a consistently formatted block of xml data containing the information I need and put it in Tasker's built-in %HTTPD variable.  Much of the rest of this task is devoted to extracting the relevant pieces.

A4: Variable Split [ Name:%HTTPD Splitter:text> Delete Base:Off ] 
A5: Variable Split [ Name:%HTTPD2 Splitter:< Delete Base:Off ] 
A6: Variable Set [ Name:%timeleft To:%HTTPD21 Do Maths:Off Append:Off ] 
A7: Variable Search Replace [ Variable:%timeleft Search:hours Ignore Case:Off Multi-Line:Off One Match Only:Off Store Matches In: Replace Matches:On Replace With:hr ] 
A8: Variable Search Replace [ Variable:%timeleft Search:mins Ignore Case:Off Multi-Line:Off One Match Only:Off Store Matches In: Replace Matches:On Replace With:min ] 

The first thing I obtain is the travel time remaining.  It's almost in the format I want, but to shorten it up a bit for display, I replace "hours" with "hr" and "mins" with "min".

A9: Variable Split [ Name:%HTTPD4 Splitter:< Delete Base:Off ] 
A10: Variable Set [ Name:%distance To:%HTTPD41 Do Maths:Off Append:Off ] 

The next piece is the distance to the destination.  This is already formatted as I want, so I just have to split it out.

A11: Variable Split [ Name:%HTTPD1 Splitter:value> Delete Base:Off ] 
A12: Variable Split [ Name:%HTTPD12 Splitter:< Delete Base:Off ] 
A13: Variable Set [ Name:%durationvalue To:%HTTPD121 Do Maths:Off Append:Off ] 

For the first two pieces of information above, I extracted the "text" versions.  That is, the information as presented in a human-readable format.  However, the xml contains "values" for this information as well: the distance is also sent as meters, and the travel time is also sent in seconds.  Because the API does not return an ETA, I need to calculate it myself, and it's easier to do by manipulating a single block of seconds rather than individual hours and minutes.  Accordingly, I grab the travel time value to use in that calculation.  

A14: Variable Split [ Name:%TIME Splitter:. Delete Base:Off ] 

This line grabs the system time from Tasker's built-in variable (which is in 24-hour format) and splits it into separate hours and minutes variables.

A15: Variable Set [ Name:%eta To:((%TIME1*3600)+(%TIME2*60))+%durationvalue Do Maths:On Append:Off ]

The line above converts the hours and minutes into a single variable that represents the seconds since midnight.  It then adds the number of seconds left to travel to that value.  The result is the ETA expressed in seconds.

A16: Variable Set [ Name:%hours To:floor(%eta/3600) Do Maths:On Append:Off ] 
A17: Variable Set [ Name:%minutes To:floor(((%eta/3600)-%hours)*60+.5) Do Maths:On Append:Off ] 

The above two lines simply convert the seconds back into hours and minutes.

A18: Variable Set [ Name:%dayhalf To:PM Do Maths:Off Append:Off ] If [ %hours > 11 & %hours < 24 ]

Now it's time to revisit that %dayhalf variable.  If %hours is greater than 11, then we will be arriving at our destination in the afternoon and need to set %dayhalf to "PM"; unless %hours is also greater than 23.  In that case, we will be arriving sometime after midnight of the next day, which is morning again so we leave %dayhalf alone.

A19: Variable Subtract [ Name:%hours Value:12 Wrap Around:0 ] If [ %hours > 12 ]
A20: Variable Subtract [ Name:%hours Value:12 Wrap Around:0 ] If [ %hours > 12 ]

I prefer the 12-hour time format, so these two lines take care of converting 24-hour time back to what I want.  The same action is used twice just in case our trip will take us past midnight.

A21: Variable Set [ Name:%V3_ETA To:%timeleft (%distance)ETA: %hours:%minutes %dayhalf Do Maths:Off Append:Off ] 

The final step is to take all the information we've derived and put it into a variable which is linked to a text box display on the main screen.

Once again, here's what that looks like:



To test this, we took the Z3 out for a little drive this weekend.  We drove over a neighboring town and when we got ready to come back, I called up my navigation panel and chose "Home" as the destination.  In about 15 seconds, the ETA display showed up on the main screen and began tracking the trip.  I watched it pretty closely and it seemed to work perfectly.  The ETA that it showed turned out to be exactly right and it counted down time and distance very accurately.

Later, I logged on to my Google Developer account and checked the stats for usage on the Distance Matrix API.  It showed 44 calls, which was exactly correct for the 22 minute trip, and every call was successful.  I also checked my cellular data usage and showed that my Bluetooth tethering app went through just under 8Mb for the day.  I have no way of knowing just how much of that was for Distance Matrix calls, but even it accounted for all of it, I'm no danger of running over my 10GB monthly allowance even if I use navigation heavily.

Update:  I tried this again today.(The weather here has been beautiful this weekend.)  Same idea, but this time it was a longer test.  On a 49-minute trip, I can see that the system handled 98 successful events and the info tracked perfectly.  My phone showed data usage of about 5MB, so it is using even less than I had previously thought.  It looks like I could run this system 24 hours per day all month and not even use half of my monthly data allowance.



No comments: