Merge branch 'master' of github.com:espruino/BangleApps

pull/1118/head^2
Gordon Williams 2021-12-16 15:07:10 +00:00
commit d3ccc08209
45 changed files with 708 additions and 305 deletions

View File

@ -1590,7 +1590,7 @@
{ {
"id": "widpedom", "id": "widpedom",
"name": "Pedometer widget", "name": "Pedometer widget",
"version": "0.19", "version": "0.20",
"description": "Daily pedometer widget", "description": "Daily pedometer widget",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
@ -3478,8 +3478,8 @@
{ {
"id": "speedalt2", "id": "speedalt2",
"name": "GPS Adventure Sports II", "name": "GPS Adventure Sports II",
"shortName": "GPS Adv Sport II", "shortName":"GPS Adv Sport II",
"version": "0.07", "version":"1.10",
"description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.", "description": "GPS speed, altitude and distance to waypoint display. Designed for easy viewing and use during outdoor activities such as para-gliding, hang-gliding, sailing, cycling etc.",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "app",
@ -4209,7 +4209,7 @@
"id": "pastel", "id": "pastel",
"name": "Pastel Clock", "name": "Pastel Clock",
"shortName": "Pastel", "shortName": "Pastel",
"version": "0.08", "version": "0.09",
"description": "A Configurable clock with custom fonts and background. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times", "description": "A Configurable clock with custom fonts and background. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
"icon": "pastel.png", "icon": "pastel.png",
"dependencies": {"mylocation":"app", "widpedom":"app"}, "dependencies": {"mylocation":"app", "widpedom":"app"},
@ -4723,8 +4723,9 @@
"id": "pebble", "id": "pebble",
"name": "Pebble Clock", "name": "Pebble Clock",
"shortName": "Pebble", "shortName": "Pebble",
"version": "0.04", "version": "0.06",
"description": "A pebble style clock to keep the rebellion going", "description": "A pebble style clock to keep the rebellion going",
"dependencies": {"widpedom":"app"},
"readme": "README.md", "readme": "README.md",
"icon": "pebble.png", "icon": "pebble.png",
"screenshots": [{"url":"pebble_screenshot.png"}], "screenshots": [{"url":"pebble_screenshot.png"}],
@ -4858,10 +4859,10 @@
"id": "ptlaunch", "id": "ptlaunch",
"name": "Pattern Launcher", "name": "Pattern Launcher",
"shortName": "Pattern Launcher", "shortName": "Pattern Launcher",
"version": "0.10", "version": "0.11",
"description": "Directly launch apps from the clock screen with custom patterns.", "description": "Directly launch apps from the clock screen with custom patterns.",
"icon": "app.png", "icon": "app.png",
"screenshots": [{"url":"main_menu_add.png"}, {"url":"add_pattern.png"}, {"url":"select_app.png"}, {"url":"main_menu_manage.png"}, {"url":"manage_patterns.png"}], "screenshots": [{"url":"manage_patterns_light.png"}],
"tags": "tools", "tags": "tools",
"supports": ["BANGLEJS2"], "supports": ["BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
@ -4876,11 +4877,11 @@
"id": "rebble", "id": "rebble",
"name": "Rebble Clock", "name": "Rebble Clock",
"shortName": "Rebble", "shortName": "Rebble",
"version": "0.02", "version": "0.03",
"description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion", "description": "A Pebble style clock, with configurable background, three sidebars including steps, day, date, sunrise, sunset, long live the rebellion",
"readme": "README.md", "readme": "README.md",
"icon": "rebble.png", "icon": "rebble.png",
"dependencies": {"mylocation":"app"}, "dependencies": {"mylocation":"app", "widpedom":"app"},
"screenshots": [{"url":"screenshot_rebble.png"}], "screenshots": [{"url":"screenshot_rebble.png"}],
"type": "clock", "type": "clock",
"tags": "clock", "tags": "clock",
@ -4918,6 +4919,7 @@
"supports" : ["BANGLEJS", "BANGLEJS2"], "supports" : ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"clicompleteclk.app.js","url":"app.js"},
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true}, {"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true},
{"name":"clicompleteclk.settings.js","url":"settings.js"} {"name":"clicompleteclk.settings.js","url":"settings.js"}
], ],
@ -4941,7 +4943,7 @@
{ "id": "pooqround", { "id": "pooqround",
"name": "pooq Round watch face", "name": "pooq Round watch face",
"shortName":"pooq Round", "shortName":"pooq Round",
"version":"0.00", "version":"0.01",
"description": "A 24 hour analogue watchface with high legibility and a novel style.", "description": "A 24 hour analogue watchface with high legibility and a novel style.",
"icon": "app.png", "icon": "app.png",
"type": "clock", "type": "clock",
@ -4977,8 +4979,8 @@
"id": "showimg", "id": "showimg",
"name": "simple image viewer", "name": "simple image viewer",
"shortName":"showImage", "shortName":"showImage",
"version":"0.1", "version":"0.2",
"description": "Displays the image file in showimage.user.img. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.", "description": "Displays the image in \"showimage.user.img\". The file has to be uploaded via the espruino IDE. Returns to watch face after 60s or button push. I use it to display my vaccination certificate.",
"icon": "app.png", "icon": "app.png",
"tags": "tool", "tags": "tool",
"supports" : ["BANGLEJS2"], "supports" : ["BANGLEJS2"],
@ -4986,5 +4988,43 @@
{"name":"showimg.app.js","url":"app.js"}, {"name":"showimg.app.js","url":"app.js"},
{"name":"showimg.img","url":"app-icon.js","evaluate":true} {"name":"showimg.img","url":"app-icon.js","evaluate":true}
] ]
} },
{
"id": "lapcounter",
"name": "Lap Counter",
"version": "0.01",
"description": "Click button to count laps. Shows count and total time snapshot (like a stopwatch, but laid back).",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"type": "app",
"tags": "tool,outdoors",
"readme":"README.md",
"supports": ["BANGLEJS", "BANGLEJS2"],
"allow_emulator": true,
"storage": [
{"name":"lapcounter.app.js","url":"app.js"},
{"name":"lapcounter.img","url":"app-icon.js","evaluate":true}
]
},
{ "id": "circlesclock",
"name": "Circles clock",
"shortName":"Circles clock",
"version":"0.01",
"description": "A clock with circles for different data at the bottom in a probably familiar style",
"icon": "app.png",
"dependencies": {"widpedom":"app"},
"type": "clock",
"tags": "clock",
"supports" : ["BANGLEJS2"],
"allow_emulator":true,
"readme": "README.md",
"storage": [
{"name":"circlesclock.app.js","url":"app.js"},
{"name":"circlesclock.img","url":"app-icon.js","evaluate":true},
{"name":"circlesclock.settings.js","url":"settings.js"}
],
"data": [
{"name":"circlesclock.json"}
]
}
] ]

