boot 0.23: Move to a precalculated .boot0 file which should speed up load time

setting 0.25: Move boot.js code into 'boot' app itself
launch 0.05: Use g.theme for colours
widbat 0.06: Use 'g.theme' (requires bootloader 0.23)
widlock: new widget
pull/756/head
Gordon Williams 2021-05-26 16:21:52 +01:00
parent 03d580160d
commit 1dca641ff0
13 changed files with 146 additions and 105 deletions

View File

@ -4,11 +4,12 @@
"tags": "tool,system", "tags": "tool,system",
"type":"bootloader", "type":"bootloader",
"icon": "bootloader.png", "icon": "bootloader.png",
"version":"0.22", "version":"0.23",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"storage": [ "storage": [
{"name":".boot0","url":"boot0.js"}, {"name":".boot0","url":"boot0.js"},
{"name":".bootcde","url":"bootloader.js"} {"name":".bootcde","url":"bootloader.js"},
{"name":"bootupdate.js","url":"bootupdate.js"}
], ],
"sortorder" : -10 "sortorder" : -10
}, },
@ -41,7 +42,7 @@
"name": "Launcher (Default)", "name": "Launcher (Default)",
"shortName":"Launcher", "shortName":"Launcher",
"icon": "app.png", "icon": "app.png",
"version":"0.04", "version":"0.05",
"description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.", "description": "This is needed by Bangle.js to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
"tags": "tool,system,launcher", "tags": "tool,system,launcher",
"type":"launch", "type":"launch",
@ -171,13 +172,12 @@
{ "id": "setting", { "id": "setting",
"name": "Settings", "name": "Settings",
"icon": "settings.png", "icon": "settings.png",
"version":"0.24", "version":"0.25",
"description": "A menu for setting up Bangle.js", "description": "A menu for setting up Bangle.js",
"tags": "tool,system", "tags": "tool,system",
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"setting.app.js","url":"settings.js"}, {"name":"setting.app.js","url":"settings.js"},
{"name":"setting.boot.js","url":"boot.js"},
{"name":"setting.img","url":"settings-icon.js","evaluate":true} {"name":"setting.img","url":"settings-icon.js","evaluate":true}
], ],
"data": [ "data": [
@ -571,7 +571,7 @@
{ "id": "widbat", { "id": "widbat",
"name": "Battery Level Widget", "name": "Battery Level Widget",
"icon": "widget.png", "icon": "widget.png",
"version":"0.05", "version":"0.06",
"description": "Show the current battery level and charging status in the top right of the clock", "description": "Show the current battery level and charging status in the top right of the clock",
"tags": "widget,battery", "tags": "widget,battery",
"type":"widget", "type":"widget",
@ -579,6 +579,17 @@
{"name":"widbat.wid.js","url":"widget.js"} {"name":"widbat.wid.js","url":"widget.js"}
] ]
}, },
{ "id": "widlock",
"name": "Lock Widget",
"icon": "widget.png",
"version":"0.01",
"description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked",
"tags": "widget,lock",
"type":"widget",
"storage": [
{"name":"widlock.wid.js","url":"widget.js"}
]
},
{ "id": "widbatpc", { "id": "widbatpc",
"name": "Battery Level Widget (with percentage)", "name": "Battery Level Widget (with percentage)",
"shortName": "Battery Widget", "shortName": "Battery Widget",

View File

@ -21,3 +21,4 @@
0.20: Allow Gadgetbridge to work even with programmable:off 0.20: Allow Gadgetbridge to work even with programmable:off
0.21: Handle echo off char from Gadgetbridge app when programmable:off (fix #558) 0.21: Handle echo off char from Gadgetbridge app when programmable:off (fix #558)
0.22: Stop LCD timeout being disabled on first run (when there is no settings.json) 0.22: Stop LCD timeout being disabled on first run (when there is no settings.json)
0.23: Move to a precalculated .boot0 file which should speed up load time

View File

@ -1,68 +1,2 @@
// This ALWAYS runs at boot // Initially this runs and rewrites itself
E.setFlags({pretokenise:1}); eval(require('Storage').read('bootupdate.js'));
// Load settings...
var s = require('Storage').readJSON('setting.json',1)||{};
if (s.ble!==false) {
if (s.HID) { // Human interface device
if (s.HID=="joy") Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));
else if (s.HID=="kb") Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));
else /*kbmedia*/Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));
NRF.setServices({}, {uart:true, hid:Bangle.HID});
}
}
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
if (s.log) Terminal.setConsole(true); // if showing debug, force REPL onto terminal
else E.setConsole(null,{force:true}); // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
/* If not programmable add our own handler for Bluetooth data
to allow Gadgetbridge commands to be received*/
Bluetooth.line="";
Bluetooth.on('data',function(d) {
var l = (Bluetooth.line + d).split("\n");
Bluetooth.line = l.pop();
l.forEach(n=>Bluetooth.emit("line",n));
});
Bluetooth.on('line',function(l) {
if (l.startsWith('\x10')) l=l.slice(1);
if (l.startsWith('GB({') && l.endsWith('})') && global.GB)
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
});
} else {
if (s.log && !NRF.getSecurityStatus().connected) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection)
else Bluetooth.setConsole(true); // else if no debug, force REPL to Bluetooth
}
// we just reset, so BLE should be on.
// Don't disconnect if something is already connected to us
if (s.ble===false && !NRF.getSecurityStatus().connected) NRF.sleep();
// Set time, vibrate, beep, etc
if (!Bangle.F_BEEPSET) {
if (!s.vibrate) Bangle.buzz=Promise.resolve;
if (s.beep===false) Bangle.beep=Promise.resolve;
else if (s.beep=="vib") Bangle.beep = function (time, freq) {
return new Promise(function(resolve) {
if ((0|freq)<=0) freq=4000;
if ((0|time)<=0) time=200;
if (time>5000) time=5000;
analogWrite(D13,0.1,{freq:freq});
setTimeout(function() {
digitalWrite(D13,0);
resolve();
}, time);
});
};
}
if (s.timeout!==undefined) Bangle.setLCDTimeout(s.timeout);
if (!s.timeout) Bangle.setLCDPower(1);
E.setTimeZone(s.timezone);
delete s;
// Draw out of memory errors onto the screen
E.on('errorFlag', function(errorFlags) {
g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip();
print("Interpreter error:", errorFlags);
E.getErrorFlags(); // clear flags so we get called next time
});
// stop users doing bad things!
global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }
// Load *.boot.js files
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
eval(require('Storage').read(bootFile));
});

95
apps/boot/bootupdate.js Normal file
View File

@ -0,0 +1,95 @@
/* This rewrites boot0.js based on current settings. If settings changed then it
recalculates, but this avoids us doing a whole bunch of reconfiguration most
of the time. */
E.showMessage("Updating boot0...");
var s = require('Storage').readJSON('setting.json',1)||{};
var boot = "";
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/));
boot += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js'));} else {\n`;
boot += `E.setFlags({pretokenise:1});\n`;
if (s.ble!==false) {
if (s.HID) { // Human interface device
if (s.HID=="joy") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBKEBCQGhAAUJGQEpBRUAJQGVBXUBgQKVA3UBgQMFAQkwCTEVgSV/dQiVAoECwMA="));`;
else if (s.HID=="kb") boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBBQcZ4CnnFQAlAXUBlQiBApUBdQiBAZUFdQEFCBkBKQWRApUBdQORAZUGdQgVACVzBQcZAClzgQAJBRUAJv8AdQiVArECwA=="));`
else /*kbmedia*/boot += `Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));`;
boot += `NRF.setServices({}, {uart:true, hid:Bangle.HID});\n`;
}
}
if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
if (s.log) boot += `Terminal.setConsole(true);\n`; // if showing debug, force REPL onto terminal
else boot += `E.setConsole(null,{force:true});\n`; // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
/* If not programmable add our own handler for Bluetooth data
to allow Gadgetbridge commands to be received*/
boot += `
Bluetooth.line="";
Bluetooth.on('data',function(d) {
var l = (Bluetooth.line + d).split("\n");
Bluetooth.line = l.pop();
l.forEach(n=>Bluetooth.emit("line",n));
});
Bluetooth.on('line',function(l) {
if (l.startsWith('\x10')) l=l.slice(1);
if (l.startsWith('GB({') && l.endsWith('})') && global.GB)
try { global.GB(JSON.parse(l.slice(3,-1))); } catch(e) {}
});\n`;
} else {
if (s.log) boot += `if (!NRF.getSecurityStatus().connected) Terminal.setConsole();\n`; // if showing debug, put REPL on terminal (until connection)
else boot += `Bluetooth.setConsole(true);\n`; // else if no debug, force REPL to Bluetooth
}
// we just reset, so BLE should be on.
// Don't disconnect if something is already connected to us
if (s.ble===false) boot += `if (!NRF.getSecurityStatus().connected) NRF.sleep();\n`;
// Set time
if (s.timeout!==undefined) boot += `Bangle.setLCDTimeout(${s.timeout});\n`;
if (!s.timeout) boot += `Bangle.setLCDPower(1);\n`;
boot += `E.setTimeZone(${s.timezone});`;
// Set vibrate, beep, etc IF on older firmwares
if (!Bangle.F_BEEPSET) {
if (!s.vibrate) boot += `Bangle.buzz=Promise.resolve;\n`
if (s.beep===false) boot += `Bangle.beep=Promise.resolve;\n`
else if (s.beep=="vib") boot += `Bangle.beep = function (time, freq) {
return new Promise(function(resolve) {
if ((0|freq)<=0) freq=4000;
if ((0|time)<=0) time=200;
if (time>5000) time=5000;
analogWrite(D13,0.1,{freq:freq});
setTimeout(function() {
digitalWrite(D13,0);
resolve();
}, time);
});
};\n`;
}
// Draw out of memory errors onto the screen
boot += `E.on('errorFlag', function(errorFlags) {
g.reset(1).setColor("#ff0000").setFont("6x8").setFontAlign(0,1).drawString(errorFlags,g.getWidth()/2,g.getHeight()-1).flip();
print("Interpreter error:", errorFlags);
E.getErrorFlags(); // clear flags so we get called next time
});\n`;
// stop users doing bad things!
if (global.save) boot += `global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }\n`;
// Apply any settings-specific stuff
if (s.options) boot+=`Bangle.setOptions(${E.toJS(s.options)});\n`;
if (s.quiet && s.qmOptions) boot+=`Bangle.setOptions(${E.toJS(s.qmOptions)});\n`;
if (s.quiet && s.qmBrightness) {
if (s.qmBrightness!=1) boot+=`Bangle.setLCDBrightness(${s.qmBrightness});\n`;
} else {
if (s.brightness && s.brightness!=1) boot+=`Bangle.setLCDBrightness(${s.brightness});\n`;
}
if (s.quiet && s.qmTimeout) boot+=`Bangle.setLCDTimeout(${s.qmTimeout});\n`;
if (s.passkey!==undefined && s.passkey.length==6) boot+=`NRF.setSecurity({passkey:${s.passkey}, mitm:1, display:1});\n`;
if (s.whitelist) boot+=`NRF.on('connect', function(addr) { if (!(require('Storage').readJSON('setting.json',1)||{}).whitelist.includes(addr)) NRF.disconnect(); });\n`;
// Pre-2v10 firmwares without a theme
if (!g.theme) {
boot += `g.theme={fg:-1,bg:0,fg2:-1,bg2:7,fgH:-1,bgH:0x02F7};\n`;
}
// Append *.boot.js files
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+"\n";
});
boot += "}\n";// initial 'if'
var s = require('Storage').write('.boot0',boot);
delete boot;
E.showMessage("Reloading...");
eval(require('Storage').read('.boot0'));
eval(require('Storage').read('.bootcde'));

View File

@ -2,3 +2,4 @@
0.02: Only store relevant app data (saves RAM when many apps) 0.02: Only store relevant app data (saves RAM when many apps)
0.03: Allow scrolling to wrap around (fix #382) 0.03: Allow scrolling to wrap around (fix #382)
0.04: Now displays widgets 0.04: Now displays widgets
0.05: Use g.theme for colours

View File

@ -12,35 +12,36 @@ var menuScroll = 0;
var menuShowing = false; var menuShowing = false;
function drawMenu() { function drawMenu() {
g.setFont("6x8",2); g.reset().setFont("6x8",2).setFontAlign(-1,0);
g.setFontAlign(-1,0); var w = g.getWidth();
var n = 3; var h = g.getHeight();
var m = w/2;
var n = (h-48)/64;
if (selected>=n+menuScroll) menuScroll = 1+selected-n; if (selected>=n+menuScroll) menuScroll = 1+selected-n;
if (selected<menuScroll) menuScroll = selected; if (selected<menuScroll) menuScroll = selected;
// arrows // arrows
g.setColor(menuScroll ? -1 : 0); g.setColor(menuScroll ? g.theme.fg : g.theme.bg);
g.fillPoly([120,6,106,20,134,20]); g.fillPoly([m,6,m-14,20,m+14,20]);
g.setColor((apps.length>n+menuScroll) ? -1 : 0); g.setColor((apps.length>n+menuScroll) ? g.theme.fg : g.theme.bg);
g.fillPoly([120,233,106,219,134,219]); g.fillPoly([m,h-7,m-14,h-21,m+14,h-21]);
// draw // draw
g.setColor(-1); g.setColor(g.theme.fg);
for (var i=0;i<n;i++) { for (var i=0;i<n;i++) {
var app = apps[i+menuScroll]; var app = apps[i+menuScroll];
if (!app) break; if (!app) break;
var y = 24+i*64; var y = 24+i*64;
if (i+menuScroll==selected) { if (i+menuScroll==selected) {
g.setColor(0.3,0.3,0.3); g.setColor(g.theme.bgH).fillRect(0,y,w-1,y+63);
g.fillRect(0,y,239,y+63); g.setColor(g.theme.fgH).drawRect(0,y,w-1,y+63);
g.setColor(1,1,1);
g.drawRect(0,y,239,y+63);
} else } else
g.clearRect(0,y,239,y+63); g.clearRect(0,y,w-1,y+63);
g.drawString(app.name,64,y+32); g.drawString(app.name,64,y+32);
var icon=undefined; var icon=undefined;
if (app.icon) icon = s.read(app.icon); if (app.icon) icon = s.read(app.icon);
if (icon) try {g.drawImage(icon,8,y+8);} catch(e){} if (icon) try {g.drawImage(icon,8,y+8);} catch(e){}
} }
} }
g.clear();
drawMenu(); drawMenu();
setWatch(function() { setWatch(function() {
selected--; selected--;

View File

@ -27,3 +27,4 @@
0.22: Move HID to BLE menu 0.22: Move HID to BLE menu
0.23: Change max time offset to 13 for NZ summer daylight time (NZDT) 0.23: Change max time offset to 13 for NZ summer daylight time (NZDT)
0.24: Add Quiet Mode settings 0.24: Add Quiet Mode settings
0.25: Move boot.js code into 'boot' app itself

View File

@ -1,15 +0,0 @@
(() => {
var settings = require('Storage').readJSON('setting.json', true);
if (!settings) return;
if (settings.options) Bangle.setOptions(settings.options);
if (settings.quiet && settings.qmOptions) Bangle.setOptions(settings.qmOptions);
if (settings.quiet && settings.qmBrightness) {
if (settings.qmBrightness!=1) Bangle.setLCDBrightness(settings.qmBrightness);
} else {
if (settings.brightness && settings.brightness!=1) Bangle.setLCDBrightness(settings.brightness);
}
if (settings.quiet && settings.qmTimeout) Bangle.setLCDTimeout(s.qmTimeout);
if (settings.passkey!==undefined && settings.passkey.length==6) NRF.setSecurity({passkey:settings.passkey, mitm:1, display:1});
if (settings.whitelist) NRF.on('connect', function(addr) { if (!settings.whitelist.includes(addr)) NRF.disconnect(); });
delete settings;
})()

View File

@ -2,3 +2,4 @@
0.03: Tweaks for variable size widget system 0.03: Tweaks for variable size widget system
0.04: Ensure redrawing works with variable size widget system 0.04: Ensure redrawing works with variable size widget system
0.05: Fix regression stopping correct widget updates 0.05: Fix regression stopping correct widget updates
0.06: Use 'g.theme' (requires bootloader 0.23)

View File

@ -7,16 +7,16 @@
function draw() { function draw() {
var s = 39; var s = 39;
var x = this.x, y = this.y; var x = this.x, y = this.y;
g.reset();
if (Bangle.isCharging()) { if (Bangle.isCharging()) {
g.setColor(CHARGING).drawImage(atob("DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y); g.setColor(CHARGING).drawImage(atob("DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y);
x+=16; x+=16;
} }
g.setColor(-1); g.setColor(g.theme.fg);
g.fillRect(x,y+2,x+s-4,y+21); g.fillRect(x,y+2,x+s-4,y+21);
g.clearRect(x+2,y+4,x+s-6,y+19); g.clearRect(x+2,y+4,x+s-6,y+19);
g.fillRect(x+s-3,y+10,x+s,y+14); g.fillRect(x+s-3,y+10,x+s,y+14);
g.setColor(CHARGING).fillRect(x+4,y+6,x+4+E.getBattery()*(s-12)/100,y+17); g.setColor(CHARGING).fillRect(x+4,y+6,x+4+E.getBattery()*(s-12)/100,y+17);
g.setColor(-1);
} }
Bangle.on('charging',function(charging) { Bangle.on('charging',function(charging) {
if(charging) Bangle.buzz(); if(charging) Bangle.buzz();

1
apps/widlock/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: First commit

10
apps/widlock/widget.js Normal file
View File

@ -0,0 +1,10 @@
(function(){
Bangle.on('lcdPower', function(on) {
WIDGETS["lock"].width = Bangle.isLCDOn()?0:16;
Bangle.drawWidgets();
});
WIDGETS["lock"]={area:"tl",width:Bangle.isLCDOn()?0:16,draw:function(w) {
if (!Bangle.isLCDOn())
g.reset().drawImage(atob("DhABH+D/wwMMDDAwwMf/v//4f+H/h/8//P/z///f/g=="), w.x, w.y);
}};
})()

BIN
apps/widlock/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B