forked from FOSS/BangleApps
add other apps
parent
fe2a5e6f28
commit
819c67d442
44
apps.json
44
apps.json
|
@ -1,4 +1,13 @@
|
|||
[
|
||||
{ "id": "boot",
|
||||
"name": "Bootloader",
|
||||
"icon": "bootloader.png",
|
||||
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
|
||||
"tags": "tool,system",
|
||||
"storage": [
|
||||
{"name":".bootcde","url":"bootloader.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "trex",
|
||||
"name": "T-Rex",
|
||||
"icon": "trex.png",
|
||||
|
@ -75,6 +84,19 @@
|
|||
{"name":"*slevel","url":"spiritlevel-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "settings",
|
||||
"name": "Settings",
|
||||
"icon": "settings.png",
|
||||
"description": "Show the current angle of the watch, so you can use it to make sure something is absolutely flat",
|
||||
"tags": "tool,system",
|
||||
"storage": [
|
||||
{"name":"+settings","url":"settings.json"},
|
||||
{"name":"-settings","url":"settings.js"},
|
||||
{"name":"=settings","url":"settings-init.js"},
|
||||
{"name":"@settings","url":"settings-default.json","evaluate":true},
|
||||
{"name":"*settings","url":"settings-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "sbat",
|
||||
"name": "Battery Level Widget",
|
||||
"icon": "widget-battery.png",
|
||||
|
@ -84,6 +106,28 @@
|
|||
{"name":"=sbat","url":"widget-battery.js"}
|
||||
]
|
||||
},
|
||||
{ "id": "hrm",
|
||||
"name": "Heart Rate Monitor",
|
||||
"icon": "heartrate.png",
|
||||
"description": "Measure your current heart rate",
|
||||
"tags": "health",
|
||||
"storage": [
|
||||
{"name":"+hrm","url":"heartrate.json"},
|
||||
{"name":"-hrm","url":"heartrate.js"},
|
||||
{"name":"*hrm","url":"heartrate-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "swatch",
|
||||
"name": "Stopwatch",
|
||||
"icon": "stopwatch.png",
|
||||
"description": "Simple stopwatch with Lap Time recording",
|
||||
"tags": "health",
|
||||
"storage": [
|
||||
{"name":"+swatch","url":"stopwatch.json"},
|
||||
{"name":"-swatch","url":"stopwatch.js"},
|
||||
{"name":"*swatch","url":"stopwatch-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "qrcode",
|
||||
"name": "Custom QR Code",
|
||||
"icon": "qrcode.png",
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
E.setTimeZone(1);
|
||||
E.setFlags({pretokenise:1});
|
||||
setWatch(function() {
|
||||
Bangle.setLCDMode("direct");
|
||||
g.clear();
|
||||
clearInterval();
|
||||
clearWatch();
|
||||
Bangle.removeAllListeners();
|
||||
|
||||
var s = require("Storage");
|
||||
var apps = s.list().filter(a=>a[0]=='+').map(app=>s.readJSON(app));
|
||||
var selected = 0;
|
||||
var menuScroll = 0;
|
||||
var menuShowing = false;
|
||||
|
||||
function drawMenu() {
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(-1,0);
|
||||
var n = 3;
|
||||
if (selected>=n+menuScroll) menuScroll = 1+selected-n;
|
||||
if (selected<menuScroll) menuScroll = selected;
|
||||
if (menuScroll) g.fillPoly([120,0,100,20,140,20]);
|
||||
else g.clearRect(100,0,140,20);
|
||||
if (apps.length>n+menuScroll) g.fillPoly([120,239,100,219,140,219]);
|
||||
else g.clearRect(100,219,140,239);
|
||||
for (var i=0;i<n;i++) {
|
||||
var app = apps[i+menuScroll];
|
||||
if (!app) break;
|
||||
var y = 24+i*64;
|
||||
if (i+menuScroll==selected) {
|
||||
g.setColor(0.3,0.3,0.3);
|
||||
g.fillRect(0,y,239,y+63);
|
||||
g.setColor(1,1,1);
|
||||
g.drawRect(0,y,239,y+63);
|
||||
} else
|
||||
g.clearRect(0,y,239,y+63);
|
||||
if (app.icon) g.drawImage(s.read(app.icon),8,y+8);
|
||||
g.drawString(app.name,64,y+32);
|
||||
}
|
||||
}
|
||||
drawMenu();
|
||||
setWatch(function() {
|
||||
if (selected>0) {
|
||||
selected--;
|
||||
drawMenu();
|
||||
}
|
||||
}, BTN1, {repeat:true});
|
||||
setWatch(function() {
|
||||
if (selected+1<apps.length) {
|
||||
selected++;
|
||||
drawMenu();
|
||||
}
|
||||
}, BTN3, {repeat:true});
|
||||
setWatch(function() { // run
|
||||
clearWatch();
|
||||
g.clear();
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(0,0);
|
||||
g.drawString("Loading...",120,120);
|
||||
if (apps[selected].name=="Clock") load();
|
||||
else { // load like this so we ensure we've cleared out our RAM
|
||||
var cmd = 'eval(require("Storage").read("'+apps[selected].src+'"));';
|
||||
setTimeout(cmd,20);
|
||||
}
|
||||
}, BTN2, {repeat:true});
|
||||
}, BTN2, {repeat:false}); // menu on middle button
|
||||
var WIDGETPOS={tl:32,tr:g.getWidth()-32,bl:32,br:g.getWidth()-32};
|
||||
var WIDGETS={};
|
||||
function drawWidgets() {
|
||||
Object.keys(WIDGETS).forEach(k=>WIDGETS[k].draw());
|
||||
}
|
||||
eval(require("Storage").read("-clock"));
|
||||
require("Storage").list().filter(a=>a[0]=='=').forEach(widget=>eval(require("Storage").read(widget)));
|
||||
setTimeout(drawWidgets,100);
|
Binary file not shown.
After Width: | Height: | Size: 741 B |
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwghC/AH4AThnMAAXABJoMHBwgJJAAYMFAAIJLFxImCBJIuLABYuI4gXNNZFCC6AIFkZIQA4szC6vEmdMC60sC6nDmc8C6RDBC4irLC4gTBocymgGBoYXO4UyUwNEAYKrMC4ZEBUwNMVAR7LC4dDCoYBBSYJ7DoZQCC4kCmczkc0JIVM4UzmgaBAAQWD4AXBggJBJAIkBocs4c0BAQXJJARBD4c8oc8HAKZCI4gWCVAYXEJIJoCOovNC4cMUIQPB4RFBTAYAFIwapEC4JyCZAalHGAvCJYZYCVAYuIMIhjE5heGCwxhDMYTtIFw4wFoYsGFxIwF4YuRGAh7DFxxhGFyIYKCxqrGIpwwKFx4YGCyJJFCyQYDCygA/AH4AFA="))
|
|
@ -0,0 +1,69 @@
|
|||
Bangle.setLCDPower(1);
|
||||
Bangle.setLCDTimeout(0);
|
||||
Bangle.ioWr(0x80,0)
|
||||
x=0;
|
||||
var min=0,max=0;
|
||||
var wasHigh = 0, wasLow = 0;
|
||||
var lastHigh = getTime();
|
||||
var hrmList = [];
|
||||
var hrm;
|
||||
|
||||
function readHRM() {
|
||||
var a = analogRead(D29);
|
||||
var h = getTime();
|
||||
min=Math.min(min*0.97+a*0.03,a);
|
||||
max=Math.max(max*0.97+a*0.03,a);
|
||||
y = E.clip(170 - (a*960*4),100,230);
|
||||
if (x==0) {
|
||||
g.clearRect(0,100,239,239);
|
||||
g.moveTo(-100,0);
|
||||
}
|
||||
/*g.setColor(0,1,0);
|
||||
var z = 170 - (min*960*4); g.fillRect(x,z,x,z);
|
||||
var z = 170 - (max*960*4); g.fillRect(x,z,x,z);*/
|
||||
g.setColor(1,1,1);
|
||||
g.lineTo(x,y);
|
||||
if ((max-min)>0.005) {
|
||||
if (4*a > (min+3*max)) { // high
|
||||
g.setColor(1,0,0);
|
||||
g.fillRect(x,230,x,239);
|
||||
g.setColor(1,1,1);
|
||||
if (!wasHigh && wasLow) {
|
||||
var currentHrm = 60/(h-lastHigh);
|
||||
lastHigh = h;
|
||||
if (currentHrm<250) {
|
||||
while (hrmList.length>12) hrmList.shift();
|
||||
hrmList.push(currentHrm);
|
||||
// median filter
|
||||
var t = hrmList.slice(); // copy
|
||||
t.sort();
|
||||
// average the middle 3
|
||||
var mid = t.length>>1;
|
||||
hrm = (t[mid]+t[mid+1]+t[mid+2])/3;
|
||||
g.setFontVector(40);
|
||||
g.setFontAlign(0,0);
|
||||
g.clearRect(0,0,239,100);
|
||||
var str = Math.round(hrm);
|
||||
var px = 120;
|
||||
g.drawString(str,px,40);
|
||||
px += g.stringWidth(str)/2;
|
||||
g.setFont("6x8");
|
||||
g.drawString("BPM",px+20,60);
|
||||
}
|
||||
}
|
||||
wasLow = 0;
|
||||
wasHigh = 1;
|
||||
} else if (4*a < (max+3*min)) { // low
|
||||
wasLow = 1;
|
||||
} else { // middle
|
||||
g.setColor(0.5,0,0);
|
||||
g.fillRect(x,230,x,239);
|
||||
g.setColor(1,1,1);
|
||||
wasHigh = 0;
|
||||
}
|
||||
}
|
||||
x++;
|
||||
if (x>239)x=0;
|
||||
}
|
||||
|
||||
setInterval(readHRM,50);
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
name:"Heart Rate",
|
||||
icon:"*hrm",
|
||||
src:"-hrm"
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
timeout: 10, // Default LCD timeout in seconds
|
||||
vibrate: true, // Vibration enabled by default. App must support
|
||||
beep: true, // Beep enabled by default. App must support
|
||||
timezone: 0, // Set the timezone for the device
|
||||
HID : false, // BLE HID mode, off by default
|
||||
debug: false, // Debug mode disabled by default. App must support
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4A/ABEIxGAAgwWO/+IAoIECDB0I////GIxGPAoIXR//854DCC54TCAAYXQCYXIDYYXOOIP4xnMAAJgBPoSMKOIP8xgXDGQJ9CGREIOIXMxmICwITBxh9CC5BEBBoIWBOweMMYZKIWwRdBAgQeDBYYXKFAKnFA4YXIKgJeBFwj0DI5UICgIXDOoIXC5gCBYRIXCN4I+CDgQXCYBJIBBwIXGBQIXX9AXJI4QXHI5Z3K/h3LTYanHX4TvLxhICAAaXCd5gnDd4gLDI5X4xj0CAAPIGwbvJIAeIDAQWBIwXPO5EIRIPP/mM5AFB5HIA4IFBC5DZECAPMDQJdB5AUKJQ3IxnvAgIsLC4ZYCAAgXnCIJxCXgQXPYYJxCAgKMMDAoRCAggA/AH4A/AAoA="))
|
|
@ -0,0 +1,55 @@
|
|||
(function() {
|
||||
var s = require('Storage').readJSON('@settings');
|
||||
if (s.HID) {
|
||||
Bangle.HID = new Uint8Array([
|
||||
0x05, 0x01,
|
||||
0x09, 0x06,
|
||||
0xA1, 0x01,
|
||||
0x05, 0x07,
|
||||
0x19, 0xe0,
|
||||
0x29, 0xe7,
|
||||
0x15, 0x00,
|
||||
0x25, 0x01,
|
||||
0x75, 0x01,
|
||||
0x95, 0x08,
|
||||
0x81, 0x02,
|
||||
0x95, 0x01,
|
||||
0x75, 0x08,
|
||||
0x81, 0x01,
|
||||
0x95, 0x05,
|
||||
0x75, 0x01,
|
||||
0x05, 0x08,
|
||||
0x19, 0x01,
|
||||
0x29, 0x05,
|
||||
0x91, 0x02,
|
||||
0x95, 0x01,
|
||||
0x75, 0x03,
|
||||
0x91, 0x01,
|
||||
0x95, 0x06,
|
||||
0x75, 0x08,
|
||||
0x15, 0x00,
|
||||
0x25, 0x73,
|
||||
0x05, 0x07,
|
||||
0x19, 0x00,
|
||||
0x29, 0x73,
|
||||
0x81, 0x00,
|
||||
0x09, 0x05,
|
||||
0x15, 0x00,
|
||||
0x26, 0xFF, 0x00,
|
||||
0x75, 0x08,
|
||||
0x95, 0x02,
|
||||
0xB1, 0x02,
|
||||
0xC0
|
||||
]);
|
||||
NRF.setServices(undefined, {
|
||||
uart: true, hid: Bangle.HID,
|
||||
});
|
||||
}
|
||||
|
||||
if (!s.vibrate) Bangle.buzz=()=>Promise.resolve();
|
||||
if (!s.beep) Bangle.beep=()=>Promise.resolve();
|
||||
Bangle.setLCDTimeout(s.timeout);
|
||||
if (!s.timeout) Bangle.setLCDPower(1);
|
||||
E.setTimeZone(s.timezone);
|
||||
})();
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
Bangle.setLCDPower(1);
|
||||
Bangle.setLCDTimeout(0);
|
||||
|
||||
g.clear();
|
||||
const storage = require('Storage');
|
||||
let settings;
|
||||
|
||||
function debug(msg, arg) {
|
||||
if (settings.debug)
|
||||
console.log(msg, arg);
|
||||
}
|
||||
|
||||
function updateSettings() {
|
||||
debug('updating settings', settings);
|
||||
storage.erase('@settings');
|
||||
storage.write('@settings', settings);
|
||||
}
|
||||
|
||||
function resetSettings() {
|
||||
settings = {
|
||||
timeout: 10,
|
||||
vibrate: true,
|
||||
beep: true,
|
||||
timezone: 0,
|
||||
HID : false,
|
||||
debug: false,
|
||||
};
|
||||
setLCDTimeout(settings.timeout);
|
||||
updateSettings();
|
||||
}
|
||||
|
||||
try {
|
||||
settings = storage.readJSON('@settings');
|
||||
} catch (e) {}
|
||||
if (!settings) resetSettings();
|
||||
|
||||
const boolFormat = (v) => v ? "On" : "Off";
|
||||
|
||||
function showMainMenu() {
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'LCD Timeout': {
|
||||
value: settings.timeout,
|
||||
min: 0,
|
||||
max: 60,
|
||||
step: 5,
|
||||
onchange: v => {
|
||||
settings.timeout = 0 | v;
|
||||
updateSettings();
|
||||
Bangle.setLCDTimeout(settings.timeout);
|
||||
}
|
||||
},
|
||||
'Beep': {
|
||||
value: settings.beep,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.beep = !settings.beep;
|
||||
updateSettings();
|
||||
if (settings.beep) {
|
||||
Bangle.beep(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
'Vibration': {
|
||||
value: settings.vibrate,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.vibrate = !settings.vibrate;
|
||||
updateSettings();
|
||||
if (settings.vibrate) {
|
||||
VIBRATE.write(1);
|
||||
setTimeout(()=>VIBRATE.write(0), 10);
|
||||
}
|
||||
}
|
||||
},
|
||||
'Time Zone': {
|
||||
value: settings.timezone,
|
||||
min: -11,
|
||||
max: 12,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
settings.timezone = 0 | v;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'HID': {
|
||||
value: settings.HID,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.HID = !settings.HID;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Debug': {
|
||||
value: settings.debug,
|
||||
format: boolFormat,
|
||||
onchange: () => {
|
||||
settings.debug = !settings.debug;
|
||||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Reset': showResetMenu,
|
||||
'Turn Off': Bangle.off,
|
||||
'< Back': load
|
||||
};
|
||||
return Bangle.menu(mainmenu);
|
||||
}
|
||||
|
||||
function showResetMenu() {
|
||||
const resetmenu = {
|
||||
'': { 'title': 'Reset' },
|
||||
'< Back': showMainMenu,
|
||||
'Reset Settings': () => {
|
||||
E.showPrompt('Reset Settings?').then((v) => {
|
||||
if (v) {
|
||||
E.showMessage('Resetting');
|
||||
resetSettings();
|
||||
}
|
||||
setTimeout(showMainMenu, 50);
|
||||
});
|
||||
},
|
||||
// this is include for debugging. remove for production
|
||||
/*'Erase': () => {
|
||||
storage.erase('=settings');
|
||||
storage.erase('-settings');
|
||||
storage.erase('@settings');
|
||||
storage.erase('*settings');
|
||||
storage.erase('+settings');
|
||||
E.reboot();
|
||||
}*/
|
||||
};
|
||||
return Bangle.menu(resetmenu);
|
||||
}
|
||||
|
||||
showMainMenu();
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
name: 'Settings',
|
||||
icon: '*settings',
|
||||
src: '-settings'
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -1,6 +1,13 @@
|
|||
g.clear();
|
||||
var old = {x:0,y:0};
|
||||
Bangle.on('accel',function(v) {
|
||||
var max = Math.max(Math.abs(v.x),Math.abs(v.y),Math.abs(v.z));
|
||||
if (Math.abs(v.y)==max) {
|
||||
v = {x:v.x,y:v.z,z:v.y};
|
||||
} else if (Math.abs(v.x)==max) {
|
||||
v = {x:v.z,y:v.y,z:v.x};
|
||||
}
|
||||
|
||||
var d = Math.sqrt(v.x*v.x+v.y*v.y);
|
||||
var ang = Math.atan2(d,Math.abs(v.z))*180/Math.PI;
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwghC/AFECkQACkAX/C/4AKgMRiMQVCYXFGYIWOxAAEgJLPCwuIwRkCCyQABC5sICIUzn/zmYYEFxs//4AC+ZJCL5QuCCwXzDAuAFxeDCYYyDnALBC5YSD+UvGApGKFwYXFGARIIC4OPCIfxj4FD/AXJRgwXFJBQJBCAYXBiYGEC5ReE/8xC4pgBC50hiQXPOwn/iMRMwgXP+QXBVAiQBC8pHCO6rvFC6IAGC5TXFAAzvLUAgAF+YXJhB4GAAiOBwAXJMBReBC5BILIxQXDGBAuBC5RIBGA4uCIxIwDDAoWCFxQwExEzn/zmYGCFxYwEAAwWMDBIWODA4WQAH4AXA=="))
|
|
@ -0,0 +1,84 @@
|
|||
var tStart = Date.now();
|
||||
var tCurrent = Date.now();
|
||||
var started = false;
|
||||
var timeY = 60;
|
||||
var hsXPos = 0;
|
||||
var lapTimes = [];
|
||||
var displayInterval;
|
||||
|
||||
function timeToText(t) {
|
||||
var secs = Math.floor(t/1000)%60;
|
||||
var mins = Math.floor(t/60000);
|
||||
var hs = Math.floor(t/10)%100;
|
||||
return mins+":"+("0"+secs).substr(-2)+"."+("0"+hs).substr(-2);
|
||||
}
|
||||
function updateLabels() {
|
||||
g.clear();
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(0,0,3);
|
||||
g.drawString(started?"STOP":"GO",230,120);
|
||||
if (!started) g.drawString("RESET",230,50);
|
||||
g.drawString("LAP",230,190);
|
||||
g.setFont("6x8",1);
|
||||
g.setFontAlign(-1,-1);
|
||||
for (var i in lapTimes) {
|
||||
g.drawString(i+": "+timeToText(lapTimes[i]),10,timeY + 30 + i*8);
|
||||
}
|
||||
drawsecs();
|
||||
}
|
||||
function drawsecs() {
|
||||
var t = tCurrent-tStart;
|
||||
g.setFont("Vector",48);
|
||||
g.setFontAlign(0,0);
|
||||
var secs = Math.floor(t/1000)%60;
|
||||
var mins = Math.floor(t/60000);
|
||||
var txt = mins+":"+("0"+secs).substr(-2);
|
||||
var x = 100;
|
||||
g.clearRect(0,timeY-26,200,timeY+26);
|
||||
g.drawString(txt,x,timeY);
|
||||
hsXPos = 5+x+g.stringWidth(txt)/2;
|
||||
drawms();
|
||||
}
|
||||
function drawms() {
|
||||
var t = tCurrent-tStart;
|
||||
var hs = Math.floor(t/10)%100;
|
||||
g.setFontAlign(-1,0);
|
||||
g.setFont("6x8",2);
|
||||
g.clearRect(hsXPos,timeY,220,timeY+20);
|
||||
g.drawString("."+("0"+hs).substr(-2),hsXPos,timeY+10);
|
||||
}
|
||||
|
||||
setWatch(function() { // Start/stop
|
||||
started = !started;
|
||||
if (started)
|
||||
tStart = Date.now()+tStart-tCurrent;
|
||||
tCurrent = Date.now();
|
||||
if (displayInterval) {
|
||||
clearInterval(displayInterval);
|
||||
displayInterval = undefined;
|
||||
}
|
||||
updateLabels();
|
||||
if (started)
|
||||
displayInterval = setInterval(function() {
|
||||
var last = tCurrent;
|
||||
if (started) tCurrent = Date.now();
|
||||
if (Math.floor(last/1000)!=Math.floor(tCurrent/1000))
|
||||
drawsecs();
|
||||
else
|
||||
drawms();
|
||||
}, 20);
|
||||
}, BTN2, {repeat:true});
|
||||
setWatch(function() { // Reset
|
||||
if (!started) {
|
||||
tStart = tCurrent = Date.now();
|
||||
}
|
||||
updateLabels();
|
||||
}, BTN1, {repeat:true});
|
||||
setWatch(function() { // Lap
|
||||
if (started) tCurrent = Date.now();
|
||||
lapTimes.unshift(tCurrent-tStart);
|
||||
tStart = tCurrent;
|
||||
updateLabels();
|
||||
}, BTN3, {repeat:true});
|
||||
|
||||
updateLabels();
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
name:"Stopwatch",
|
||||
icon:"*swatch",
|
||||
src:"-swatch"
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Loading…
Reference in New Issue