diff --git a/apps.json b/apps.json
index 08d2d4c7b..53ff73f8e 100644
--- a/apps.json
+++ b/apps.json
@@ -55,6 +55,7 @@
"tags": "tool,system,health",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
+ "interface": "interface.html",
"storage": [
{"name":"health.app.js","url":"app.js"},
{"name":"health.img","url":"app-icon.js","evaluate":true},
@@ -623,7 +624,7 @@
"id": "recorder",
"name": "Recorder (BETA)",
"shortName": "Recorder",
- "version": "0.01",
+ "version": "0.02",
"description": "Record GPS position, heart rate and more in the background, then download to your PC.",
"icon": "app.png",
"tags": "tool,outdoors,gps,widget",
diff --git a/apps/ffcniftya/app.js b/apps/ffcniftya/app.js
index 0b8865bec..31742f64a 100644
--- a/apps/ffcniftya/app.js
+++ b/apps/ffcniftya/app.js
@@ -27,7 +27,7 @@ function draw() {
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0));
const minutes = d02(now.getMinutes());
- const day = d02(now.getDay());
+ const day = d02(now.getDate());
const month = d02(now.getMonth() + 1);
const year = now.getFullYear();
diff --git a/apps/ffcniftyb/app.js b/apps/ffcniftyb/app.js
index 58bf0b24b..75d217ab4 100644
--- a/apps/ffcniftyb/app.js
+++ b/apps/ffcniftyb/app.js
@@ -31,7 +31,7 @@ function renderText(g) {
const hour = d02(now.getHours() - (is12Hour && now.getHours() > 12 ? 12 : 0));
const minutes = d02(now.getMinutes());
- const day = d02(now.getDay());
+ const day = d02(now.getDate());
const month = d02(now.getMonth() + 1);
const year = now.getFullYear();
diff --git a/apps/health/interface.html b/apps/health/interface.html
new file mode 100644
index 000000000..f04857926
--- /dev/null
+++ b/apps/health/interface.html
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/health/lib.js b/apps/health/lib.js
index 20d73a907..70305bff8 100644
--- a/apps/health/lib.js
+++ b/apps/health/lib.js
@@ -16,7 +16,6 @@ function getRecordIdx(d) {
// Read all records from the given month
exports.readAllRecords = function(d, cb) {
- var rec = getRecordIdx(d);
var fn = getRecordFN(d);
var f = require("Storage").read(fn);
if (f===undefined) return;
diff --git a/apps/recorder/ChangeLog b/apps/recorder/ChangeLog
index 5560f00bc..bf90d0384 100644
--- a/apps/recorder/ChangeLog
+++ b/apps/recorder/ChangeLog
@@ -1 +1,3 @@
0.01: New App!
+0.02: Use 'recorder.log..' rather than 'record.log..'
+ Fix interface.html
diff --git a/apps/recorder/app.js b/apps/recorder/app.js
index 9b9c06c78..ac3e391fc 100644
--- a/apps/recorder/app.js
+++ b/apps/recorder/app.js
@@ -13,7 +13,7 @@ function loadSettings() {
var changed = false;
if (!settings.file) {
changed = true;
- settings.file = "record.log0.csv";
+ settings.file = "recorder.log0.csv";
}
if (!Array.isArray(settings.record)) {
settings.record = ["gps"];
@@ -31,7 +31,7 @@ function updateSettings() {
}
function getTrackNumber(filename) {
- return parseInt(filename.match(/^record\.log(.*)\.csv$/)[1]||0);
+ return parseInt(filename.match(/^recorder\.log(.*)\.csv$/)[1]||0);
}
function showMainMenu() {
@@ -73,7 +73,7 @@ function showMainMenu() {
step: 1,
onchange: v => {
settings.recording = false; // stop recording if we change anything
- settings.file = "record.log"+v+".csv";
+ settings.file = "recorder.log"+v+".csv";
updateSettings();
}
},
@@ -105,7 +105,7 @@ function viewTracks() {
'': { 'title': 'Tracks' }
};
var found = false;
- require("Storage").list(/^record\.log.*\.csv$/,{sf:true}).forEach(filename=>{
+ require("Storage").list(/^recorder\.log.*\.csv$/,{sf:true}).forEach(filename=>{
found = true;
menu["Track "+getTrackNumber(filename)] = ()=>viewTrack(filename,false);
});
diff --git a/apps/recorder/interface.html b/apps/recorder/interface.html
index 2ae1c3e71..eca13d263 100644
--- a/apps/recorder/interface.html
+++ b/apps/recorder/interface.html
@@ -3,7 +3,6 @@
- THIS IS NOT CURRENTLY IMPLEMENTED
@@ -14,14 +13,35 @@ function saveKML(track,title) {
var kml = `
-
- ${title}
-
-
- ${track.map(pt=>[pt.lon, pt.lat, pt.alt].join(",")).join("\n ")}
-
-
-
+
+${track[0].Heartrate!==undefined ? `
+ Heart Rate
+ `:``}
+${track[0].Steps!==undefined ? `
+ Step Count`:``}
+
+
+
+ Tracks
+
+ ${title}
+
+${track.map(pt=>` ${pt.Time.toISOString()}\n`).join("")}
+${track.map(pt=>` ${pt.Longitude} ${pt.Latitude} ${pt.Altitude}\n`).join("")}
+
+
+
+${track[0].Heartrate!==undefined ? `
+${track.map(pt=>` ${0|pt.Heartrate}\n`).join("")}
+ `:``}
+${track[0].Steps!==undefined ? `
+${track.map(pt=>` ${0|pt.Steps}\n`).join("")}
+ `:``}
+
+
+
+
+ `;
var a = document.createElement("a"),
@@ -39,6 +59,7 @@ function saveKML(track,title) {
function saveGPX(track, title) {
var gpx = `
+
@@ -48,11 +69,19 @@ function saveGPX(track, title) {
`;
track.forEach(pt=>{
gpx += `
-
- ${pt.alt}
-
+
+ ${pt.Altitude}
+
+
+
+ ${pt.Heartrate ? `${pt.Heartrate}`:``}
+ ${""/*...*/}
+ ${""/* 65 */}
+
+ `;
});
+ // https://www8.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
gpx += `
@@ -70,104 +99,153 @@ function saveGPX(track, title) {
}, 0);
}
-function trackLineToObject(l, hasTrackNumber) {
+function saveCSV(track, title) {
+ var headers = Object.keys(track[0]);
+ var csv = headers.join(",")+"\n";
+ track.forEach(t=>{
+ csv += headers.map(k=>{
+ if (t[k] instanceof Date) return t[k].toISOString();
+ return t[k];
+ }).join(",")+"\n";
+ });
+ Util.saveCSV(title, csv);
+}
+
+function trackLineToObject(headers, l) {
var t = l.trim().split(",");
- var n = hasTrackNumber ? 1 : 0;
- var o = {
- date : new Date(parseInt(t[n+0])),
- lat : parseFloat(t[n+1]),
- lon : parseFloat(t[n+2]),
- alt : parseFloat(t[n+3])
- };
- if (hasTrackNumber)
- o.number = t[0];
+ var o = {};
+ headers.forEach((header,i) => o[header] = t[i]);
+ if (o.Time) o.Time = new Date(o.Time*1000);
return o;
}
-function downloadTrack(trackid, callback) {
+function downloadTrack(filename, callback) {
Util.showModal("Downloading Track...");
- Util.readStorageFile(`.gpsrc${trackid.toString(36)}`,data=>{
+ Util.readStorageFile(filename,data=>{
Util.hideModal();
- var track = data.trim().split("\n").map(l=>trackLineToObject(l,false));
+ var lines = data.trim().split("\n");
+ var headers = lines.shift().split(",");
+ var track = lines.map(l=>trackLineToObject(headers, l));
callback(track);
});
}
+
function getTrackList() {
- Util.showModal("Loading Tracks...");
+ Util.showModal("Loading Track List...");
domTracks.innerHTML = "";
- Puck.write(`\x10(function() {
- Bluetooth.println("");
- for (var n=0;n<36;n++) {
- var f = require("Storage").open(".gpsrc"+n.toString(36),"r");
- var l = f.readLine();
- Bluetooth.println((l!==undefined) ? (n + "," + l.trim()) : "");
- }
- })()\n`,tracklist=>{
- var trackLines = tracklist.trim().split("\n").filter(l=>l!="");
- var html = `
-
\n`;
- trackLines.forEach(l => {
- var track = trackLineToObject(l, true /*has track number*/);
- html += `
-
-
-
Track ${track.number}
-
${track.date.toString().substr(0,24)}
-
-
-
-
-
-
-
- `;
- });
- if (trackLines.length==0) {
- html += `
-
-
-
No tracks
-
No GPS tracks found
-
-
- `;
- }
- html += `
-
-
`;
- domTracks.innerHTML = html;
- Util.hideModal();
- var buttons = domTracks.querySelectorAll("button");
- for (var i=0;i {
- var button = event.currentTarget;
- var trackid = parseInt(button.getAttribute("trackid"));
- var task = button.getAttribute("task");
- if (task=="delete") {
- Util.showModal("Deleting Track...");
- Util.eraseStorageFile(`.gpsrc${trackid.toString(36)}`,()=>{
- Util.hideModal();
- getTrackList();
+ Puck.eval(`require("Storage").list(/^recorder\\.log.*\\.csv$/,{sf:1})`,files=>{
+ var trackList = [];
+ var promise = Promise.resolve();
+ /* For each file ask Bangle.js for info. Since we now start recording even
+ before we have a GPS trace, we get the Bangle to do a *quick* search for us
+ to see if it found any data first. */
+ files.forEach(filename => {
+ promise = promise.then(()=>new Promise(resolve => {
+ var trackNo = filename.match(/^recorder\.log(.*)\.csv$/)[1];
+ Util.showModal(`Loading Track ${trackNo}...`);
+ Puck.eval(`(function(fn) {
+ var f = require("Storage").open(fn,"r");
+ var headers = f.readLine();
+ var data = f.readLine();
+ var lIdx = headers.split(",").indexOf("Latitude");
+ if (lIdx >= 0) {
+ var tries = 100;
+ var l = data;
+ while (l && l.split(",")[lIdx]=="" && tries++)
+ l = f.readLine();
+ if (l) data = l;
+ }
+ return {headers:headers,l:data};
+})(${JSON.stringify(filename)})`, trackInfo=>{
+ console.log(filename," => ",trackInfo);
+ trackInfo.headers = trackInfo.headers.split(",");
+ trackList.push({
+ filename : filename,
+ number : trackNo,
+ info : trackInfo
});
+ resolve();
+ });
+ }));
+ });
+ // ================================================
+ // When 'promise' completes we now have all the info in trackList
+ promise.then(() => {
+ var html = `