View File

@ -0,0 +1 @@
0.01: New clock

View File

@ -0,0 +1,19 @@
# Circles clock
A clock with circles for different data at the bottom in a probably familiar style
It shows besides time, date and day of week the following information:
* Steps (requires [pedometer widget](https://banglejs.com/apps/#pedometer))
* Heart rate (when screen is on and unlocked)
* Battery
## Screenshot
![Screenshot](screenshot.png)
## TODO
* Show weather information
## Creator
Marco ([myxor](https://github.com/myxor))

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("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"))

218
apps/circlesclock/app.js Normal file
View File

@ -0,0 +1,218 @@
const locale = require("locale");
const heatshrink = require("heatshrink");
var shoesIcon = heatshrink.decompress(atob("h0OwYJGgmAAgUBkgECgVJB4cSoAUDyEBkARDpADBhMAyQRBgVAkgmDhIUDAAuQAgY1DAAYA="));
var heartIcon = heatshrink.decompress(atob("h0OwYOLkmQhMkgACByVJgESpIFBpEEBAIFBCgIFCCgsABwcAgQOCAAMSpAwDyBNM"));
var powerIcon = heatshrink.decompress(atob("h0OwYQNsAED7AEDmwEDtu2AgUbtuABwXbBIUN23AAoYOCgEDFIgODABI"));
const SETTINGS_FILE = "circlesclock.json";
let settings;
function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {
'maxHR': 200,
'stepGoal': 10000
};
}
const colorFg = '#fff';
const colorBg = '#000';
const colorGrey = '#808080';
let hrtValue;
const h = g.getHeight();
const w = g.getWidth();
const hOffset = 30;
const h1 = Math.round(1 * h / 5 - hOffset);
const h2 = Math.round(3 * h / 5 - hOffset);
const h3 = Math.round(8 * h / 8 - hOffset);
const w1 = Math.round(w / 6);
const w2 = Math.round(3 * w / 6);
const w3 = Math.round(5 * w / 6);
const radiusOuter = 22;
const radiusInner = 16;
function draw() {
g.reset();
g.setColor(colorBg);
g.fillRect(0, 0, w, h);
// time
g.setFont("Vector:50");
g.setFontAlign(-1, -1);
g.setColor(colorFg);
g.drawString(locale.time(new Date(), 1), w / 10, h1 + 8);
// date & dow
g.setFont("Vector:20");
g.setFontAlign(-1, 0);
g.drawString(locale.date(new Date()), w / 10, h2);
g.drawString(locale.dow(new Date()), w / 10, h2 + 22);
// Steps circle
drawSteps();
// Heart circle
drawHeartRate();
// Battery circle
drawBattery();
}
function drawSteps() {
const steps = getSteps();
const blue = '#0000ff';
g.setColor(colorGrey);
g.fillCircle(w1, h3, radiusOuter);
const stepGoal = settings.stepGoal;
if (stepGoal > 0) {
let percent = steps / stepGoal;
if (stepGoal < steps) percent = 1;
drawGauge(w1, h3, percent, blue);
}
g.setColor(colorBg);
g.fillCircle(w1, h3, radiusInner);
g.fillPoly([w1, h3, w1 - 15, h3 + radiusOuter + 5, w1 + 15, h3 + radiusOuter + 5]);
g.setFont("Vector:12");
g.setFontAlign(0, 0);
g.setColor(colorFg);
g.drawString(shortValue(steps), w1 + 2, h3);
g.drawImage(shoesIcon, w1 - 6, h3 + radiusOuter - 6);
}
function drawHeartRate() {
const red = '#ff0000';
g.setColor(colorGrey);
g.fillCircle(w2, h3, radiusOuter);
if (hrtValue != undefined) {
const percent = hrtValue / settings.maxHR;
drawGauge(w2, h3, percent, red);
}
g.setColor(colorBg);
g.fillCircle(w2, h3, radiusInner);
g.fillPoly([w2, h3, w2 - 15, h3 + radiusOuter + 5, w2 + 15, h3 + radiusOuter + 5]);
g.setFont("Vector:12");
g.setFontAlign(0, 0);
g.setColor(colorFg);
g.drawString(hrtValue != undefined ? hrtValue : 0, w2, h3);
g.drawImage(heartIcon, w2 - 6, h3 + radiusOuter - 6);
}
function drawBattery() {
const battery = E.getBattery();
const yellow = '#ffff00';
g.setColor(colorGrey);
g.fillCircle(w3, h3, radiusOuter);
if (battery > 0) {
const percent = battery / 100;
drawGauge(w3, h3, percent, yellow);
}
g.setColor(colorBg);
g.fillCircle(w3, h3, radiusInner);
g.fillPoly([w3, h3, w3 - 15, h3 + radiusOuter + 5, w3 + 15, h3 + radiusOuter + 5]);
g.setFont("Vector:12");
g.setFontAlign(0, 0);
g.setColor(colorFg);
g.drawString(battery + '%', w3, h3);
g.drawImage(powerIcon, w3 - 6, h3 + radiusOuter - 6);
}
function radians(a) {
return a * Math.PI / 180;
}
function drawGauge(cx, cy, percent, color) {
let offset = 30;
let end = 300;
var i = 0;
var r = radiusInner + 3;
if (percent > 1) percent = 1;
var startrot = -offset;
var endrot = startrot - ((end - offset) * percent);
g.setColor(color);
// draw gauge
for (i = startrot; i > endrot; i -= 4) {
x = cx + r * Math.sin(radians(i));
y = cy + r * Math.cos(radians(i));
g.fillCircle(x, y, 4);
}
}
function shortValue(v) {
if (isNaN(v)) return '-';
if (v <= 999) return v;
if (v >= 1000 && v < 10000) {
v = Math.floor(v / 100) * 100;
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
if (v >= 10000) {
v = Math.floor(v / 1000) * 1000;
return (v / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
}
function getSteps() {
if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
}
return 0;
}
Bangle.on('lock', function(isLocked) {
if (!isLocked) {
Bangle.setHRMPower(1, "watch");
} else {
Bangle.setHRMPower(0, "watch");
}
drawHeartRate();
drawSteps();
});
Bangle.on('HRM', function(hrm) {
//if(hrm.confidence > 90){
hrtValue = hrm.bpm;
if (Bangle.isLCDOn())
drawHeartRate();
//} else {
// hrtValue = undefined;
//}
});
g.clear();
Bangle.loadWidgets();
/*
* we are not drawing the widgets as we are taking over the whole screen
* so we will blank out the draw() functions of each widget and change the
* area to the top bar doesn't get cleared.
*/
for (let wd of WIDGETS) {
wd.draw = () => {};
wd.area = "";
}
loadSettings();
setInterval(draw, 60000);
draw();
Bangle.setUI("clock");

BIN
apps/circlesclock/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,33 @@
(function(back) {
const SETTINGS_FILE = "circlesclock.json";
const storage = require('Storage');
let settings = storage.readJSON(SETTINGS_FILE, 1) || {};
function save(key, value) {
settings[key] = value;
storage.write(SETTINGS_FILE, settings);
}
E.showMenu({
'': { 'title': 'circlesclock' },
'max heartrate': {
value: "maxHR" in settings ? settings.maxHR : 200,
min: 20,
max : 250,
step: 10,
format: x => {
return x;
},
onchange: x => save('maxHR', x),
},
'step goal': {
value: "stepGoal" in settings ? settings.stepGoal : 10000,
min: 2000,
max : 50000,
step: 2000,
format: x => {
return x;
},
onchange: x => save('stepGoal', x),
},
'< Back': back,
});
});

View File

@ -0,0 +1 @@
0.01: first release

19
apps/lapcounter/README.md Normal file
View File

@ -0,0 +1,19 @@
# Lap Counter
Click button to count laps (e.g. in a swimming pool).
Also shows total duration snapshot (like a stopwatch, but laid back).
![Screenshot](screenshot.png)
## Usage
* Click BTN1 to start counting. Counter becomes `0`, duration becomes `00:00.0`
* Each time you click BTN1, counter is incremented, and you see duration between first and last clicks.
## Features
Disables LCD timeout (so that you can be _sure_ what BTN1 would do).
## Creator
[Nimrod Kerrett](https://zzzen.com)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap"))

53
apps/lapcounter/app.js Normal file
View File

@ -0,0 +1,53 @@
const w = g.getWidth();
const h = g.getHeight();
const wid_h = 24;
let tStart;
let tNow;
let counter=-1;
const icon = require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap"));
function timeToText(t) { // Courtesy of stopwatch app
let hrs = Math.floor(t/3600000);
let mins = Math.floor(t/60000)%60;
let secs = Math.floor(t/1000)%60;
let tnth = Math.floor(t/100)%10;
let text;
if (hrs === 0)
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2) + "." + tnth;
else
text = ("0"+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
//log_debug(text);
return text;
}
function doCounter() {
if (counter<0) {
tStart = Date.now();
tNow = tStart;
} else {
tNow = Date.now();
}
counter++;
let dT = tNow-tStart;
g.clearRect(0,wid_h,w,h-wid_h);
g.setFontAlign(0,0);
g.setFont("Vector",72);
g.drawString(counter,w/2,h/2);
g.setFont("Vector",24);
g.drawString(timeToText(dT),w/2,h/2+50);
}
setWatch(doCounter, BTN1, true);
g.clear(true);
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setLCDTimeout(0);
g.drawImage(icon,w/2-24,h/2-24);
g.setFontAlign(0,0);
require("Font8x12").add(Graphics);
g.setFont("8x12");
g.drawString("Click button to count.", w/2, h/2+22);

BIN
apps/lapcounter/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@ -6,3 +6,4 @@
0.06: Converted fonts to font modules 0.06: Converted fonts to font modules
0.07: Added info line that cycles on BTN1/BTN3 (or vitual buttons on a bangle 2) 0.07: Added info line that cycles on BTN1/BTN3 (or vitual buttons on a bangle 2)
0.08: Added dependancy on MyLocation 0.08: Added dependancy on MyLocation
0.09: Added dependancy on Pedometer Widget

View File

@ -3,3 +3,4 @@
0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot 0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot
0.04: Fix widget hiding code (fix #1046) 0.04: Fix widget hiding code (fix #1046)
0.05: Fix typo in settings - Purple 0.05: Fix typo in settings - Purple
0.06: Added dependancy on Pedometer Widget

View File

@ -1 +1,2 @@
0.00: Initial check-in. 0.00: Initial check-in.
0.01: Add tap-to-decorate feature. Bugfixes.

View File

@ -10,16 +10,18 @@ Either you'll like that, or you won't.
## Options ## Options
Because sometimes I don't want to burn what I'm cooking and others I'm lazy and just want to know if it's afternoon yet,
you can alter the number of hands on the display. When the watch is unlocked, slide up to add dots representing the minute and second,
or down to remove the distraction. There's also a setting that displays the second hand, but only if the watch is perfectly face-to-the-sky,
in case you want the ability to check the _exact_ time, hands free, without the impact on battery life this usually entails.
Although we generally obey the system-wide theming, you can long press on the display for a menu of additional options specific to the face. Although we generally obey the system-wide theming, you can long press on the display for a menu of additional options specific to the face.
We don't observe the system 12/24 setting, since it the design of the face is equally good in either interpretation. We don't observe the system 12/24 setting, since it the design of the face is equally good in either interpretation.
By default, there is a backlight that comes on when you twist your wrist. This, of course, somewhat increases power draw and could be If you like an uncluttered display style, you can still bring up the day, date and minute hand transiently with a tap on the watchface (when unlocked).
annoying in an intentionally dark environment, so there is an option to disable it.
Similarly, because sometimes I don't want to burn what I'm cooking and others I'm lazy and just want to know if it's afternoon yet,
you can quickly alter the number of hands on the display. When the watch is unlocked, slide up to add dots representing the minute and second,
or down to remove the distraction. There's also a setting that displays the second hand, but only if the watch is perfectly face-to-the-sky,
in case you want the ability to check the _exact_ time, hands free, without the impact on battery life this usually entails.
In some versions of the Bangle.js firmware, the backlight doesn't come on automatically when you twist your wrist. There's currently a workaround
for this integrated into the watchface; you can disable it in the menu, if you prefer.
## Limitations ## Limitations

View File

@ -83,7 +83,6 @@ class Options {
Bangle.removeListener('drag', this.reactivator); Bangle.removeListener('drag', this.reactivator);
this.emit('done'); this.emit('done');
} }
g.clear(true);
E.showMenu(m); E.showMenu(m);
} }
@ -309,7 +308,7 @@ class Round {
buffer: this.c.buffer, transparent: 0 buffer: this.c.buffer, transparent: 0
}; };
this.options = new RoundOptions(); this.options = new RoundOptions();
this.timescales = [1000, 0, 60000, 900000]; this.timescales = [1000, [1000, 60000], 60000, 900000];
this.state = {}; this.state = {};
// Precomputed polygons for the border areas. // Precomputed polygons for the border areas.
this.tl = [0, 0, 58, 0, 0, 58]; this.tl = [0, 0, 58, 0, 0, 58];
@ -323,13 +322,15 @@ class Round {
this.r = this.xc - this.minR; this.r = this.xc - this.minR;
} }
reset() {this.state = {}; this.g.clear(true);} reset(clear) {this.state = {}; clear && this.g.clear(true);}
doIcons(which) { doIcons(which) {
this.state[which] = null; this.state[which] = null;
this.render(new Date()); // Not quite right, I think. this.render(new Date()); // Not quite right, I think.
} }
enhanceUntil(t) {this.enhance = t;}
pie(f, a0, a1, invert) { pie(f, a0, a1, invert) {
if (!invert) return this.pie(f, a1, a0 + 1, true); if (!invert) return this.pie(f, a1, a0 + 1, true);
let t0 = Math.tan(a0 * 2 * Math.PI), t1 = Math.tan(a1 * 2 * Math.PI); let t0 = Math.tan(a0 * 2 * Math.PI), t1 = Math.tan(a1 * 2 * Math.PI);
@ -369,17 +370,18 @@ class Round {
const g = this.g; const g = this.g;
const b = this.b, bI = this.bI; const b = this.b, bI = this.bI;
const c = this.c, cI = this.cI; const c = this.c, cI = this.cI;
const e = d < this.enhance;
const state = this.state; const state = this.state;
const options = this.options; const options = this.options;
const cal = options.calendric; const cal = options.calendric;
const res = options.resolution; const res = options.resolution;
const dow = (cal == 1 || cal > 2) && d.getDay(); const dow = (e || cal == 1 || cal > 2) && d.getDay();
const ts = res < 2 && d.getSeconds(); const ts = res < 2 && d.getSeconds();
const tm = res < 3 && d.getMinutes() + ts / 60; const tm = (e || res < 3) && d.getMinutes() + ts / 60;
const th = d.getHours() + d.getMinutes() / 60; const th = d.getHours() + d.getMinutes() / 60;
const dd = cal > 1 && d.getDate(); const dd = (e || cal > 1) && d.getDate();
const dm = cal > 3 && d.getMonth(); const dm = (e || cal > 3) && d.getMonth();
const dy = cal > 4 && d.getFullYear(); const dy = (e || cal > 4) && d.getFullYear();
const xc = this.xc, yc = this.yc, r = this.r; const xc = this.xc, yc = this.yc, r = this.r;
const dlr = xc * 3/4, dlw = 8, dlhw = 4; const dlr = xc * 3/4, dlw = 8, dlhw = 4;
@ -475,7 +477,6 @@ class Clock {
this.timescales = face.timescales; this.timescales = face.timescales;
this.options = face.options; this.options = face.options;
this.rates = {}; this.rates = {};
this.faceUp = null;
this.options.on('done', () => this.start()); this.options.on('done', () => this.start());
@ -485,7 +486,6 @@ class Clock {
lock: () => {face.doIcons('locked'); this.active();}, lock: () => {face.doIcons('locked'); this.active();},
faceUp: up => { faceUp: up => {
this.conservative = !up; this.conservative = !up;
this.faceUp = up;
this.active(); this.active();
}, },
twist: _ => this.options.autolight && Bangle.setLCDPower(true), twist: _ => this.options.autolight && Bangle.setLCDPower(true),
@ -504,9 +504,15 @@ class Clock {
this.options.resolution++; this.options.resolution++;
this.rates.clock = this.timescales[this.options.resolution]; this.rates.clock = this.timescales[this.options.resolution];
this.active(); this.active();
} else if (this.yX - this.yN < 20 && Date.now() - this.t0 > 500) { } else if (this.yX - this.yN < 20) {
this.stop(); const now = new Date();
this.options.interact(); if (now - this.t0 < 250) {
face.enhanceUntil(now + 30000);
face.render(now);
} else if (now - this.t0 > 500) {
this.stop();
this.options.interact();
}
} }
this.t0 = null; this.t0 = null;
} }
@ -520,7 +526,7 @@ class Clock {
redraw(rate) { redraw(rate) {
const now = this.updated = new Date(); const now = this.updated = new Date();
if (this.refresh) this.face.reset(); if (this.refresh) this.face.reset(true);
this.refresh = false; this.refresh = false;
rate = this.face.render(now, rate); rate = this.face.render(now, rate);
if (rate !== this.rates.face) { if (rate !== this.rates.face) {
@ -535,7 +541,7 @@ class Clock {
this.exception && clearTimeout(this.exception); this.exception && clearTimeout(this.exception);
this.interval && clearInterval(this.interval); this.interval && clearInterval(this.interval);
this.timeout = this.exception = this.interval = this.rate = null; this.timeout = this.exception = this.interval = this.rate = null;
this.face.reset(); // Cancel any ongoing background rendering this.face.reset(false); // Cancel any ongoing background rendering
return this; return this;
} }

View File

@ -2,3 +2,4 @@
0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings 0.02: Turn on lcd when launching an app if the lock screen was disabled in the settings
0.03: Make tap to confirm new pattern more reliable. Also allow for easier creation of single circle patterns. 0.03: Make tap to confirm new pattern more reliable. Also allow for easier creation of single circle patterns.
0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible. 0.10: Improve the management of existing patterns: Draw the linked pattern on the left hand side of the app name within a scroller, similar to the default launcher. Slighlty clean up the code to make it less horrible.
0.11: Respect theme colors. Fix: Do not pollute global space with internal variables ans functions in boot.js

View File

@ -10,14 +10,21 @@ Then launch the linked apps directly from the clock screen by simply drawing the
## Add Pattern Screenshots ## Add Pattern Screenshots
![](main_menu_add.png) ![](main_menu_add_light.png)
![](add_pattern.png) ![](add_pattern_light.png)
![](select_app.png) ![](select_app_light.png)
![](main_menu_add_dark.png)
![](add_pattern_dark.png)
![](select_app_dark.png)
## Manage Pattern Screenshots ## Manage Pattern Screenshots
![](main_menu_manage.png) ![](main_menu_manage_light.png)
![](manage_patterns.png) ![](manage_patterns_light.png)
![](main_menu_manage_dark.png)
![](manage_patterns_dark.png)
## Detailed Steps ## Detailed Steps

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -119,8 +119,7 @@ var recognizeAndDrawPattern = () => {
return new Promise((resolve) => { return new Promise((resolve) => {
E.showMenu(); E.showMenu();
g.clear(); g.clear();
g.setColor(0, 0, 0); drawCirclesWithPattern([]);
CIRCLES.forEach((circle) => drawCircle(circle));
var pattern = []; var pattern = [];
@ -369,7 +368,6 @@ var drawAppWithPattern = (i, r, storedPatterns) => {
offset: { x: 1, y: 3 + r.y }, offset: { x: 1, y: 3 + r.y },
}); });
g.setColor(0, 0, 0);
if (!storedPattern.wrappedAppName) { if (!storedPattern.wrappedAppName) {
storedPattern.wrappedAppName = g storedPattern.wrappedAppName = g
.wrapString(app.name, g.getWidth() - 64) .wrapString(app.name, g.getWidth() - 64)
@ -490,7 +488,7 @@ var drawCircle = (circle, drawBuffer, scale) => {
log("drawing circle"); log("drawing circle");
log({ x: x, y: y, r: r }); log({ x: x, y: y, r: r });
drawBuffer.fillCircle(x, y, r); drawBuffer.drawCircle(x, y, r);
}; };
var cachedCirclesDrawings = {}; var cachedCirclesDrawings = {};
@ -535,17 +533,15 @@ var drawCirclesWithPattern = (pattern, options) => {
{ msb: true } { msb: true }
); );
drawBuffer.setColor(1);
CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale)); CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale));
drawBuffer.setColor(0);
drawBuffer.setFontAlign(0, 0); drawBuffer.setFontAlign(0, 0);
drawBuffer.setFont("6x8", 4 * scale); drawBuffer.setFont("Vector", 40 * scale);
pattern.forEach((circleIndex, patternIndex) => { pattern.forEach((circleIndex, patternIndex) => {
var circle = CIRCLES[circleIndex]; var circle = CIRCLES[circleIndex];
drawBuffer.drawString( drawBuffer.drawString(
patternIndex + 1, patternIndex + 1,
circle.x * scale, (circle.x + (scale === 1 ? 1 : 5)) * scale,
circle.y * scale circle.y * scale
); );
}); });

View File

@ -1,167 +1,167 @@
var DEBUG = true;
var log = (message) => {
if (DEBUG) {
console.log(JSON.stringify(message));
}
};
var storedPatterns;
var positions = [];
var dragHandler = (position) => {
positions.push(position);
debounce().then(() => {
log(positions.length);
var CIRCLE_RADIUS = 25;
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
var circles = [
{ x: 25, y: 25, i: 0 },
{ x: 87, y: 25, i: 1 },
{ x: 150, y: 25, i: 2 },
{ x: 25, y: 87, i: 3 },
{ x: 87, y: 87, i: 4 },
{ x: 150, y: 87, i: 5 },
{ x: 25, y: 150, i: 6 },
{ x: 87, y: 150, i: 7 },
{ x: 150, y: 150, i: 8 },
];
var pattern = [];
var step = Math.floor(positions.length / 100) + 1;
var p, a, b, circle;
for (var i = 0; i < positions.length; i += step) {
p = positions[i];
circle = circles[0];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(0, 1);
}
}
circle = circles[1];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(1, 1);
}
}
circle = circles[2];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(2, 1);
}
}
circle = circles[3];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(3, 1);
}
}
circle = circles[4];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(4, 1);
}
}
circle = circles[5];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(5, 1);
}
}
circle = circles[6];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(6, 1);
}
}
circle = circles[7];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(7, 1);
}
}
circle = circles[8];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(8, 1);
}
}
}
positions = [];
pattern = pattern.join("");
if (pattern) {
if (storedPatterns[pattern]) {
var app = storedPatterns[pattern].app;
if (!!app && !!app.src) {
if (storedPatterns.settings) {
if (storedPatterns.settings.lockDisabled) {
Bangle.setLCDPower(true);
}
}
Bangle.removeListener("drag", dragHandler);
load(app.src);
}
}
}
});
};
var debounceTimeoutId;
var debounce = (delay) => {
if (debounceTimeoutId) {
clearTimeout(debounceTimeoutId);
}
return new Promise((resolve) => {
debounceTimeoutId = setTimeout(() => {
debounceTimeoutId = undefined;
resolve();
}, delay || 500);
});
};
(function () { (function () {
var DEBUG = false;
var log = (message) => {
if (DEBUG) {
console.log(JSON.stringify(message));
}
};
var storedPatterns;
var positions = [];
var dragHandler = (position) => {
positions.push(position);
debounce().then(() => {
log(positions.length);
var CIRCLE_RADIUS = 25;
var CIRCLE_RADIUS_2 = CIRCLE_RADIUS * CIRCLE_RADIUS;
var circles = [
{ x: 25, y: 25, i: 0 },
{ x: 87, y: 25, i: 1 },
{ x: 150, y: 25, i: 2 },
{ x: 25, y: 87, i: 3 },
{ x: 87, y: 87, i: 4 },
{ x: 150, y: 87, i: 5 },
{ x: 25, y: 150, i: 6 },
{ x: 87, y: 150, i: 7 },
{ x: 150, y: 150, i: 8 },
];
var pattern = [];
var step = Math.floor(positions.length / 100) + 1;
var p, a, b, circle;
for (var i = 0; i < positions.length; i += step) {
p = positions[i];
circle = circles[0];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(0, 1);
}
}
circle = circles[1];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(1, 1);
}
}
circle = circles[2];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(2, 1);
}
}
circle = circles[3];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(3, 1);
}
}
circle = circles[4];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(4, 1);
}
}
circle = circles[5];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(5, 1);
}
}
circle = circles[6];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(6, 1);
}
}
circle = circles[7];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(7, 1);
}
}
circle = circles[8];
if (circle) {
a = p.x - circle.x;
b = p.y - circle.y;
if (CIRCLE_RADIUS_2 - (a * a + b * b) >= 0) {
pattern.push(circle.i);
circles.splice(8, 1);
}
}
}
positions = [];
pattern = pattern.join("");
if (pattern) {
if (storedPatterns[pattern]) {
var app = storedPatterns[pattern].app;
if (!!app && !!app.src) {
if (storedPatterns.settings) {
if (storedPatterns.settings.lockDisabled) {
Bangle.setLCDPower(true);
}
}
Bangle.removeListener("drag", dragHandler);
load(app.src);
}
}
}
});
};
var debounceTimeoutId;
var debounce = (delay) => {
if (debounceTimeoutId) {
clearTimeout(debounceTimeoutId);
}
return new Promise((resolve) => {
debounceTimeoutId = setTimeout(() => {
debounceTimeoutId = undefined;
resolve();
}, delay || 500);
});
};
var sui = Bangle.setUI; var sui = Bangle.setUI;
Bangle.setUI = function (mode, cb) { Bangle.setUI = function (mode, cb) {
sui(mode, cb); sui(mode, cb);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -1,2 +1,3 @@
0.01: First release 0.01: First release
0.02: Fix dependancies, fix type to Purple 0.02: Fix typo to Purple
0.03: Added dependancy on Pedometer Widget

2
apps/showimg/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.1: Initial release
0.2: Fixed launcher image

View File

@ -1 +1 @@
E.toArrayBuffer(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAf////AHP/////AH//////AP/8AAAHAP4AAAAHAOAAMAAHAOAAeAAHAOAA+cAHAOAA/+AHAOAA/+AHAOAf/+AHAOA//+AHgOA///AHgOA///gDgOA/z/gDgOAfz/gDgOA///gDgOA///gDgOA//uADgOAf3+ADgOAP/+ADgOAD/8ADwOAB+4ADwOAA8AABwOAAAABBwOA8DgPxwOB/Dg/xwOB/jh/xwOB3zj5xwOB57nzxwOA4/njhwOA4/vHhwOA8f+PB4OAef+PB4OAef8eB4OAPP58A4OAHv/4A4OAH//wA4OAD//AA4OAA/8fn4PDgP///4P//////4P////9/wD///4AAA")) require("heatshrink").decompress(atob("mEwwkBIf4Aah//BRQAMDowUNC5AARC4YKKL5gTC+B3TCpAyIC5oNBEA4XNJwS4GC55pHC8TEHC57QHC4wSEC5YpEC6YwEC5oEEC5x3DC6ZHbC7PwcYxfNAYYXPJA4XQDAwKEBYQXJIoReHC5gMFAAojBC5QUIC5Y5JMgYXIUQYJFPggXMAwICCBAYXMCAQJDDwQUCC5QOCUwQdEC5QqFDghNFC5wrEC5gQDPgoTCDYYXFMAgXaCQoXJEwZ4FLQbhFC4imDAAglFC5QAGBgYXKIoYWIC5YYFG4ZkDC4YjCYYwAJC4gASC6THFH5pqGAAY"))

View File

@ -1,2 +1,3 @@
0.01: Initial import. 0.01: Initial import.
0.07: Add swipe to change screens. 0.07: Add swipe to change screens.
1.06: Misc memory and screen optimisations.

View File

@ -1,11 +1,9 @@
/* /*
Speed and Altitude [speedalt2] Speed and Altitude [speedalt2]
Mike Bennett mike[at]kereru.com Mike Bennett mike[at]kereru.com
0.01 : Initial 1.10 : add inverted colours
0.06 : Add Posn screen
0.07 : Add swipe to change screens same as BTN3
*/ */
var v = '1.05'; var v = '1.10';
/*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */ /*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */
var KalmanFilter = (function () { var KalmanFilter = (function () {
@ -173,6 +171,15 @@ var KalmanFilter = (function () {
var buf = Graphics.createArrayBuffer(240,160,2,{msb:true}); var buf = Graphics.createArrayBuffer(240,160,2,{msb:true});
let LED = // LED as minimal and only definition (as instance / singleton)
{ isOn: false // status on / off, not needed if you don't need to ask for it
, set: function(v) { // turn on w/ no arg or truey, else off
g.setColor((this.isOn=(v===undefined||!!v))?1:0,0,0).fillCircle(40,10,10); }
, reset: function() { this.set(false); } // turn off
, write: function(v) { this.set(v); } // turn on w/ no arg or truey, else off
, toggle: function() { this.set( ! this.isOn); } // toggle the LED
}, LED1 = LED; // LED1 as 'synonym' for LED
// Load fonts // Load fonts
//require("Font7x11Numeric7Seg").add(Graphics); //require("Font7x11Numeric7Seg").add(Graphics);
@ -183,17 +190,16 @@ var canDraw = 1;
var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time. var time = ''; // Last time string displayed. Re displayed in background colour to remove before drawing new time.
var tmrLP; // Timer for delay in switching to low power after screen turns off var tmrLP; // Timer for delay in switching to low power after screen turns off
var max = {}; var maxSpd = 0;
max.spd = 0; var maxAlt = 0;
max.alt = 0; var maxN = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data.
max.n = 0; // counter. Only start comparing for max after a certain number of fixes to allow kalman filter to have smoohed the data.
var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 1 = running in emulator. Supplies test values; var emulator = (process.env.BOARD=="EMSCRIPTEN")?1:0; // 1 = running in emulator. Supplies test values;
var wp = {}; // Waypoint to use for distance from cur position. var wp = {}; // Waypoint to use for distance from cur position.
function nxtWp(inc){ function nxtWp(){
cfg.wp+=inc; cfg.wp++;
loadWp(); loadWp();
} }
@ -227,6 +233,7 @@ function drawScrn(dat) {
if (!canDraw) return; if (!canDraw) return;
buf.clear(); buf.clear();
buf.setBgColor(0);
var n; var n;
n = dat.val.toString(); n = dat.val.toString();
@ -252,29 +259,21 @@ function drawScrn(dat) {
buf.setFontVector(35); buf.setFontVector(35);
buf.drawString(dat.unit,5,164); buf.drawString(dat.unit,5,164);
if ( dat.max ) drawMax(); // MAX display indicator drawMax(dat.max); // MAX display indicator
if ( dat.wp ) drawWP(); // Waypoint name drawWP(dat.wp); // Waypoint name
drawSats(dat.sats);
//Sats
if ( dat.sat ) {
if ( dat.age > 10 ) {
if ( dat.age > 90 ) dat.age = '>90';
drawSats('Age:'+dat.age);
}
else drawSats('Sats:'+dat.sats);
}
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
if ( pwrSav ) LED1.reset(); LED1.write(!pwrSav);
else LED1.set();
} }
function drawPosn(dat) { function drawPosn(dat) {
if (!canDraw) return; if (!canDraw) return;
buf.clear(); buf.clear();
buf.setBgColor(0);
var x, y; var x, y;
x=210; x=210;
@ -293,20 +292,12 @@ function drawPosn(dat) {
buf.drawString(dat.ew,x,y+70); buf.drawString(dat.ew,x,y+70);
//Sats drawSats(dat.sats);
if ( dat.sat ) {
if ( dat.age > 10 ) {
if ( dat.age > 90 ) dat.age = '>90';
drawSats('Age:'+dat.age);
}
else drawSats('Sats:'+dat.sats);
}
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
if ( pwrSav ) LED1.reset(); LED1.write(!pwrSav);
else LED1.set();
} }
@ -314,6 +305,8 @@ function drawClock() {
if (!canDraw) return; if (!canDraw) return;
buf.clear(); buf.clear();
buf.setBgColor(0);
var x, y; var x, y;
x=185; x=185;
y=0; y=0;
@ -329,19 +322,14 @@ function drawClock() {
g.reset(); g.reset();
g.drawImage(img,0,40); g.drawImage(img,0,40);
if ( pwrSav ) LED1.reset(); LED1.write(!pwrSav);
else LED1.set();
} }
function drawWP() { function drawWP(wp) {
var nm = wp.name; buf.setColor(3);
if ( nm == undefined || nm == 'NONE' || cfg.modeA ==1 ) nm = '';
buf.setColor(2);
buf.setFontAlign(0,1); //left, bottom buf.setFontAlign(0,1); //left, bottom
buf.setFontVector(48); buf.setFontVector(48);
buf.drawString(nm.substring(0,8),120,140); buf.drawString(wp,120,140);
} }
function drawSats(sats) { function drawSats(sats) {
@ -351,16 +339,15 @@ function drawSats(sats) {
buf.drawString(sats,240,160); buf.drawString(sats,240,160);
} }
function drawMax() { function drawMax(max) {
buf.setFontVector(30); buf.setFontVector(30);
buf.setColor(2); buf.setColor(2);
buf.setFontAlign(0,1); //centre, bottom buf.setFontAlign(0,1); //centre, bottom
buf.drawString('MAX',120,164); buf.drawString(max,120,164);
} }
function onGPS(fix) { function onGPS(fix) {
if ( emulator ) {
if ( emulator ) {
fix.fix = 1; fix.fix = 1;
fix.speed = 10 + (Math.random()*5); fix.speed = 10 + (Math.random()*5);
fix.alt = 354 + (Math.random()*50); fix.alt = 354 + (Math.random()*50);
@ -382,7 +369,12 @@ function onGPS(fix) {
var ns = ''; var ns = '';
var ew = ''; var ew = '';
var lon = '---.--'; var lon = '---.--';
var sats = '---';
// Waypoint name
var wpName = wp.name;
if ( wpName == undefined || wpName == 'NONE' ) wpName = '';
wpName = wpName.substring(0,8);
if (fix.fix) lf = fix; if (fix.fix) lf = fix;
@ -393,10 +385,9 @@ function onGPS(fix) {
if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed); if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed);
if ( cfg.altFilt ) lf.alt = altFilter.filter(lf.alt); if ( cfg.altFilt ) lf.alt = altFilter.filter(lf.alt);
lf.smoothed = 1; lf.smoothed = 1;
if ( max.n <= 15 ) max.n++; if ( maxN <= 15 ) maxN++;
} }
// Speed // Speed
if ( cfg.spd == 0 ) { if ( cfg.spd == 0 ) {
m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units m = require("locale").speed(lf.speed).match(/([0-9,\.]+)(.*)/); // regex splits numbers from units
@ -407,18 +398,19 @@ function onGPS(fix) {
if ( sp < 10 ) sp = sp.toFixed(1); if ( sp < 10 ) sp = sp.toFixed(1);
else sp = Math.round(sp); else sp = Math.round(sp);
if (isNaN(sp)) sp = '---';
if (parseFloat(sp) > parseFloat(max.spd) && max.n > 15 ) max.spd = sp; if (parseFloat(sp) > parseFloat(maxSpd) && maxN > 15 ) maxSpd = sp;
// Altitude // Altitude
al = lf.alt; al = lf.alt;
al = Math.round(parseFloat(al)/parseFloat(cfg.alt)); al = Math.round(parseFloat(al)/parseFloat(cfg.alt));
if (parseFloat(al) > parseFloat(maxAlt) && maxN > 15 ) maxAlt = al;
if (parseFloat(al) > parseFloat(max.alt) && max.n > 15 ) max.alt = al; if (isNaN(al)) al = '---';
// Distance to waypoint // Distance to waypoint
di = distance(lf,wp); di = distance(lf,wp);
if (isNaN(di)) di = 0; if (isNaN(di)) di = '--------';
// Age of last fix (secs) // Age of last fix (secs)
age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000)); age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000));
@ -432,29 +424,34 @@ function onGPS(fix) {
if ( lf.lon < 0 ) ew = 'W'; if ( lf.lon < 0 ) ew = 'W';
lon = Math.abs(lf.lon.toFixed(2)); lon = Math.abs(lf.lon.toFixed(2));
// Sats
if ( age > 10 ) {
sats = 'Age:'+Math.round(age);
if ( age > 90 ) sats = 'Age:>90';
}
else sats = 'Sats:'+lf.satellites;
} }
if ( cfg.modeA == 0 ) { if ( cfg.modeA == 0 ) {
// Speed // Speed
if ( showMax ) if ( showMax )
drawScrn({ drawScrn({
val:max.spd, val:maxSpd,
unit:cfg.spd_unit, unit:cfg.spd_unit,
sats:lf.satellites, sats:sats,
age:age, age:age,
max:true, max:'MAX',
wp:false, wp:''
sat:true
}); // Speed maximums }); // Speed maximums
else else
drawScrn({ drawScrn({
val:sp, val:sp,
unit:cfg.spd_unit, unit:cfg.spd_unit,
sats:lf.satellites, sats:sats,
age:age, age:age,
max:false, max:'SPD',
wp:false, wp:''
sat:true
}); });
} }
@ -462,23 +459,21 @@ function onGPS(fix) {
// Alt // Alt
if ( showMax ) if ( showMax )
drawScrn({ drawScrn({
val:max.alt, val:maxAlt,
unit:cfg.alt_unit, unit:cfg.alt_unit,
sats:lf.satellites, sats:sats,
age:age, age:age,
max:true, max:'MAX',
wp:false, wp:''
sat:true
}); // Alt maximums }); // Alt maximums
else else
drawScrn({ drawScrn({
val:al, val:al,
unit:cfg.alt_unit, unit:cfg.alt_unit,
sats:lf.satellites, sats:sats,
age:age, age:age,
max:false, max:'ALT',
wp:false, wp:''
sat:true
}); });
} }
@ -487,24 +482,22 @@ function onGPS(fix) {
drawScrn({ drawScrn({
val:di, val:di,
unit:cfg.dist_unit, unit:cfg.dist_unit,
sats:lf.satellites, sats:sats,
age:age, age:age,
max:false, max:'DST',
wp:true, wp:wpName
sat:true
}); });
} }
if ( cfg.modeA == 3 ) { if ( cfg.modeA == 3 ) {
// Position // Position
drawPosn({ drawPosn({
sats:lf.satellites, sats:sats,
age:age, age:age,
lat:lat, lat:lat,
lon:lon, lon:lon,
ns:ns, ns:ns,
ew:ew, ew:ew
sat:true
}); });
} }
@ -534,9 +527,9 @@ function nextFunc(dur) {
if ( cfg.modeA == 0 || cfg.modeA == 1 ) { if ( cfg.modeA == 0 || cfg.modeA == 1 ) {
// Spd+Alt mode - Switch between fix and MAX // Spd+Alt mode - Switch between fix and MAX
if ( dur < 2 ) showMax = !showMax; // Short press toggle fix/max display if ( dur < 2 ) showMax = !showMax; // Short press toggle fix/max display
else { max.spd = 0; max.alt = 0; } // Long press resets max values. else { maxSpd = 0; maxAlt = 0; } // Long press resets max values.
} }
else if ( cfg.modeA == 2) nxtWp(1); // Dist mode - Select next waypoint else if ( cfg.modeA == 2) nxtWp(); // Dist mode - Select next waypoint
onGPS(lf); onGPS(lf);
} }
@ -545,7 +538,7 @@ function updateClock() {
if (!canDraw) return; if (!canDraw) return;
if ( cfg.modeA != 4 ) return; if ( cfg.modeA != 4 ) return;
drawClock(); drawClock();
if ( emulator ) {max.spd++;max.alt++;} if ( emulator ) {maxSpd++;maxAlt++;}
} }
function startDraw(){ function startDraw(){
@ -585,7 +578,6 @@ function setButtons(){
setWatch(function(e){ setWatch(function(e){
pwrSav=!pwrSav; pwrSav=!pwrSav;
if ( pwrSav ) { if ( pwrSav ) {
LED1.reset();
var s = require('Storage').readJSON('setting.json',1)||{}; var s = require('Storage').readJSON('setting.json',1)||{};
var t = s.timeout||10; var t = s.timeout||10;
Bangle.setLCDTimeout(t); Bangle.setLCDTimeout(t);
@ -593,8 +585,8 @@ function setButtons(){
else { else {
Bangle.setLCDTimeout(0); Bangle.setLCDTimeout(0);
// Bangle.setLCDPower(1); // Bangle.setLCDPower(1);
LED1.set();
} }
LED1.write(!pwrSav);
}, BTN2, {repeat:true,edge:"falling"}); }, BTN2, {repeat:true,edge:"falling"});
// BTN3 - next screen // BTN3 - next screen
@ -690,7 +682,8 @@ var img = {
}; };
if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]); if ( cfg.colour == 1 ) img.palette = new Uint16Array([0,0xFFFF,0xFFF6,0xDFFF]);
if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xFF800,0xFAE0,0xF813]); if ( cfg.colour == 2 ) img.palette = new Uint16Array([0,0xF800,0xFAE0,0xF813]);
if ( cfg.colour == 3 ) img.palette = new Uint16Array([0xFFFF,0x007F,0x0054,0x0054]);
var SCREENACCESS = { var SCREENACCESS = {
withApp:true, withApp:true,

View File

@ -65,7 +65,8 @@
'< Back': function() { E.showMenu(appMenu); }, '< Back': function() { E.showMenu(appMenu); },
'Default' : function() { setColour(0); }, 'Default' : function() { setColour(0); },
'Hi Contrast' : function() { setColour(1); }, 'Hi Contrast' : function() { setColour(1); },
'Night' : function() { setColour(2); } 'Night' : function() { setColour(2); },
'Inverted' : function() { setColour(3); }
}; };
const kalMenu = { const kalMenu = {

View File

@ -31,3 +31,6 @@ Which one is which ?
![](A.jpg) ![](A.jpg)
![](B.jpg) ![](B.jpg)
Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)

View File

@ -19,3 +19,4 @@
Stop goal drawing outside widget area Stop goal drawing outside widget area
Fix issue with widget overwrite in large font mode Fix issue with widget overwrite in large font mode
Memory usage enhancements Memory usage enhancements
0.20: Fix issue where step count would randomly reset

View File

@ -53,7 +53,7 @@
E.on('kill', () => { E.on('kill', () => {
if (!settings) { loadSettings() } if (!settings) { loadSettings() }
let d = { let d = {
lastUpdate : lastUpdate.toISOString(), lastUpdate : lastUpdate.valueOf(),
stepsToday : stp_today, stepsToday : stp_today,
settings : settings, settings : settings,
}; };

2
core

@ -1 +1 @@
Subproject commit 2a8e872ecb143a10e53273b4d3473164e104e1d3 Subproject commit b033af017f6789a6a7777e6ef1428d94995a9b8b