1
0
Fork 0

health 0.02: Modified data format to include daily summaries

master
Gordon Williams 2021-10-29 10:34:14 +01:00
parent f6ea1b116f
commit 2a97f49ded
6 changed files with 158 additions and 71 deletions

116
apps.json
View File

@ -49,7 +49,7 @@
{ {
"id": "health", "id": "health",
"name": "Health Tracking", "name": "Health Tracking",
"version": "0.01", "version": "0.02",
"description": "Logs health data and provides an app to view it (BETA - requires firmware 2v11)", "description": "Logs health data and provides an app to view it (BETA - requires firmware 2v11)",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system", "tags": "tool,system",
@ -61,36 +61,7 @@
{"name":"health.boot.js","url":"boot.js"}, {"name":"health.boot.js","url":"boot.js"},
{"name":"health","url":"lib.js"} {"name":"health","url":"lib.js"}
], ],
"sortorder": -8 "sortorder": -2
},
{
"id": "moonphase",
"name": "Moonphase",
"version": "0.02",
"description": "Shows current moon phase. Now with GPS function.",
"icon": "app.png",
"tags": "",
"supports": ["BANGLEJS"],
"allow_emulator": true,
"storage": [
{"name":"moonphase.app.js","url":"app.js"},
{"name":"moonphase.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "daysl",
"name": "Days left",
"version": "0.03",
"description": "Shows you the days left until a certain date. Date can be set with a settings app and is written to a file.",
"icon": "app.png",
"tags": "",
"supports": ["BANGLEJS"],
"allow_emulator": false,
"storage": [
{"name":"daysl.app.js","url":"app.js"},
{"name":"daysl.img","url":"app-icon.js","evaluate":true},
{"name":"daysl.wid.js","url":"widget.js"}
]
}, },
{ {
"id": "launch", "id": "launch",
@ -108,6 +79,22 @@
], ],
"sortorder": -10 "sortorder": -10
}, },
{
"id": "setting",
"name": "Settings",
"version": "0.31",
"description": "A menu for setting up Bangle.js",
"icon": "settings.png",
"tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"setting.app.js","url":"settings.js"},
{"name":"setting.img","url":"settings-icon.js","evaluate":true}
],
"data": [{"name":"setting.json","url":"settings.min.json","evaluate":true}],
"sortorder": -5
},
{ {
"id": "about", "id": "about",
"name": "About", "name": "About",
@ -123,6 +110,24 @@
{"name":"about.img","url":"app-icon.js","evaluate":true} {"name":"about.img","url":"app-icon.js","evaluate":true}
] ]
}, },
{
"id": "alarm",
"name": "Default Alarm & Timer",
"shortName": "Alarms",
"version": "0.13",
"description": "Set and respond to alarms and timers",
"icon": "app.png",
"tags": "tool,alarm,widget",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"alarm.app.js","url":"app.js"},
{"name":"alarm.boot.js","url":"boot.js"},
{"name":"alarm.js","url":"alarm.js"},
{"name":"alarm.img","url":"app-icon.js","evaluate":true},
{"name":"alarm.wid.js","url":"widget.js"}
],
"data": [{"name":"alarm.json"}]
},
{ {
"id": "locale", "id": "locale",
"name": "Languages", "name": "Languages",
@ -240,38 +245,33 @@
"sortorder": -9 "sortorder": -9
}, },
{ {
"id": "setting", "id": "moonphase",
"name": "Settings", "name": "Moonphase",
"version": "0.31", "version": "0.02",
"description": "A menu for setting up Bangle.js", "description": "Shows current moon phase. Now with GPS function.",
"icon": "settings.png", "icon": "app.png",
"tags": "tool,system", "tags": "",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS"],
"readme": "README.md", "allow_emulator": true,
"storage": [ "storage": [
{"name":"setting.app.js","url":"settings.js"}, {"name":"moonphase.app.js","url":"app.js"},
{"name":"setting.img","url":"settings-icon.js","evaluate":true} {"name":"moonphase.img","url":"app-icon.js","evaluate":true}
], ]
"data": [{"name":"setting.json","url":"settings.min.json","evaluate":true}],
"sortorder": -2
}, },
{ {
"id": "alarm", "id": "daysl",
"name": "Default Alarm & Timer", "name": "Days left",
"shortName": "Alarms", "version": "0.03",
"version": "0.13", "description": "Shows you the days left until a certain date. Date can be set with a settings app and is written to a file.",
"description": "Set and respond to alarms and timers",
"icon": "app.png", "icon": "app.png",
"tags": "tool,alarm,widget", "tags": "",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS"],
"allow_emulator": false,
"storage": [ "storage": [
{"name":"alarm.app.js","url":"app.js"}, {"name":"daysl.app.js","url":"app.js"},
{"name":"alarm.boot.js","url":"boot.js"}, {"name":"daysl.img","url":"app-icon.js","evaluate":true},
{"name":"alarm.js","url":"alarm.js"}, {"name":"daysl.wid.js","url":"widget.js"}
{"name":"alarm.img","url":"app-icon.js","evaluate":true}, ]
{"name":"alarm.wid.js","url":"widget.js"}
],
"data": [{"name":"alarm.json"}]
}, },
{ {
"id": "wclock", "id": "wclock",

View File

@ -1 +1,2 @@
0.01: New App! 0.01: New App!
0.02: Modified data format to include daily summaries

View File

@ -28,7 +28,6 @@ to grab historical health info.
## TODO ## TODO
* **Extend file format to include combined data for each day (to make graphs faster)**
* `interface` page for desktop to allow data to be viewed and exported in common formats * `interface` page for desktop to allow data to be viewed and exported in common formats
* More features in app: * More features in app:
* Step counting goal (ensure pedometers use this) * Step counting goal (ensure pedometers use this)

View File

@ -11,7 +11,8 @@ function menuStepCount() {
E.showMenu({ E.showMenu({
"":{title:"Step Counting"}, "":{title:"Step Counting"},
"< Back":()=>menuMain(), "< Back":()=>menuMain(),
"per hour":()=>stepsPerHour() "per hour":()=>stepsPerHour(),
"per day":()=>stepsPerDay()
}); });
} }
@ -19,7 +20,8 @@ function menuMovement() {
E.showMenu({ E.showMenu({
"":{title:"Movement"}, "":{title:"Movement"},
"< Back":()=>menuMain(), "< Back":()=>menuMain(),
"per hour":()=>movementPerHour() "per hour":()=>movementPerHour(),
"per day":()=>movementPerDay(),
}); });
} }
@ -40,6 +42,23 @@ function stepsPerHour() {
Bangle.setUI("updown", ()=>menuStepCount()); Bangle.setUI("updown", ()=>menuStepCount());
} }
function stepsPerDay() {
E.showMessage("Loading...");
var data = new Uint16Array(24);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
g.clear(1);
Bangle.drawWidgets();
g.reset();
require("graph").drawBar(g, data, {
y:24,
miny: 0,
axes : true,
gridx : 5,
gridy : 2000
});
Bangle.setUI("updown", ()=>menuStepCount());
}
function movementPerHour() { function movementPerHour() {
E.showMessage("Loading..."); E.showMessage("Loading...");
var data = new Uint16Array(24); var data = new Uint16Array(24);
@ -57,6 +76,23 @@ function movementPerHour() {
Bangle.setUI("updown", ()=>menuStepCount()); Bangle.setUI("updown", ()=>menuStepCount());
} }
function movementPerDay() {
E.showMessage("Loading...");
var data = new Uint16Array(24);
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
g.clear(1);
Bangle.drawWidgets();
g.reset();
require("graph").drawBar(g, data, {
y:24,
miny: 0,
axes : true,
gridx : 5,
ylabel : null
});
Bangle.setUI("updown", ()=>menuStepCount());
}
Bangle.loadWidgets(); Bangle.loadWidgets();
Bangle.drawWidgets(); Bangle.drawWidgets();
menuMain(); menuMain();

View File

@ -4,7 +4,7 @@ Bangle.on("health", health => {
const DB_RECORD_LEN = 4; const DB_RECORD_LEN = 4;
const DB_RECORDS_PER_HR = 6; const DB_RECORDS_PER_HR = 6;
const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24; const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24 + 1/*summary*/;
const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31; const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31;
const DB_HEADER_LEN = 8; const DB_HEADER_LEN = 8;
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
@ -17,6 +17,12 @@ Bangle.on("health", health => {
(DB_RECORDS_PER_HR*d.getHours()) + (DB_RECORDS_PER_HR*d.getHours()) +
(0|(d.getMinutes()*DB_RECORDS_PER_HR/60)); (0|(d.getMinutes()*DB_RECORDS_PER_HR/60));
} }
function getRecordData(health) {
return String.fromCharCode(
health.steps>>8,health.steps&255, // 16 bit steps
health.bpm, // 8 bit bpm
Math.min(health.movement / 8, 255)); // movement
}
var rec = getRecordIdx(d); var rec = getRecordIdx(d);
var fn = getRecordFN(d); var fn = getRecordFN(d);
@ -30,9 +36,30 @@ Bangle.on("health", health => {
} else { } else {
require("Storage").write(fn, "HEALTH1\0", 0, DB_FILE_LEN); // header require("Storage").write(fn, "HEALTH1\0", 0, DB_FILE_LEN); // header
} }
var recordData = String.fromCharCode( var recordPos = DB_HEADER_LEN+(rec*DB_RECORD_LEN);
health.steps>>8,health.steps&255, // 16 bit steps require("Storage").write(fn, getRecordData(health), recordPos, DB_FILE_LEN);
health.bpm, // 8 bit bpm if (rec%DB_RECORDS_PER_DAY != DB_RECORDS_PER_DAY-1) return;
Math.min(health.movement / 8, 255)); // movement // we're at the end of the day. Read in all of the data for the day and sum it up
require("Storage").write(fn, recordData, DB_HEADER_LEN+(rec*DB_RECORD_LEN), DB_FILE_LEN); var sumPos = recordPos + DB_RECORD_LEN; // record after the current one is the sum
if (f.substr(sumPos, DB_RECORD_LEN)!="\xFF\xFF\xFF\xFF") {
print("HEALTH ERR: Daily summary already written!");
return;
}
health = { steps:0, bpm:0, movement:0, records:0};
var records = DB_RECORDS_PER_HR*24;
for (var i=0;i<records;i++) {
var dt = f.substr(recordPos, DB_RECORD_LEN);
if (dt!="\xFF\xFF\xFF\xFF") {
health.records++;
health.steps += (dt.charCodeAt(1)<<8)+dt.charCodeAt(1);
health.bpm += dt.charCodeAt(2);
health.movement += dt.charCodeAt(2);
}
recordPos -= DB_RECORD_LEN;
}
if (health.records) {
health.bpm /= health.records;
health.movement /= health.records;
}
require("Storage").write(fn, getRecordData(health), sumPos, DB_FILE_LEN);
}); });

