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;
}
Views