forked from FOSS/BangleApps
health 0.02: Modified data format to include daily summaries
parent
f6ea1b116f
commit
2a97f49ded
116
apps.json
116
apps.json
|
@ -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",
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
0.01: New App!
|
0.01: New App!
|
||||||
|
0.02: Modified data format to include daily summaries
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue