Ultimate Traffic Recipe

This rule tracks your commutes and supplies you with up-to-date traffic information to and from work.

Description:

This rule gives you traffic updates on screen unlocks (1 per 3 minutes) from your home to work. Once you leave home, GPS tracks you to give you traffic updates on screen unlock (1 per minute) until you reach your destination. No matter where you start or finish the day, the “start/stop_morning/evening” variables ensure the rule does not run away with GPS battery life.

———————————————————–

Usability:

Change “home_location” variable to the latitude and longitude of your home (FORMAT: latitude,longitude)
Change “work_location” variable to the latitude and longitude of your work (FORMAT: latitude,longitude)
Change “start_morning” variable to the time you wish to start tracking your morning commute (should be the earliest you leave for work)
Change “stop_morning” variable to the time you wish to stop tracking your morning commute (should be the latest you get to work)
Change “start_evening” variable to the time you wish to start tracking your evening commute (should be the earliest you leave work)
Change “stop_evening” variable to the time you wish to stop tracking your evening commute (should be the latest you get home from work)

===========================================================

Because this is a fairly complex script, it needs some people to test it more thoroughly than just I can. It relies on “Region” detection, as well as “GPS” (for a short amount of time).

Please let me know your reactions, comments, and suggestions!


To use this on{X} Recipe, simply copy the code below and modify if needed.

/*   Ultimate Traffic On{x} Rule
 *
 *   Coded by Christopher King
 *   Released under GPL v3
 *
 *   Description:
 *
 *     This rule gives you traffic updates on screen unlocks (1 per 3 minutes) from
 *     your home to work.  Once you leave home, GPS tracks you to give you traffic
 *     updates on screen unlock (1 per minute) until you reach your destination.  No
 *     matter where you start or finish the day, the "start/stop_morning/evening" 
 *     variables ensure the rule does not run away with GPS battery life.
 *
 *
 *   Usability:
 *
 *     Change "home_location" variable to the latitude and longitude of your home  (FORMAT: latitude,longitude)
 *     Change "work_location" variable to the latitude and longitude of your work  (FORMAT: latitude,longitude)
 *     Change "start_morning" variable to the time you wish to start tracking your morning commute (should be the earliest you leave for work)
 *     Change "stop_morning"  variable to the time you wish to stop tracking your morning commute (should be the latest you get to work)
 *     Change "start_evening" variable to the time you wish to start tracking your evening commute (should be the earliest you leave work)
 *     Change "stop_evening"  variable to the time you wish to stop tracking your evening commute (should be the latest you get home from work)
 *     Change "type_gps"      variable to the GPS type you would like to use: CELL, PASSIVE, or GPS
 *     Change "gps_interval"  variable to the time between GPS updates you would like (in milliseconds)
 */


var home_location = '32.859473,-96.273947';
var work_location = '33.273748,-96.929384';

var start_morning = '6:00 AM';
var stop_morning = '9:00 AM';
var start_evening = '4:00 PM';
var stop_evening = '7:00 PM';

var type_gps = 'CELL'; // Could be 'GPS'
var gps_interval = 60000; // 1 minute



/* -----------------------------------------------------------------
 * |                DO NOT CHANGE BELOW THIS LINE                  |
 * -----------------------------------------------------------------
 */
 
console.log('Started beta traffic script');

// In the next few lines, we load the variables from storage if they exist.  This would happen if the app gets killed by the Android OS, which happens quite often.

// Load wps variable (location settings) from storage if it exists
var wps = device.localStorage.getItem('wps');
if (wps === null)
{
    wps = []; // If it doesn't exist in storage, make it a blank array
    device.localStorage.setItem('wps',JSON.stringify(wps)); // Store the blank array into storage
}
else
	wps = JSON.parse(wps); // If it does exist, we need to parse it (since local storage only stores strings, we had to "stringify" it above

// Create two regions: one around home, and one around work.  We don't need to store these because the regions don't change
// The splits you see below just parse the work_location and home_location variables defined at the top of the script.
// This isn't the most efficient way, but it works for right now.  To be corrected later...
var work_region = device.regions.createRegion({ latitude: parseFloat(work_location.split(",")[0]), longitude: parseFloat(work_location.split(",")[1]), name: "Work", radius: 500});
var home_region = device.regions.createRegion({ latitude: parseFloat(home_location.split(",")[0]), longitude: parseFloat(home_location.split(",")[1]), name: "Home", radius: 500});

var found_loc = []; // Declare an array that will store GPS coordinates once we need/want them

// Grab direction from storage if it exists (either "to" or "from", meaning "to" work or "from" work)
var direction = device.localStorage.getItem('direction');
if (direction === null)
{
	direction = 'to'; // If its not in storage, initialize it to "to"
	device.localStorage.setItem('direction', direction); // store in storage
}

