var Clay = require('pebble-clay'); var clayConfig = require('./config'); var clay = new Clay(clayConfig, null, { autoHandleEvents: false }); var messageKeys = require('message_keys'); Pebble.addEventListener('showConfiguration', function(e) { // This is an example of how you might load a different config based on platform. var platform = clay.meta.activeWatchInfo.platform || 'aplite'; if (platform === 'aplite') { clay.config = clayConfigAplite; } Pebble.openURL(clay.generateUrl()); }); Pebble.addEventListener('webviewclosed', function(e) { if (!t || t.response) { try { if (data = JSON.parse(t.response), data.strava) { //data.strava will be like this : // {"code":"db896b06f89804997a8088320fba755e6299c0d6","scope":"read,activity:write","state":"bike_companion"} // so need to parse it correctly //console.log("strava temp code to exchange to access token : " + data.code) // placeholder to get token from code from php return token } } catch (t) { i && console.error("Parsing error:", t) } var dict = clay.getSettings(t.response); /* assess if needed to send setting to watch as already in phone (done with "getsettings") m.live_mmt_enable = dict[p.live_mmt_enable] m.live_mmt_login = dict[p.live_mmt_login] m.live_mmt_password = dict[p.live_mmt_password] m.live_jayps_enable = dict[p.live_jayps_enable] m.live_jayps_login = dict[p.live_jayps_login] m.live_jayps_password = dict[p.live_jayps_password] m.strava_automatic_upload = dict[p.strava_automatic_upload] i = dict[p.debug] console.log("debug:" + i) g.setDebug(i) h.setDebug(i) b.setDebug(i) y.setDebug(i) m.setDebug(i) m.save() // Assess if needed to send settings values to watch side /* Pebble.sendAppMessage(dict, function(e) { console.log('Sent config data to Pebble'); }, function(e) { console.log('Failed to send config data!'); console.log(JSON.stringify(e)); });*/ } }); var message; var gpx_to_strava var gpx_to_web var locate_me var locationInterval; var instantLocationInterval; var firstlocationOptions = { 'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time) 'timeout': 60000, //60s timeout to get a first good signal 'maximumAge': 0 // no cache }; var locationOptions = { 'enableHighAccuracy': true, // default = false (quick and dirty mode), can be true (more accurate but need more power and time) 'timeout': 5000, //5s timeout to get a good signal 'maximumAge': 0 // no cache }; var geoloc_id; // Store location in Pebble app local storage // function storeLocation(position) { var latitude = position.coords.latitude; var longitude = position.coords.longitude; var timestamp = position.timestamp; localStorage.setItem("latitude", latitude); localStorage.setItem("longitude", longitude); localStorage.setItem("timestamp", timestamp); // console.log("Stored location " + position.coords.latitude + ',' + position.coords.longitude); } // Get location from Pebble app local storage // function getLocation() { if (localStorage.getItem("latitude") || localStorage.getItem("longitude") || localStorage.getItem("timestamp")) { var la = localStorage.getItem("latitude"); var lo = localStorage.getItem("longitude"); var ti = localStorage.getItem("timestamp"); var co = { "latitude": la, "longitude": lo }; var pos = { "coords": co, "timestamp": ti }; // console.log("Stored location " + pos.co.la + ',' + pos.co.lo); return pos; } else { return null; } } // Get max speed of the run // function getMaxSpeed(lastSpeed) { oldmax = localStorage.getItem("maxSpeed") || -1; if (oldmax < lastSpeed) { maxSpeed = lastSpeed } else if (oldmax > lastSpeed) { maxSpeed = oldmax } else { maxSpeed = oldmax } localStorage.setItem("maxSpeed", maxSpeed); return maxSpeed } // split float number into an array of int (null returned instead of 0 for decimal) // function splitFloatNumber(num) { const intStr = num.toString().split('.')[0]; const decimalStr = num.toString().split('.')[1]; return [Number(intStr), Number(decimalStr)]; } // Build GPX headers // function GPXHeadersBuilder(timestamp, name, type) { var headers = '' + name + '' + type + ''; var ret = localStorage.setItem("GPX", headers); return true; } // Build GPX footer // function GPXtrkptBuilder(lat, lon, ele, timestamp) { var GPX = localStorage.getItem("GPX"); var trkpt = '' + ele + ''; localStorage.setItem("GPX", GPX + trkpt); return true; } // Build GPX footer // function GPXfooterBuilder() { var GPX = localStorage.getItem("GPX"); var footer = ''; var ret = localStorage.setItem("GPX", GPX + footer); //console.log("GPX closed : " + GPX + footer); return ret; } // Send GPX to Strava profile // TODO : get authentication creds from settings function SendToStrava() { console.log('--- GPX upload to strava'); var gpxfile = localStorage.getItem("GPX"); // need to automate oAUTH var bearer = "09f93068353f11f09d22059f1e8e42ef526949a5" params = { url: "https://www.strava.com/api/v3/uploads", method: "POST", data: { description: "desc", data_type: "gpx" }, files: { file: gpxfile }, authorization: "Bearer " + bearer, callback: function(e) { var message = ""; // what is 'r' // what is 'e' // what is 'o' // what is 'z' if (console.log(e.status + " - " + e.txt), 201 == e.status) { message = "Your activity has been created"; } else if (400 == e.status) { message = "An error has occurred. If you've already uploaded the current activity, please delete it in Strava."; } else if (401 == e.status) { message = "Error - Unauthorized. Please check your credentials in the settings.", o.setAccessToken(""); } else { try { response_json = JSON.parse(e.txt) response_json.error ? (console.log("error:" + response_json.error), message = response_json.error, o.setAccessToken("")) : response_json.status && (console.log("status:" + response_json.status), z = response_json.status) } catch (e) { console.log("Error log, " + e) } } message && Pebble.showSimpleNotificationOnPebble("Ventoo SE - Strava", message) } } var XHR = new XMLHttpRequest; var n = this; console.log(params.url); XHR.open(params.method, params.url, !0); var body = ""; var boundary = Math.random().toString().substring(2); XHR.setRequestHeader("content-type", "multipart/form-data; charset=utf-8; boundary=" + boundary) XHR.setRequestHeader("Authorization", params.authorization); for (var i in params.data) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '"\r\n\r\n' + params.data[i] + "\r\n"; for (var i in params.files) body += "--" + boundary + '\r\nContent-Disposition: form-data; name="' + i + '" ; filename=test.gpx\r\n\r\n' + params.files[i] + "\r\n"; body += "--" + boundary + "--\r\n" XHR.onreadystatechange = function() { // what is 'n' try { 4 == XHR.readyState && (n.status = XHR.status, n.txt = XHR.responseText, n.xml = XHR.responseXML, params.callback && params.callback(n)) } catch (e) { console.error("Error2 loading, ", e) } } XHR.send(body) } // Send GPX to web server (need configuration on serverside) // TODO : secure it ? function PostToWeb() { console.log('--- GPX upload to custom web server'); var GPX = localStorage.getItem("GPX"); var url = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_url + "?name=pebblegpx&type=application/gpx+xml"; var xhr = new XMLHttpRequest(); xhr.timeout = 10000; // time in milliseconds xhr.open("POST", url, false); //console.log('------ CSV / xhr opened') xhr.onload = function() { //console.log('------xhr onload') if (xhr.readyState === 4) { //console.log('------xhr request returned with ' + xhr.status); //console.log(this.responseText); if (xhr.status == 200) { //console.log('--> HTTP 200'); return true; } else { //console.log('--> HTTP ' + xhr.status); return false; } } }; //send CSV in body xhr.send(GPX); } // Send location to web server for instant location (no live tracking) // TODO : secure it ? function instantLocationUpdate(pos) { console.log('--- Instant location update'); // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy); var url = JSON.parse(localStorage.getItem('clay-settings')).ping_location_url + "?lat=" + pos.coords.latitude + "&long=" + pos.coords.longitude + "&acc=" + pos.coords.accuracy + "×tamp=" + pos.timestamp; var xhr = new XMLHttpRequest(); xhr.timeout = 10000; // time in milliseconds xhr.open("POST", url); //console.log('------ instant / xhr opened') xhr.onload = function() { //console.log('------xhr onload') if (xhr.readyState === 4) { //console.log('------xhr request returned with ' + xhr.status); //console.log(this.responseText); if (xhr.status == 200) { //console.log('--> HTTP 200'); return true; } else { //console.log('--> HTTP ' + xhr.status); return false; } } }; //send without body xhr.send(); } // called in case of successful geoloc gathering and sends the coordinate to watch // function locationSuccess(new_pos) { console.log('--- locationSuccess'); // console.log(" location is " + new_pos.coords.latitude + ',' + new_pos.coords.longitude + ' , acc. ' + new_pos.coords.accuracy); var prev_pos = getLocation(); storeLocation(new_pos); if (prev_pos === null) { console.log('--- start building gpx'); if (gpx_to_strava || gpx_to_web) { // Start the GPX file GPXHeadersBuilder(new Date(new_pos.timestamp).toISOString(), "test", "18"); } } else { //clear watch of new position to avoid overlap //navigator.geolocation.clearWatch(geoloc_id); //console.log('--- watch geoloc cleared'); //var speed = speed_from_distance_and_time(prev_pos, new_pos); //get speed from geoloc API isntead of calculate it // speed is initially in m/s, get it at km/h if (new_pos.coords.speed === null) { var speed = 0; } else { var speed = new_pos.coords.speed * 3.6; } // Prepare display on watch // now it's only raw data // init strings var latitudeString = ""; var longitudeString = ""; var accuracyString = ""; var altitudeString = ""; var speedString = ""; //formating for precision and max size latitudeString = new_pos.coords.latitude.toString().substring(0, 12); longitudeString = new_pos.coords.longitude.toString().substring(0, 12); accuracyString = new_pos.coords.accuracy.toString().substring(0, 4); //console.log("split num : " + new_pos.coords.altitude); altitudeString = splitFloatNumber(new_pos.coords.altitude)[0].toString().substring(0, 5); timestampISO = new Date(new_pos.timestamp).toISOString(); //console.log("split num : " + speed); if (isNaN(speed)) { speedString = "---"; } else { speedString = splitFloatNumber(speed)[0].toString().substring(0, 3) + "." + splitFloatNumber(speed)[1].toString().substring(0, 1); if (speedString == "0.N") { speedString = "0.0"; } //console.log("split num : " + getMaxSpeed(speed)); maxSpeedString = splitFloatNumber(getMaxSpeed(speed))[0].toString().substring(0, 3); } if (gpx_to_strava || gpx_to_web) { //add a new datapoint to GPX file GPXtrkptBuilder(latitudeString, longitudeString, altitudeString, timestampISO); } // Build message message = "OK"; var dict = { 'accuracy': accuracyString, 'altitude': altitudeString, 'speed': speedString, 'max_speed': maxSpeedString, 'status': message }; // Send the message Pebble.sendAppMessage(dict, function() { console.log('Message sent successfully: ' + JSON.stringify(dict)); }, function(e) { console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e)); }); } } function locationError(err) { console.warn('location error (' + err.code + '): ' + err.message); } function get_coordinate() { console.log('--- Starting regular getCurrentPosition loop using setInterval at 1 sec'); locationInterval = setInterval(function() { navigator.geolocation.getCurrentPosition(locationSuccess, locationError, locationOptions); }, 1000); if (locate_me) { instantLocationInterval = setInterval(function() { navigator.geolocation.getCurrentPosition(instantLocationUpdate, locationError, locationOptions); }, 60000); } } function init() { // local storage init gpx_to_strava = JSON.parse(localStorage.getItem('clay-settings')).strava_upload; gpx_to_web = JSON.parse(localStorage.getItem('clay-settings')).gpx_web_enable; locate_me = JSON.parse(localStorage.getItem('clay-settings')).ping_location_enable; var ce = gpx_to_web; var cu = localStorage.getItem("custom_enabled"); var se = gpx_to_strava; var su = localStorage.getItem("strava_enabled"); if ((se && !su) || (ce && !cu)) { //posting any missed XHR from previous ride session if (ce) { PostToWeb(); } if (se) { SendToStrava(); } } else { localStorage.setItem("GPX", ""); localStorage.setItem("maxSpeed", ""); localStorage.setItem("latitude", ""); localStorage.setItem("longitude", ""); localStorage.setItem("timestamp", ""); localStorage.setItem("custom_uploaded", false); localStorage.setItem("strava_uploaded", false); } // clear any other var to do clearInterval(locationInterval); clearInterval(instantLocationInterval); navigator.geolocation.getCurrentPosition(locationSuccess, locationError, firstlocationOptions); } // Get JS readiness events Pebble.addEventListener('ready', function(e) { console.log('PebbleKit JS is ready'); // Update Watch on this Pebble.sendAppMessage({ 'JSReady': 1 }); init(); }); // Get AppMessage events Pebble.addEventListener('appmessage', function(e) { // Get the dictionary from the message var dict = e.payload; //console.log(dict[0].toString()); switch (dict[0]) { case 'get': get_coordinate(); break; case 'exit': clearInterval(instantLocationInterval); clearInterval(locationInterval); if (gpx_to_strava || gpx_to_web) { //End GPX file GPXfooterBuilder(); if (gpx_to_strava) { //send to strava through API SendToStrava(); } if (gpx_to_web) { // send CSV to web server through API PostToWeb(); } } // Send the message var dict = { 'status': "EXIT" }; Pebble.sendAppMessage(dict, function() { console.log('Message sent successfully: ' + JSON.stringify(dict)); }, function(e) { console.log('Message (' + JSON.stringify(dict) + ') failed: ' + JSON.stringify(e)); }); break; default: console.log('Sorry. I don\'t understand your request :' + dict[0]); } });