forked from FOSS/BangleApps
Merge branch 'espruino:master' into master
commit
c2286a91d8
|
@ -237,6 +237,11 @@ and which gives information about the app for the Launcher.
|
|||
// like this one with 'storage','name' and 'id' set up
|
||||
// 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
|
||||
// iframe, and it may interact with the connected Bangle
|
||||
// to retrieve information from it
|
||||
|
|
30
apps.json
30
apps.json
|
@ -4,7 +4,7 @@
|
|||
"tags": "tool,system,b2",
|
||||
"type":"bootloader",
|
||||
"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",
|
||||
"storage": [
|
||||
{"name":".boot0","url":"boot0.js"},
|
||||
|
@ -55,7 +55,7 @@
|
|||
"name": "Launcher (Bangle.js 2)",
|
||||
"shortName":"Launcher",
|
||||
"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.",
|
||||
"tags": "tool,system,launcher,b2,bno1",
|
||||
"type":"launch",
|
||||
|
@ -402,9 +402,9 @@
|
|||
{ "id": "trex",
|
||||
"name": "T-Rex",
|
||||
"icon": "trex.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "T-Rex game in the style of Chrome's offline game",
|
||||
"tags": "game",
|
||||
"tags": "game,b2",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"trex.app.js","url":"trex.js"},
|
||||
|
@ -418,9 +418,9 @@
|
|||
{ "id": "astroid",
|
||||
"name": "Asteroids!",
|
||||
"icon": "asteroids.png",
|
||||
"version":"0.02",
|
||||
"version":"0.03",
|
||||
"description": "Retro asteroids game",
|
||||
"tags": "game",
|
||||
"tags": "game,b2",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"astroid.app.js","url":"asteroids.js"},
|
||||
|
@ -814,8 +814,8 @@
|
|||
"icon": "app.png",
|
||||
"version":"0.02",
|
||||
"description": "Use this to upload a customised QR code to Bangle.js",
|
||||
"tags": "qrcode",
|
||||
"custom": "custom.html",
|
||||
"tags": "qrcode,b2",
|
||||
"custom": "custom.html", "customConnect":true,
|
||||
"storage": [
|
||||
{"name":"qrcode.app.js"},
|
||||
{"name":"qrcode.img","url":"app-icon.js","evaluate":true}
|
||||
|
@ -1096,9 +1096,9 @@
|
|||
{ "id": "flappy",
|
||||
"name": "Flappy Bird",
|
||||
"icon": "app.png",
|
||||
"version":"0.04",
|
||||
"version":"0.05",
|
||||
"description": "A Flappy Bird game clone",
|
||||
"tags": "game",
|
||||
"tags": "game,b2",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"flappy.app.js","url":"app.js"},
|
||||
|
@ -1183,7 +1183,7 @@
|
|||
{ "id": "widpedom",
|
||||
"name": "Pedometer widget",
|
||||
"icon": "widget.png",
|
||||
"version":"0.14",
|
||||
"version":"0.17",
|
||||
"description": "Daily pedometer widget",
|
||||
"tags": "widget,b2",
|
||||
"type":"widget",
|
||||
|
@ -1520,7 +1520,7 @@
|
|||
"version":"0.08",
|
||||
"description": "[BETA] Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are",
|
||||
"tags": "outdoors,gps,b2",
|
||||
"custom": "custom.html",
|
||||
"custom": "custom.html", "customConnect":true,
|
||||
"storage": [
|
||||
{"name":"openstmap","url":"openstmap.js"},
|
||||
{"name":"openstmap.app.js","url":"app.js"},
|
||||
|
@ -2039,7 +2039,7 @@
|
|||
"id": "beebclock",
|
||||
"name": "Beeb Clock",
|
||||
"icon": "beebclock.png",
|
||||
"version":"0.04",
|
||||
"version":"0.05",
|
||||
"description": "Clock face that may be coincidentally familiar to BBC viewers",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
|
@ -2735,7 +2735,7 @@
|
|||
"name": "Heart Rate Variability monitor",
|
||||
"shortName":"HRV monitor",
|
||||
"icon": "hrv.png",
|
||||
"version":"0.03",
|
||||
"version":"0.04",
|
||||
"description": "Heart Rate Variability monitor, see Readme for more info",
|
||||
"tags": "",
|
||||
"readme": "README.md",
|
||||
|
@ -3208,7 +3208,7 @@
|
|||
{ "id": "kitchen",
|
||||
"name": "Kitchen Combo",
|
||||
"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",
|
||||
"tags": "tool,outdoors,gps",
|
||||
"type":"clock",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
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.03: Add RMSSD recording
|
||||
0.04: Modify to work with new heart rate API, but still not sure it's working correctly
|
||||
|
|
146
apps/HRV/app.js
146
apps/HRV/app.js
|
@ -17,22 +17,23 @@ var csv = [
|
|||
logfile.write(csv.join(",")+"\n");
|
||||
|
||||
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 raw_HR_array = new Float32Array(1536);
|
||||
var alternate_array = new Float32Array(3072);
|
||||
var pulse_array = [];
|
||||
var pulsecount = 0;
|
||||
var cutoff_threshold = 0.5;
|
||||
var sample_frequency = 51.6;
|
||||
var gap_threshold = 0.15;
|
||||
var hr_min = 40;
|
||||
var hr_max = 160;
|
||||
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;
|
||||
// shift elements backwards - note the 4, because a Float32 is 4 bytes
|
||||
log.set(new Float32Array(log.buffer, 4 /*bytes*/));
|
||||
|
@ -41,59 +42,58 @@ function storeMyData(data, file_type) {
|
|||
}
|
||||
|
||||
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++) {
|
||||
sum += parseFloat(samples[i]);
|
||||
}
|
||||
var avg = sum / samples.length;
|
||||
return avg;
|
||||
return avg;*/
|
||||
}
|
||||
|
||||
function StandardDeviation (array) {
|
||||
const n = array.length;
|
||||
const mean = array.reduce((a, b) => a + b) / n;
|
||||
return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).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(E.variance(array, mean));
|
||||
}
|
||||
|
||||
function turn_off() {
|
||||
Bangle.setHRMPower(0);
|
||||
|
||||
var accel = setInterval(function () {
|
||||
movement = movement + Bangle.getAccel().diff;
|
||||
}, 1000);
|
||||
|
||||
g.clear();
|
||||
g.drawString("processing 1/5", 120, 120);
|
||||
g.drawString("processing 1/5", px, py);
|
||||
|
||||
rolling_average(raw_HR_array,5);
|
||||
g.clear();
|
||||
g.drawString("processing 2/5", 120, 120);
|
||||
g.drawString("processing 2/5", px, py);
|
||||
|
||||
upscale();
|
||||
g.clear();
|
||||
g.drawString("processing 3/5", 120, 120);
|
||||
g.drawString("processing 3/5", px, py);
|
||||
|
||||
rolling_average(alternate_array,5);
|
||||
g.clear();
|
||||
g.drawString("processing 4/5", 120, 120);
|
||||
g.drawString("processing 4/5", px, py);
|
||||
|
||||
apply_cutoff();
|
||||
find_peaks();
|
||||
|
||||
g.clear();
|
||||
g.drawString("processing 5/5", 120, 120);
|
||||
g.drawString("processing 5/5", px, py);
|
||||
|
||||
calculate_HRV();
|
||||
}
|
||||
|
||||
function bernstein(A, B, C, D, E, t) {
|
||||
function bernstein(A, B, C, D, E, t) { "ram"
|
||||
s = 1 - 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));
|
||||
return x;
|
||||
}
|
||||
|
||||
function upscale() {
|
||||
function upscale() { "ram"
|
||||
var index = 0;
|
||||
for (let i = raw_HR_array.length - 1; i > 5; i -= 5) {
|
||||
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 = [];
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
temp_array = [];
|
||||
for (let x = 0; x < count; x++)
|
||||
temp_array.push(values[i + x]);
|
||||
|
||||
values[i] = average(temp_array);
|
||||
}
|
||||
}
|
||||
|
||||
function apply_cutoff() {
|
||||
function apply_cutoff() { "ram"
|
||||
var x;
|
||||
for (let i = 0; i < alternate_array.length; i++) {
|
||||
x = alternate_array[i];
|
||||
|
@ -132,7 +131,7 @@ function apply_cutoff() {
|
|||
}
|
||||
}
|
||||
|
||||
function find_peaks() {
|
||||
function find_peaks() { "ram"
|
||||
var previous;
|
||||
var previous_slope = 0;
|
||||
var slope;
|
||||
|
@ -157,7 +156,7 @@ function find_peaks() {
|
|||
}
|
||||
}
|
||||
|
||||
function RMSSD(samples){
|
||||
function RMSSD(samples){ "ram"
|
||||
var sum = 0;
|
||||
var square = 0;
|
||||
var data = [];
|
||||
|
@ -192,7 +191,7 @@ function calculate_HRV() {
|
|||
gap_average = average(temp_array);
|
||||
var calculatedHR = (sample_frequency*60)/(gap_average/2);
|
||||
if(option == 0)
|
||||
g.flip();
|
||||
Bangle.setLCDPower(1);
|
||||
g.clear();
|
||||
//var display_stdv = StandardDeviation(pulse_array).toFixed(1);
|
||||
var SDNN = (StandardDeviation(temp_array) * (1 / (sample_frequency * 2) * 1000)).toFixed(0);
|
||||
|
@ -200,14 +199,13 @@ function calculate_HRV() {
|
|||
g.drawString("SDNN:" + SDNN
|
||||
+"\nRMSSD:" + RMS_SD
|
||||
+ "\nHR:" + calculatedHR.toFixed(0)
|
||||
+"\nSample Count:" + temp_array.length, 120, 120);
|
||||
|
||||
if(option == 0){
|
||||
+"\nSample Count:" + temp_array.length, px, py);
|
||||
Bangle.setLCDPower(1);
|
||||
if(option == 0) { // single run
|
||||
Bangle.buzz(500,1);
|
||||
clearInterval(routine);
|
||||
}
|
||||
|
||||
else{
|
||||
option = null;
|
||||
drawButtons();
|
||||
} else {
|
||||
var csv = [
|
||||
0|getTime(),
|
||||
temp_array.length,
|
||||
|
@ -219,85 +217,87 @@ function calculate_HRV() {
|
|||
];
|
||||
logfile.write(csv.join(",")+"\n");
|
||||
|
||||
movement = 0;
|
||||
|
||||
// for (let i = 0; i < raw_HR_array.length; i++) {
|
||||
// raw_HR_array[i] = null;
|
||||
//}
|
||||
|
||||
turn_on();
|
||||
}
|
||||
}
|
||||
|
||||
function btn1Pressed() {
|
||||
if(option === null){
|
||||
clearInterval(accel);
|
||||
g.clear();
|
||||
g.drawString("one-off assessment", 120, 120);
|
||||
g.drawString("one-off assessment", px, py);
|
||||
option = 0;
|
||||
Bangle.setHRMPower(1);
|
||||
|
||||
turn_on();
|
||||
}
|
||||
}
|
||||
|
||||
function btn3Pressed() {
|
||||
if(option === null){
|
||||
logfile.write(""); //reset HRV log
|
||||
clearInterval(accel);
|
||||
g.clear();
|
||||
g.drawString("continuous mode", 120, 120);
|
||||
g.drawString("continuous mode", px, py);
|
||||
option = 1;
|
||||
Bangle.setHRMPower(1);
|
||||
|
||||
turn_on();
|
||||
}
|
||||
}
|
||||
|
||||
var routine = setInterval(function () {
|
||||
clearInterval(accel);
|
||||
first_signals = 0; // ignore the first several signals
|
||||
pulsecount = 0;
|
||||
function turn_on() {
|
||||
BPM_array = [];
|
||||
heartrate = [];
|
||||
pulse_array = [];
|
||||
Bangle.setHRMPower(1);
|
||||
}, 180000);
|
||||
|
||||
var accel = setInterval(function () {
|
||||
samples = 0;
|
||||
if (accel) clearInterval(accel);
|
||||
movement = 0;
|
||||
accel = setInterval(function () {
|
||||
movement = movement + Bangle.getAccel().diff;
|
||||
}, 1000);
|
||||
Bangle.setHRMPower(1);
|
||||
collectData = true;
|
||||
}
|
||||
|
||||
g.clear();
|
||||
function drawButtons() {
|
||||
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);
|
||||
g.setColor("#ffffff");
|
||||
g.setFontAlign(0, 0);
|
||||
}
|
||||
|
||||
g.clear();
|
||||
|
||||
drawButtons();
|
||||
|
||||
g.setFont("6x8", 2);
|
||||
g.setColor("#ffffff");
|
||||
g.setFontAlign(0, 0); // center font
|
||||
g.drawString("check app README", 120, 120);
|
||||
g.drawString("for more info", 120, 140);
|
||||
g.drawString("check app README\nfor more info", px, py);
|
||||
|
||||
setWatch(btn1Pressed, BTN1, {repeat:true});
|
||||
setWatch(btn3Pressed, BTN3, {repeat:true});
|
||||
|
||||
Bangle.on('HRM', function (hrm) {
|
||||
if(option == 0)
|
||||
g.flip();
|
||||
if (first_signals < 3) {
|
||||
g.clear();
|
||||
g.drawString("setting up...\nremain still " + first_signals * 20 + "%", 120, 120);
|
||||
first_signals++;
|
||||
|
||||
|
||||
Bangle.on('HRM-raw', function (e) {
|
||||
if (!collectData) return;
|
||||
storeMyData(e.raw, 0);
|
||||
if (!(samples & 7)) {
|
||||
Bangle.setLCDPower(1);
|
||||
g.clearRect(0, py-10, g.getWidth(), py+22);
|
||||
if (samples < 100)
|
||||
g.drawString("setting up...\nremain still " + samples + "%", px, py, true);
|
||||
else
|
||||
g.drawString("logging: " + (samples*100/raw_HR_array.length).toFixed(0) + "%", px, py, true);
|
||||
}
|
||||
else {
|
||||
BPM_array = hrm.raw;
|
||||
if(hrm.bpm > hr_min && hrm.bpm < hr_max)
|
||||
heartrate.push(hrm.bpm);
|
||||
if (pulsecount < 7) {
|
||||
for (let i = 0; i < 256; i++) {
|
||||
storeMyData(BPM_array[i], 0);
|
||||
}
|
||||
g.clear();
|
||||
g.drawString("logging: " + ((pulsecount/6)*100).toFixed(0) + "%", 120, 120);
|
||||
}
|
||||
if(pulsecount == 6)
|
||||
if (samples > raw_HR_array.length) {
|
||||
collectData = false;
|
||||
turn_off();
|
||||
pulsecount++;
|
||||
}
|
||||
samples++;
|
||||
});
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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 |
|
@ -1 +1,2 @@
|
|||
0.02: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
|
||||
0.03: Bangle 2 support
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
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 H = g.getHeight();
|
||||
g.setFontAlign(0,-1);
|
||||
var BTNL = BTN4;
|
||||
var BTNR = BTN5;
|
||||
var BTNU = BTN1;
|
||||
var BTNA = BTN2;
|
||||
g.clear().setFontAlign(0,-1);
|
||||
|
||||
function newAst(x,y) {
|
||||
var a = {
|
||||
|
@ -92,8 +104,7 @@ function onFrame() {
|
|||
}
|
||||
|
||||
g.clear();
|
||||
|
||||
g.drawString(score,120,0);
|
||||
g.drawString(score,W-20,0);
|
||||
var rs = Math.PI*0.8;
|
||||
g.drawPoly([
|
||||
ship.x+Math.cos(ship.r)*4, ship.y+Math.sin(ship.r)*4,
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
0.02: Fixes; widget support
|
||||
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.05: Avoid 'loadWidgets' at LCD on, which will cause memory leak
|
||||
Avoid clearTimeout() usage, as it may break other widgets
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
const storage = require("Storage");
|
||||
const filename = 'beebjson';
|
||||
var timeout;
|
||||
|
||||
require('FontTeletext10x18Ascii').add(Graphics);
|
||||
|
||||
|
@ -153,15 +154,21 @@ let hours, minutes, seconds, date;
|
|||
// Schedule event for calling at the start of the next second
|
||||
const inOneSecond = (cb) => {
|
||||
let now = new Date();
|
||||
clearTimeout();
|
||||
setTimeout(cb, 1000 - now.getMilliseconds());
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(function() {
|
||||
timeout = undefined;
|
||||
cb();
|
||||
}, 1000 - now.getMilliseconds());
|
||||
};
|
||||
|
||||
// Schedule event for calling at the start of the next minute
|
||||
const inOneMinute = (cb) => {
|
||||
let now = new Date();
|
||||
clearTimeout();
|
||||
setTimeout(cb, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds()));
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(function() {
|
||||
timeout = undefined;
|
||||
cb();
|
||||
}, 60000 - (now.getSeconds() * 1000 + now.getMilliseconds()));
|
||||
};
|
||||
|
||||
// Draw a fat hour/minute hand
|
||||
|
@ -356,6 +363,7 @@ const changeSeconds = () => {
|
|||
};
|
||||
|
||||
Bangle.loadWidgets();
|
||||
// widgets are drawn in drawAll()
|
||||
|
||||
// Restore mode
|
||||
try {
|
||||
|
@ -373,10 +381,9 @@ drawAll();
|
|||
|
||||
Bangle.on('lcdPower', (on) => {
|
||||
if (on) {
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
drawAll();
|
||||
} else {
|
||||
clearTimeout();
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -27,3 +27,5 @@
|
|||
0.26: Remove buzz in setUI polyfill (#750)
|
||||
0.27: Update polyfill for most recent changes
|
||||
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
|
||||
|
|
|
@ -6,7 +6,7 @@ var s = require('Storage').readJSON('setting.json',1)||{};
|
|||
var isB2 = process.env.HWVERSION; // Is Bangle.js 2
|
||||
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 += `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`;
|
||||
if (s.ble!==false) {
|
||||
if (s.HID) { // Human interface device
|
||||
|
@ -133,9 +133,11 @@ else if (mode=="updown") {
|
|||
}
|
||||
// Append *.boot.js files
|
||||
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);
|
||||
delete boot;
|
||||
E.showMessage("Reloading...");
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
0.02: Tweaks to make flappy bird run with less RAM available
|
||||
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.05: Don't use Bangle.setLCDMode, just use offscreen buffer (allows widgets)
|
||||
|
|
|
@ -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 FLOORIMG = require("heatshrink").decompress(atob("iEKxH+kklABuLAAlgAAwNFB34OLmAAO0YAO5wAOA"));
|
||||
|
||||
|
@ -33,26 +51,26 @@ function gameStop() {
|
|||
|
||||
function draw() {
|
||||
"ram"
|
||||
var H = g.getHeight()-24;
|
||||
g.setColor("#71c6cf");
|
||||
g.fillRect(0,0,g.getWidth(),H-1);
|
||||
var H = b.getHeight()-24;
|
||||
b.setColor("#71c6cf");
|
||||
b.fillRect(0,0,b.getWidth(),H-1);
|
||||
floorpos++;
|
||||
for (var x=-(floorpos&15);x<g.getWidth();x+=16)
|
||||
g.drawImage(FLOORIMG,x,H);
|
||||
for (var x=-(floorpos&15);x<b.getWidth();x+=16)
|
||||
b.drawImage(FLOORIMG,x,H);
|
||||
|
||||
|
||||
if (!running) {
|
||||
var x = g.getWidth()/2;
|
||||
g.setColor("#000000");
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("4x6",2);
|
||||
g.drawString("GAME OVER!",x,20);
|
||||
g.setFont("6x8",1);
|
||||
g.drawString("Score",x,40);
|
||||
g.drawString(score,x,56);
|
||||
g.drawString("Tap screen to",x,76);
|
||||
g.drawString("restart and flap",x,84);
|
||||
g.flip();
|
||||
var x = b.getWidth()/2;
|
||||
b.setColor("#000000");
|
||||
b.setFontAlign(0,0);
|
||||
b.setFont("4x6",2);
|
||||
b.drawString("GAME OVER!",x,20);
|
||||
b.setFont("6x8",1);
|
||||
b.drawString("Score",x,40);
|
||||
b.drawString(score,x,56);
|
||||
b.drawString("Tap screen to",x,76);
|
||||
b.drawString("restart and flap",x,84);
|
||||
b.flip();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,30 +81,30 @@ function draw() {
|
|||
if (birdy > H)
|
||||
gameStop();
|
||||
// draw bird
|
||||
g.drawImage(BIRDIMG, 6,birdy, {rotate:Math.atan2(birdvy,15)});
|
||||
b.drawImage(BIRDIMG, 6,birdy, {rotate:Math.atan2(birdvy,15)});
|
||||
// draw barriers
|
||||
barriers.forEach(function(b) {
|
||||
b.x1--;
|
||||
b.x2--;
|
||||
var btop = b.y-b.gap;
|
||||
var bbot = b.y+b.gap;
|
||||
g.setColor("#73bf2f"); // middle
|
||||
g.fillRect(b.x1+4, 0, b.x2-4, btop-1);
|
||||
g.fillRect(b.x1+4, bbot, b.x2-4, H-1);
|
||||
g.setColor("#c0f181"); // left
|
||||
g.fillRect(b.x1+1, 0, b.x1+3, btop-1);
|
||||
g.fillRect(b.x1+1, bbot, b.x1+3, H-1);
|
||||
g.setColor("#538917"); // right
|
||||
g.fillRect(b.x2-3, 0, b.x2-1, btop-1);
|
||||
g.fillRect(b.x2-3, bbot, b.x2-1, H-1);
|
||||
g.setColor("#808080"); // outlines
|
||||
g.drawRect(b.x1, btop-5, b.x2, btop); // top
|
||||
g.drawLine(b.x1+1, 0, b.x1+1, btop-6);
|
||||
g.drawLine(b.x2-2, 0, b.x2-2, btop-6);
|
||||
g.drawRect(b.x1, bbot, b.x2, bbot+5); // bottom
|
||||
g.drawLine(b.x1+1, bbot+6, b.x1+1, H-1);
|
||||
g.drawLine(b.x2-1, bbot+6, b.x2-1, H-1);
|
||||
if (b.x1<6 && (birdy-3<btop || birdy+3>bbot))
|
||||
barriers.forEach(function(r) {
|
||||
r.x1--;
|
||||
r.x2--;
|
||||
var btop = r.y-r.gap;
|
||||
var bbot = r.y+r.gap;
|
||||
b.setColor("#73bf2f"); // middle
|
||||
b.fillRect(r.x1+4, 0, r.x2-4, btop-1);
|
||||
b.fillRect(r.x1+4, bbot, r.x2-4, H-1);
|
||||
b.setColor("#c0f181"); // left
|
||||
b.fillRect(r.x1+1, 0, r.x1+3, btop-1);
|
||||
b.fillRect(r.x1+1, bbot, r.x1+3, H-1);
|
||||
b.setColor("#538917"); // right
|
||||
b.fillRect(r.x2-3, 0, r.x2-1, btop-1);
|
||||
b.fillRect(r.x2-3, bbot, r.x2-1, H-1);
|
||||
b.setColor("#808080"); // outlines
|
||||
b.drawRect(r.x1, btop-5, r.x2, btop); // top
|
||||
b.drawLine(r.x1+1, 0, r.x1+1, btop-6);
|
||||
b.drawLine(r.x2-2, 0, r.x2-2, btop-6);
|
||||
b.drawRect(r.x1, bbot, r.x2, bbot+5); // bottom
|
||||
b.drawLine(r.x1+1, bbot+6, r.x1+1, H-1);
|
||||
b.drawLine(r.x2-1, bbot+6, r.x2-1, H-1);
|
||||
if (r.x1<6 && (birdy-3<btop || birdy+3>bbot))
|
||||
gameStop();
|
||||
});
|
||||
while (barriers.length && barriers[0].x2<=0) {
|
||||
|
@ -94,7 +112,7 @@ function draw() {
|
|||
newBarrier(g.getWidth());
|
||||
}
|
||||
|
||||
g.flip();
|
||||
b.flip();
|
||||
}
|
||||
|
||||
Bangle.on('touch', function(button) {
|
||||
|
@ -105,11 +123,9 @@ Bangle.on('touch', function(button) {
|
|||
}
|
||||
});
|
||||
|
||||
// Finally, start everything going
|
||||
setTimeout(()=>{
|
||||
Bangle.setLCDMode("120x120");
|
||||
g.setBgColor("#e3db9d");
|
||||
Bangle.loadWidgets();
|
||||
g.clear();
|
||||
Bangle.drawWidgets();
|
||||
b.setBgColor("#e3db9d");
|
||||
gameStart();
|
||||
setInterval(draw, 100);
|
||||
},10);
|
||||
|
|
|
@ -10,3 +10,4 @@
|
|||
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.12: Added stepo2 as a replacement for stepo and digi
|
||||
0.13: Added long press BTN2 toggle gpsrec status in GPS clock
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
|
||||
function onButtonLong(btn) {
|
||||
log_debug("markWaypoint()");
|
||||
if (btn !== 1) return;
|
||||
if (btn === 1) {
|
||||
if (gpsObject.getState() !== gpsObject.GPS_RUNNING) return;
|
||||
log_debug("markWaypoint()");
|
||||
|
||||
|
@ -93,6 +93,11 @@
|
|||
resetPrevious();
|
||||
getWaypoint();
|
||||
drawGPSData();
|
||||
return;
|
||||
}
|
||||
|
||||
if (btn === 2)
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
|
||||
function getWaypoint() {
|
||||
|
|
|
@ -55,7 +55,10 @@
|
|||
if (btn === 1) cycleInfoMode();
|
||||
}
|
||||
|
||||
function onButtonLong(btn) {}
|
||||
function onButtonLong(btn) {
|
||||
if (btn === 2) Bangle.showLauncher();
|
||||
}
|
||||
|
||||
function getGPSfix() { return undefined; }
|
||||
function setGPSfix(f) {}
|
||||
|
||||
|
|
|
@ -45,7 +45,15 @@
|
|||
}
|
||||
|
||||
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(){
|
||||
|
@ -143,7 +151,8 @@
|
|||
g.drawString(activityStr, 120, Y_ACTIVITY);
|
||||
g.setFont("6x8",2);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,8 @@ function buttonReleased(btn) {
|
|||
face.onButtonLong(btn);
|
||||
break;
|
||||
case 2:
|
||||
Bangle.showLauncher();
|
||||
face.onButtonLong(btn);
|
||||
//Bangle.showLauncher();
|
||||
break;
|
||||
case 3:
|
||||
// do nothing
|
||||
|
@ -94,8 +95,8 @@ function setButtons(){
|
|||
}
|
||||
|
||||
Bangle.on('kill',()=>{
|
||||
Bangle.setCompassPower(0);
|
||||
Bangle.setGPSPower(0);
|
||||
Bangle.setCompassPower(0,'kitchen');
|
||||
Bangle.setGPSPower(0,'kitchen');
|
||||
});
|
||||
|
||||
Bangle.on('lcdPower',function(on) {
|
||||
|
@ -214,7 +215,7 @@ GPS.prototype.toggleGPSPower = function() {
|
|||
this.log_debug("toggleGPSPower()");
|
||||
this.gpsPowerState = Bangle.isGPSOn();
|
||||
this.gpsPowerState = !this.gpsPowerState;
|
||||
Bangle.setGPSPower(this.gpsPowerState ? 1 : 0);
|
||||
Bangle.setGPSPower((this.gpsPowerState ? 1 : 0), 'kitchen');
|
||||
|
||||
this.resetLastFix();
|
||||
this.determineGPSState();
|
||||
|
@ -369,6 +370,26 @@ GPS.prototype.nextWaypoint = function(inc) {
|
|||
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();
|
||||
|
||||
|
||||
|
|
|
@ -19,9 +19,14 @@
|
|||
}
|
||||
|
||||
function onButtonLong(btn) {
|
||||
if (btn === 1) {
|
||||
trip.resetTrip(getSteps());
|
||||
trip.setTripState(true);
|
||||
drawStepText();
|
||||
return;
|
||||
}
|
||||
|
||||
if (btn === 2) Bangle.showLauncher();
|
||||
}
|
||||
|
||||
function radians(a) {
|
||||
|
|
|
@ -62,10 +62,15 @@
|
|||
}
|
||||
|
||||
function onButtonLong(btn) {
|
||||
if (btn === 1) {
|
||||
trip.resetTrip(getSteps());
|
||||
infoMode = INFO_TRIP;
|
||||
forceRedraw();
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
|
||||
if (btn === 2) Bangle.showLauncher();
|
||||
}
|
||||
|
||||
function radians(a) {
|
||||
|
@ -207,7 +212,8 @@
|
|||
var midrot = -180 - (360 * percent);
|
||||
var endrot = -360 - 180;
|
||||
|
||||
g.setColor(0x07FF); // light cyan
|
||||
//g.setColor(0x07FF); // light cyan
|
||||
g.setColor(0xFFC0); // yellow
|
||||
|
||||
// draw guauge
|
||||
for (i = startrot; i > midrot; i -= 3) {
|
||||
|
@ -218,8 +224,8 @@
|
|||
|
||||
// change the remaining color to RED if battery is below 25%
|
||||
if (E.getBattery() > 25) {
|
||||
//g.setColor(0x7BEF); // grey
|
||||
g.setColor(0x000D); // dark navy
|
||||
g.setColor(0x7BEF); // grey
|
||||
//g.setColor(0x000D); // dark navy
|
||||
} else {
|
||||
g.setColor(0xF800); // red
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
function onButtonLong(btn) {}
|
||||
function onButtonLong(btn) {
|
||||
if (btn === 2) Bangle.showLauncher();
|
||||
}
|
||||
|
||||
return {init:init, freeResources:freeResources, startTimer:startTimer, stopTimer:stopTimer,
|
||||
onButtonShort:onButtonShort, onButtonLong:onButtonLong};
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Fix occasional missed image when scrolling up
|
||||
|
|
|
@ -47,8 +47,13 @@ Bangle.on('drag',e=>{
|
|||
g.reset().setClipRect(0,24,g.getWidth()-1,g.getHeight()-1);
|
||||
g.scroll(0,dy);
|
||||
menuScroll -= dy;
|
||||
if (e.dy < 0) drawApp(Math.floor((menuScroll+24)/APPH)+n-1);
|
||||
else drawApp(Math.floor((menuScroll+24)/APPH));
|
||||
if (e.dy < 0) {
|
||||
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);
|
||||
});
|
||||
Bangle.on("touch",(_,e)=>{
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<div id="map">
|
||||
</div>
|
||||
<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/>
|
||||
<canvas id="maptiles" style="display:none"></canvas>
|
||||
<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
|
||||
|
||||
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 = [];
|
||||
tileLayer.addTo(map);
|
||||
|
||||
|
|
|
@ -34,6 +34,23 @@
|
|||
<script src="../../core/lib/imageconverter.js"></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
|
||||
function escapeString (string) {
|
||||
var to_escape = ['\\', ';', ',', ':', '"'];
|
||||
|
@ -70,13 +87,7 @@
|
|||
qrcode.makeCode(document.getElementById("url").value);
|
||||
}
|
||||
}
|
||||
var qrcode = new QRCode("qrcode", {
|
||||
text: document.getElementById("url").value,
|
||||
width: 200,
|
||||
height: 200,
|
||||
colorDark : "#000000",
|
||||
colorLight : "#ffffff",
|
||||
});
|
||||
var qrcode;
|
||||
|
||||
document.getElementById("url").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 app = `var img = ${img};
|
||||
var content = ${JSON.stringify(content)};
|
||||
g.setColor(1,1,1);
|
||||
g.fillRect(0,0,239,239);
|
||||
g.drawImage(img,20,20);
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("6x8");
|
||||
g.setColor(0,0,0);
|
||||
g.drawString(content,120,230);
|
||||
g.clear(1).setColor(1,1,1).setBgColor(0,0,0);
|
||||
g.fillRect(0,0,g.getWidth()-1,g.getHeight()-1);
|
||||
g.drawImage(img,(g.getWidth()-img[0])/2,(g.getHeight()-img[1])/2);
|
||||
g.setFontAlign(0,0).setFont("6x8").setColor(0,0,0);
|
||||
g.drawString(content,g.getWidth()/2,g.getHeight()-(g.getHeight()-img[1])/4));
|
||||
g.setColor(1,1,1);
|
||||
`;
|
||||
sendCustomizedApp({
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
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.04: Bangle.js 2 support
|
||||
|
|
|
@ -8,8 +8,32 @@ function saveHighScore(score) {
|
|||
f.write(score + "\n");
|
||||
}
|
||||
|
||||
greal = g;
|
||||
g.clear();
|
||||
|
||||
var BTNL, BTNR, BTNU;
|
||||
if (process.env.HWVERSION==2) {
|
||||
var tap = {};
|
||||
// use tapping on screen for left and right
|
||||
Bangle.on('drag',e=>tap=e);
|
||||
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({
|
||||
|
@ -18,10 +42,8 @@ g.flip = function() {
|
|||
buffer:g.buffer
|
||||
},0,(240-128)/2,{scale:2});
|
||||
};
|
||||
}
|
||||
var W = g.getWidth();
|
||||
var BTNL = BTN2;
|
||||
var BTNR = BTN3;
|
||||
var BTNU = BTN1;
|
||||
|
||||
// Images can be added like this in Espruino v2.00
|
||||
var IMG = {
|
||||
|
|
|
@ -11,3 +11,6 @@
|
|||
0.12: Respect Quiet Mode
|
||||
0.13: Now use system color theme
|
||||
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
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
let s = {
|
||||
'goal': 10000,
|
||||
'progress': false,
|
||||
'large': false,
|
||||
'hide': false
|
||||
}
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
|
@ -41,6 +43,22 @@
|
|||
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,
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
(() => {
|
||||
const PEDOMFILE = "wpedom.json"
|
||||
// Last time Bangle.on('step' was called
|
||||
let lastUpdate = new Date();
|
||||
// Last step count when Bangle.on('step' was called
|
||||
var lastStepCount;
|
||||
let stp_today = 0;
|
||||
let settings;
|
||||
|
||||
|
@ -14,11 +17,14 @@
|
|||
const DEFAULTS = {
|
||||
'goal': 10000,
|
||||
'progress': false,
|
||||
'large': false,
|
||||
'hide': false
|
||||
}
|
||||
return (key in settings) ? settings[key] : DEFAULTS[key];
|
||||
}
|
||||
|
||||
function drawProgress(stps) {
|
||||
if (setting('hide')) return;
|
||||
const width = 24, half = width/2;
|
||||
const goal = setting('goal'), left = Math.max(goal-stps,0);
|
||||
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
|
||||
function draw() {
|
||||
if (setting('hide')) return;
|
||||
var width = 24;
|
||||
if (stp_today > 99999){
|
||||
stp_today = stp_today % 100000; // cap to five digits + comma = 6 characters
|
||||
}
|
||||
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
|
||||
if (setting('progress')){ drawProgress.call(this, stps); }
|
||||
g.setColor(g.theme.fg);
|
||||
|
@ -73,13 +95,16 @@
|
|||
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();
|
||||
if (lastUpdate.getDate() == date.getDate()){
|
||||
stp_today ++;
|
||||
stp_today += steps;
|
||||
} else {
|
||||
// TODO: could save this to PEDOMFILE for lastUpdate's day?
|
||||
stp_today = 1;
|
||||
stp_today = steps;
|
||||
}
|
||||
if (stp_today === setting('goal')
|
||||
&& !(require('Storage').readJSON('setting.json',1)||{}).quiet) {
|
||||
|
|
|
@ -51,7 +51,7 @@ try{
|
|||
|
||||
const APP_KEYS = [
|
||||
'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'
|
||||
];
|
||||
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 (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.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.dependencies) {
|
||||
if (("object"==typeof app.dependencies) && !Array.isArray(app.dependencies)) {
|
||||
|
|
2
core
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 27f9a7125146a38c4357d679ec783f6e98a983c6
|
||||
Subproject commit 39f89acf1468dc7d58ac54d15a092b2bd6d73cce
|
|
@ -3,14 +3,32 @@
|
|||
|
||||
Usage:
|
||||
|
||||
```
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( layoutObject, btns )
|
||||
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:
|
||||
|
||||
* A `type` field of:
|
||||
* `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`
|
||||
* `"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
|
||||
|
@ -83,10 +101,12 @@ function Layout(layout, buttons) {
|
|||
this._l,
|
||||
{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
|
||||
var ll = this;
|
||||
|
@ -130,7 +150,7 @@ function updateMin(l) {
|
|||
case "txt": {
|
||||
if (l.font.endsWith("%"))
|
||||
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(":")) {
|
||||
var f = l.font.split(":");
|
||||
l.font = f[0];
|
||||
|
@ -222,8 +242,8 @@ Layout.prototype.render = function (l) {
|
|||
Layout.prototype.layout = function (l) {
|
||||
// l = current layout element
|
||||
// exw,exh = extra width/height available
|
||||
var fillx = l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
var filly = l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
switch (l.type) {
|
||||
case "h": {
|
||||
let x = l.x + (l.w-l._w)/2;
|
||||
|
|
|
@ -3,14 +3,32 @@
|
|||
|
||||
Usage:
|
||||
|
||||
```
|
||||
var Layout = require("Layout");
|
||||
var layout = new Layout( layoutObject, btns )
|
||||
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:
|
||||
|
||||
* A `type` field of:
|
||||
* `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`
|
||||
* `"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
|
||||
|
@ -83,10 +101,12 @@ function Layout(layout, buttons) {
|
|||
this._l,
|
||||
{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
|
||||
var ll = this;
|
||||
|
@ -130,7 +150,7 @@ function updateMin(l) {
|
|||
case "txt": {
|
||||
if (l.font.endsWith("%"))
|
||||
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(":")) {
|
||||
var f = l.font.split(":");
|
||||
l.font = f[0];
|
||||
|
@ -222,8 +242,8 @@ Layout.prototype.render = function (l) {
|
|||
Layout.prototype.layout = function (l) {
|
||||
// l = current layout element
|
||||
// exw,exh = extra width/height available
|
||||
var fillx = l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
var filly = l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
var fillx = l.c && l.c.reduce((a,l)=>a+(0|l.fillx),0);
|
||||
var filly = l.c && l.c.reduce((a,l)=>a+(0|l.filly),0);
|
||||
switch (l.type) {
|
||||
case "h": {
|
||||
let x = l.x + (l.w-l._w)/2;
|
||||
|
|
Loading…
Reference in New Issue