Merge branch 'master' of github.com:espruino/BangleApps
68
apps.json
|
@ -1590,7 +1590,7 @@
|
|||
{
|
||||
"id": "widpedom",
|
||||
"name": "Pedometer widget",
|
||||
"version": "0.19",
|
||||
"version": "0.20",
|
||||
"description": "Daily pedometer widget",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
|
@ -3478,8 +3478,8 @@
|
|||
{
|
||||
"id": "speedalt2",
|
||||
"name": "GPS Adventure Sports II",
|
||||
"shortName": "GPS Adv Sport II",
|
||||
"version": "0.07",
|
||||
"shortName":"GPS Adv Sport II",
|
||||
"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.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
@ -4209,7 +4209,7 @@
|
|||
"id": "pastel",
|
||||
"name": "Pastel Clock",
|
||||
"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",
|
||||
"icon": "pastel.png",
|
||||
"dependencies": {"mylocation":"app", "widpedom":"app"},
|
||||
|
@ -4723,8 +4723,9 @@
|
|||
"id": "pebble",
|
||||
"name": "Pebble Clock",
|
||||
"shortName": "Pebble",
|
||||
"version": "0.04",
|
||||
"version": "0.06",
|
||||
"description": "A pebble style clock to keep the rebellion going",
|
||||
"dependencies": {"widpedom":"app"},
|
||||
"readme": "README.md",
|
||||
"icon": "pebble.png",
|
||||
"screenshots": [{"url":"pebble_screenshot.png"}],
|
||||
|
@ -4858,10 +4859,10 @@
|
|||
"id": "ptlaunch",
|
||||
"name": "Pattern Launcher",
|
||||
"shortName": "Pattern Launcher",
|
||||
"version": "0.10",
|
||||
"version": "0.11",
|
||||
"description": "Directly launch apps from the clock screen with custom patterns.",
|
||||
"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",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
@ -4876,11 +4877,11 @@
|
|||
"id": "rebble",
|
||||
"name": "Rebble Clock",
|
||||
"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",
|
||||
"readme": "README.md",
|
||||
"icon": "rebble.png",
|
||||
"dependencies": {"mylocation":"app"},
|
||||
"dependencies": {"mylocation":"app", "widpedom":"app"},
|
||||
"screenshots": [{"url":"screenshot_rebble.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
|
@ -4918,6 +4919,7 @@
|
|||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"clicompleteclk.app.js","url":"app.js"},
|
||||
{"name":"clicompleteclk.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"clicompleteclk.settings.js","url":"settings.js"}
|
||||
],
|
||||
|
@ -4941,7 +4943,7 @@
|
|||
{ "id": "pooqround",
|
||||
"name": "pooq Round watch face",
|
||||
"shortName":"pooq Round",
|
||||
"version":"0.00",
|
||||
"version":"0.01",
|
||||
"description": "A 24 hour analogue watchface with high legibility and a novel style.",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
|
@ -4977,14 +4979,52 @@
|
|||
"id": "showimg",
|
||||
"name": "simple image viewer",
|
||||
"shortName":"showImage",
|
||||
"version":"0.1",
|
||||
"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.",
|
||||
"version":"0.2",
|
||||
"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",
|
||||
"tags": "tool",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"showimg.app.js","url":"app.js"},
|
||||
{"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"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New clock
|
|
@ -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
|
||||
|
||||

|
||||
|
||||
## TODO
|
||||
* Show weather information
|
||||
|
||||
|
||||
## Creator
|
||||
Marco ([myxor](https://github.com/myxor))
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("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"))
|
|
@ -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");
|
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.5 KiB |
|
@ -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,
|
||||
});
|
||||
});
|
|
@ -0,0 +1 @@
|
|||
0.01: first release
|
|
@ -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).
|
||||
|
||||

