Merge pull request #1 from espruino/master

Grabbing Gordon's latest
master
Conor O'Neill 2019-11-08 14:03:16 +00:00 committed by GitHub
commit 5850633258
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 580 additions and 58 deletions

View File

@ -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

View File

@ -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",

View File

@ -1,5 +1,5 @@
{
"name":"Animals Game",
"name":"Animals Game", "type":"app",
"icon":"*animals",
"src":"-animals"
}

View File

@ -1,5 +1,5 @@
{
"name":"Asteroids!",
"name":"Asteroids!","type":"app",
"icon":"*astroid",
"src":"-astroid"
}

View File

@ -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);

5
apps/clock-morphing.json Normal file
View File

@ -0,0 +1,5 @@
{
"name":"Morphing Clock","type":"clock",
"icon":"*mclock",
"src":"-mclock"
}

1
apps/clock-word-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgIIFiOAgEgoN4n+AsEw4f8mEAoEwwIaBAok/AoMZwP4AocBwARCjYuBAoUPAodgh0D/kQBYMGgP8IIseF4f8FINgnIFBmOAoEfBAM4AQMPAQIIBoEOAoMHCIILCkIFBjYFBDoUZNAJrCmPAsA4CkCMHn+Y8U/4MAl2M80/O4MEhnmTQQFB8gFDhu0CIcF3gLDg8cAokYAoOAAoQvB/A9H4BBDwPwX4P4nEDnAFBEAMGFIPDhk4g0MAoN8n4FD/ARBAoODuAFBjExwYdBEYMZwYOBgPwh8DhhBHACo"))

137
apps/clock-word.js Normal file
View File

@ -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();
})();

5
apps/clock-word.json Normal file
View File

@ -0,0 +1,5 @@
{
"name":"Word Clock","type":"clock",
"icon":"*wclock",
"src":"-wclock"
}

BIN
apps/clock-word.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 843 B

View File

@ -1,5 +0,0 @@
{
"name":"Clock",
"icon":"*clock",
"src":"-clock"
}

View File

@ -1,5 +1,5 @@
{
"name":"Compass",
"name":"Compass","type":"app",
"icon":"*compass",
"src":"-compass"
}

View File

@ -1,5 +1,5 @@
{
"name":"App Manager",
"name":"App Manager","type":"app",
"icon":"*files",
"src":"-files"
}

View File

@ -1,5 +1,5 @@
{
"name":"GPS Time",
"name":"GPS Time","type":"app",
"icon":"*gpstime",
"src":"-gpstime"
}

View File

@ -1,5 +1,5 @@
{
"name":"Heart Rate",
"name":"Heart Rate","type":"app",
"icon":"*hrm",
"src":"-hrm"
}

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AH4A/AHeIDC+AC60IC/51ELoIXTCYMIDISLpC/67dxAAJHBYsLhAYKLhgXXHhWAdoQbBApAvhBgYcCAogXkI64XYhDtII/oMUERTyDABJ0FPI4A/AH4A/AGY="))

86
apps/hid-keyboard.js Normal file
View File

@ -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();
}

5
apps/hid-keyboard.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "Keyboard Control","type":"app",
"icon": "*hidkbd",
"src": "-hidkbd"
}

BIN
apps/hid-keyboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

1
apps/hid-music-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AH4A5xGICquZzAVUAAIXQCogXQCoxHPCox0BxIXNxIVFBAQXPUAwXPBw4XowAvuC/4X/C9sIC6kIxGZzIXSFgIWBC6QWEC6RECAAOJwAXQFwoXLxAqBC4MICweZCxhWEC4mICxxuDA4I3BCxQ/FQxpyEK6AucC4idMI5OICyQwBQpgA/AH4Au"))

85
apps/hid-music.js Normal file
View File

@ -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();
}

5
apps/hid-music.json Normal file
View File

@ -0,0 +1,5 @@
{
"name": "Music Control","type":"app",
"icon": "*hidmsic",
"src": "-hidmsic"
}

BIN
apps/hid-music.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

View File

@ -1,4 +1,4 @@
{
"name":"Open Location",
"name":"Open Location","type":"app",
"src":"-openloc"
}

View File

@ -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;

View File

@ -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);
})();
})()

View File

@ -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();

View File

@ -1,5 +1,5 @@
{
"name": "Settings",
"name": "Settings","type":"app",
"icon": "*settings",
"src": "-settings"
}

View File

@ -1,5 +1,5 @@
{
"name":"Speedo",
"name":"Speedo","type":"app",
"icon":"*speedo",
"src":"-speedo"
}

View File

@ -1,5 +1,5 @@
{
"name":"Spirit Level",
"name":"Spirit Level","type":"app",
"icon":"*slevel",
"src":"-slevel"
}

View File

@ -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;

View File

@ -1,5 +1,5 @@
{
"name":"Stopwatch",
"name":"Stopwatch","type":"app",
"icon":"*swatch",
"src":"-swatch"
}

View File

@ -1,5 +1,5 @@
{
"name":"T-Rex",
"name":"T-Rex","type":"app",
"icon":"*trex",
"src":"-trex"
}

4
apps/widget-battery.json Normal file
View File

@ -0,0 +1,4 @@
{
"name":"Battery Level","type":"widget",
"src":"=sbat"
}

View File

@ -0,0 +1,4 @@
{
"name":"bluetooth","type":"widget",
"src":"=sbt"
}

View File

@ -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) => {