// Grab state from storage (very important)
var state = device.localStorage.getItem('state');
if (state === null)
{
	state = 'inactive'; // If it doesn't exist in storage, set into "inactive" state (possible states: inactive, begin, enroute, end)
	device.localStorage.setItem('state', state); // Store in storage
}

// Grab query_delay from storage
var query_delay = device.localStorage.getItem('query_delay');
if (query_delay === null)
{
	query_delay = 3*60; // If it doesn't exist in storage, initialize it to 3 minutes 
	device.localStorage.setItem('query_delay', query_delay); // Store in storage
}

// Initialize GPS listener
var accurate_listener = device.location.createListener(type_gps, gps_interval);
accurate_listener.on('changed', function(signal) {
	console.log(type_gps + ' location found: ' + signal.location.latitude + ', ' + signal.location.longitude);
	found_loc = [signal.location.latitude, signal.location.longitude]; // On GPS change, store the coordinates inside found_loc
});

// Create handler for when you enter the work region
work_region.on('enter', function(signal) {
	console.log('ON ENTER REGION: WORK');
	if (direction === 'to') // If you are going TO work, this means that you have entered the destination region
	{
		console.log('Setting state from ' + state + ' to end');
		state = 'end'; // End of the journey, set state to 'end'
		query_delay = 3*60; // Set query delay back to 3 minutes
		device.localStorage.setItem('query_delay', query_delay); // Store query delay in storage
		device.localStorage.setItem('state',state); // Store state in storage
	}
});

// Create handler for when you exit the work region
work_region.on('exit', function(signal) {
	console.log('ON EXIT REGION: WORK');
	if (direction === 'from') // If you are coming FROM work, you are starting your journey
	{
		console.log('Setting state from ' + state + ' to enroute');
		state = 'enroute'; // Set the state to 'enroute', which makes you use more accurate GPS
		accurate_listener.start(); // Start the GPS listener
		query_delay = 1*60; // Set the query delay to a shorter period: 1 minute (so we can check periodically while in the car)
		device.localStorage.setItem('query_delay', query_delay); // Store query delay inside storage
		device.localStorage.setItem('state',state); // Store state in storage
	}
});

// Create handler for when you enter the home region
home_region.on('enter', function(signal) {
	console.log('ON ENTER REGION: HOME');
	if (direction === 'from') // If you are coming FROM work, you are ending your journey
	{
		console.log('Setting state from ' + state + ' to end');
		state = 'end'; // Set the state to 'end' because you have reached your destination
		query_delay = 3*60; // Reset query delay to 3 minutes
		device.localStorage.setItem('query_delay', query_delay); // Store query delay in storage
		device.localStorage.setItem('state',state); // Store state in storage
	}
});

// Create handler for when you exit the home region
home_region.on('exit', function(signal) {
	console.log('ON EXIT REGION: HOME');
	if (direction === 'to') // If you are going TO work, you are beginning your journey
	{
		console.log('Setting state from ' + state + ' to enroute');
		state = 'enroute'; // Set the state to 'enroute', which makes you use more accurate GPS
		accurate_listener.start(); // Start the GPS listener
		query_delay = 1*60; // Set the query delay to a shorter period: 1 minute (so we can check periodically while in the car)
		device.localStorage.setItem('query_delay', query_delay); // Store query delay inside storage
		device.localStorage.setItem('state',state); // Store state in storage
	}
});

// Parse the start/stop times into Date objects (parseTime function is at bottom of script)
var start_morning = parseTime(start_morning);
var stop_morning = parseTime(stop_morning);
var start_evening = parseTime(start_evening);
var stop_evening = parseTime(stop_evening);


var nowTemp = new Date(); // Declare new temporary variable with right now's time
nowTemp = formatTimeNum(nowTemp.getHours())+''+formatTimeNum(nowTemp.getMinutes()) + formatTimeNum(nowTemp.getSeconds()); // Format a comparable representation of the time: HHMMSS

// Start the morning starting timer
device.scheduler.setTimer({
	name: 'Start Morning',
	time: start_morning.getTime(), // Start the timer
	interval: 'day', // Repeat every day
	exact: false }, // Don't make it exact, so you save more battery (mentioned in documentation)
	function()
	{ // If the time is less than the stopping morning time, and the state is inactive, then execute the morning scheduler
		if (parseInt(nowTemp,10) <= parseInt(formatTimeNum(stop_morning.getHours())+''+formatTimeNum(stop_morning.getMinutes()) + formatTimeNum(stop_morning.getSeconds()),10) && state === 'inactive')
		{
			console.log('Start morning scheduler fired!  It must be ' + start_morning.toLocaleTimeString());
			wps = [home_location, work_location]; // Set the waypoints for traffic
			direction = 'to'; // Set the direction
			state = 'begin'; // Set the state to the beginning of the journey
			device.localStorage.setItem('wps', JSON.stringify(wps)); // Store wps as stringified in storage
			device.localStorage.setItem('direction',direction); // Save direction in storage
			device.localStorage.setItem('state', state); // Save state in storage
			device.regions.startMonitoring(work_region); // Start work region monitoring
			device.regions.startMonitoring(home_region); // Start home region monitoring
		}
		else
			console.log('Its not morning, so skipping start morning scheduler');
	});

