commit
5850633258
|
|
@ -1,6 +1,8 @@
|
|||
Bangle.js App Loader (and Apps)
|
||||
================================
|
||||
|
||||
Try it live at [banglejs.com/apps](https://banglejs.com/apps)
|
||||
|
||||
### How does it work?
|
||||
|
||||
* A list of apps is in `apps.json`
|
||||
|
|
@ -61,6 +63,7 @@ the *default* of `To RAM`
|
|||
"name": "Readable name", // readable name
|
||||
"icon": "icon.png", // icon in apps/
|
||||
"description": "...", // long description
|
||||
"type":"...", // optional(if app) - 'app' or 'widget'
|
||||
"tags": "", // comma separated tag list for searching
|
||||
|
||||
"custom": "custom.html", // if supplied, apps/custom.html is loaded in an
|
||||
|
|
|
|||
60
apps.json
60
apps.json
|
|
@ -9,19 +9,31 @@
|
|||
],
|
||||
"sortorder" : -1
|
||||
},
|
||||
{ "id": "clock",
|
||||
{ "id": "mclock",
|
||||
"name": "Morphing Clock",
|
||||
"icon": "clock-morphing.png",
|
||||
"description": "7 segment clock that morphs between minutes and hours",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"storage": [
|
||||
{"name":"+clock","url":"clock.json"},
|
||||
{"name":"-clock","url":"clock-morphing.js"},
|
||||
{"name":"*clock","url":"clock-icon.js","evaluate":true}
|
||||
{"name":"+mclock","url":"clock-morphing.json"},
|
||||
{"name":"-mclock","url":"clock-morphing.js"},
|
||||
{"name":"*mclock","url":"clock-morphing-icon.js","evaluate":true}
|
||||
],
|
||||
"sortorder" : -1
|
||||
},
|
||||
|
||||
{ "id": "wclock",
|
||||
"name": "Word Clock",
|
||||
"icon": "clock-word.png",
|
||||
"description": "Display Time as Text",
|
||||
"tags": "clock",
|
||||
"type":"clock",
|
||||
"storage": [
|
||||
{"name":"+wclock","url":"clock-word.json"},
|
||||
{"name":"-wclock","url":"clock-word.js"},
|
||||
{"name":"*wclock","url":"clock-word-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "trex",
|
||||
"name": "T-Rex",
|
||||
"icon": "trex.png",
|
||||
|
|
@ -98,17 +110,17 @@
|
|||
{"name":"*slevel","url":"spiritlevel-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "settings",
|
||||
{ "id": "setting",
|
||||
"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}
|
||||
{"name":"+setting","url":"settings.json"},
|
||||
{"name":"-setting","url":"settings.js"},
|
||||
{"name":"=setting","url":"settings-init.js"},
|
||||
{"name":"@setting","url":"settings-default.json","evaluate":true},
|
||||
{"name":"*setting","url":"settings-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "files",
|
||||
|
|
@ -127,7 +139,9 @@
|
|||
"icon": "widget-battery.png",
|
||||
"description": "Show the current battery level and charging status in the top right of the clock",
|
||||
"tags": "widget,battery",
|
||||
"type":"widget",
|
||||
"storage": [
|
||||
{"name":"+sbat","url":"widget-battery.json"},
|
||||
{"name":"=sbat","url":"widget-battery.js"}
|
||||
]
|
||||
},
|
||||
|
|
@ -136,7 +150,9 @@
|
|||
"icon": "widget-bluetooth.png",
|
||||
"description": "Show the current Bluetooth connection status in the top right of the clock",
|
||||
"tags": "widget,bluetooth",
|
||||
"type":"widget",
|
||||
"storage": [
|
||||
{"name":"+sbt","url":"widget-bluetooth.json"},
|
||||
{"name":"=sbt","url":"widget-bluetooth.js"}
|
||||
]
|
||||
},
|
||||
|
|
@ -162,6 +178,28 @@
|
|||
{"name":"*swatch","url":"stopwatch-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "hidmsic",
|
||||
"name": "Bluetooth Music Controls",
|
||||
"icon": "hid-music.png",
|
||||
"description": "Enable HID in settings, pair with your phone, then use this app to control music from your watch!",
|
||||
"tags": "bluetooth",
|
||||
"storage": [
|
||||
{"name":"+hidmsic","url":"hid-music.json"},
|
||||
{"name":"-hidmsic","url":"hid-music.js"},
|
||||
{"name":"*hidmsic","url":"hid-music-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "hidkbd",
|
||||
"name": "Bluetooth Keyboard",
|
||||
"icon": "hid-keyboard.png",
|
||||
"description": "Enable HID in settings, pair with your phone/PC, then use this app to control other apps",
|
||||
"tags": "bluetooth",
|
||||
"storage": [
|
||||
{"name":"+hidkbd","url":"hid-keyboard.json"},
|
||||
{"name":"-hidkbd","url":"hid-keyboard.js"},
|
||||
{"name":"*hidkbd","url":"hid-keyboard-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "animals",
|
||||
"name": "Animals Game",
|
||||
"icon": "animals.png",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Animals Game",
|
||||
"name":"Animals Game", "type":"app",
|
||||
"icon":"*animals",
|
||||
"src":"-animals"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Asteroids!",
|
||||
"name":"Asteroids!","type":"app",
|
||||
"icon":"*astroid",
|
||||
"src":"-astroid"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ setWatch(function() {
|
|||
apps = s.list().filter(a=>a[0]=='+').map(app=>{
|
||||
try { return s.readJSON(app); }
|
||||
catch (e) { return {name:"DEAD: "+app.substr(1)} }
|
||||
});
|
||||
}).filter(app=>app.type=="app" || app.type=="clock" || !app.type);
|
||||
var selected = 0;
|
||||
var menuScroll = 0;
|
||||
var menuShowing = false;
|
||||
|
|
@ -75,6 +75,12 @@ var WIDGETS={};
|
|||
function drawWidgets() {
|
||||
Object.keys(WIDGETS).forEach(k=>WIDGETS[k].draw());
|
||||
}
|
||||
eval(require("Storage").read("-clock"));
|
||||
var clockApp = require("Storage").list().filter(a=>a[0]=='+').map(app=>{
|
||||
try { return require("Storage").readJSON(app); }
|
||||
catch (e) {}
|
||||
}).find(app=>app.type=="clock");
|
||||
if (clockApp) eval(require("Storage").read(clockApp.src));
|
||||
else E.showMessage("No Clock Found");
|
||||
delete clockApp;
|
||||
require("Storage").list().filter(a=>a[0]=='=').forEach(widget=>eval(require("Storage").read(widget)));
|
||||
setTimeout(drawWidgets,100);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name":"Morphing Clock","type":"clock",
|
||||
"icon":"*mclock",
|
||||
"src":"-mclock"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgIIFiOAgEgoN4n+AsEw4f8mEAoEwwIaBAok/AoMZwP4AocBwARCjYuBAoUPAodgh0D/kQBYMGgP8IIseF4f8FINgnIFBmOAoEfBAM4AQMPAQIIBoEOAoMHCIILCkIFBjYFBDoUZNAJrCmPAsA4CkCMHn+Y8U/4MAl2M80/O4MEhnmTQQFB8gFDhu0CIcF3gLDg8cAokYAoOAAoQvB/A9H4BBDwPwX4P4nEDnAFBEAMGFIPDhk4g0MAoN8n4FD/ARBAoODuAFBjExwYdBEYMZwYOBgPwh8DhhBHACo"))
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/* jshint esversion: 6 */
|
||||
(function() {
|
||||
|
||||
const allWords = [
|
||||
"ATWENTYD",
|
||||
"QUARTERY",
|
||||
"FIVEHALF",
|
||||
"DPASTORO",
|
||||
"FIVEIGHT",
|
||||
"SIXTHREE",
|
||||
"TWELEVEN",
|
||||
"FOURNINE"
|
||||
];
|
||||
const hours = {
|
||||
0: ["", 0, 0],
|
||||
1: ["ONE", 17, 47, 77],
|
||||
2: ["TWO", 06, 16, 17],
|
||||
3: ["THREE", 35, 45, 55, 65, 75],
|
||||
4: ["FOUR", 07, 17, 27, 37],
|
||||
5: ["FIVE", 04, 14, 24, 34],
|
||||
6: ["SIX", 05, 15, 25],
|
||||
7: ["SEVEN", 05, 46, 56, 66, 67],
|
||||
8: ["EIGHT", 34, 44, 54, 64, 74],
|
||||
9: ["NINE", 47, 57, 67, 77],
|
||||
10: ["TEN", 74, 75, 76],
|
||||
11: ["ELEVEN", 26, 36, 46, 56, 66, 76],
|
||||
12: ["TWELVE", 06, 16, 26, 36, 56, 66]
|
||||
};
|
||||
|
||||
const mins = {
|
||||
0: ["A", 0, 0],
|
||||
1: ["FIVE", 02, 12, 22, 32],
|
||||
2: ["TEN", 10, 30, 40],
|
||||
3: ["QUARTER", 01, 11, 21, 31, 41, 51, 61],
|
||||
4: ["TWENTY", 10, 20, 30, 40, 50, 60],
|
||||
5: ["HALF", 42, 52, 62, 72],
|
||||
6: ["PAST", 13, 23, 33, 43],
|
||||
7: ["TO", 43, 53]
|
||||
};
|
||||
|
||||
// offsets and incerments
|
||||
const xs = 30;
|
||||
const ys = 20;
|
||||
const dy = 22;
|
||||
const dx = 25;
|
||||
|
||||
// font size and color
|
||||
const wordFontSize = 20;
|
||||
const timeFontSize = 30;
|
||||
const passivColor = 0x3186/*grey*/;
|
||||
const activeColor = 0xF800/*red*/;
|
||||
|
||||
function drawWordClock() {
|
||||
|
||||
// get time
|
||||
var t = new Date();
|
||||
var h = t.getHours();
|
||||
var m = t.getMinutes();
|
||||
var time = ("0" + h).substr(-2) + ":" + ("0" + m).substr(-2);
|
||||
|
||||
var hidx;
|
||||
var midx;
|
||||
var midxA=[];
|
||||
|
||||
g.setFontVector(wordFontSize);
|
||||
g.setColor(passivColor);
|
||||
g.setFontAlign(0, -1, 0);
|
||||
|
||||
// draw allWords
|
||||
var c;
|
||||
var y = ys;
|
||||
var x = xs;
|
||||
allWords.forEach((line) => {
|
||||
x = xs;
|
||||
for (c in line) {
|
||||
g.drawString(line[c], x, y);
|
||||
x += dx;
|
||||
}
|
||||
y += dy;
|
||||
});
|
||||
|
||||
|
||||
// calc indexes
|
||||
midx = Math.round(m / 5);
|
||||
hidx = h % 12;
|
||||
if (hidx === 0) { hidx = 12; }
|
||||
if (midx > 6) {
|
||||
if (midx == 12) { midx = 0; }
|
||||
hidx++;
|
||||
}
|
||||
if (midx !== 0) {
|
||||
if (midx <= 6) {
|
||||
midxA = [midx, 6];
|
||||
} else {
|
||||
midxA = [12 - midx, 7];
|
||||
}
|
||||
}
|
||||
|
||||
// write hour in active color
|
||||
g.setColor(activeColor);
|
||||
g.setFontVector(wordFontSize);
|
||||
|
||||
hours[hidx][0].split('').forEach((c, pos) => {
|
||||
x = xs + (hours[hidx][pos + 1] / 10 | 0) * dx;
|
||||
y = ys + (hours[hidx][pos + 1] % 10) * dy;
|
||||
|
||||
g.drawString(c, x, y);
|
||||
});
|
||||
|
||||
// write min words in active color
|
||||
midxA.forEach(idx => {
|
||||
mins[idx][0].split('').forEach((c, pos) => {
|
||||
x = xs + (mins[idx][pos + 1] / 10 | 0) * dx;
|
||||
y = ys + (mins[idx][pos + 1] % 10) * dy;
|
||||
g.drawString(c, x, y);
|
||||
});
|
||||
});
|
||||
|
||||
// display digital time
|
||||
g.setColor(activeColor);
|
||||
g.setFontVector(timeFontSize);
|
||||
g.clearRect(0,200,240,240);
|
||||
g.drawString(time, 120, 200);
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', function(on) {
|
||||
if (on) {
|
||||
drawWordClock();
|
||||
drawWidgets();
|
||||
}
|
||||
});
|
||||
|
||||
g.clear();
|
||||
setInterval(drawWordClock, 1E4);
|
||||
drawWordClock();
|
||||
|
||||
})();
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name":"Word Clock","type":"clock",
|
||||
"icon":"*wclock",
|
||||
"src":"-wclock"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 843 B |
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name":"Clock",
|
||||
"icon":"*clock",
|
||||
"src":"-clock"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Compass",
|
||||
"name":"Compass","type":"app",
|
||||
"icon":"*compass",
|
||||
"src":"-compass"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"App Manager",
|
||||
"name":"App Manager","type":"app",
|
||||
"icon":"*files",
|
||||
"src":"-files"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"GPS Time",
|
||||
"name":"GPS Time","type":"app",
|
||||
"icon":"*gpstime",
|
||||
"src":"-gpstime"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Heart Rate",
|
||||
"name":"Heart Rate","type":"app",
|
||||
"icon":"*hrm",
|
||||
"src":"-hrm"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4A/AHeIDC+AC60IC/51ELoIXTCYMIDISLpC/67dxAAJHBYsLhAYKLhgXXHhWAdoQbBApAvhBgYcCAogXkI64XYhDtII/oMUERTyDABJ0FPI4A/AH4A/AGY="))
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
var storage = require('Storage');
|
||||
|
||||
const settings = storage.readJSON('@setting') || { HID: false };
|
||||
|
||||
var sendHid, next, prev, toggle, up, down, profile;
|
||||
|
||||
if (settings.HID) {
|
||||
profile = 'Keyboard';
|
||||
sendHid = function (code, cb) {
|
||||
try {
|
||||
NRF.sendHIDReport([2,0,0,code,0,0,0,0,0], () => {
|
||||
NRF.sendHIDReport([2,0,0,0,0,0,0,0,0], () => {
|
||||
if (cb) cb();
|
||||
});
|
||||
});
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
};
|
||||
next = function (cb) { sendHid(0x4f, cb); };
|
||||
prev = function (cb) { sendHid(0x50, cb); };
|
||||
toggle = function (cb) { sendHid(0x2c, cb); };
|
||||
up = function (cb) {sendHid(0x52, cb); };
|
||||
down = function (cb) { sendHid(0x51, cb); };
|
||||
} else {
|
||||
E.showMessage('HID disabled');
|
||||
setTimeout(load, 1000);
|
||||
}
|
||||
|
||||
function drawApp() {
|
||||
g.clear();
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(0,0);
|
||||
g.drawString(profile, 120, 120);
|
||||
const d = g.getWidth() - 18;
|
||||
|
||||
function c(a) {
|
||||
return {
|
||||
width: 8,
|
||||
height: a.length,
|
||||
bpp: 1,
|
||||
buffer: (new Uint8Array(a)).buffer
|
||||
};
|
||||
}
|
||||
|
||||
g.drawImage(c([16,56,124,254,16,16,16,16]),d,40);
|
||||
g.drawImage(c([16,16,16,16,254,124,56,16]),d,194);
|
||||
g.drawImage(c([0,8,12,14,255,14,12,8]),d,116);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
setWatch(function(e) {
|
||||
var len = e.time - e.lastTime;
|
||||
if (len > 0.3 && len < 0.9) {
|
||||
E.showMessage('prev');
|
||||
setTimeout(drawApp, 1000);
|
||||
prev(() => {});
|
||||
} else {
|
||||
E.showMessage('up');
|
||||
setTimeout(drawApp, 1000);
|
||||
up(() => {});
|
||||
}
|
||||
}, BTN1, { edge:"falling",repeat:true,debounce:50});
|
||||
|
||||
setWatch(function(e) {
|
||||
var len = e.time - e.lastTime;
|
||||
if (len > 0.3 && len < 0.9) {
|
||||
E.showMessage('next');
|
||||
setTimeout(drawApp, 1000);
|
||||
next(() => {});
|
||||
} else {
|
||||
E.showMessage('down');
|
||||
setTimeout(drawApp, 1000);
|
||||
down(() => {});
|
||||
}
|
||||
}, BTN3, { edge:"falling",repeat:true,debounce:50});
|
||||
|
||||
setWatch(function(e) {
|
||||
E.showMessage('toggle')
|
||||
setTimeout(drawApp, 1000);
|
||||
toggle();
|
||||
}, BTN2, { edge:"falling",repeat:true,debounce:50});
|
||||
|
||||
drawApp();
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Keyboard Control","type":"app",
|
||||
"icon": "*hidkbd",
|
||||
"src": "-hidkbd"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 506 B |
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4A5xGICquZzAVUAAIXQCogXQCoxHPCox0BxIXNxIVFBAQXPUAwXPBw4XowAvuC/4X/C9sIC6kIxGZzIXSFgIWBC6QWEC6RECAAOJwAXQFwoXLxAqBC4MICweZCxhWEC4mICxxuDA4I3BCxQ/FQxpyEK6AucC4idMI5OICyQwBQpgA/AH4Au"))
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
var storage = require('Storage');
|
||||
|
||||
const settings = storage.readJSON('@setting') || { HID: false };
|
||||
|
||||
var sendHid, next, prev, toggle, up, down, profile;
|
||||
|
||||
if (settings.HID) {
|
||||
profile = 'Music';
|
||||
sendHid = function (code, cb) {
|
||||
try {
|
||||
NRF.sendHIDReport([1,code], () => {
|
||||
NRF.sendHIDReport([1,0], () => {
|
||||
if (cb) cb();
|
||||
});
|
||||
});
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
};
|
||||
next = function (cb) { sendHid(0x01, cb); };
|
||||
prev = function (cb) { sendHid(0x02, cb); };
|
||||
toggle = function (cb) { sendHid(0x10, cb); };
|
||||
up = function (cb) {sendHid(0x40, cb); };
|
||||
down = function (cb) { sendHid(0x80, cb); };
|
||||
} else {
|
||||
E.showMessage('HID disabled');
|
||||
setTimeout(load, 1000);
|
||||
}
|
||||
|
||||
function drawApp() {
|
||||
g.clear();
|
||||
g.setFont("6x8",2);
|
||||
g.setFontAlign(0,0);
|
||||
g.drawString(profile, 120, 120);
|
||||
const d = g.getWidth() - 18;
|
||||
|
||||
function c(a) {
|
||||
return {
|
||||
width: 8,
|
||||
height: a.length,
|
||||
bpp: 1,
|
||||
buffer: (new Uint8Array(a)).buffer
|
||||
};
|
||||
}
|
||||
|
||||
g.drawImage(c([16,56,124,254,16,16,16,16]),d,40);
|
||||
g.drawImage(c([16,16,16,16,254,124,56,16]),d,194);
|
||||
g.drawImage(c([0,8,12,14,255,14,12,8]),d,116);
|
||||
}
|
||||
|
||||
if (next) {
|
||||
setWatch(function(e) {
|
||||
var len = e.time - e.lastTime;
|
||||
if (len > 0.3 && len < 0.9) {
|
||||
E.showMessage('prev');
|
||||
setTimeout(drawApp, 1000);
|
||||
prev(() => {});
|
||||
} else {
|
||||
E.showMessage('up');
|
||||
setTimeout(drawApp, 1000);
|
||||
up(() => {});
|
||||
}
|
||||
}, BTN1, { edge:"falling",repeat:true,debounce:50});
|
||||
|
||||
setWatch(function(e) {
|
||||
var len = e.time - e.lastTime;
|
||||
if (len > 0.3 && len < 0.9) {
|
||||
E.showMessage('next');
|
||||
setTimeout(drawApp, 1000);
|
||||
next(() => {});
|
||||
} else {
|
||||
E.showMessage('down');
|
||||
setTimeout(drawApp, 1000);
|
||||
down(() => {});
|
||||
}
|
||||
}, BTN3, { edge:"falling",repeat:true,debounce:50});
|
||||
|
||||
setWatch(function(e) {
|
||||
E.showMessage('toggle')
|
||||
setTimeout(drawApp, 1000);
|
||||
toggle();
|
||||
}, BTN2, { edge:"falling",repeat:true,debounce:50});
|
||||
|
||||
drawApp();
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Music Control","type":"app",
|
||||
"icon": "*hidmsic",
|
||||
"src": "-hidmsic"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 632 B |
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"name":"Open Location",
|
||||
"name":"Open Location","type":"app",
|
||||
"src":"-openloc"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ function fileLoaded() {
|
|||
log("No 'coordinates' node found");
|
||||
return;
|
||||
}
|
||||
var coordinateLine = coordinateNode[0].textContent;
|
||||
var coordinateLine = coordinateNode[0].textContent.trim();
|
||||
var coordinateList = coordinateLine.split(/\s+/);
|
||||
var coords = [];
|
||||
var pmin, pmax;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
(function() {
|
||||
var s = require('Storage').readJSON('@settings');
|
||||
var s = require('Storage').readJSON('@setting');
|
||||
var adv = { uart: true };
|
||||
if (s.HID) {
|
||||
// Report from https://notes.iopush.net/custom-usb-hid-device-descriptor-media-keyboard/
|
||||
Bangle.HID = new Uint8Array([
|
||||
// Keyboard Controls
|
||||
0x05, 0x01,
|
||||
0x09, 0x06,
|
||||
0xA1, 0x01,
|
||||
0x85, 0x02,
|
||||
0x05, 0x07,
|
||||
0x19, 0xe0,
|
||||
0x29, 0xe7,
|
||||
|
|
@ -39,17 +43,42 @@
|
|||
0x75, 0x08,
|
||||
0x95, 0x02,
|
||||
0xB1, 0x02,
|
||||
0xC0,
|
||||
|
||||
// Music Controls
|
||||
0x05, 0x0C,
|
||||
0x09, 0x01,
|
||||
0xA1, 0x01,
|
||||
0x85, 0x01,
|
||||
0x15, 0x00,
|
||||
0x25, 0x01,
|
||||
0x75, 0x01,
|
||||
0x95, 0x01,
|
||||
0x09, 0xB5,
|
||||
0x81, 0x02,
|
||||
0x09, 0xB6,
|
||||
0x81, 0x02,
|
||||
0x09, 0xB7,
|
||||
0x81, 0x02,
|
||||
0x09, 0xB8,
|
||||
0x81, 0x02,
|
||||
0x09, 0xCD,
|
||||
0x81, 0x02,
|
||||
0x09, 0xE2,
|
||||
0x81, 0x02,
|
||||
0x09, 0xE9,
|
||||
0x81, 0x02,
|
||||
0x09, 0xEA,
|
||||
0x81, 0x02,
|
||||
0xC0
|
||||
]);
|
||||
NRF.setServices(undefined, {
|
||||
uart: true, hid: Bangle.HID,
|
||||
});
|
||||
adv.hid = Bangle.HID;
|
||||
}
|
||||
NRF.setServices(undefined, adv);
|
||||
|
||||
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);
|
||||
})();
|
||||
|
||||
})()
|
||||
|
|
|
|||
103
apps/settings.js
103
apps/settings.js
|
|
@ -12,8 +12,8 @@ function debug(msg, arg) {
|
|||
|
||||
function updateSettings() {
|
||||
debug('updating settings', settings);
|
||||
storage.erase('@settings');
|
||||
storage.write('@settings', settings);
|
||||
storage.erase('@setting');
|
||||
storage.write('@setting', settings);
|
||||
}
|
||||
|
||||
function resetSettings() {
|
||||
|
|
@ -30,7 +30,7 @@ function resetSettings() {
|
|||
}
|
||||
|
||||
try {
|
||||
settings = storage.readJSON('@settings');
|
||||
settings = storage.readJSON('@setting');
|
||||
} catch (e) {}
|
||||
if (!settings) resetSettings();
|
||||
|
||||
|
|
@ -99,6 +99,7 @@ function showMainMenu() {
|
|||
updateSettings();
|
||||
}
|
||||
},
|
||||
'Set Time': showSetTimeMenu,
|
||||
'Reset': showResetMenu,
|
||||
'Turn Off': Bangle.off,
|
||||
'< Back': load
|
||||
|
|
@ -121,15 +122,101 @@ function showResetMenu() {
|
|||
},
|
||||
// this is include for debugging. remove for production
|
||||
/*'Erase': () => {
|
||||
storage.erase('=settings');
|
||||
storage.erase('-settings');
|
||||
storage.erase('@settings');
|
||||
storage.erase('*settings');
|
||||
storage.erase('+settings');
|
||||
storage.erase('=setting');
|
||||
storage.erase('-setting');
|
||||
storage.erase('@setting');
|
||||
storage.erase('*setting');
|
||||
storage.erase('+setting');
|
||||
E.reboot();
|
||||
}*/
|
||||
};
|
||||
return Bangle.menu(resetmenu);
|
||||
}
|
||||
|
||||
function showSetTimeMenu() {
|
||||
d = new Date();
|
||||
const timemenu = {
|
||||
'': {
|
||||
'title': 'Set Time',
|
||||
'predraw': function() {
|
||||
d = new Date();
|
||||
timemenu.Hour.value = d.getHours();
|
||||
timemenu.Minute.value = d.getMinutes();
|
||||
timemenu.Second.value = d.getSeconds();
|
||||
timemenu.Date.value = d.getDate();
|
||||
timemenu.Month.value = d.getMonth() + 1;
|
||||
timemenu.Year.value = d.getFullYear();
|
||||
}
|
||||
},
|
||||
'< Back': showMainMenu,
|
||||
'Hour': {
|
||||
value: d.getHours(),
|
||||
min: 0,
|
||||
max: 23,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setHours(v);
|
||||
setTime(d.getTime()/1000);
|
||||
}
|
||||
},
|
||||
'Minute': {
|
||||
value: d.getMinutes(),
|
||||
min: 0,
|
||||
max: 59,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setMinutes(v);
|
||||
setTime(d.getTime()/1000);
|
||||
}
|
||||
},
|
||||
'Second': {
|
||||
value: d.getSeconds(),
|
||||
min: 0,
|
||||
max: 59,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setSeconds(v);
|
||||
setTime(d.getTime()/1000);
|
||||
}
|
||||
},
|
||||
'Date': {
|
||||
value: d.getDate(),
|
||||
min: 1,
|
||||
max: 31,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setDate(v);
|
||||
setTime(d.getTime()/1000);
|
||||
}
|
||||
},
|
||||
'Month': {
|
||||
value: d.getMonth() + 1,
|
||||
min: 1,
|
||||
max: 12,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setMonth(v - 1);
|
||||
setTime(d.getTime()/1000);
|
||||
}
|
||||
},
|
||||
'Year': {
|
||||
value: d.getFullYear(),
|
||||
min: d.getFullYear() - 10,
|
||||
max: d.getFullYear() + 10,
|
||||
step: 1,
|
||||
onchange: v => {
|
||||
d = new Date();
|
||||
d.setFullYear(v);
|
||||
setTime(d.getTime()/1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
return Bangle.menu(timemenu);
|
||||
}
|
||||
|
||||
showMainMenu();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "Settings",
|
||||
"name": "Settings","type":"app",
|
||||
"icon": "*settings",
|
||||
"src": "-settings"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Speedo",
|
||||
"name":"Speedo","type":"app",
|
||||
"icon":"*speedo",
|
||||
"src":"-speedo"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Spirit Level",
|
||||
"name":"Spirit Level","type":"app",
|
||||
"icon":"*slevel",
|
||||
"src":"-slevel"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ function drawms() {
|
|||
|
||||
setWatch(function() { // Start/stop
|
||||
started = !started;
|
||||
Bangle.beep();
|
||||
if (started)
|
||||
tStart = Date.now()+tStart-tCurrent;
|
||||
tCurrent = Date.now();
|
||||
|
|
@ -69,12 +70,15 @@ setWatch(function() { // Start/stop
|
|||
}, 20);
|
||||
}, BTN2, {repeat:true});
|
||||
setWatch(function() { // Reset
|
||||
Bangle.beep();
|
||||
if (!started) {
|
||||
tStart = tCurrent = Date.now();
|
||||
}
|
||||
lapTimes = [];
|
||||
updateLabels();
|
||||
}, BTN1, {repeat:true});
|
||||
setWatch(function() { // Lap
|
||||
Bangle.beep();
|
||||
if (started) tCurrent = Date.now();
|
||||
lapTimes.unshift(tCurrent-tStart);
|
||||
tStart = tCurrent;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"Stopwatch",
|
||||
"name":"Stopwatch","type":"app",
|
||||
"icon":"*swatch",
|
||||
"src":"-swatch"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name":"T-Rex",
|
||||
"name":"T-Rex","type":"app",
|
||||
"icon":"*trex",
|
||||
"src":"-trex"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name":"Battery Level","type":"widget",
|
||||
"src":"=sbat"
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name":"bluetooth","type":"widget",
|
||||
"src":"=sbt"
|
||||
}
|
||||
39
comms.js
39
comms.js
|
|
@ -18,18 +18,39 @@ uploadApp : app => {
|
|||
return new Promise((resolve,reject) => {
|
||||
// Load all files
|
||||
Promise.all(app.storage.map(storageFile => {
|
||||
var promise;
|
||||
if (storageFile.content)
|
||||
promise = Promise.resolve(storageFile.content);
|
||||
return Promise.resolve(storageFile);
|
||||
else if (storageFile.url)
|
||||
promise = httpGet("apps/"+storageFile.url);
|
||||
else promise = Promise.resolve();
|
||||
return httpGet("apps/"+storageFile.url).then(content => {
|
||||
return {
|
||||
name : storageFile.name,
|
||||
content : content,
|
||||
evaluate : storageFile.evaluate
|
||||
}});
|
||||
else return Promise.resolve();
|
||||
})).then(fileContents => { // now we just have a list of files + contents...
|
||||
// filter out empty files
|
||||
fileContents = fileContents.filter(x=>x!==undefined);
|
||||
// then map each file to a command to load into storage
|
||||
return promise.then(contents =>
|
||||
contents?`\x10require('Storage').write(${toJS(storageFile.name)},${storageFile.evaluate ? contents.trim() : toJS(contents)});`:"")
|
||||
})) // now we just have a list of commands...
|
||||
.then((fileContents) => {
|
||||
fileContents = fileContents.join("\n")+"\n";
|
||||
fileContents.forEach(storageFile => {
|
||||
// check if this is the JSON file
|
||||
if (storageFile.name[0]=="+") {
|
||||
storageFile.evaluate = true;
|
||||
var json = {};
|
||||
try {
|
||||
json = JSON.parse(storageFile.content);
|
||||
} catch (e) {
|
||||
reject(storageFile.name+" is not valid JSON");
|
||||
}
|
||||
json.files = fileContents.map(storageFile=>storageFile.name).join(",");
|
||||
storageFile.content = JSON.stringify(json);
|
||||
}
|
||||
// format ready for Espruino
|
||||
var js = storageFile.evaluate ? storageFile.content.trim() : toJS(storageFile.content);
|
||||
storageFile.cmd = `\x10require('Storage').write(${toJS(storageFile.name)},${js});`;
|
||||
});
|
||||
|
||||
fileContents = fileContents.map(storageFile=>storageFile.cmd).join("\n")+"\n";
|
||||
console.log("uploadApp",fileContents);
|
||||
// reset to ensure we have enough memory to upload what we need to
|
||||
Puck.write("\x03reset();\n", (result) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue