1
0
Fork 0

Merge branch 'espruino:master' into master

master
Ronin0000 2021-09-04 08:47:39 -07:00 committed by GitHub
commit c2286a91d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 480 additions and 245 deletions

View File

@ -237,6 +237,11 @@ and which gives information about the app for the Launcher.
// like this one with 'storage','name' and 'id' set up // like this one with 'storage','name' and 'id' set up
// see below for more info // see below for more info
"customConnect": true, // if supplied, ensure we are connected to a device
// before the "custom.html" iframe is loaded. An
// onInit function in "custom.html" is then called
// with info on the currently connected device.
"interface": "interface.html", // if supplied, apps/interface.html is loaded in an "interface": "interface.html", // if supplied, apps/interface.html is loaded in an
// iframe, and it may interact with the connected Bangle // iframe, and it may interact with the connected Bangle
// to retrieve information from it // to retrieve information from it

View File

@ -4,7 +4,7 @@
"tags": "tool,system,b2", "tags": "tool,system,b2",
"type":"bootloader", "type":"bootloader",
"icon": "bootloader.png", "icon": "bootloader.png",
"version":"0.28", "version":"0.29",
"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"},
@ -55,7 +55,7 @@
"name": "Launcher (Bangle.js 2)", "name": "Launcher (Bangle.js 2)",
"shortName":"Launcher", "shortName":"Launcher",
"icon": "app.png", "icon": "app.png",
"version":"0.01", "version":"0.02",
"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.", "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", "tags": "tool,system,launcher,b2,bno1",
"type":"launch", "type":"launch",
@ -402,9 +402,9 @@
{ "id": "trex", { "id": "trex",
"name": "T-Rex", "name": "T-Rex",
"icon": "trex.png", "icon": "trex.png",
"version":"0.03", "version":"0.04",
"description": "T-Rex game in the style of Chrome's offline game", "description": "T-Rex game in the style of Chrome's offline game",
"tags": "game", "tags": "game,b2",
"allow_emulator":true, "allow_emulator":true,
"storage": [ "storage": [
{"name":"trex.app.js","url":"trex.js"}, {"name":"trex.app.js","url":"trex.js"},
@ -418,9 +418,9 @@
{ "id": "astroid", { "id": "astroid",
"name": "Asteroids!", "name": "Asteroids!",
"icon": "asteroids.png", "icon": "asteroids.png",
"version":"0.02", "version":"0.03",
"description": "Retro asteroids game", "description": "Retro asteroids game",
"tags": "game", "tags": "game,b2",
"allow_emulator":true, "allow_emulator":true,
"storage": [ "storage": [
{"name":"astroid.app.js","url":"asteroids.js"}, {"name":"astroid.app.js","url":"asteroids.js"},
@ -814,8 +814,8 @@
"icon": "app.png", "icon": "app.png",
"version":"0.02", "version":"0.02",
"description": "Use this to upload a customised QR code to Bangle.js", "description": "Use this to upload a customised QR code to Bangle.js",
"tags": "qrcode", "tags": "qrcode,b2",
"custom": "custom.html", "custom": "custom.html", "customConnect":true,
"storage": [ "storage": [
{"name":"qrcode.app.js"}, {"name":"qrcode.app.js"},
{"name":"qrcode.img","url":"app-icon.js","evaluate":true} {"name":"qrcode.img","url":"app-icon.js","evaluate":true}
@ -1096,9 +1096,9 @@
{ "id": "flappy", { "id": "flappy",
"name": "Flappy Bird", "name": "Flappy Bird",
"icon": "app.png", "icon": "app.png",
"version":"0.04", "version":"0.05",
"description": "A Flappy Bird game clone", "description": "A Flappy Bird game clone",
"tags": "game", "tags": "game,b2",
"allow_emulator":true, "allow_emulator":true,
"storage": [ "storage": [
{"name":"flappy.app.js","url":"app.js"}, {"name":"flappy.app.js","url":"app.js"},
@ -1183,7 +1183,7 @@
{ "id": "widpedom", { "id": "widpedom",
"name": "Pedometer widget", "name": "Pedometer widget",
"icon": "widget.png", "icon": "widget.png",
"version":"0.14", "version":"0.17",
"description": "Daily pedometer widget", "description": "Daily pedometer widget",
"tags": "widget,b2", "tags": "widget,b2",
"type":"widget", "type":"widget",
@ -1520,7 +1520,7 @@
"version":"0.08", "version":"0.08",
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are", "description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
"tags": "outdoors,gps,b2", "tags": "outdoors,gps,b2",
"custom": "custom.html", "custom": "custom.html", "customConnect":true,
"storage": [ "storage": [
{"name":"openstmap","url":"openstmap.js"}, {"name":"openstmap","url":"openstmap.js"},
{"name":"openstmap.app.js","url":"app.js"}, {"name":"openstmap.app.js","url":"app.js"},
@ -2039,7 +2039,7 @@
"id": "beebclock", "id": "beebclock",
"name": "Beeb Clock", "name": "Beeb Clock",
"icon": "beebclock.png", "icon": "beebclock.png",
"version":"0.04", "version":"0.05",
"description": "Clock face that may be coincidentally familiar to BBC viewers", "description": "Clock face that may be coincidentally familiar to BBC viewers",
"tags": "clock", "tags": "clock",
"type": "clock", "type": "clock",
@ -2735,7 +2735,7 @@
"name": "Heart Rate Variability monitor", "name": "Heart Rate Variability monitor",
"shortName":"HRV monitor", "shortName":"HRV monitor",
"icon": "hrv.png", "icon": "hrv.png",
"version":"0.03", "version":"0.04",
"description": "Heart Rate Variability monitor, see Readme for more info", "description": "Heart Rate Variability monitor, see Readme for more info",
"tags": "", "tags": "",
"readme": "README.md", "readme": "README.md",
@ -3208,7 +3208,7 @@
{ "id": "kitchen", { "id": "kitchen",
"name": "Kitchen Combo", "name": "Kitchen Combo",
"icon": "kitchen.png", "icon": "kitchen.png",
"version":"0.12", "version":"0.13",
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later", "description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'. Requires firmware v2.08.167 or later",
"tags": "tool,outdoors,gps", "tags": "tool,outdoors,gps",
"type":"clock", "type":"clock",

View File

@ -1,3 +1,4 @@
0.01: New App! 0.01: New App!
0.02: Added options to either run as a one-off reading, or a continuous mode to log data until the watch is reset 0.02: Added options to either run as a one-off reading, or a continuous mode to log data until the watch is reset
0.03: Add RMSSD recording 0.03: Add RMSSD recording
0.04: Modify to work with new heart rate API, but still not sure it's working correctly

View File

@ -17,22 +17,23 @@ var csv = [
logfile.write(csv.join(",")+"\n"); logfile.write(csv.join(",")+"\n");
var debugging = true; var debugging = true;
var samples = 0; // how many samples have we connected?
var collectData = false; // are we currently collecting data?
var first_signals = 0; // ignore the first several signals
var heartrate = [];
var BPM_array = []; var BPM_array = [];
var raw_HR_array = new Float32Array(1536); var raw_HR_array = new Float32Array(1536);
var alternate_array = new Float32Array(3072); var alternate_array = new Float32Array(3072);
var pulse_array = []; var pulse_array = [];
var pulsecount = 0;
var cutoff_threshold = 0.5; var cutoff_threshold = 0.5;
var sample_frequency = 51.6; var sample_frequency = 51.6;
var gap_threshold = 0.15; var gap_threshold = 0.15;
var hr_min = 40;
var hr_max = 160;
var movement = 0; var movement = 0;
function storeMyData(data, file_type) { var px = g.getWidth()/2;
var py = g.getHeight()/2;
var accel; // interval for acceleration logging
function storeMyData(data, file_type) { "ram"
log = raw_HR_array; log = raw_HR_array;
// shift elements backwards - note the 4, because a Float32 is 4 bytes // shift elements backwards - note the 4, because a Float32 is 4 bytes
log.set(new Float32Array(log.buffer, 4 /*bytes*/)); log.set(new Float32Array(log.buffer, 4 /*bytes*/));
@ -41,59 +42,58 @@ function storeMyData(data, file_type) {
} }
function average(samples) { function average(samples) {
var sum = 0; return E.sum(samples) / samples.length; // faster builtin
/* var sum = 0;
for (var i = 0; i < samples.length; i++) { for (var i = 0; i < samples.length; i++) {
sum += parseFloat(samples[i]); sum += parseFloat(samples[i]);
} }
var avg = sum / samples.length; var avg = sum / samples.length;
return avg; return avg;*/
} }
function StandardDeviation (array) { function StandardDeviation (array) {
const n = array.length; const n = array.length;
const mean = array.reduce((a, b) => a + b) / n; const mean = E.sum(array) / n; //array.reduce((a, b) => a + b) / n;
return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n); //return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
return Math.sqrt(E.variance(array, mean));
} }
function turn_off() { function turn_off() {
Bangle.setHRMPower(0); Bangle.setHRMPower(0);
var accel = setInterval(function () {
movement = movement + Bangle.getAccel().diff;
}, 1000);
g.clear(); g.clear();
g.drawString("processing 1/5", 120, 120); g.drawString("processing 1/5", px, py);
rolling_average(raw_HR_array,5); rolling_average(raw_HR_array,5);
g.clear(); g.clear();
g.drawString("processing 2/5", 120, 120); g.drawString("processing 2/5", px, py);
upscale(); upscale();
g.clear(); g.clear();
g.drawString("processing 3/5", 120, 120); g.drawString("processing 3/5", px, py);
rolling_average(alternate_array,5); rolling_average(alternate_array,5);
g.clear(); g.clear();
g.drawString("processing 4/5", 120, 120); g.drawString("processing 4/5", px, py);
apply_cutoff(); apply_cutoff();
find_peaks(); find_peaks();
g.clear(); g.clear();
g.drawString("processing 5/5", 120, 120); g.drawString("processing 5/5", px, py);
calculate_HRV(); calculate_HRV();
} }
function bernstein(A, B, C, D, E, t) { function bernstein(A, B, C, D, E, t) { "ram"
s = 1 - t; s = 1 - t;
x = (A * Math.pow(s, 4)) + (B * 4 * Math.pow(s, 3) * t) + (C * 6 * s * s * t * t) x = (A * Math.pow(s, 4)) + (B * 4 * Math.pow(s, 3) * t) + (C * 6 * s * s * t * t)
+ (D * 4 * s * Math.pow(t, 3)) + (E * Math.pow(t, 4)); + (D * 4 * s * Math.pow(t, 3)) + (E * Math.pow(t, 4));
return x; return x;
} }
function upscale() { function upscale() { "ram"
var index = 0; var index = 0;
for (let i = raw_HR_array.length - 1; i > 5; i -= 5) { for (let i = raw_HR_array.length - 1; i > 5; i -= 5) {
p0 = raw_HR_array[i]; p0 = raw_HR_array[i];
@ -110,19 +110,18 @@ function upscale() {
} }
} }
function rolling_average(values, count) { function rolling_average(values, count) { "ram"
var temp_array = []; var temp_array = [];
for (let i = 0; i < values.length; i++) { for (let i = 0; i < values.length; i++) {
temp_array = []; temp_array = [];
for (let x = 0; x < count; x++) for (let x = 0; x < count; x++)
temp_array.push(values[i + x]); temp_array.push(values[i + x]);
values[i] = average(temp_array); values[i] = average(temp_array);
} }
} }
function apply_cutoff() { function apply_cutoff() { "ram"
var x; var x;
for (let i = 0; i < alternate_array.length; i++) { for (let i = 0; i < alternate_array.length; i++) {
x = alternate_array[i]; x = alternate_array[i];
@ -132,7 +131,7 @@ function apply_cutoff() {
} }
} }
function find_peaks() { function find_peaks() { "ram"
var previous; var previous;
var previous_slope = 0; var previous_slope = 0;
var slope; var slope;
@ -157,7 +156,7 @@ function find_peaks() {
} }
} }
function RMSSD(samples){ function RMSSD(samples){ "ram"
var sum = 0; var sum = 0;
var square = 0; var square = 0;
var data = []; var data = [];
@ -192,7 +191,7 @@ function calculate_HRV() {
gap_average = average(temp_array); gap_average = average(temp_array);
var calculatedHR = (sample_frequency*60)/(gap_average/2); var calculatedHR = (sample_frequency*60)/(gap_average/2);
if(option == 0) if(option == 0)
g.flip(); Bangle.setLCDPower(1);
g.clear(); g.clear();
//var display_stdv = StandardDeviation(pulse_array).toFixed(1); //var display_stdv = StandardDeviation(pulse_array).toFixed(1);
var SDNN = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0); var SDNN = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0);
@ -200,14 +199,13 @@ function calculate_HRV() {
g.drawString("SDNN:" + SDNN g.drawString("SDNN:" + SDNN
+"\nRMSSD:" + RMS_SD +"\nRMSSD:" + RMS_SD
+ "\nHR:" + calculatedHR.toFixed(0) + "\nHR:" + calculatedHR.toFixed(0)
+"\nSample Count:" + temp_array.length, 120, 120); +"\nSample Count:" + temp_array.length, px, py);
Bangle.setLCDPower(1);
if(option == 0){ if(option == 0) { // single run
Bangle.buzz(500,1); Bangle.buzz(500,1);
clearInterval(routine); option = null;
} drawButtons();
} else {
else{
var csv = [ var csv = [
0|getTime(), 0|getTime(),
temp_array.length, temp_array.length,
@ -219,85 +217,87 @@ function calculate_HRV() {
]; ];
logfile.write(csv.join(",")+"\n"); logfile.write(csv.join(",")+"\n");
movement = 0;
// for (let i = 0; i < raw_HR_array.length; i++) { // for (let i = 0; i < raw_HR_array.length; i++) {
// raw_HR_array[i] = null; // raw_HR_array[i] = null;
//} //}
turn_on();
} }
} }
function btn1Pressed() { function btn1Pressed() {
if(option === null){ if(option === null){
clearInterval(accel);
g.clear(); g.clear();
g.drawString("one-off assessment", 120, 120); g.drawString("one-off assessment", px, py);
option = 0; option = 0;
Bangle.setHRMPower(1);
turn_on();
} }
} }
function btn3Pressed() { function btn3Pressed() {
if(option === null){ if(option === null){
logfile.write(""); //reset HRV log logfile.write(""); //reset HRV log
clearInterval(accel);
g.clear(); g.clear();
g.drawString("continuous mode", 120, 120); g.drawString("continuous mode", px, py);
option = 1; option = 1;
Bangle.setHRMPower(1);
} turn_on();
}
} }
var routine = setInterval(function () { function turn_on() {
clearInterval(accel);
first_signals = 0; // ignore the first several signals
pulsecount = 0;
BPM_array = []; BPM_array = [];
heartrate = [];
pulse_array = []; pulse_array = [];
samples = 0;
if (accel) clearInterval(accel);
movement = 0;
accel = setInterval(function () {
movement = movement + Bangle.getAccel().diff;
}, 1000);
Bangle.setHRMPower(1); Bangle.setHRMPower(1);
}, 180000); collectData = true;
}
var accel = setInterval(function () { function drawButtons() {
movement = movement + Bangle.getAccel().diff; g.setColor("#00ff7f");
}, 1000); g.setFont("6x8", 2);
g.setFontAlign(-1,1);
g.drawString("continuous", 120, 210);
g.drawString("one-time", 140, 50);
g.setColor("#ffffff");
g.setFontAlign(0, 0);
}
g.clear(); g.clear();
g.setColor("#00ff7f");
g.setFont("6x8", 2);
g.setFontAlign(-1,1);
g.drawString("continuous", 120, 210);
g.setFontAlign(-1,1);
g.drawString("one-time", 140, 50);
drawButtons();
g.setFont("6x8", 2);
g.setColor("#ffffff"); g.setColor("#ffffff");
g.setFontAlign(0, 0); // center font g.setFontAlign(0, 0); // center font
g.drawString("check app README", 120, 120); g.drawString("check app README\nfor more info", px, py);
g.drawString("for more info", 120, 140);
setWatch(btn1Pressed, BTN1, {repeat:true}); setWatch(btn1Pressed, BTN1, {repeat:true});
setWatch(btn3Pressed, BTN3, {repeat:true}); setWatch(btn3Pressed, BTN3, {repeat:true});
Bangle.on('HRM', function (hrm) {
if(option == 0)
g.flip(); Bangle.on('HRM-raw', function (e) {
if (first_signals < 3) { if (!collectData) return;
g.clear(); storeMyData(e.raw, 0);
g.drawString("setting up...\nremain still " + first_signals * 20 + "%", 120, 120); if (!(samples & 7)) {
first_signals++; Bangle.setLCDPower(1);
} g.clearRect(0, py-10, g.getWidth(), py+22);
else { if (samples < 100)
BPM_array = hrm.raw; g.drawString("setting up...\nremain still " + samples + "%", px, py, true);
if(hrm.bpm > hr_min && hrm.bpm < hr_max) else
heartrate.push(hrm.bpm); g.drawString("logging: " + (samples*100/raw_HR_array.length).toFixed(0) + "%", px, py, true);
if (pulsecount < 7) { }
for (let i = 0; i < 256; i++) { if (samples > raw_HR_array.length) {
storeMyData(BPM_array[i], 0); collectData = false;
} turn_off();
g.clear(); }
g.drawString("logging: " + ((pulsecount/6)*100).toFixed(0) + "%", 120, 120); samples++;
}
if(pulsecount == 6)
turn_off();
pulsecount++;
}
}); });

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
require("heatshrink").decompress(atob("oFAwhC/AH4A2xAedhGIwA+dAAI+dAAJAcEAJ/fD/4f5hAJIYSo6JIihaKxBATxAACC4YnBAAQ+TD7o/CFRJfTP5a/eb6pEOD/7JFwczAA0ybgwfUkUjD4QaDD6syiMSD4ZABCIa8JD547BCIY/ZHQQ+LD6Z/cHZYf/f6E4D6uCD4gACD6wgBkQACBAYfWABYf/D/4fPwf/ABgf/D6czABBf/P/5f/P/5f/D+WIABwfuAH4A/ADo=")) require("heatshrink").decompress(atob("mEwwgsphAXWxGACyoABDCwWVC/4XcXY8IDZsIxAXVXYQZDDwIGEC6gwMI5IvNGAJfVR5DXyPgczAAwSEC58iC4R3DC50xiMjC4QTBXY4XNGAIPCF6QTCI6gXCYYwXQcQ4XemUiC6IRBa4wXOABQX/C9OD/4AKC5WPC+Hzaw0zI/5H/C83zI/mIUo4ACnAXLABgXI"))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +1,2 @@
0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast 0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.03: Bangle 2 support

View File

@ -1,12 +1,24 @@
Bangle.setLCDMode("doublebuffered"); var BTNL, BTNR, BTNU, BTNA;
if (process.env.HWVERSION==2) {
var tap = {};
// use tapping on screen for left,right,accel
Bangle.on('drag',e=>tap=e);
BTNL = { read : _=>tap.b && tap.x < 58};
BTNR = { read : _=>tap.b && tap.x > 117};
BTNU = { read : _=>tap.b && tap.x > 58 && tap.x < 117};
// use button for fire
BTNA = BTN1;
} else {
// use hard buttons
BTNL = BTN4;
BTNR = BTN5;
BTNU = BTN1;
BTNA = BTN2;
Bangle.setLCDMode("doublebuffered");
}
var W = g.getWidth(); var W = g.getWidth();
var H = g.getHeight(); var H = g.getHeight();
g.setFontAlign(0,-1); g.clear().setFontAlign(0,-1);
var BTNL = BTN4;
var BTNR = BTN5;
var BTNU = BTN1;
var BTNA = BTN2;
function newAst(x,y) { function newAst(x,y) {
var a = { var a = {
@ -92,8 +104,7 @@ function onFrame() {
} }
g.clear(); g.clear();
g.drawString(score,W-20,0);
g.drawString(score,120,0);
var rs = Math.PI*0.8; var rs = Math.PI*0.8;
g.drawPoly([ g.drawPoly([
ship.x+Math.cos(ship.r)*4, ship.y+Math.sin(ship.r)*4, ship.x+Math.cos(ship.r)*4, ship.y+Math.sin(ship.r)*4,

View File

@ -2,3 +2,5 @@
0.02: Fixes; widget support 0.02: Fixes; widget support
0.03: Remove hardcoded hour buzz (you can install widchime if you miss it) 0.03: Remove hardcoded hour buzz (you can install widchime if you miss it)
0.04: Update to use Bangle.setUI instead of setWatch 0.04: Update to use Bangle.setUI instead of setWatch
0.05: Avoid 'loadWidgets' at LCD on, which will cause memory leak
Avoid clearTimeout() usage, as it may break other widgets

View File

@ -5,6 +5,7 @@
const storage = require("Storage"); const storage = require("Storage");
const filename = 'beebjson'; const filename = 'beebjson';
var timeout;
require('FontTeletext10x18Ascii').add(Graphics); require('FontTeletext10x18Ascii').add(Graphics);
@ -153,15 +154,21 @@ let hours, minutes, seconds, date;
// Schedule event for calling at the start of the next second // Schedule event for calling at the start of the next second
const inOneSecond = (cb) => { const inOneSecond = (cb) => {
let now = new Date(); let now = new Date();
clearTimeout(); if (timeout) clearTimeout(timeout);
setTimeout(cb, 1000 - now.getMilliseconds()); timeout = setTimeout(function() {
timeout = undefined;
cb();
}, 1000 - now.getMilliseconds());
}; };
// Schedule event for calling at the start of the next minute // Schedule event for calling at the start of the next minute
const inOneMinute = (cb) => { const inOneMinute = (cb) => {
let now = new Date(); let now = new Date();
clearTimeout(); if (timeout) clearTimeout(timeout);
setTimeout(cb, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds())); timeout = setTimeout(function() {
timeout = undefined;
cb();
}, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds()));
}; };
// Draw a fat hour/minute hand // Draw a fat hour/minute hand
@ -356,6 +363,7 @@ const changeSeconds = () => {
}; };
Bangle.loadWidgets(); Bangle.loadWidgets();
// widgets are drawn in drawAll()
// Restore mode // Restore mode
try { try {
@ -373,10 +381,9 @@ drawAll();
Bangle.on('lcdPower', (on) => { Bangle.on('lcdPower', (on) => {
if (on) { if (on) {
Bangle.loadWidgets();
Bangle.drawWidgets();
drawAll(); drawAll();
} else { } else {
clearTimeout(); if (timeout) clearTimeout(timeout);
timeout = undefined;
} }
}); });

View File

@ -27,3 +27,5 @@
0.26: Remove buzz in setUI polyfill (#750) 0.26: Remove buzz in setUI polyfill (#750)
0.27: Update polyfill for most recent changes 0.27: Update polyfill for most recent changes
0.28: Fix double clock load after settings are changed 0.28: Fix double clock load after settings are changed
0.29: Update boot0 to avoid code block (faster execution)
Fix issues where 'Uncaught Error: Function not found' could happen with multiple .boot.js

View File

@ -6,7 +6,7 @@ var s = require('Storage').readJSON('setting.json',1)||{};
var isB2 = process.env.HWVERSION; // Is Bangle.js 2 var isB2 = process.env.HWVERSION; // Is Bangle.js 2
var boot = ""; var boot = "";
var CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/)); 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 += `if (E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))!=${CRC}) { eval(require('Storage').read('bootupdate.js')); throw "Storage Updated!"}\n`;
boot += `E.setFlags({pretokenise:1});\n`; boot += `E.setFlags({pretokenise:1});\n`;
if (s.ble!==false) { if (s.ble!==false) {
if (s.HID) { // Human interface device if (s.HID) { // Human interface device
@ -133,9 +133,11 @@ else if (mode=="updown") {
} }
// Append *.boot.js files // Append *.boot.js files
require('Storage').list(/\.boot\.js/).forEach(bootFile=>{ require('Storage').list(/\.boot\.js/).forEach(bootFile=>{
boot += "//"+bootFile+"\n"+require('Storage').read(bootFile)+"\n"; // we add a semicolon so if the file is wrapped in (function(){ ... }()
// with no semicolon we don't end up with (function(){ ... }()(function(){ ... }()
// which would cause an error!
boot += require('Storage').read(bootFile)+";\n";
}); });
boot += "}\n";// initial 'if'
require('Storage').write('.boot0',boot); require('Storage').write('.boot0',boot);
delete boot; delete boot;
E.showMessage("Reloading..."); E.showMessage("Reloading...");

View File

@ -1,3 +1,4 @@
0.02: Tweaks to make flappy bird run with less RAM available 0.02: Tweaks to make flappy bird run with less RAM available
0.03: A few tweaks to improve rendering speed 0.03: A few tweaks to improve rendering speed
0.04: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast 0.04: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.05: Don't use Bangle.setLCDMode, just use offscreen buffer (allows widgets)

View File

@ -1,3 +1,21 @@
b = Graphics.createArrayBuffer(120,120,8);
var gimg = {
width:120,
height:104,
bpp:8,
buffer:b.buffer
};
if (process.env.HWVERSION==2) {
b.flip = function() {
g.drawImage(gimg,28,50);
};
} else {
b.flip = function() {
g.drawImage(gimg,0,24,{scale:2});
};
}
var BIRDIMG = E.toArrayBuffer(atob("EQyI/v7+/v7+/gAAAAAAAP7+/v7+/v7+/gYG0tLS0gDXAP7+/v7+/v4A0tLS0tIA19fXAP7+/v4AAAAA0tLS0gDX1wDXAP7+ANfX19cA0tLSANfXANcA/v4A19fX19cA0tLSANfX1wD+/gDS19fX0gDS0tLSAAAAAAD+/gDS0tIA0tLS0gDAwMDAwAD+/gAAAM3Nzc0AwAAAAAAA/v7+/v4Azc3Nzc0AwMDAwAD+/v7+/v4AAM3Nzc0AAAAAAP7+/v7+/v7+AAAAAP7+/v7+/g==")) var BIRDIMG = E.toArrayBuffer(atob("EQyI/v7+/v7+/gAAAAAAAP7+/v7+/v7+/gYG0tLS0gDXAP7+/v7+/v4A0tLS0tIA19fXAP7+/v4AAAAA0tLS0gDX1wDXAP7+ANfX19cA0tLSANfXANcA/v4A19fX19cA0tLSANfX1wD+/gDS19fX0gDS0tLSAAAAAAD+/gDS0tIA0tLS0gDAwMDAwAD+/gAAAM3Nzc0AwAAAAAAA/v7+/v4Azc3Nzc0AwMDAwAD+/v7+/v4AAM3Nzc0AAAAAAP7+/v7+/v7+AAAAAP7+/v7+/g=="))
var FLOORIMG = require("heatshrink").decompress(atob("iEKxH+kklABuLAAlgAAwNFB34OLmAAO0YAO5wAOA")); var FLOORIMG = require("heatshrink").decompress(atob("iEKxH+kklABuLAAlgAAwNFB34OLmAAO0YAO5wAOA"));
@ -33,26 +51,26 @@ function gameStop() {
function draw() { function draw() {
"ram" "ram"
var H = g.getHeight()-24; var H = b.getHeight()-24;
g.setColor("#71c6cf"); b.setColor("#71c6cf");
g.fillRect(0,0,g.getWidth(),H-1); b.fillRect(0,0,b.getWidth(),H-1);
floorpos++; floorpos++;
for (var x=-(floorpos&15);x<g.getWidth();x+=16) for (var x=-(floorpos&15);x<b.getWidth();x+=16)
g.drawImage(FLOORIMG,x,H); b.drawImage(FLOORIMG,x,H);
if (!running) { if (!running) {
var x = g.getWidth()/2; var x = b.getWidth()/2;
g.setColor("#000000"); b.setColor("#000000");
g.setFontAlign(0,0); b.setFontAlign(0,0);
g.setFont("4x6",2); b.setFont("4x6",2);
g.drawString("GAME OVER!",x,20); b.drawString("GAME OVER!",x,20);
g.setFont("6x8",1); b.setFont("6x8",1);
g.drawString("Score",x,40); b.drawString("Score",x,40);
g.drawString(score,x,56); b.drawString(score,x,56);
g.drawString("Tap screen to",x,76); b.drawString("Tap screen to",x,76);
g.drawString("restart and flap",x,84); b.drawString("restart and flap",x,84);
g.flip(); b.flip();
return; return;
} }
@ -63,30 +81,30 @@ function draw() {
if (birdy > H) if (birdy > H)
gameStop(); gameStop();
// draw bird // draw bird
g.drawImage(BIRDIMG, 6,birdy, {rotate:Math.atan2(birdvy,15)}); b.drawImage(BIRDIMG, 6,birdy, {rotate:Math.atan2(birdvy,15)});
// draw barriers // draw barriers
barriers.forEach(function(b) { barriers.forEach(function(r) {
b.x1--; r.x1--;
b.x2--; r.x2--;
var btop = b.y-b.gap; var btop = r.y-r.gap;
var bbot = b.y+b.gap; var bbot = r.y+r.gap;
g.setColor("#73bf2f"); // middle b.setColor("#73bf2f"); // middle
g.fillRect(b.x1+4, 0, b.x2-4, btop-1); b.fillRect(r.x1+4, 0, r.x2-4, btop-1);
g.fillRect(b.x1+4, bbot, b.x2-4, H-1); b.fillRect(r.x1+4, bbot, r.x2-4, H-1);
g.setColor("#c0f181"); // left b.setColor("#c0f181"); // left
g.fillRect(b.x1+1, 0, b.x1+3, btop-1); b.fillRect(r.x1+1, 0, r.x1+3, btop-1);
g.fillRect(b.x1+1, bbot, b.x1+3, H-1); b.fillRect(r.x1+1, bbot, r.x1+3, H-1);
g.setColor("#538917"); // right b.setColor("#538917"); // right
g.fillRect(b.x2-3, 0, b.x2-1, btop-1); b.fillRect(r.x2-3, 0, r.x2-1, btop-1);
g.fillRect(b.x2-3, bbot, b.x2-1, H-1); b.fillRect(r.x2-3, bbot, r.x2-1, H-1);
g.setColor("#808080"); // outlines b.setColor("#808080"); // outlines
g.drawRect(b.x1, btop-5, b.x2, btop); // top b.drawRect(r.x1, btop-5, r.x2, btop); // top
g.drawLine(b.x1+1, 0, b.x1+1, btop-6); b.drawLine(r.x1+1, 0, r.x1+1, btop-6);
g.drawLine(b.x2-2, 0, b.x2-2, btop-6); b.drawLine(r.x2-2, 0, r.x2-2, btop-6);
g.drawRect(b.x1, bbot, b.x2, bbot+5); // bottom b.drawRect(r.x1, bbot, r.x2, bbot+5); // bottom
g.drawLine(b.x1+1, bbot+6, b.x1+1, H-1); b.drawLine(r.x1+1, bbot+6, r.x1+1, H-1);
g.drawLine(b.x2-1, bbot+6, b.x2-1, H-1); b.drawLine(r.x2-1, bbot+6, r.x2-1, H-1);
if (b.x1<6 && (birdy-3<btop || birdy+3>bbot)) if (r.x1<6 && (birdy-3<btop || birdy+3>bbot))
gameStop(); gameStop();
}); });
while (barriers.length && barriers[0].x2<=0) { while (barriers.length && barriers[0].x2<=0) {
@ -94,7 +112,7 @@ function draw() {
newBarrier(g.getWidth()); newBarrier(g.getWidth());
} }
g.flip(); b.flip();
} }
Bangle.on('touch', function(button) { Bangle.on('touch', function(button) {
@ -105,11 +123,9 @@ Bangle.on('touch', function(button) {
} }
}); });
// Finally, start everything going Bangle.loadWidgets();
setTimeout(()=>{ g.clear();
Bangle.setLCDMode("120x120"); Bangle.drawWidgets();
g.setBgColor("#e3db9d"); b.setBgColor("#e3db9d");
g.clear(); gameStart();
gameStart(); setInterval(draw, 100);
setInterval(draw, 100);
},10);

View File

@ -10,3 +10,4 @@
0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo 0.10: Converted Stepo to use direct screen writes, added a Trip Counter feature to stepo
0.11: Detect when waypoints.json is not present, error E-WPT 0.11: Detect when waypoints.json is not present, error E-WPT
0.12: Added stepo2 as a replacement for stepo and digi 0.12: Added stepo2 as a replacement for stepo and digi
0.13: Added long press BTN2 toggle gpsrec status in GPS clock

View File

@ -85,14 +85,19 @@
function onButtonLong(btn) { function onButtonLong(btn) {
log_debug("markWaypoint()"); log_debug("markWaypoint()");
if (btn !== 1) return; if (btn === 1) {
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return; if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;
log_debug("markWaypoint()"); log_debug("markWaypoint()");
gpsObject.markWaypoint(); gpsObject.markWaypoint();
resetPrevious(); resetPrevious();
getWaypoint(); getWaypoint();
drawGPSData(); drawGPSData();
return;
}
if (btn === 2)
Bangle.showLauncher();
} }
function getWaypoint() { function getWaypoint() {

View File

@ -55,7 +55,10 @@
if (btn === 1) cycleInfoMode(); if (btn === 1) cycleInfoMode();
} }
function onButtonLong(btn) {} function onButtonLong(btn) {
if (btn === 2) Bangle.showLauncher();
}
function getGPSfix() { return undefined; } function getGPSfix() { return undefined; }
function setGPSfix(f) {} function setGPSfix(f) {}

View File

@ -45,7 +45,15 @@
} }
function onButtonLong(btn) { function onButtonLong(btn) {
if (btn === 1) toggleGPSPower(); switch(btn) {
case 1:
toggleGPSPower();
return;
case 2:
if (gpsObject.getState() === gpsObject.GPS_RUNNING)
gpsObject.toggleGpsLogging();
return;
}
} }
function draw(){ function draw(){
@ -143,7 +151,8 @@
g.drawString(activityStr, 120, Y_ACTIVITY); g.drawString(activityStr, 120, Y_ACTIVITY);
g.setFont("6x8",2); g.setFont("6x8",2);
g.setColor(1,1,1); g.setColor(1,1,1);
g.drawString(age, 120, Y_ACTIVITY + 46); var age_and_logging = age + " logging " + gpsObject.loggingStatus();
g.drawString(age_and_logging, 120, Y_ACTIVITY + 46);
} }
} }

View File

@ -71,7 +71,8 @@ function buttonReleased(btn) {
face.onButtonLong(btn); face.onButtonLong(btn);
break; break;
case 2: case 2:
Bangle.showLauncher(); face.onButtonLong(btn);
//Bangle.showLauncher();
break; break;
case 3: case 3:
// do nothing // do nothing
@ -94,8 +95,8 @@ function setButtons(){
} }
Bangle.on('kill',()=>{ Bangle.on('kill',()=>{
Bangle.setCompassPower(0); Bangle.setCompassPower(0,'kitchen');
Bangle.setGPSPower(0); Bangle.setGPSPower(0,'kitchen');
}); });
Bangle.on('lcdPower',function(on) { Bangle.on('lcdPower',function(on) {
@ -214,7 +215,7 @@ GPS.prototype.toggleGPSPower = function() {
this.log_debug("toggleGPSPower()"); this.log_debug("toggleGPSPower()");
this.gpsPowerState = Bangle.isGPSOn(); this.gpsPowerState = Bangle.isGPSOn();
this.gpsPowerState = !this.gpsPowerState; this.gpsPowerState = !this.gpsPowerState;
Bangle.setGPSPower(this.gpsPowerState ? 1 : 0); Bangle.setGPSPower((this.gpsPowerState ? 1 : 0), 'kitchen');
this.resetLastFix(); this.resetLastFix();
this.determineGPSState(); this.determineGPSState();
@ -369,6 +370,26 @@ GPS.prototype.nextWaypoint = function(inc) {
return this.wp_current; return this.wp_current;
} }
GPS.prototype.toggleGpsLogging = function() {
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
if (settings == {}) return false;
settings.recording = !settings.recording;
require("Storage").write("gpsrec.json", settings);
if (WIDGETS["gpsrec"])
WIDGETS["gpsrec"].reload();
return true;
}
GPS.prototype.loggingStatus = function() {
var settings = require("Storage").readJSON("gpsrec.json",1)||{};
if (settings == {}) return "E-LOG";
if (settings.recording) return "ON";
return "OFF";
}
var gpsObj = new GPS(); var gpsObj = new GPS();

View File

@ -19,9 +19,14 @@
} }
function onButtonLong(btn) { function onButtonLong(btn) {
trip.resetTrip(getSteps()); if (btn === 1) {
trip.setTripState(true); trip.resetTrip(getSteps());
drawStepText(); trip.setTripState(true);
drawStepText();
return;
}
if (btn === 2) Bangle.showLauncher();
} }
function radians(a) { function radians(a) {

View File

@ -62,10 +62,15 @@
} }
function onButtonLong(btn) { function onButtonLong(btn) {
trip.resetTrip(getSteps()); if (btn === 1) {
infoMode = INFO_TRIP; trip.resetTrip(getSteps());
forceRedraw(); infoMode = INFO_TRIP;
draw(); forceRedraw();
draw();
return;
}
if (btn === 2) Bangle.showLauncher();
} }
function radians(a) { function radians(a) {
@ -207,7 +212,8 @@
var midrot = -180 - (360 * percent); var midrot = -180 - (360 * percent);
var endrot = -360 - 180; var endrot = -360 - 180;
g.setColor(0x07FF); // light cyan //g.setColor(0x07FF); // light cyan
g.setColor(0xFFC0); // yellow
// draw guauge // draw guauge
for (i = startrot; i > midrot; i -= 3) { for (i = startrot; i > midrot; i -= 3) {
@ -218,8 +224,8 @@
// change the remaining color to RED if battery is below 25% // change the remaining color to RED if battery is below 25%
if (E.getBattery() > 25) { if (E.getBattery() > 25) {
//g.setColor(0x7BEF); // grey g.setColor(0x7BEF); // grey
g.setColor(0x000D); // dark navy //g.setColor(0x000D); // dark navy
} else { } else {
g.setColor(0xF800); // red g.setColor(0xF800); // red
} }

View File

@ -31,7 +31,9 @@
} }
} }
function onButtonLong(btn) {} function onButtonLong(btn) {
if (btn === 2) Bangle.showLauncher();
}
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer, return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
onButtonShort:onButtonShort, onButtonLong:onButtonLong}; onButtonShort:onButtonShort, onButtonLong:onButtonLong};

View File

@ -1 +1,2 @@
0.01: New App! 0.01: New App!
0.02: Fix occasional missed image when scrolling up

View File

@ -47,8 +47,13 @@ Bangle.on('drag',e=>{
g.reset().setClipRect(0,24,g.getWidth()-1,g.getHeight()-1); g.reset().setClipRect(0,24,g.getWidth()-1,g.getHeight()-1);
g.scroll(0,dy); g.scroll(0,dy);
menuScroll -= dy; menuScroll -= dy;
if (e.dy < 0) drawApp(Math.floor((menuScroll+24)/APPH)+n-1); if (e.dy < 0) {
else drawApp(Math.floor((menuScroll+24)/APPH)); drawApp(Math.floor((menuScroll+24+g.getHeight())/APPH)-1);
if (e.dy <= -APPH) drawApp(Math.floor((menuScroll+24+g.getHeight())/APPH)-2);
} else {
drawApp(Math.floor((menuScroll+24)/APPH));
if (e.dy >= APPH) drawApp(Math.floor((menuScroll+24)/APPH)+1);
}
g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1); g.setClipRect(0,0,g.getWidth()-1,g.getHeight()-1);
}); });
Bangle.on("touch",(_,e)=>{ Bangle.on("touch",(_,e)=>{

View File

@ -32,7 +32,7 @@
<div id="map"> <div id="map">
</div> </div>
<div id="controls"> <div id="controls">
<div style="display:inline-block;text-align:center;vertical-align: top;"> <input type="checkbox" id="3bit"></input><br/><span>3 bit</span></div> <div style="display:inline-block;text-align:center;vertical-align: top;" id="3bitdiv"> <input type="checkbox" id="3bit"></input><br/><span>3 bit</span></div>
<button id="getmap" class="btn btn-primary">Get Map</button><br/> <button id="getmap" class="btn btn-primary">Get Map</button><br/>
<canvas id="maptiles" style="display:none"></canvas> <canvas id="maptiles" style="display:none"></canvas>
<div id="uploadbuttons" style="display:none"><button id="upload" class="btn btn-primary">Upload</button> <div id="uploadbuttons" style="display:none"><button id="upload" class="btn btn-primary">Upload</button>
@ -72,6 +72,16 @@ TODO:
}); });
// Could optionally overlay trails: https://wiki.openstreetmap.org/wiki/Tiles // Could optionally overlay trails: https://wiki.openstreetmap.org/wiki/Tiles
function onInit(device) {
if (device && device.info && device.info.g) {
// On 3 bit devices, don't even offer the option. 3 bit is the only way
if (device.info.g.bpp==3) {
document.getElementById("3bit").checked = true;
document.getElementById("3bitdiv").style = "display:none";
}
}
}
var mapFiles = []; var mapFiles = [];
tileLayer.addTo(map); tileLayer.addTo(map);

View File

@ -34,6 +34,23 @@
<script src="../../core/lib/imageconverter.js"></script> <script src="../../core/lib/imageconverter.js"></script>
<script> <script>
var targetWidth = 200;
var targetHeight = 200;
function onInit(device) {
if (device && device.info && device.info.g) {
targetWidth = device.info.g.width - 20;
targetHeight = device.info.g.height - 20;
}
qrcode = new QRCode("qrcode", {
text: document.getElementById("url").value,
width: targetWidth,
height: targetHeight,
colorDark : "#000000",
colorLight : "#ffffff",
});
}
//https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168 //https://github.com/evgeni/qifi/blob/gh-pages/index.html#L168
function escapeString (string) { function escapeString (string) {
var to_escape = ['\\', ';', ',', ':', '"']; var to_escape = ['\\', ';', ',', ':', '"'];
@ -70,13 +87,7 @@
qrcode.makeCode(document.getElementById("url").value); qrcode.makeCode(document.getElementById("url").value);
} }
} }
var qrcode = new QRCode("qrcode", { var qrcode;
text: document.getElementById("url").value,
width: 200,
height: 200,
colorDark : "#000000",
colorLight : "#ffffff",
});
document.getElementById("url").addEventListener("change", refreshQRCode); document.getElementById("url").addEventListener("change", refreshQRCode);
document.getElementById("ssid").addEventListener("change",refreshQRCode); document.getElementById("ssid").addEventListener("change",refreshQRCode);
@ -93,13 +104,11 @@
var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true}); var img = imageconverter.canvastoString(document.getElementsByTagName("canvas")[0],{mode:"1bit",output:"string",compression:true});
var app = `var img = ${img}; var app = `var img = ${img};
var content = ${JSON.stringify(content)}; var content = ${JSON.stringify(content)};
g.setColor(1,1,1); g.clear(1).setColor(1,1,1).setBgColor(0,0,0);
g.fillRect(0,0,239,239); g.fillRect(0,0,g.getWidth()-1,g.getHeight()-1);
g.drawImage(img,20,20); g.drawImage(img,(g.getWidth()-img[0])/2,(g.getHeight()-img[1])/2);
g.setFontAlign(0,0); g.setFontAlign(0,0).setFont("6x8").setColor(0,0,0);
g.setFont("6x8"); g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
g.setColor(0,0,0);
g.drawString(content,120,230);
g.setColor(1,1,1); g.setColor(1,1,1);
`; `;
sendCustomizedApp({ sendCustomizedApp({

View File

@ -1,2 +1,3 @@
0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast 0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.03: Enabled BTN2 and BTN3, added highscore (score is saved to storage and can be reset in app settings menu) 0.03: Enabled BTN2 and BTN3, added highscore (score is saved to storage and can be reset in app settings menu)
0.04: Bangle.js 2 support

View File

@ -8,20 +8,42 @@ function saveHighScore(score) {
f.write(score + "\n"); f.write(score + "\n");
} }
greal = g;
g.clear(); g.clear();
g = Graphics.createArrayBuffer(120,64,1,{msb:true});
g.flip = function() { var BTNL, BTNR, BTNU;
greal.drawImage({ if (process.env.HWVERSION==2) {
width:120, var tap = {};
height:64, // use tapping on screen for left and right
buffer:g.buffer Bangle.on('drag',e=>tap=e);
},0,(240-128)/2,{scale:2}); BTNL = { read : _=>tap.b && tap.x < 88};
}; BTNR = { read : _=>tap.b && tap.x > 88};
// use button for jump
BTNU = BTN1;
greal = g;
g = Graphics.createArrayBuffer(88,64,1,{msb:true});
g.flip = function() {
greal.drawImage({
width:88,
height:64,
buffer:g.buffer
},0,(176-128)/2,{scale:2});
};
} else {
// use hard buttons
BTNL = BTN2;
BTNR = BTN3;
BTNU = BTN1;
greal = g;
g = Graphics.createArrayBuffer(120,64,1,{msb:true});
g.flip = function() {
greal.drawImage({
width:120,
height:64,
buffer:g.buffer
},0,(240-128)/2,{scale:2});
};
}
var W = g.getWidth(); var W = g.getWidth();
var BTNL = BTN2;
var BTNR = BTN3;
var BTNU = BTN1;
// Images can be added like this in Espruino v2.00 // Images can be added like this in Espruino v2.00
var IMG = { var IMG = {

View File

@ -11,3 +11,6 @@
0.12: Respect Quiet Mode 0.12: Respect Quiet Mode
0.13: Now use system color theme 0.13: Now use system color theme
0.14: Improve memory usage 0.14: Improve memory usage
0.15: Settings option to hide the widget icon
0.16: Settings option to show large digits in widget area
0.17: Cope with 2v10+ firmware sometimes reporting >1 step

View File

@ -5,6 +5,8 @@
let s = { let s = {
'goal': 10000, 'goal': 10000,
'progress': false, 'progress': false,
'large': false,
'hide': false
} }
// ...and overwrite them with any saved values // ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings // This way saved values are preserved if a new version adds more settings
@ -41,6 +43,22 @@
save() save()
}, },
}, },
'Large Digits': {
value: s.large,
format: () => (s.large ? 'Yes' : 'No'),
onchange: () => {
s.large = !s.large
save()
},
},
'Hide Widget': {
value: s.hide,
format: () => (s.hide ? 'Yes' : 'No'),
onchange: () => {
s.hide = !s.hide
save()
},
},
'< Back': back, '< Back': back,
}) })
}) })

View File

@ -1,6 +1,9 @@
(() => { (() => {
const PEDOMFILE = "wpedom.json" const PEDOMFILE = "wpedom.json"
// Last time Bangle.on('step' was called
let lastUpdate = new Date(); let lastUpdate = new Date();
// Last step count when Bangle.on('step' was called
var lastStepCount;
let stp_today = 0; let stp_today = 0;
let settings; let settings;
@ -14,11 +17,14 @@
const DEFAULTS = { const DEFAULTS = {
'goal': 10000, 'goal': 10000,
'progress': false, 'progress': false,
'large': false,
'hide': false
} }
return (key in settings) ? settings[key] : DEFAULTS[key]; return (key in settings) ? settings[key] : DEFAULTS[key];
} }
function drawProgress(stps) { function drawProgress(stps) {
if (setting('hide')) return;
const width = 24, half = width/2; const width = 24, half = width/2;
const goal = setting('goal'), left = Math.max(goal-stps,0); const goal = setting('goal'), left = Math.max(goal-stps,0);
const c = left ? "#00f" : "#090"; // blue or dark green const c = left ? "#00f" : "#090"; // blue or dark green
@ -46,13 +52,29 @@
} }
} }
// show the step count in the widget area in a readable sized font
function draw_large(st) {
var width = 12 * st.length;
g.reset();
g.clearRect(this.x, this.y, this.x + width, this.y + 16); // erase background
g.setColor(g.theme.fg);
g.setFont("6x8",2);
g.setFontAlign(-1, -1);
g.drawString(st, this.x + 4, this.y + 2);
}
// draw your widget // draw your widget
function draw() { function draw() {
if (setting('hide')) return;
var width = 24; var width = 24;
if (stp_today > 99999){ if (stp_today > 99999){
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
} }
let stps = stp_today.toString(); let stps = stp_today.toString();
if (setting('large')) {
draw_large.call(this, stps);
return;
}
g.reset().clearRect(this.x, this.y, this.x + width, this.y + 23); // erase background g.reset().clearRect(this.x, this.y, this.x + width, this.y + 23); // erase background
if (setting('progress')){ drawProgress.call(this, stps); } if (setting('progress')){ drawProgress.call(this, stps); }
g.setColor(g.theme.fg); g.setColor(g.theme.fg);
@ -73,13 +95,16 @@
draw() draw()
} }
Bangle.on('step', (up) => { Bangle.on('step', stepCount => {
var steps = stepCount-lastStepCount;
if (lastStepCount===undefined || steps<0) steps=1;
lastStepCount = stepCount;
let date = new Date(); let date = new Date();
if (lastUpdate.getDate() == date.getDate()){ if (lastUpdate.getDate() == date.getDate()){
stp_today ++; stp_today += steps;
} else { } else {
// TODO: could save this to PEDOMFILE for lastUpdate's day? // TODO: could save this to PEDOMFILE for lastUpdate's day?
stp_today = 1; stp_today = steps;
} }
if (stp_today === setting('goal') if (stp_today === setting('goal')
&& !(require('Storage').readJSON('setting.json',1)||{}).quiet) { && !(require('Storage').readJSON('setting.json',1)||{}).quiet) {

View File

@ -51,7 +51,7 @@ try{
const APP_KEYS = [ const APP_KEYS = [
'id', 'name', 'shortName', 'version', 'icon', 'description', 'tags', 'type', 'id', 'name', 'shortName', 'version', 'icon', 'description', 'tags', 'type',
'sortorder', 'readme', 'custom', 'interface', 'storage', 'data', 'allow_emulator', 'sortorder', 'readme', 'custom', 'customConnect', 'interface', 'storage', 'data', 'allow_emulator',
'dependencies' 'dependencies'
]; ];
const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite']; const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite'];
@ -100,6 +100,7 @@ apps.forEach((app,appIdx) => {
if (!fs.existsSync(appDir+app.icon)) ERROR(`App ${app.id} icon doesn't exist`); if (!fs.existsSync(appDir+app.icon)) ERROR(`App ${app.id} icon doesn't exist`);
if (app.readme && !fs.existsSync(appDir+app.readme)) ERROR(`App ${app.id} README file doesn't exist`); if (app.readme && !fs.existsSync(appDir+app.readme)) ERROR(`App ${app.id} README file doesn't exist`);
if (app.custom && !fs.existsSync(appDir+app.custom)) ERROR(`App ${app.id} custom HTML doesn't exist`); if (app.custom && !fs.existsSync(appDir+app.custom)) ERROR(`App ${app.id} custom HTML doesn't exist`);
if (app.customConnect && !app.custom) ERROR(`App ${app.id} has customConnect but no customn HTML`);
if (app.interface && !fs.existsSync(appDir+app.interface)) ERROR(`App ${app.id} interface HTML doesn't exist`); if (app.interface && !fs.existsSync(appDir+app.interface)) ERROR(`App ${app.id} interface HTML doesn't exist`);
if (app.dependencies) { if (app.dependencies) {
if (("object"==typeof app.dependencies) && !Array.isArray(app.dependencies)) { if (("object"==typeof app.dependencies) && !Array.isArray(app.dependencies)) {

2
core

@ -1 +1 @@
Subproject commit 27f9a7125146a38c4357d679ec783f6e98a983c6 Subproject commit 39f89acf1468dc7d58ac54d15a092b2bd6d73cce

View File

@ -3,14 +3,32 @@
Usage: Usage:
```
var Layout = require("Layout");
var layout = new Layout( layoutObject, btns ) var layout = new Layout( layoutObject, btns )
layout.render(optionalObject); layout.render(optionalObject);
```
For example:
```
var Layout = require("Layout");
var layout = new Layout( {
type:"v", c: [
{type:"txt", font:"20%", label:"12:00" },
{type:"txt", font:"6x8", label:"The Date" }
]
});
g.clear();
layout.render();
```
layoutObject has: layoutObject has:
* A `type` field of: * A `type` field of:
* `undefined` - blank, can be used for padding * `undefined` - blank, can be used for padding
* `"txt"` - a text label, with value `label` and `r` for text rotation * `"txt"` - a text label, with value `label` and `r` for text rotation. 'font' is required
* `"btn"` - a button, with value `label` and callback `cb` * `"btn"` - a button, with value `label` and callback `cb`
* `"img"` - an image where the function `src` is called to return an image to draw * `"img"` - an image where the function `src` is called to return an image to draw
* `"custom"` - a custom block where `render(layoutObj)` is called to render * `"custom"` - a custom block where `render(layoutObj)` is called to render
@ -83,10 +101,12 @@ function Layout(layout, buttons) {
this._l, this._l,
{type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))} {type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))}
]}; ]};
Bangle.touchHandler = (_,e) => touchHandler(this._l,e);
Bangle.on('touch',Bangle.touchHandler);
} }
} }
if (process.env.HWVERSION==2) {
Bangle.touchHandler = (_,e) => touchHandler(layout,e);
Bangle.on('touch',Bangle.touchHandler);
}
// add IDs // add IDs
var ll = this; var ll = this;
@ -130,7 +150,7 @@ function updateMin(l) {
case "txt": { case "txt": {
if (l.font.endsWith("%")) if (l.font.endsWith("%"))
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100); l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
// Not needed in new firmwares - 'font' is enough // FIXME ':'/fsz not needed in new firmwares - it's handled internally
if (l.font.includes(":")) { if (l.font.includes(":")) {
var f = l.font.split(":"); var f = l.font.split(":");
l.font = f[0]; l.font = f[0];
@ -222,8 +242,8 @@ Layout.prototype.render = function (l) {
Layout.prototype.layout = function (l) { Layout.prototype.layout = function (l) {
// l = current layout element // l = current layout element
// exw,exh = extra width/height available // exw,exh = extra width/height available
var fillx = l.c.reduce((a,l)=>a+(0|l.fillx),0); var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
var filly = l.c.reduce((a,l)=>a+(0|l.filly),0); var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
switch (l.type) { switch (l.type) {
case "h": { case "h": {
let x = l.x + (l.w-l._w)/2; let x = l.x + (l.w-l._w)/2;

32
modules/Layout.min.js vendored
View File

@ -3,14 +3,32 @@
Usage: Usage:
```
var Layout = require("Layout");
var layout = new Layout( layoutObject, btns ) var layout = new Layout( layoutObject, btns )
layout.render(optionalObject); layout.render(optionalObject);
```
For example:
```
var Layout = require("Layout");
var layout = new Layout( {
type:"v", c: [
{type:"txt", font:"20%", label:"12:00" },
{type:"txt", font:"6x8", label:"The Date" }
]
});
g.clear();
layout.render();
```
layoutObject has: layoutObject has:
* A `type` field of: * A `type` field of:
* `undefined` - blank, can be used for padding * `undefined` - blank, can be used for padding
* `"txt"` - a text label, with value `label` and `r` for text rotation * `"txt"` - a text label, with value `label` and `r` for text rotation. 'font' is required
* `"btn"` - a button, with value `label` and callback `cb` * `"btn"` - a button, with value `label` and callback `cb`
* `"img"` - an image where the function `src` is called to return an image to draw * `"img"` - an image where the function `src` is called to return an image to draw
* `"custom"` - a custom block where `render(layoutObj)` is called to render * `"custom"` - a custom block where `render(layoutObj)` is called to render
@ -83,10 +101,12 @@ function Layout(layout, buttons) {
this._l, this._l,
{type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))} {type:"v", c: buttons.map(b=>(b.type="btn",b.h=btnHeight,b.w=32,b.r=1,b))}
]}; ]};
Bangle.touchHandler = (_,e) => touchHandler(this._l,e);
Bangle.on('touch',Bangle.touchHandler);
} }
} }
if (process.env.HWVERSION==2) {
Bangle.touchHandler = (_,e) => touchHandler(layout,e);
Bangle.on('touch',Bangle.touchHandler);
}
// add IDs // add IDs
var ll = this; var ll = this;
@ -130,7 +150,7 @@ function updateMin(l) {
case "txt": { case "txt": {
if (l.font.endsWith("%")) if (l.font.endsWith("%"))
l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100); l.font = "Vector"+Math.round(g.getHeight()*l.font.slice(0,-1)/100);
// Not needed in new firmwares - 'font' is enough // FIXME ':'/fsz not needed in new firmwares - it's handled internally
if (l.font.includes(":")) { if (l.font.includes(":")) {
var f = l.font.split(":"); var f = l.font.split(":");
l.font = f[0]; l.font = f[0];
@ -222,8 +242,8 @@ Layout.prototype.render = function (l) {
Layout.prototype.layout = function (l) { Layout.prototype.layout = function (l) {
// l = current layout element // l = current layout element
// exw,exh = extra width/height available // exw,exh = extra width/height available
var fillx = l.c.reduce((a,l)=>a+(0|l.fillx),0); var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
var filly = l.c.reduce((a,l)=>a+(0|l.filly),0); var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
switch (l.type) { switch (l.type) {
case "h": { case "h": {
let x = l.x + (l.w-l._w)/2; let x = l.x + (l.w-l._w)/2;