// Start the morning ending timer
device.scheduler.setTimer({
	name: 'End Morning',
	time: stop_morning.getTime(), // Start the timer
	interval: 'day', // Repeat every day
	exact: false }, // Don't make it exact, so you save more battery
	function()
	{
		console.log('End morning scheduler fired!  It must be ' + stop_morning.toLocaleTimeString());
		state = 'inactive'; // Set state into inactive mode (meaning no notifications, gps, or traffic updates occur)
		wps = []; // Set waypoints to blank array
		device.localStorage.setItem('wps','[]'); // Store wps in storage
		device.localStorage.setItem('state', state); // Store state in storage
		accurate_listener.stop(); // Stop the GPS listener (if its running)
		device.regions.stopMonitoring(work_region); // Stop work region monitoring
		device.regions.stopMonitoring(home_region); // Stop home region monitoring
	});
	
// Start the evening starting timer
device.scheduler.setTimer({
	name: 'Start Evening',
	time: start_evening.getTime(), // Start the timer
	interval: 'day', // Repeat every day
	exact: false }, // Don't make it exact, so you save more battery
	function()
	{
		if (parseInt(nowTemp,10) <= parseInt(formatTimeNum(stop_evening.getHours())+''+formatTimeNum(stop_evening.getMinutes()) + formatTimeNum(stop_evening.getSeconds()),10) && state === 'inactive')
		{
			console.log('Start evening scheduler fired!  It must be '  + start_evening.toLocaleTimeString());
			direction = 'from'; // Set the direction
			state = 'begin'; // Set the state to the beginning of the journey
			wps = [work_location, home_location]; // Set the waypoints (work first)
			device.localStorage.setItem('wps',JSON.stringify(wps)); // Store wps in storage
			device.localStorage.setItem('direction',direction); // Store direction in storage
			device.localStorage.setItem('state', state); // Store state in storage
			device.regions.startMonitoring(work_region); // Start work region monitoring
			device.regions.startMonitoring(home_region); // Start home region monitoring
		}
		else
			console.log('Its past evening, so skipping start evening scheduler');
	});

// Start the evening stopping timer
device.scheduler.setTimer({
	name: 'End Evening',
	time: stop_evening.getTime(), // Start the timer
	interval: 'day', // Repeat every day
	exact: false}, // Don't make it exact, so you save more battery
	function()
	{
		console.log('End evening scheduler fired!  It must be ' + stop_evening.toLocaleTimeString());
		state = 'inactive'; // Set state into inactive mode (meaning no notifications, gps, or traffic updates occur)
		wps = []; // Set waypoints to blank array
		device.localStorage.setItem('wps','[]'); // Store wps in storage
		device.localStorage.setItem('state', state); // Store state in storage
		accurate_listener.stop(); // Stop the GPS listener (if its running)
		device.regions.stopMonitoring(work_region); // Stop work region monitoring
		device.regions.stopMonitoring(home_region); // Stop home region monitoring
	});


	