View File

@ -1,6 +1,6 @@
const DB_RECORD_LEN = 4; const DB_RECORD_LEN = 4;
const DB_RECORDS_PER_HR = 6; const DB_RECORDS_PER_HR = 6;
const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24; const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24 + 1/*summary*/;
const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31; const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31;
const DB_HEADER_LEN = 8; const DB_HEADER_LEN = 8;
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN; const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
@ -19,9 +19,10 @@ exports.readAllRecords = function(d, cb) {
var rec = getRecordIdx(d); var rec = getRecordIdx(d);
var fn = getRecordFN(d); var fn = getRecordFN(d);
var f = require("Storage").read(fn); var f = require("Storage").read(fn);
if (f===undefined) return;
var idx = DB_HEADER_LEN; var idx = DB_HEADER_LEN;
for (var day=0;day<31;day++) { for (var day=0;day<31;day++) {
for (var hr=0;hr<24;hr++) { for (var hr=0;hr<24;hr++) { // actually 25, see below
for (var m=0;m<DB_RECORDS_PER_HR;m++) { for (var m=0;m<DB_RECORDS_PER_HR;m++) {
var h = f.substr(idx, DB_RECORD_LEN); var h = f.substr(idx, DB_RECORD_LEN);
if (h!="\xFF\xFF\xFF\xFF") { if (h!="\xFF\xFF\xFF\xFF") {
@ -32,9 +33,31 @@ exports.readAllRecords = function(d, cb) {
movement : h.charCodeAt(3) movement : h.charCodeAt(3)
}); });
} }
idx += 4; idx += DB_RECORD_LEN;
} }
} }
idx += DB_RECORD_LEN; // +1 because we have an extra record with totals for the end of the day
}
}
// Read daily summaries from the given month
exports.readDailySummaries = function(d, cb) {
var rec = getRecordIdx(d);
var fn = getRecordFN(d);
var f = require("Storage").read(fn);
if (f===undefined) return;
var idx = DB_HEADER_LEN + (DB_RECORDS_PER_DAY-1)*DB_RECORD_LEN; // summary is at the end of each day
for (var day=0;day<31;day++) {
var h = f.substr(idx, DB_RECORD_LEN);
if (h!="\xFF\xFF\xFF\xFF") {
cb({
day:day+1,
steps : (h.charCodeAt(0)<<8) | h.charCodeAt(1),
bpm : h.charCodeAt(2),
movement : h.charCodeAt(3)
});
}
idx += DB_RECORDS_PER_DAY*DB_RECORD_LEN;
} }
} }
@ -43,6 +66,7 @@ exports.readDay = function(d, cb) {
var rec = getRecordIdx(d); var rec = getRecordIdx(d);
var fn = getRecordFN(d); var fn = getRecordFN(d);
var f = require("Storage").read(fn); var f = require("Storage").read(fn);
if (f===undefined) return;
var idx = DB_HEADER_LEN + (DB_RECORD_LEN*DB_RECORDS_PER_DAY*(d.getDate()-1)); var idx = DB_HEADER_LEN + (DB_RECORD_LEN*DB_RECORDS_PER_DAY*(d.getDate()-1));
for (var hr=0;hr<24;hr++) { for (var hr=0;hr<24;hr++) {
for (var m=0;m<DB_RECORDS_PER_HR;m++) { for (var m=0;m<DB_RECORDS_PER_HR;m++) {
@ -55,7 +79,7 @@ exports.readDay = function(d, cb) {
movement : h.charCodeAt(3) movement : h.charCodeAt(3)
}); });
} }
idx += 4; idx += DB_RECORD_LEN;
} }
} }
} }