forked from FOSS/BangleApps
Merge remote-tracking branch 'upstream/master'
commit
6c221f19fe
30
apps.json
30
apps.json
|
@ -13,6 +13,21 @@
|
|||
],
|
||||
"sortorder" : -10
|
||||
},
|
||||
{ "id": "health",
|
||||
"name": "Health Tracking",
|
||||
"tags": "tool,system,b2",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Logs health data and provides an app to view it (BETA - requires firmware 2v11)",
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"health.app.js","url":"app.js"},
|
||||
{"name":"health.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"health.boot.js","url":"boot.js"},
|
||||
{"name":"health","url":"lib.js"}
|
||||
],
|
||||
"sortorder" : -10
|
||||
},
|
||||
{ "id": "moonphase",
|
||||
"name": "Moonphase",
|
||||
"icon": "app.png",
|
||||
|
@ -55,7 +70,7 @@
|
|||
"name": "Launcher (Bangle.js 2)",
|
||||
"shortName":"Launcher",
|
||||
"icon": "app.png",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "This is needed by Bangle.js 2.0 to display a menu allowing you to choose your own applications. It will not work on Bangle.js 1.0.",
|
||||
"tags": "tool,system,launcher,b2,bno1",
|
||||
"type":"launch",
|
||||
|
@ -67,7 +82,7 @@
|
|||
{ "id": "about",
|
||||
"name": "About",
|
||||
"icon": "app.png",
|
||||
"version":"0.08",
|
||||
"version":"0.09",
|
||||
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
|
||||
"tags": "tool,system,b2",
|
||||
"allow_emulator":true,
|
||||
|
@ -3572,5 +3587,16 @@
|
|||
"data": [
|
||||
{"name":"score.json"}
|
||||
]
|
||||
},
|
||||
{ "id": "menusmall",
|
||||
"name": "Small Menus",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Replace Bangle.js 2's menus with a version that contains smaller text",
|
||||
"tags": "b2,bno1,system",
|
||||
"type": "boot",
|
||||
"storage": [
|
||||
{"name":"menusmall.boot.js","url":"boot.js"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
0.06: Actual pixels as of 12 Jun 2020
|
||||
0.07: Pressing a button now exits immediately (fix #618)
|
||||
0.08: Make about (mostly) work on non-240px screens
|
||||
0.09: Actual Bangle.js 1 pixels as of 13 Oct 2021
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,38 @@
|
|||
# Health Tracking
|
||||
|
||||
Logs health data to a file every 10 minutes, and provides an app to view it
|
||||
|
||||
**BETA - requires firmware 2v11**
|
||||
|
||||
## Usage
|
||||
|
||||
Once installed, health data is logged automatically.
|
||||
|
||||
To view data, run the `Health` app from your watch.
|
||||
|
||||
## Features
|
||||
|
||||
Stores:
|
||||
|
||||
* Heart rate (TODO)
|
||||
* Step count
|
||||
* Movement
|
||||
|
||||
## Technical Info
|
||||
|
||||
Once installed, the `health.boot.js` hooks onto the `Bangle.health` event and
|
||||
writes data to a binary file (one per month).
|
||||
|
||||
A library (that can be used with `require("health").readXYZ` can then be used
|
||||
to grab historical health info.
|
||||
|
||||
## 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
|
||||
* More features in app:
|
||||
* Step counting goal (ensure pedometers use this)
|
||||
* Calendar view showing steps per day
|
||||
* Yearly view
|
||||
* Heart rate 'zone' graph
|
||||
* .. other
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4UA///8H5AYM7/5L/ACsBqtQAgMFqtABYcVqtVAgIDBqgLDAwITBDYNVrQiEAANQEQNVtWAFIYfCE4Xq0AuEAAdX1W0BZFe1XUHQgADvWrJogAE9WtBYl66ouD2oLEtQGBFwQQBBYgeBFwYjFA4QuCBYgfCFwYLCL4IICFwacCPwetEwYLCR4QJBFwbFCU4QhBFwbMDNAYuCHQQwFFwowFFwowFFwwwEFwzNGFwjxFFwowEFw7aFBQwwDFwwwEFwwwEFw4wDBRAwBFxAwCFxAwCFxIA/AB4A="))
|
|
@ -0,0 +1,60 @@
|
|||
function menuMain() {
|
||||
E.showMenu({
|
||||
"":{title:"Health Tracking"},
|
||||
"< Back":()=>load(),
|
||||
"Step Counting":()=>menuStepCount(),
|
||||
"Movement":()=>menuMovement()
|
||||
});
|
||||
}
|
||||
|
||||
function menuStepCount() {
|
||||
E.showMenu({
|
||||
"":{title:"Step Counting"},
|
||||
"per hour":()=>stepsPerHour()
|
||||
});
|
||||
}
|
||||
|
||||
function menuMovement() {
|
||||
E.showMenu({
|
||||
"":{title:"Movement"},
|
||||
"per hour":()=>movementPerHour()
|
||||
});
|
||||
}
|
||||
|
||||
function stepsPerHour() {
|
||||
E.showMessage("Loading...");
|
||||
var data = new Uint16Array(24);
|
||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
g.reset();
|
||||
require("graph").drawBar(g, data, {
|
||||
y:24,
|
||||
miny: 0,
|
||||
axes : true,
|
||||
gridx : 6,
|
||||
gridy : 500
|
||||
});
|
||||
Bangle.setUI("updown", ()=>menuStepCount());
|
||||
}
|
||||
|
||||
function movementPerHour() {
|
||||
E.showMessage("Loading...");
|
||||
var data = new Uint16Array(24);
|
||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
g.reset();
|
||||
require("graph").drawLine(g, data, {
|
||||
y:24,
|
||||
miny: 0,
|
||||
axes : true,
|
||||
gridx : 6,
|
||||
ylabel : null
|
||||
});
|
||||
Bangle.setUI("updown", ()=>menuStepCount());
|
||||
}
|
||||
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
menuMain();
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,38 @@
|
|||
Bangle.on("health", health => {
|
||||
// ensure we write health info for *last* block
|
||||
var d = new Date(Date.now() - 590000);
|
||||
|
||||
const DB_RECORD_LEN = 4;
|
||||
const DB_RECORDS_PER_HR = 6;
|
||||
const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24;
|
||||
const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31;
|
||||
const DB_HEADER_LEN = 8;
|
||||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||
|
||||
function getRecordFN(d) {
|
||||
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw";
|
||||
}
|
||||
function getRecordIdx(d) {
|
||||
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +
|
||||
(DB_RECORDS_PER_HR*d.getHours()) +
|
||||
(0|(d.getMinutes()*DB_RECORDS_PER_HR/60));
|
||||
}
|
||||
|
||||
var rec = getRecordIdx(d);
|
||||
var fn = getRecordFN(d);
|
||||
var f = require("Storage").read(fn);
|
||||
if (f) {
|
||||
var dt = f.substr(DB_HEADER_LEN+(rec*DB_RECORD_LEN), DB_RECORD_LEN);
|
||||
if (dt!="\xFF\xFF\xFF\xFF") {
|
||||
print("HEALTH ERR: Already written!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
require("Storage").write(fn, "HEALTH1\0", 0, DB_FILE_LEN); // header
|
||||
}
|
||||
var recordData = String.fromCharCode(
|
||||
health.steps>>8,health.steps&255, // 16 bit steps
|
||||
health.bpm, // 8 bit bpm
|
||||
Math.min(health.movement / 8, 255)); // movement
|
||||
require("Storage").write(fn, recordData, DB_HEADER_LEN+(rec*DB_RECORD_LEN), DB_FILE_LEN);
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
const DB_RECORD_LEN = 4;
|
||||
const DB_RECORDS_PER_HR = 6;
|
||||
const DB_RECORDS_PER_DAY = DB_RECORDS_PER_HR*24;
|
||||
const DB_RECORDS_PER_MONTH = DB_RECORDS_PER_DAY*31;
|
||||
const DB_HEADER_LEN = 8;
|
||||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||
|
||||
function getRecordFN(d) {
|
||||
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw";
|
||||
}
|
||||
function getRecordIdx(d) {
|
||||
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +
|
||||
(DB_RECORDS_PER_HR*d.getHours()) +
|
||||
(0|(d.getMinutes()*DB_RECORDS_PER_HR/60));
|
||||
}
|
||||
|
||||
// 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);
|
||||
var idx = DB_HEADER_LEN;
|
||||
for (var day=0;day<31;day++) {
|
||||
for (var hr=0;hr<24;hr++) {
|
||||
for (var m=0;m<DB_RECORDS_PER_HR;m++) {
|
||||
var h = f.substr(idx, DB_RECORD_LEN);
|
||||
if (h!="\xFF\xFF\xFF\xFF") {
|
||||
cb({
|
||||
day:day+1, hr : hr, min:m*10,
|
||||
steps : (h.charCodeAt(0)<<8) | h.charCodeAt(1),
|
||||
bpm : h.charCodeAt(2),
|
||||
movement : h.charCodeAt(3)
|
||||
});
|
||||
}
|
||||
idx += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read all records from the given month
|
||||
exports.readDay = function(d, cb) {
|
||||
var rec = getRecordIdx(d);
|
||||
var fn = getRecordFN(d);
|
||||
var f = require("Storage").read(fn);
|
||||
var idx = DB_HEADER_LEN + (DB_RECORD_LEN*DB_RECORDS_PER_DAY*(d.getDate()-1));
|
||||
for (var hr=0;hr<24;hr++) {
|
||||
for (var m=0;m<DB_RECORDS_PER_HR;m++) {
|
||||
var h = f.substr(idx, DB_RECORD_LEN);
|
||||
if (h!="\xFF\xFF\xFF\xFF") {
|
||||
cb({
|
||||
hr : hr, min:m*10,
|
||||
steps : (h.charCodeAt(0)<<8) | h.charCodeAt(1),
|
||||
bpm : h.charCodeAt(2),
|
||||
movement : h.charCodeAt(3)
|
||||
});
|
||||
}
|
||||
idx += 4;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New App!
|
||||
0.02: Fix occasional missed image when scrolling up
|
||||
0.03: Text wrapping, better font
|
||||
|
|
|
@ -14,17 +14,23 @@ var w = g.getWidth();
|
|||
var h = g.getHeight();
|
||||
var n = Math.ceil((h-24)/APPH);
|
||||
var menuScrollMax = APPH*apps.length - (h-24);
|
||||
// FIXME: not needed after 2v11
|
||||
var font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
|
||||
|
||||
apps.forEach(app=>{
|
||||
if (app.icon)
|
||||
app.icon = s.read(app.icon); // should just be a link to a memory area
|
||||
});
|
||||
if (g.wrapString) { // FIXME: check not needed after 2v11
|
||||
g.setFont(font);
|
||||
apps.forEach(app=>app.name = g.wrapString(app.name, g.getWidth()-64).join("\n"));
|
||||
}
|
||||
|
||||
function drawApp(i) {
|
||||
var y = 24+i*APPH-menuScroll;
|
||||
var app = apps[i];
|
||||
if (!app || y<-APPH || y>=g.getHeight()) return;
|
||||
g.setFont("6x8",2).setFontAlign(-1,0).drawString(app.name,64,y+32);
|
||||
g.setFont(font).setFontAlign(-1,0).drawString(app.name,64,y+32);
|
||||
if (app.icon) try {g.drawImage(app.icon,8,y+8);} catch(e){}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
Binary file not shown.
After Width: | Height: | Size: 619 B |
|
@ -0,0 +1,121 @@
|
|||
"";//not entirely sure why we need this - related to how bootupdate adds these to .boot0
|
||||
E.showMenu = function(items) {
|
||||
g.clear(1).flip(); // clear screen if no menu supplied
|
||||
Bangle.drawWidgets();
|
||||
if (!items) {
|
||||
Bangle.setUI();
|
||||
return;
|
||||
}
|
||||
var w = g.getWidth();
|
||||
var h = g.getHeight();
|
||||
var menuItems = Object.keys(items);
|
||||
var options = items[""];
|
||||
if (options) menuItems.splice(menuItems.indexOf(""),1);
|
||||
if (!(options instanceof Object)) options = {};
|
||||
options.fontHeight=14;
|
||||
options.x=0;
|
||||
options.x2=w-1;
|
||||
options.y=24;
|
||||
options.y2=h-12;
|
||||
if (options.selected === undefined)
|
||||
options.selected = 0;
|
||||
var x = 0|options.x;
|
||||
var x2 = options.x2||(g.getWidth()-1);
|
||||
var y = 0|options.y;
|
||||
var y2 = options.y2||(g.getHeight()-1);
|
||||
if (options.title)
|
||||
y += 15;
|
||||
var loc = require("locale");
|
||||
var l = {
|
||||
lastIdx : 0,
|
||||
draw : function(rowmin,rowmax) {
|
||||
var rows = 0|Math.min((y2-y) / options.fontHeight,menuItems.length);
|
||||
var idx = E.clip(options.selected-(rows>>1),0,menuItems.length-rows);
|
||||
if (idx!=l.lastIdx) rowmin=undefined; // redraw all if we scrolled
|
||||
l.lastIdx = idx;
|
||||
var iy = y;
|
||||
g.reset().setFontAlign(0,-1,0);
|
||||
g.setFontCustom(atob("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"), 32, atob("AwIGCAgICAMFBQYIAwYDBwcFBgYHBgYGBgYDAwYHBgcHBgYGBgYGBgYEBgYGBgYGBgYGBgYGBggGBgYEBwQGBwQGBgYGBgYHBgYGBgYGBgYGBgYGBgYGBgYGBgQCBAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAHCAYGBgAGBgYGAAYGBQYABgMGBgQABgYHBgAGBgYGBgYGBgYGBgYGBgYEBAYGBgYGBgYGAAYGBgYGBgYHBgYGBgYGBgYGBgYGBgYGBwcGBgYGBgYABgYGBgYGBg=="), 15);
|
||||
|
||||
if (rowmin===undefined && options.title)
|
||||
g.drawString(options.title,(x+x2)/2,y-14).drawLine(x,y-2,x2,y-2).
|
||||
setColor(g.theme.fg).setBgColor(g.theme.bg);
|
||||
iy += 12;
|
||||
g.setColor((idx>0)?g.theme.fg:g.theme.bg).fillPoly([72,iy,104,iy,88,iy-12]);
|
||||
if (rowmin!==undefined) {
|
||||
if (idx<rowmin) {
|
||||
iy += options.fontHeight*(rowmin-idx);
|
||||
idx=rowmin;
|
||||
}
|
||||
if (idx+rows>rowmax) {
|
||||
rows = 1+rowmax-rowmin;
|
||||
}
|
||||
}
|
||||
var less = idx>0;
|
||||
while (rows--) {
|
||||
var name = menuItems[idx];
|
||||
var item = items[name];
|
||||
var hl = (idx==options.selected && !l.selectEdit);
|
||||
g.setColor(hl ? g.theme.bgH : g.theme.bg);
|
||||
g.fillRect(x,iy,x2,iy+options.fontHeight-1);
|
||||
g.setColor(hl ? g.theme.fgH : g.theme.fg);
|
||||
g.setFontAlign(-1,-1);
|
||||
g.drawString(loc.translate(name),x+1,iy+1);
|
||||
if ("object" == typeof item) {
|
||||
var xo = x2;
|
||||
var v = item.value;
|
||||
if (item.format) v=item.format(v);
|
||||
v = loc.translate(""+v);
|
||||
if (l.selectEdit && idx==options.selected) {
|
||||
xo -= 24 + 1;
|
||||
g.setColor(g.theme.bgH).fillRect(xo-(g.stringWidth(v)+4),iy,x2,iy+options.fontHeight-1);
|
||||
g.setColor(g.theme.fgH).drawImage("\x0c\x05\x81\x00 \x07\x00\xF9\xF0\x0E\x00@",xo,iy+(options.fontHeight-10)/2,{scale:2});
|
||||
}
|
||||
g.setFontAlign(1,-1);
|
||||
g.drawString(v,xo-2,iy+1);
|
||||
}
|
||||
g.setColor(g.theme.fg);
|
||||
iy += options.fontHeight;
|
||||
idx++;
|
||||
}
|
||||
g.setFontAlign(-1,-1);
|
||||
g.setColor((idx<menuItems.length)?g.theme.fg:g.theme.bg).fillPoly([72,166,104,166,88,174]);
|
||||
g.flip();
|
||||
},
|
||||
select : function(dir) {
|
||||
var item = items[menuItems[options.selected]];
|
||||
if ("function" == typeof item) item(l);
|
||||
else if ("object" == typeof item) {
|
||||
// if a number, go into 'edit mode'
|
||||
if ("number" == typeof item.value)
|
||||
l.selectEdit = l.selectEdit?undefined:item;
|
||||
else { // else just toggle bools
|
||||
if ("boolean" == typeof item.value) item.value=!item.value;
|
||||
if (item.onchange) item.onchange(item.value);
|
||||
}
|
||||
l.draw();
|
||||
}
|
||||
},
|
||||
move : function(dir) {
|
||||
if (l.selectEdit) {
|
||||
var item = l.selectEdit;
|
||||
item.value -= (dir||1)*(item.step||1);
|
||||
if (item.min!==undefined && item.value<item.min) item.value = item.min;
|
||||
if (item.max!==undefined && item.value>item.max) item.value = item.max;
|
||||
if (item.onchange) item.onchange(item.value);
|
||||
l.draw(options.selected,options.selected);
|
||||
} else {
|
||||
var a=options.selected;
|
||||
options.selected = (dir+options.selected)%menuItems.length;
|
||||
if (options.selected<0) options.selected += menuItems.length;
|
||||
l.draw(Math.min(a,options.selected), Math.max(a,options.selected));
|
||||
}
|
||||
}
|
||||
};
|
||||
l.draw();
|
||||
Bangle.setUI("updown",dir => {
|
||||
if (dir) l.move(dir);
|
||||
else l.select();
|
||||
});
|
||||
return l;
|
||||
};
|
Loading…
Reference in New Issue