// Set the screen unlock trigger
device.screen.on('unlock', function() {
	console.log('ON SCREEN UNLOCK');
	if (state != 'inactive') // If the state is NOT inactive, then continue
	{
		var today = new Date(); // Get most current Date object 
		var last_query = device.localStorage.getItem('last_query'); // Grab last_query variable from storage (doesn't matter if its null)
		console.log('Today, last_query, query_delay: ' + today.getTime() + ', ' + last_query + ', ' + query_delay);
		if (state === 'end') // If you are at the destination (AKA: 'end' state)
		{
			console.log('DESTINATION_ACHIEVED');
			device.notifications.createNotification('Destination reached').show();  // Show notification
			device.regions.stopMonitoring(work_region); // Stop work region monitoring
			device.regions.stopMonitoring(home_region); // Stop home region monitoring
			accurate_listener.stop(); // Stop GPS locating
			state = 'inactive'; // Set the state to inactive
			device.localStorage.setItem('state', state); // Store state in storage
			return; // Exit the unlock trigger handler immediately
		}
		if (today.getDay() < 6 && today.getDay() > 0 && (today.getTime() - last_query)/1000 >= query_delay) // If it is a weekday AND if the last query was not within the query_delay
		{
			console.log('Inside weekday/delay timing');
			if (state === 'enroute') // If we are enroute to the destination (accurate GPS)
			{
				console.log('We are enroute, so use more accurate GPS if available');
				if (found_loc.length === 2) // If we have GPS coordinates
				{
					wps[0] = found_loc[0] + ',' + found_loc[1]; // Set the first waypoint to our current location
					console.log('Found lock: ' + found_loc[0] + ',' + found_loc[1]);
				}
				else
					console.log('No accurate GPS available'); // Otherwise, we still use home/work location (or last known location)
			}
			console.log('Getting traffic with state: ' + state);
			// Get the traffic feed now
			feeds.traffic.get(
				{wps: wps, unittype: 'i'}, // Use wps as waypoints, and imperical units (miles)
				function (traffic, textStatus, response)
				{
					var hours = Math.floor(traffic.totalTravelDuration/(60*60)); // Get hours from totalTravelDuration
					var divisor = traffic.totalTravelDuration % (60*60);
					var minutes = Math.floor(divisor / 60); // Get minutes from totalTravelDuration
					divisor = divisor % 60;
					var seconds = Math.ceil(divisor); // Get seconds from totalTravelDuration (probably unnecessary)
					var durText = 'Estimated duration: '; // Start creating duration text
					if (hours !== 0) // If hours is greater than 0:
						durText += hours + ':'; // Put hours into notification
					// Put minutes and seconds (with leading 0s if necessary), as well as incidents/warnings in the notification (not sure of the difference between the two)
					durText += formatTimeNum(minutes) + ':' + formatTimeNum(seconds) + ' with ' + traffic.incidents.length + '/' + traffic.warnings.length + ' incidents/warnings';
					var temp_today = new Date(); // Make temporary Date object
					temp_today.setSeconds(temp_today.getSeconds()+(traffic.tot alTravelDuration)); // Add the totalTravelDuration to the current Date so we get the ETA
					var mainNotify = device.notifications.createNotification('Traffic ' + direction + ' work: ETA ' + temp_today.toLocaleTimeString()); // Show ETA
					mainNotify.content = durText; // Set the notification content as the duration text
					mainNotify.on('click', function() {
						feeds.traffic.show({wps: wps, unittype:'i'}); // On notification click, show the map of the route
					});
					var notify, x;
					// Loop through incident reports
					for (x=0;x<traffic.incidents.length;++x)
					{
						notify = device.notifications.createNotification('Incident Reported:'); // Create a new notification for each incident
						notify.content = traffic.incidents[x].severity + ' ' + traffic.incidents[x].type + ' ' + traffic.incidents[x].description; // Set content for each incident
						notify.show(); // Show the incident
					}
					
					// Loop through the warning reports
					for (x=0; x<traffic.warnings.length;++x)
					{
						notify = device.notifications.createNotification('Warning Report:'); // Create a new notification for each warning
						notify.content = traffic.warnings[x].text; // Set content for each warning
						notify.show(); // Show the wanring
					}
					mainNotify.show(); // Show the main traffic duration last so it shows up on top (newer notifications are on top)
				},
				function(response, textStatus) // On error, log errors
				{
					var notification = device.notifications.createNotification('Traffic Report ' + direction + ' work');
					notification.content = 'Error getting traffic: ' + textStatus;
				}
			);
			device.localStorage.setItem('last_query', today.getTime()); // After everything, store the last_query for next time
		}
		else
			console.log('Not the correct day or delay');
	}
	else
		console.log('State is currently inactive');
	console.log('END UNLOCK SCREEN FUNCTION');
});

// parseTime simply takes a timeString and converts it into a Date object.  This was taken from the pre-created recipe named "Show me the weather forecast"
function parseTime(timeString) {
        if (timeString === '') {
            return null;
        }

        var time = timeString.match(/^(\d+)(:(\d\d))?\s*((a|(p))m?)?$/i);

        if (time === null) {
            return null;
        }

        var hours = parseInt(time[1],10);
        if (hours === 12 && !time[6]) {
            hours = 0;
        } else {
            hours += (hours < 12 && time[6]) ? 12 : 0;
        }

        var d = new Date();
        d.setHours(hours);
        d.setMinutes(parseInt(time[3], 10) || 0);
        d.setSeconds(0, 0);
        return d;
    }

// formatTimeNum simply takes a number (or string of a number) and adds one 0 onto the end of it if required.
function formatTimeNum(val) {
    if (parseInt(val,10) < 10)
        return '0' + val;
    return val;
}

Recipe Wall

1,088

Views