|
||||
|
||||
## 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)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkBiIA/AH4A/AAkQgEBAREAC6oABdZQXkI6wuKC5iPUFxoXIOpoX/C6QFCC6IsCC6ZEDC/4XcPooXOFgoXQIgwX/C7IUFC5wsIC5ouCC6hcJC5h1DF9YwBChCPOAH4A/AH4Ap"))
|
|
@ -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);
|
After Width: | Height: | Size: 749 B |
After Width: | Height: | Size: 6.4 KiB |
|
@ -6,3 +6,4 @@
|
|||
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.08: Added dependancy on MyLocation
|
||||
0.09: Added dependancy on Pedometer Widget
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
0.03: Changed time+calendar font to LECO1976Regular, changed to slanting boot
|
||||
0.04: Fix widget hiding code (fix #1046)
|
||||
0.05: Fix typo in settings - Purple
|
||||
0.06: Added dependancy on Pedometer Widget
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.00: Initial check-in.
|
||||
0.01: Add tap-to-decorate feature. Bugfixes.
|
||||
|
|
|
@ -10,16 +10,18 @@ Either you'll like that, or you won't.
|
|||
|
||||
## 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.
|
||||
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
|
||||
annoying in an intentionally dark environment, so there is an option to disable it.
|
||||
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).
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@ class Options {
|
|||
Bangle.removeListener('drag', this.reactivator);
|
||||
this.emit('done');
|
||||
}
|
||||
g.clear(true);
|
||||
E.showMenu(m);
|
||||
}
|
||||
|
||||
|
@ -309,7 +308,7 @@ class Round {
|
|||
buffer: this.c.buffer, transparent: 0
|
||||
};
|
||||
this.options = new RoundOptions();
|
||||
this.timescales = [1000, 0, 60000, 900000];
|
||||
this.timescales = [1000, [1000, 60000], 60000, 900000];
|
||||
this.state = {};
|
||||
// Precomputed polygons for the border areas.
|
||||
this.tl = [0, 0, 58, 0, 0, 58];
|
||||
|
@ -323,13 +322,15 @@ class Round {
|
|||
this.r = this.xc - this.minR;
|
||||
}
|
||||
|
||||
reset() {this.state = {}; this.g.clear(true);}
|
||||
reset(clear) {this.state = {}; clear && this.g.clear(true);}
|
||||
|
||||
doIcons(which) {
|
||||
this.state[which] = null;
|
||||
this.render(new Date()); // Not quite right, I think.
|
||||
}
|
||||
|
||||
enhanceUntil(t) {this.enhance = t;}
|
||||
|
||||
pie(f, a0, a1, invert) {
|
||||
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);
|
||||
|
@ -369,17 +370,18 @@ class Round {
|
|||
const g = this.g;
|
||||
const b = this.b, bI = this.bI;
|
||||
const c = this.c, cI = this.cI;
|
||||
const e = d < this.enhance;
|
||||
const state = this.state;
|
||||
const options = this.options;
|
||||
const cal = options.calendric;
|
||||
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 tm = res < 3 && d.getMinutes() + ts / 60;
|
||||
const tm = (e || res < 3) && d.getMinutes() + ts / 60;
|
||||
const th = d.getHours() + d.getMinutes() / 60;
|
||||
const dd = cal > 1 && d.getDate();
|
||||
const dm = cal > 3 && d.getMonth();
|
||||
const dy = cal > 4 && d.getFullYear();
|
||||
const dd = (e || cal > 1) && d.getDate();
|
||||
const dm = (e || cal > 3) && d.getMonth();
|
||||
const dy = (e || cal > 4) && d.getFullYear();
|
||||
const xc = this.xc, yc = this.yc, r = this.r;
|
||||
const dlr = xc * 3/4, dlw = 8, dlhw = 4;
|
||||
|
||||
|
@ -475,7 +477,6 @@ class Clock {
|
|||
this.timescales = face.timescales;
|
||||
this.options = face.options;
|
||||
this.rates = {};
|
||||
this.faceUp = null;
|
||||
|
||||
this.options.on('done', () => this.start());
|
||||
|
||||
|
@ -485,7 +486,6 @@ class Clock {
|
|||
lock: () => {face.doIcons('locked'); this.active();},
|
||||
faceUp: up => {
|
||||
this.conservative = !up;
|
||||
this.faceUp = up;
|
||||
this.active();
|
||||
},
|
||||
twist: _ => this.options.autolight && Bangle.setLCDPower(true),
|
||||
|
@ -504,9 +504,15 @@ class Clock {
|
|||
this.options.resolution++;
|
||||
this.rates.clock = this.timescales[this.options.resolution];
|
||||
this.active();
|
||||
} else if (this.yX - this.yN < 20 && Date.now() - this.t0 > 500) {
|
||||
this.stop();
|
||||
this.options.interact();
|
||||
} else if (this.yX - this.yN < 20) {
|
||||
const now = new Date();
|
||||
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;
|
||||
}
|
||||
|
@ -520,7 +526,7 @@ class Clock {
|
|||
|
||||
redraw(rate) {
|
||||
const now = this.updated = new Date();
|
||||
if (this.refresh) this.face.reset();
|
||||
if (this.refresh) this.face.reset(true);
|
||||
this.refresh = false;
|
||||
rate = this.face.render(now, rate);
|
||||
if (rate !== this.rates.face) {
|
||||
|
@ -535,7 +541,7 @@ class Clock {
|
|||
this.exception && clearTimeout(this.exception);
|
||||
this.interval && clearInterval(this.interval);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
0.01: Initial creation of the pattern launch app
|
||||
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.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
|
|
@ -10,14 +10,21 @@ Then launch the linked apps directly from the clock screen by simply drawing the
|
|||
|
||||
## Add Pattern Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## Manage Pattern Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## Detailed Steps
|
||||
|
||||
|
|
Before Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
|
@ -119,8 +119,7 @@ var recognizeAndDrawPattern = () => {
|
|||
return new Promise((resolve) => {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
g.setColor(0, 0, 0);
|
||||
CIRCLES.forEach((circle) => drawCircle(circle));
|
||||
drawCirclesWithPattern([]);
|
||||
|
||||
var pattern = [];
|
||||
|
||||
|
@ -369,7 +368,6 @@ var drawAppWithPattern = (i, r, storedPatterns) => {
|
|||
offset: { x: 1, y: 3 + r.y },
|
||||
});
|
||||
|
||||
g.setColor(0, 0, 0);
|
||||
if (!storedPattern.wrappedAppName) {
|
||||
storedPattern.wrappedAppName = g
|
||||
.wrapString(app.name, g.getWidth() - 64)
|
||||
|
@ -490,7 +488,7 @@ var drawCircle = (circle, drawBuffer, scale) => {
|
|||
log("drawing circle");
|
||||
log({ x: x, y: y, r: r });
|
||||
|
||||
drawBuffer.fillCircle(x, y, r);
|
||||
drawBuffer.drawCircle(x, y, r);
|
||||
};
|
||||
|
||||
var cachedCirclesDrawings = {};
|
||||
|
@ -535,17 +533,15 @@ var drawCirclesWithPattern = (pattern, options) => {
|
|||
{ msb: true }
|
||||
);
|
||||
|
||||
drawBuffer.setColor(1);
|
||||
CIRCLES.forEach((circle) => drawCircle(circle, drawBuffer, scale));
|
||||
|
||||
drawBuffer.setColor(0);
|
||||
drawBuffer.setFontAlign(0, 0);
|
||||
drawBuffer.setFont("6x8", 4 * scale);
|
||||
drawBuffer.setFont("Vector", 40 * scale);
|
||||
pattern.forEach((circleIndex, patternIndex) => {
|
||||
var circle = CIRCLES[circleIndex];
|
||||
drawBuffer.drawString(
|
||||
patternIndex + 1,
|
||||
circle.x * scale,
|
||||
(circle.x + (scale === 1 ? 1 : 5)) * scale,
|
||||
circle.y * scale
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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 () {
|
||||
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;
|
||||
Bangle.setUI = function (mode, cb) {
|
||||
sui(mode, cb);
|
||||
|
|
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -1,2 +1,3 @@
|
|||
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
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.1: Initial release
|
||||
0.2: Fixed launcher image
|
|
@ -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"))
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: Initial import.
|
||||
0.07: Add swipe to change screens.
|
||||
1.06: Misc memory and screen optimisations.
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
/*
|
||||
Speed and Altitude [speedalt2]
|
||||
Mike Bennett mike[at]kereru.com
|
||||
0.01 : Initial
|
||||
0.06 : Add Posn screen
|
||||
0.07 : Add swipe to change screens same as BTN3
|
||||
1.10 : add inverted colours
|
||||
*/
|
||||
var v = '1.05';
|
||||
var v = '1.10';
|
||||
|
||||
/*kalmanjs, Wouter Bulten, MIT, https://github.com/wouterbulten/kalmanjs */
|
||||
var KalmanFilter = (function () {
|
||||
|
@ -173,6 +171,15 @@ var KalmanFilter = (function () {
|
|||
|
||||
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
|
||||
//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 tmrLP; // Timer for delay in switching to low power after screen turns off
|
||||
|
||||
var max = {};
|
||||
max.spd = 0;
|
||||
max.alt = 0;
|
||||
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 maxSpd = 0;
|
||||
var maxAlt = 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.
|
||||
|
||||
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.
|
||||
|
||||
function nxtWp(inc){
|
||||
cfg.wp+=inc;
|
||||
function nxtWp(){
|
||||
cfg.wp++;
|
||||
loadWp();
|
||||
}
|
||||
|
||||
|
@ -227,7 +233,8 @@ function drawScrn(dat) {
|
|||
if (!canDraw) return;
|
||||
|
||||
buf.clear();
|
||||
|
||||
buf.setBgColor(0);
|
||||
|
||||
var n;
|
||||
n = dat.val.toString();
|
||||
|
||||
|
@ -252,29 +259,21 @@ function drawScrn(dat) {
|
|||
buf.setFontVector(35);
|
||||
buf.drawString(dat.unit,5,164);
|
||||
|
||||
if ( dat.max ) drawMax(); // MAX display indicator
|
||||
if ( dat.wp ) drawWP(); // Waypoint name
|
||||
|
||||
//Sats
|
||||
if ( dat.sat ) {
|
||||
if ( dat.age > 10 ) {
|
||||
if ( dat.age > 90 ) dat.age = '>90';
|
||||
drawSats('Age:'+dat.age);
|
||||
}
|
||||
else drawSats('Sats:'+dat.sats);
|
||||
}
|
||||
|
||||
drawMax(dat.max); // MAX display indicator
|
||||
drawWP(dat.wp); // Waypoint name
|
||||
drawSats(dat.sats);
|
||||
|
||||
g.reset();
|
||||
g.drawImage(img,0,40);
|
||||
|
||||
if ( pwrSav ) LED1.reset();
|
||||
else LED1.set();
|
||||
LED1.write(!pwrSav);
|
||||
|
||||
}
|
||||
|
||||
function drawPosn(dat) {
|
||||
if (!canDraw) return;
|
||||
buf.clear();
|
||||
buf.setBgColor(0);
|
||||
|
||||
var x, y;
|
||||
x=210;
|
||||
|
@ -293,20 +292,12 @@ function drawPosn(dat) {
|
|||
buf.drawString(dat.ew,x,y+70);
|
||||
|
||||
|
||||
//Sats
|
||||
if ( dat.sat ) {
|
||||
if ( dat.age > 10 ) {
|
||||
if ( dat.age > 90 ) dat.age = '>90';
|
||||
drawSats('Age:'+dat.age);
|
||||
}
|
||||
else drawSats('Sats:'+dat.sats);
|
||||
}
|
||||
drawSats(dat.sats);
|
||||
|
||||
g.reset();
|
||||
g.drawImage(img,0,40);
|
||||
|
||||
if ( pwrSav ) LED1.reset();
|
||||
else LED1.set();
|
||||
LED1.write(!pwrSav);
|
||||
|
||||
}
|
||||
|
||||
|
@ -314,6 +305,8 @@ function drawClock() {
|
|||
if (!canDraw) return;
|
||||
|
||||
buf.clear();
|
||||
buf.setBgColor(0);
|
||||
|
||||
var x, y;
|
||||
x=185;
|
||||
y=0;
|
||||
|
@ -329,19 +322,14 @@ function drawClock() {
|
|||
g.reset();
|
||||
g.drawImage(img,0,40);
|
||||
|
||||
if ( pwrSav ) LED1.reset();
|
||||
else LED1.set();
|
||||
LED1.write(!pwrSav);
|
||||
}
|
||||
|
||||
function drawWP() {
|
||||
var nm = wp.name;
|
||||
if ( nm == undefined || nm == 'NONE' || cfg.modeA ==1 ) nm = '';
|
||||
buf.setColor(2);
|
||||
|
||||
function drawWP(wp) {
|
||||
buf.setColor(3);
|
||||
buf.setFontAlign(0,1); //left, bottom
|
||||
buf.setFontVector(48);
|
||||
buf.drawString(nm.substring(0,8),120,140);
|
||||
|
||||
buf.drawString(wp,120,140);
|
||||
}
|
||||
|
||||
function drawSats(sats) {
|
||||
|
@ -351,16 +339,15 @@ function drawSats(sats) {
|
|||
buf.drawString(sats,240,160);
|
||||
}
|
||||
|
||||
function drawMax() {
|
||||
function drawMax(max) {
|
||||
buf.setFontVector(30);
|
||||
buf.setColor(2);
|
||||
buf.setFontAlign(0,1); //centre, bottom
|
||||
buf.drawString('MAX',120,164);
|
||||
buf.drawString(max,120,164);
|
||||
}
|
||||
|
||||
function onGPS(fix) {
|
||||
|
||||
if ( emulator ) {
|
||||
if ( emulator ) {
|
||||
fix.fix = 1;
|
||||
fix.speed = 10 + (Math.random()*5);
|
||||
fix.alt = 354 + (Math.random()*50);
|
||||
|
@ -382,10 +369,15 @@ function onGPS(fix) {
|
|||
var ns = '';
|
||||
var ew = '';
|
||||
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 (lf.fix) {
|
||||
|
||||
// Smooth data
|
||||
|
@ -393,10 +385,9 @@ function onGPS(fix) {
|
|||
if ( cfg.spdFilt ) lf.speed = spdFilter.filter(lf.speed);
|
||||
if ( cfg.altFilt ) lf.alt = altFilter.filter(lf.alt);
|
||||
lf.smoothed = 1;
|
||||
if ( max.n <= 15 ) max.n++;
|
||||
if ( maxN <= 15 ) maxN++;
|
||||
}
|
||||
|
||||
|
||||
// Speed
|
||||
if ( cfg.spd == 0 ) {
|
||||
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);
|
||||
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
|
||||
al = lf.alt;
|
||||
al = Math.round(parseFloat(al)/parseFloat(cfg.alt));
|
||||
|
||||
if (parseFloat(al) > parseFloat(max.alt) && max.n > 15 ) max.alt = al;
|
||||
if (parseFloat(al) > parseFloat(maxAlt) && maxN > 15 ) maxAlt = al;
|
||||
if (isNaN(al)) al = '---';
|
||||
|
||||
// Distance to waypoint
|
||||
di = distance(lf,wp);
|
||||
if (isNaN(di)) di = 0;
|
||||
if (isNaN(di)) di = '--------';
|
||||
|
||||
// Age of last fix (secs)
|
||||
age = Math.max(0,Math.round(getTime())-(lf.time.getTime()/1000));
|
||||
|
@ -431,6 +423,13 @@ function onGPS(fix) {
|
|||
ew = 'E';
|
||||
if ( lf.lon < 0 ) ew = 'W';
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -438,23 +437,21 @@ function onGPS(fix) {
|
|||
// Speed
|
||||
if ( showMax )
|
||||
drawScrn({
|
||||
val:max.spd,
|
||||
val:maxSpd,
|
||||
unit:cfg.spd_unit,
|
||||
sats:lf.satellites,
|
||||
sats:sats,
|
||||
age:age,
|
||||
max:true,
|
||||
wp:false,
|
||||
sat:true
|
||||
max:'MAX',
|
||||
wp:''
|
||||
}); // Speed maximums
|
||||
else
|
||||
drawScrn({
|
||||
val:sp,
|
||||
unit:cfg.spd_unit,
|
||||
sats:lf.satellites,
|
||||
sats:sats,
|
||||
age:age,
|
||||
max:false,
|
||||
wp:false,
|
||||
sat:true
|
||||
max:'SPD',
|
||||
wp:''
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -462,23 +459,21 @@ function onGPS(fix) {
|
|||
// Alt
|
||||
if ( showMax )
|
||||
drawScrn({
|
||||
val:max.alt,
|
||||
val:maxAlt,
|
||||
unit:cfg.alt_unit,
|
||||
sats:lf.satellites,
|
||||
sats:sats,
|
||||
age:age,
|
||||
max:true,
|
||||
wp:false,
|
||||
sat:true
|
||||
max:'MAX',
|
||||
wp:''
|
||||
}); // Alt maximums
|
||||
else
|
||||
drawScrn({
|
||||
val:al,
|
||||
unit:cfg.alt_unit,
|
||||
sats:lf.satellites,
|
||||
sats:sats,
|
||||
age:age,
|
||||
max:false,
|
||||
wp:false,
|
||||
sat:true
|
||||
max:'ALT',
|
||||
wp:''
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -487,24 +482,22 @@ function onGPS(fix) {
|
|||
drawScrn({
|
||||
val:di,
|
||||
unit:cfg.dist_unit,
|
||||
sats:lf.satellites,
|
||||
sats:sats,
|
||||
age:age,
|
||||
max:false,
|
||||
wp:true,
|
||||
sat:true
|
||||
max:'DST',
|
||||
wp:wpName
|
||||
});
|
||||
}
|
||||
|
||||
if ( cfg.modeA == 3 ) {
|
||||
// Position
|
||||
drawPosn({
|
||||
sats:lf.satellites,
|
||||
drawPosn({
|
||||
sats:sats,
|
||||
age:age,
|
||||
lat:lat,
|
||||
lon:lon,
|
||||
ns:ns,
|
||||
ew:ew,
|
||||
sat:true
|
||||
ew:ew
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -534,9 +527,9 @@ function nextFunc(dur) {
|
|||
if ( cfg.modeA == 0 || cfg.modeA == 1 ) {
|
||||
// Spd+Alt mode - Switch between fix and MAX
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -545,7 +538,7 @@ function updateClock() {
|
|||
if (!canDraw) return;
|
||||
if ( cfg.modeA != 4 ) return;
|
||||
drawClock();
|
||||
if ( emulator ) {max.spd++;max.alt++;}
|
||||
if ( emulator ) {maxSpd++;maxAlt++;}
|
||||
}
|
||||
|
||||
function startDraw(){
|
||||
|
@ -585,7 +578,6 @@ function setButtons(){
|
|||
setWatch(function(e){
|
||||
pwrSav=!pwrSav;
|
||||
if ( pwrSav ) {
|
||||
LED1.reset();
|
||||
var s = require('Storage').readJSON('setting.json',1)||{};
|
||||
var t = s.timeout||10;
|
||||
Bangle.setLCDTimeout(t);
|
||||
|
@ -593,8 +585,8 @@ function setButtons(){
|
|||
else {
|
||||
Bangle.setLCDTimeout(0);
|
||||
// Bangle.setLCDPower(1);
|
||||
LED1.set();
|
||||
}
|
||||
LED1.write(!pwrSav);
|
||||
}, BTN2, {repeat:true,edge:"falling"});
|
||||
|
||||
// BTN3 - next screen
|
||||
|
@ -690,7 +682,8 @@ var img = {
|
|||
};
|
||||
|
||||
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 = {
|
||||
withApp:true,
|
||||
|
|
|
@ -65,7 +65,8 @@
|
|||
'< Back': function() { E.showMenu(appMenu); },
|
||||
'Default' : function() { setColour(0); },
|
||||
'Hi Contrast' : function() { setColour(1); },
|
||||
'Night' : function() { setColour(2); }
|
||||
'Night' : function() { setColour(2); },
|
||||
'Inverted' : function() { setColour(3); }
|
||||
};
|
||||
|
||||
const kalMenu = {
|
||||
|
|
|
@ -31,3 +31,6 @@ Which one is which ?
|
|||
|
||||

|
||||

|
||||
|
||||
|
||||
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/)
|
||||
|
|
|
@ -19,3 +19,4 @@
|
|||
Stop goal drawing outside widget area
|
||||
Fix issue with widget overwrite in large font mode
|
||||
Memory usage enhancements
|
||||
0.20: Fix issue where step count would randomly reset
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
E.on('kill', () => {
|
||||
if (!settings) { loadSettings() }
|
||||
let d = {
|
||||
lastUpdate : lastUpdate.toISOString(),
|
||||
lastUpdate : lastUpdate.valueOf(),
|
||||
stepsToday : stp_today,
|
||||
settings : settings,
|
||||
};
|
||||
|
|
2
core
|
@ -1 +1 @@
|
|||
Subproject commit 2a8e872ecb143a10e53273b4d3473164e104e1d3
|
||||
Subproject commit b033af017f6789a6a7777e6ef1428d94995a9b8b
|