Merge branch 'master' of https://github.com/peerdavid/BangleApps
96
apps.json
|
@ -77,7 +77,7 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.13",
|
||||
"version": "0.14",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
@ -132,7 +132,7 @@
|
|||
{
|
||||
"id": "health",
|
||||
"name": "Health Tracking",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Logs health data and provides an app to view it (requires firmware 2v10.100 or later)",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,health",
|
||||
|
@ -167,7 +167,7 @@
|
|||
{
|
||||
"id": "setting",
|
||||
"name": "Settings",
|
||||
"version": "0.37",
|
||||
"version": "0.38",
|
||||
"description": "A menu for setting up Bangle.js",
|
||||
"icon": "settings.png",
|
||||
"tags": "tool,system",
|
||||
|
@ -554,7 +554,7 @@
|
|||
{
|
||||
"id": "impwclock",
|
||||
"name": "Imprecise Word Clock",
|
||||
"version": "0.03",
|
||||
"version": "0.04",
|
||||
"description": "Imprecise word clock for vacations, weekends, and those who never need accurate time.",
|
||||
"icon": "clock-impword.png",
|
||||
"type": "clock",
|
||||
|
@ -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",
|
||||
|
@ -4014,11 +4014,12 @@
|
|||
{
|
||||
"id": "thermom",
|
||||
"name": "Thermometer",
|
||||
"version": "0.04",
|
||||
"description": "Displays the current temperature in degree Celsius, updated every 20 seconds",
|
||||
"version": "0.05",
|
||||
"description": "Displays the current temperature in degree Celsius/Fahrenheit (depending on locale), updates every 10 seconds with average of last 5 readings.",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"thermom.app.js","url":"app.js"},
|
||||
|
@ -4080,7 +4081,7 @@
|
|||
"id": "thermomF",
|
||||
"name": "Fahrenheit Temp",
|
||||
"version": "0.01",
|
||||
"description": "A modification of the Thermometer App to display temprature in Fahrenheit",
|
||||
"description": "[NOT RECOMMENDED] A modification of the Thermometer App to display temprature in Fahrenheit. Please use the 'Thermometer App' and install 'Languages' to get the temperature in the correct format for your locale.",
|
||||
"icon": "thermf.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS"],
|
||||
|
@ -4209,7 +4210,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"},
|
||||
|
@ -4724,8 +4725,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"}],
|
||||
|
@ -4859,10 +4861,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",
|
||||
|
@ -4877,11 +4879,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",
|
||||
|
@ -4919,6 +4921,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"}
|
||||
],
|
||||
|
@ -4942,7 +4945,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",
|
||||
|
@ -4978,8 +4981,8 @@
|
|||
"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 \"showimg.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"],
|
||||
|
@ -5004,5 +5007,60 @@
|
|||
{"name":"lapcounter.app.js","url":"app.js"},
|
||||
{"name":"lapcounter.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "pebbled",
|
||||
"name": "Pebble Clock with distance",
|
||||
"shortName": "Pebble + distance",
|
||||
"version": "0.1",
|
||||
"description": "Fork of Pebble Clock with distance in KM. Both step count and the distance are on the main screen. Default step length = 0.75m (can be changed in settings).",
|
||||
"readme": "README.md",
|
||||
"icon": "pebbled.png",
|
||||
"screenshots": [{"url":"pebble_screenshot.png"}],
|
||||
"type": "clock",
|
||||
"tags": "clock,distance",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"pebbled.app.js","url":"pebbled.app.js"},
|
||||
{"name":"pebbled.settings.js","url":"pebbled.settings.js"},
|
||||
{"name":"pebbled.img","url":"pebbled.icon.js","evaluate":true}
|
||||
]
|
||||
},
|
||||
{ "id": "circlesclock",
|
||||
"name": "Circles clock",
|
||||
"shortName":"Circles clock",
|
||||
"version":"0.02",
|
||||
"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"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ltherm",
|
||||
"name": "Localized Thermometer",
|
||||
"shortName": "Thermometer",
|
||||
"version": "0.01",
|
||||
"description": "Displays the current temperature in localized units.",
|
||||
"icon": "thermf.png",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"ltherm.app.js","url":"app.js"},
|
||||
{"name":"ltherm.img","url":"icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
0.04: Fix tapping at very bottom of list, exit on inactivity
|
||||
0.03: Add "Calculating" placeholder, update JSON save format
|
||||
0.02: Fix JSON save format
|
||||
0.01: First release
|
||||
0.02: Fix JSON save format
|
||||
0.03: Add "Calculating" placeholder, update JSON save format
|
||||
0.04: Fix tapping at very bottom of list, exit on inactivity
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAMGAAAAAAYDAAAAAAwBgAAAABgAwAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABAAQAAAABgAwAAAAAwBgAAAAAYDAAAAAAMGAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAP+AAAAAAf/AAAAAA//gAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAB//wAAAAA//gAAAAAf/AAAAAAP+AAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
require("heatshrink").decompress(atob("mEwgIurg/wAocMjAFDjEMIAkGAodggYFDoBLEAq4jFF4o7FI4pTFOLsP/AFDj/8Aoc//wFDv//As4vFHYpHFOLoAPA=="))
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New clock
|
||||
0.02: Fix icon & add battery warn functionality
|
|
@ -0,0 +1,21 @@
|
|||
# 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 (including charging and battery low)
|
||||
|
||||
## Screenshot
|
||||
|
||||

|
||||
|
||||
## TODO
|
||||
* Show weather information
|
||||
|
||||
## Creator
|
||||
Marco ([myxor](https://github.com/myxor))
|
||||
|
||||
## Icons
|
||||
Icons taken from [materialdesignicons](https://materialdesignicons.com) under Apache License 2.0
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwcCIf4ALv///gFCv0Agf+CJP/wAODAwPAEpAjCCIX8h4RMj/+g/8gP4CA4LBDoP/GpkH8EP4/8LIIRMAQIOCCJU/CgQOBEwMPI5ARCR4YRJgP/gB3CI5Z0CCIiABfHRfEj+BAoN+n4FBLIkP/8chwRBx5cC//8v4REhytDgYRCv//8fxEYwRFgfxA4I1FRgI1D+JHE/7FINZzCBAAc4CRU4/kB44FCjgRKLQRlBPQ4RHgYCB/jpBABB6BPoKzBCJYAGuD/vAB1JkgLJm3bAgUCpMnwDdCPwIFChu27dgAoMSCIP+FAQRB+AFBtoRBtgFByQCBRIIoBAocDtonBAQWQdgXAgVIAocDEAUNwEEyEHBYUSoE//gRCsI7BxvACIILDCIcBCIYFCCJ3/wIRCIIYRBI4h6CAoJrDLJYRDDwJ9LAoKhBoMDUIcEgFwUIQREgUBaAcIkhPCAAQzBAAUBdIhhDAAMGCIkAkAFEdAQAFA=="))
|
|
@ -0,0 +1,235 @@
|
|||
const locale = require("locale");
|
||||
const heatshrink = require("heatshrink");
|
||||
|
||||
const shoesIcon = heatshrink.decompress(atob("h0OwYJGgmAAgUBkgECgVJB4cSoAUDyEBkARDpADBhMAyQRBgVAkgmDhIUDAAuQAgY1DAAYA="));
|
||||
const heartIcon = heatshrink.decompress(atob("h0OwYOLkmQhMkgACByVJgESpIFBpEEBAIFBCgIFCCgsABwcAgQOCAAMSpAwDyBNM"));
|
||||
const powerIcon = heatshrink.decompress(atob("h0OwYQNsAED7AEDmwEDtu2AgUbtuABwXbBIUN23AAoYOCgEDFIgODABI"));
|
||||
const powerIconGreen = heatshrink.decompress(atob("h0OwYQNkAEDpAEDiQEDkmSAgUJkmABwVJBIUEyVAAoYOCgEBFIgODABI"));
|
||||
const powerIconRed = heatshrink.decompress(atob("h0OwYQNoAEDyAEDkgEDpIFDiVJBweSAgUJkmAAoYZDgQpEBwYAJA"));
|
||||
|
||||
const SETTINGS_FILE = "circlesclock.json";
|
||||
let settings;
|
||||
|
||||
function loadSettings() {
|
||||
settings = require("Storage").readJSON(SETTINGS_FILE, 1) || {
|
||||
'maxHR': 200,
|
||||
'stepGoal': 10000,
|
||||
'batteryWarn': 30
|
||||
};
|
||||
}
|
||||
|
||||
const colorFg = '#fff';
|
||||
const colorBg = '#000';
|
||||
const colorGrey = '#808080';
|
||||
const colorRed = '#ff0000';
|
||||
const colorGreen = '#00ff00';
|
||||
|
||||
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() {
|
||||
g.setColor(colorGrey);
|
||||
g.fillCircle(w2, h3, radiusOuter);
|
||||
|
||||
if (hrtValue != undefined) {
|
||||
const percent = hrtValue / settings.maxHR;
|
||||
drawGauge(w2, h3, percent, colorRed);
|
||||
}
|
||||
|
||||
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 : "-", 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);
|
||||
|
||||
let icon = powerIcon;
|
||||
let color = colorFg;
|
||||
if (Bangle.isCharging()) {
|
||||
color = colorGreen;
|
||||
icon = powerIconGreen;
|
||||
}
|
||||
else {
|
||||
if (settings.batteryWarn != undefined && battery <= settings.batteryWarn) {
|
||||
color = colorRed;
|
||||
icon = powerIconRed;
|
||||
}
|
||||
}
|
||||
g.setColor(color);
|
||||
g.drawString(battery + '%', w3, h3);
|
||||
|
||||
g.drawImage(icon, 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: 18 KiB |
After Width: | Height: | Size: 3.5 KiB |
|
@ -0,0 +1,43 @@
|
|||
(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),
|
||||
},
|
||||
'battery warn lvl': {
|
||||
value: "batteryWarn" in settings ? settings.batteryWarn : 30,
|
||||
min: 10,
|
||||
max : 100,
|
||||
step: 10,
|
||||
format: x => {
|
||||
return x + '%';
|
||||
},
|
||||
onchange: x => save('batteryWarn', x),
|
||||
},
|
||||
'< Back': back,
|
||||
});
|
||||
});
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New clock!
|
||||
0.02: Load steps from Health Tracking app (if installed)
|
||||
0.03: ...
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.1: New app
|
||||
0.01: New app
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("/wA/AH4A/AH4A/ACmsAEQuMlcAAD0rGBQKBFr4ADGBOsqwvjqwvJRsCRFF/8Gg4ADEZYQEgwvWg8+AAgwKCJgvQDgoABF5IRMF5xEBJpBhGCJwvNDQM4AYMNAAQaBnCAFCJ4vNIwQeBAAkxQAwGCmIRFFwIRDF64dDgwGBgwRNF/4v/F/4v/F/4v/F/4dJmIdECIkxF7MHFwUHhoACg4eCAYIACCJ4vNDQIgCAAgICKwoROF5yAEAAgtFCKAvQJpAAICJgvQgEGg4ADFxIwCAAcGBYovRADov6qwvjqwvJ1gvjEoIvHGASRgRoIuJGAYAhFxQA/AH4A/AH4A/ABQ"))
|
||||
require("heatshrink").decompress(atob("mEw4UA///A4N551ulxL/ACkK1QAG0ALBlNVAA1oBYOlBY9aBYO1BY9eBYOVBY9WBbf/+oIBr//BYlX//9BYN///VC599qtX6oBBqt9BYYRBr/1AIIdBBf4L/BY6bLZcb7MBau1BY9eBYOlBY9aBYMpBY9oBYMK1QAG0ALBAH4ASA"))
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
0.04: reset to clock after 2 mins of inactivity
|
||||
0.05: add Bangle 2 version
|
||||
0.06: Adds settings page (hide clocks or launchers)
|
||||
0.06: Adds setting for directly launching app on touch for Bangle 2
|
||||
0.07: Adds setting for directly launching app on touch for Bangle 2
|
||||
|
|
|
@ -7,3 +7,4 @@
|
|||
0.06: Fix daily health summary for movement (a line got deleted!)
|
||||
0.07: Added coloured bar charts
|
||||
0.08: Suppress bleed through of E.showMenu's when displaying bar charts
|
||||
0.09: Fix file naming so months are 1-based (not 0) (fix #1119)
|
||||
|
|
|
@ -27,7 +27,7 @@ Bangle.on("health", health => {
|
|||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||
|
||||
function getRecordFN(d) {
|
||||
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw";
|
||||
return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw";
|
||||
}
|
||||
function getRecordIdx(d) {
|
||||
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +
|
||||
|
|
|
@ -6,7 +6,7 @@ const DB_HEADER_LEN = 8;
|
|||
const DB_FILE_LEN = DB_HEADER_LEN + DB_RECORDS_PER_MONTH*DB_RECORD_LEN;
|
||||
|
||||
function getRecordFN(d) {
|
||||
return "health-"+d.getFullYear()+"-"+d.getMonth()+".raw";
|
||||
return "health-"+d.getFullYear()+"-"+(d.getMonth()+1)+".raw";
|
||||
}
|
||||
function getRecordIdx(d) {
|
||||
return (DB_RECORDS_PER_DAY*(d.getDate()-1)) +
|
||||
|
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 3.3 KiB |
|
@ -154,7 +154,8 @@ var locales = {
|
|||
month: "Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus", "< Back": "< Zurück" }
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
|
||||
},
|
||||
"en_US": {
|
||||
lang: "en_US",
|
||||
|
@ -327,13 +328,15 @@ var locales = {
|
|||
speed: "kmh",
|
||||
distance: { 0: "m", 1: "km" },
|
||||
temperature: "°C",
|
||||
ampm: { 0: "", 1: "" },
|
||||
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
|
||||
datePattern: { 0: "%A, %d. %B %Y", "1": "%d.%m.%y" }, // Sonntag, 1. März 2020 // 01.03.20
|
||||
abmonth: "Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez",
|
||||
month: "Jänner,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember",
|
||||
abday: "So,Mo,Di,Mi,Do,Fr,Sa",
|
||||
day: "Sonntag,Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag",
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus" }
|
||||
trans: { yes: "ja", Yes: "Ja", no: "nein", No: "Nein", ok: "ok", on: "an", off: "aus",
|
||||
"< Back": "< Zurück", "Delete": "Löschen", "Mark Unread": "Als ungelesen markieren" }
|
||||
},
|
||||
"en_IL": {
|
||||
lang: "en_IL",
|
||||
|
@ -369,7 +372,8 @@ var locales = {
|
|||
month: "enero,febrero,marzo,abril,mayo,junio,julio,agosto,septiembre,octubre,noviembre,diciembre",
|
||||
abday: "dom,lun,mar,mié,jue,vie,sáb",
|
||||
day: "domingo,lunes,martes,miércoles,jueves,viernes,sábado",
|
||||
trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off" }
|
||||
trans: { yes: "sí", Yes: "Sí", no: "no", No: "No", ok: "ok", on: "on", off: "off",
|
||||
"< Back": "< Atrás", "Delete": "Borrar ", "Mark Unread": "Marcar como no leído" }
|
||||
},
|
||||
"fr_BE": {
|
||||
lang: "fr_BE",
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# Thermometer
|
||||
|
||||
Localized Bangle.js 2 thermometer app. It also starts maintaining an average of the temperature to help lower the margin of error after 10 consecutive readings; due to the low quality die-thermometer.
|
|
@ -0,0 +1,25 @@
|
|||
function drawTemperature() {
|
||||
g.reset(1).clearRect(0,24,g.getWidth(),g.getHeight());
|
||||
g.setFont("6x8",2).setFontAlign(0,0);
|
||||
var x = g.getWidth()/2;
|
||||
var y = g.getHeight()/2 + 10;
|
||||
g.drawString("Temp", x, y - 45);
|
||||
g.setFontVector(70).setFontAlign(0,0);
|
||||
var h = E.getTemperature();
|
||||
if (avg.length < 10) {
|
||||
avg[avg.length] = h;
|
||||
} else {
|
||||
avg.shift();
|
||||
avg[avg.length] = h;
|
||||
h = ((avg[0] + avg[1] + avg[2] + avg[3] + avg[4] + avg[5] + avg[6] + avg[7] + avg[8] + avg[9]) / 10);
|
||||
}
|
||||
var t = require('locale').temp(h);
|
||||
g.drawString(t, x, y);
|
||||
}
|
||||
const avg = [];
|
||||
setInterval(function() {
|
||||
drawTemperature();
|
||||
}, 2000);
|
||||
E.showMessage("Loading...");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4AChGIxGAC6eIAQgARFgUIC9ReCAYJgSC7BHDF6gUBC6ovWI/5Hga/6P/ABsCkABDC/4XxkQXDkQuSAQwXPDQkAC6BBCkQDDC6MCmczFoIXQCQQXBDgQXP2EA2YXBncAhYXR3YXB3YXRCQWznYcCC6ICBAYYXPhYrBApAwPFyQqCIoYuRLwZgDAH4A/"))
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -20,3 +20,4 @@
|
|||
0.13: Add /*LANG*/ comments for internationalisation
|
||||
Add 'Delete All' option to message options
|
||||
Now update correctly when 'require("messages").clearAll()' is called
|
||||
0.14: Hide widget when all unread notifications are dismissed from phone
|
||||
|
|
|
@ -30,6 +30,10 @@ exports.pushMessage = function(event) {
|
|||
require("Storage").writeJSON("messages.json",messages);
|
||||
// if in app, process immediately
|
||||
if (inApp) return onMessagesModified(mIdx<0 ? {id:event.id} : messages[mIdx]);
|
||||
// if we've removed the last new message, hide the widget
|
||||
if (event.t=="remove" && !messages.some(m=>m.new)) {
|
||||
if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.hide();
|
||||
}
|
||||
// ok, saved now - we only care if it's new
|
||||
if (event.t!="add") {
|
||||
return;
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("MDABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"))
|
||||
require("heatshrink").decompress(atob("mEwwIHEgPAAocP+AFDv4FDgf/Aoc/AocB/4FDh4FEv4FEAgIFIDgQFR+FwAoeAAof8gAFDLoIFC/wyBAoQ4CAoXgAoh0CAtybCAoJPBAoahDAoMHAoicBAoM54EfAoJqCAoQUBAoYUBAoYCBAoXgZAIFC4AFCCgOAYYI1CZIRHB/AFDcwmAAoj9Dj6mCdoQaBAAYWDgA"))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.1: first release
|
|
@ -0,0 +1,22 @@
|
|||
# Pebble with distance and steps
|
||||
|
||||
- Forked from [Pebble](https://github.com/espruino/BangleApps/tree/master/apps/pebble)
|
||||
- Added distance in km (kilometers) based on step length (can be changed in settings and is equal 0.75m by default)
|
||||
- Battery warning changed to 15% instead of 30%
|
||||
|
||||

|
||||

|
||||
|
||||
Initially written by: [Hugh Barney](https://github.com/hughbarney)
|
||||
|
||||
Forked and changed by [RomanistHere](https://github.com/RomanistHere)
|
||||
|
||||
For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
||||
## How to measure step length
|
||||
|
||||
It's much easier than you think. When you're walking, just note number of current steps at two points and then see the distance in any map service. For example, your route from bus station to home. Write number of steps at bus station (let's say 3451) and when you entered your home (3921). You passed 3921 - 3451 = 470 steps. Then see the actual distance in Google maps. Let's say it shows 300 meters. So your step length (in settings) used in app should be 300 / 470 = 0.64. After you have set it, the displayed distance at the main screen should be more accurate.
|
||||
|
||||
## Plans
|
||||
|
||||
Make step length depend on height/sex/age for lazy ones who don't want to measure it.
|
After Width: | Height: | Size: 25 KiB |
|
@ -0,0 +1,129 @@
|
|||
Graphics.prototype.setFontLECO1976Regular42 = function(scale) {
|
||||
// Actual height 42 (41 - 0)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAA/AAAAAAAAH/AAAAAAAA//AAAAAAAP//AAAAAAB///AAAAAAP///AAAAAB////AAAAAf////AAAAD////4AAAAf////AAAAH////4AAAA////+AAAAA////wAAAAA///+AAAAAA///gAAAAAA//8AAAAAAA//gAAAAAAA/4AAAAAAAA/AAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAH/AAAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA//h////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////gD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4AAAH/AAA/4B/gH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAA////wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAAAAB/wAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA////x//AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/4B////AAA/wB////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA//gAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA/4AAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA////wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA/4B/wH/AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAA///////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAP+AAH/AAAAH+AAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), 46, atob("ERkmHyYmJiYmJCYmEQ=="), 60+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
Graphics.prototype.setFontLECO1976Regular22 = function(scale) {
|
||||
// Actual height 22 (21 - 0)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nA/+cD/5wP/nAAAAAAAAPwAA/gAD+AAPwAAAAAD+AAP4AA/gAAAAAAAAAAAAAcOAP//A//8D//wP//AHDgAcOAP//A//8D//wP//AHDgAAAAAAAAH/jgf+OB/44H/jj8OP/w4//Dj/8OPxw/4HD/gcP+Bw/4AAAAAAAP+AA/8AD/wQOHHA4c8D//wP/8A//gAD4AAfAAH/8A//wP//A84cDjhwIP/AA/8AB/wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8ABwAAAAAAAAD8AAP4AA/gAD8AAAAAAAAAAAEAAD+AB//A///v/D//gB/wABwAAAAAADgAA/wAf/4P8///wf/4AP8AAOAAAAAAAAAyAAHcAAPwAD/gAP/AA/8AA/AAH8AAMwAAAAAAAAAAAAADgAAOAAA4AAf8AD/wAP/AA/8AAOAAA4AADgAAAAAAAAAAD8AAfwAB/AAD8AAAAAAAADgAAOAAA4AADgAAOAAA4AADgAAAAAAAAAADgAAOAAA4AADgAAAAAAAAABwAB/AA/8A//gP/gA/wADwAAIAAAAAAD//wP//A//8D//wOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA4AcDgBwOAHA//8D//wP//A//8AABwAAHAAAcAAAAAAAA+f8D5/wPn/A+f8DhxwOHHA4ccDhxwP/HA/8cD/xwP/HAAAAAAAAOAHA4AcDhxwOHHA4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/wAP/AA/8AD/wAAHAAAcAABwAAHAA//8D//wP//A//8AAAAAAAA/98D/3wP/fA/98DhxwOHHA4ccDhxwOH/A4f8Dh/wOH/AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccDh/wOH/A4f8Dh/wAAAAAAAD4AAPgAA+AADgAAOAAA4AADgAAP//A//8D//wP//AAAAAAAAP//A//8D//wP//A4ccDhxwOHHA4ccD//wP//A//8D//wAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA//8D//wP//A//8AAAAAAAAOA4A4DgDgOAOA4AAAAAAAAOA/A4H8DgfwOA/AAAAAAAAB4AAPwAA/AAD8AAf4ABzgAPPAA8cAHh4AAAAAAAAAAAAHHAAccABxwAHHAAccABxwAHHAAccABxwAHHAAAAAAAAAOHAA4cADzwAPPAAf4AB/gAD8AAPwAAeAAB4AAAAAAAAA+AAD4AAPgAA+ecDh9wOH3A4fcDhwAP/AA/8AD/wAP/AAAAAAAAAP//4///j//+P//44ADjn/OOf845/zjnHOP8c4//zj//OP/84AAAAAAAP//A//8D//wP//A4cADhwAOHAA4cAD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA//8D//wP9/A/j8AAAAAAAA//8D//wP//A//8DgBwOAHA4AcDgBwOAHA4AcDgBwOAHAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA8A8D//wH/+AP/wAf+AAAAAAAAD//wP//A//8D//wOHHA4ccDhxwOHHA4ccDhxwOAHA4AcAAAAAAAA//8D//wP//A//8DhwAOHAA4cADhwAOHAA4cADgAAOAAAAAAD//wP//A//8D//wOAHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA//8D//wP//A//8ABwAAHAAAcAABwAP//A//8D//wP//AAAAAAAAP//A//8D//wP//AAAAAAAAOAHA4AcDgBwOAHA4AcDgBwOAHA//8D//wP//A//8AAAAAAAA//8D//wP//A//8AHwAA/AAP8AB/wAPn/A8f8DB/wIH/AAAAAAAAP//A//8D//wP//AAAcAABwAAHAAAcAABwAAHAAAAAAAAP//A//8D//wP//Af8AAP+AAH/AAD8AAHwAD/AB/wAf8AP+AA//8D//wP//AAAAAAAAP//A//8D//wP//AfwAAfwAAfwAAfwAAfwP//A//8D//wAAAAAAAAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//wP//A//8D//wAAAAAAAD//wP//A//8D//wOHAA4cADhwAOHAA/8AD/wAP/AA/8AAAAAP//A//8D//wP//A4AcDgBwOAHA4AcD//+P//4///j//+AAA4AADgAAAP//A//8D//wP//A4eADh+AOH8A4f4D/3wP/HA/8MD/wQAAAAAAAD/xwP/HA/8cD/xwOHHA4ccDhxwOHHA4f8Dh/wOH/A4f8AAAAAAAA4AADgAAOAAA//8D//wP//A//8DgAAOAAA4AADgAAAAAA//8D//wP//A//8AABwAAHAAAcAABwP//A//8D//wP//AAAADAAAPgAA/wAD/4AB/8AA/8AAfwAB/AA/8Af+AP/AA/wAD4AAMAAA4AAD+AAP/gA//8AH/wAB/AAf8Af/wP/4A/4AD/gAP/4AH/8AB/wAB/AB/8D//wP/gA/gADgAAIABA4AcDwDwPw/Afn4Af+AA/wAD/AA//AH5+A/D8DwDwOAHAgAEAAAAP/AA/8AD/wAP/AAAf8AB/wAH/AAf8D/wAP/AA/8AD/wAAAAAAAADh/wOH/A4f8Dh/wOHHA4ccDhxwOHHA/8cD/xwP/HA/8cAAAAAAAAf//9///3///f//9wAA3AADcAAMAAAOAAA/gAD/wAH/8AB/8AA/wAAPAAAEAAAAHAADcAANwAB3///f//9///wAA"), 32, atob("BwYLDg4UDwYJCQwMBgkGCQ4MDg4ODg4NDg4GBgwMDA4PDg4ODg4NDg4GDQ4MEg8ODQ8ODgwODhQODg4ICQg="), 22+(scale<<8)+(1<<16));
|
||||
};
|
||||
|
||||
const SETTINGS_FILE = "pebbleDistance.json";
|
||||
let settings;
|
||||
|
||||
function loadSettings() {
|
||||
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green', 'avStep': 0.75};
|
||||
}
|
||||
|
||||
var img = require("heatshrink").decompress(atob("oFAwkEogA/AH4A/AH4A/AH4A/AE8AAAoeXoAfeDQUBmcyD7A+Dh///8QD649CiAfaHwUvD4sEHy0DDYIfEICg+Cn4fHICY+DD4nxcgojOHwgfEIAYfRCIQaDD4ZAFD5r7DH4//kAfRCIZ/GAAnwD5p9DX44fTHgYSBf4ofVDAQEBl4fFUAgfOXoQzBgIfFBAIfPP4RAEAoYAB+cRiK/SG4h/WIBAfXIA7CBAAswD55AHn6fUIBMCD65AHl4gCmcziAfQQJqfQQJpiDgk0IDXxQLRAEECaBM+QgRYRYgUIA0CD4ggSQJiDCiAKBICszAAswD55AHABKBVD7BAFABIqBD5pAFABPxD55AOD6BADiIAJQAyxLABwf/gaAPAH4A/AH4ARA=="));
|
||||
|
||||
const h = g.getHeight();
|
||||
const w = g.getWidth();
|
||||
const ha = 2*h/5 - 11;
|
||||
const h2 = 3*h/5 - 19;
|
||||
const h3 = 7*h/8 - 10;
|
||||
|
||||
let batteryWarning = false;
|
||||
|
||||
function draw() {
|
||||
let date = new Date();
|
||||
let da = date.toString().split(" ");
|
||||
let timeStr = da[4].substr(0,5);
|
||||
const t = 6;
|
||||
const stps = getSteps();
|
||||
|
||||
// turn the warning on once we have dipped below 15%
|
||||
if (E.getBattery() < 15)
|
||||
batteryWarning = true;
|
||||
|
||||
// turn the warning off once we have dipped above 20%
|
||||
if (E.getBattery() > 20)
|
||||
batteryWarning = false;
|
||||
|
||||
g.reset();
|
||||
g.setColor(settings.bg);
|
||||
g.fillRect(0, 0, w, h2 - t);
|
||||
|
||||
// contrast bar
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(0, h2 - t, w, h2);
|
||||
|
||||
// day and steps
|
||||
if (settings.color == 'Blue' || settings.color == 'Red')
|
||||
g.setColor('#fff'); // white on blue or red best contrast
|
||||
else
|
||||
g.setColor('#000'); // otherwise black regardless of theme
|
||||
|
||||
g.setFontLECO1976Regular22();
|
||||
g.setFontAlign(0, -1);
|
||||
g.drawString(da[0].toUpperCase(), w/4, ha); // day of week
|
||||
g.drawString(stps, 3*w/4, ha);
|
||||
|
||||
// time
|
||||
// white on red for battery warning
|
||||
g.setColor(!batteryWarning ? g.theme.bg : '#f00');
|
||||
g.fillRect(0, h2, w, h3);
|
||||
|
||||
g.setFontLECO1976Regular42();
|
||||
g.setFontAlign(0, -1);
|
||||
g.setColor(!batteryWarning ? g.theme.fg : '#fff');
|
||||
g.drawString(timeStr, w/2, h2 + 8);
|
||||
|
||||
// contrast bar
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(0, h3, w, h3 + t);
|
||||
|
||||
// the bottom
|
||||
g.setColor(settings.bg);
|
||||
g.fillRect(0, h3 + t, w, h);
|
||||
|
||||
g.setColor(settings.bg);
|
||||
g.drawImage(img, w/2 + ((w/2) - 64)/2, -2, { scale: 1 });
|
||||
drawCalendar(((w/2) - 42)/2, 11, 42, 4, da[2]);
|
||||
|
||||
// distance
|
||||
if (settings.color == 'Blue' || settings.color == 'Red')
|
||||
g.setColor('#fff'); // white on blue or red best contrast
|
||||
else
|
||||
g.setColor('#000'); // otherwise black regardless of theme
|
||||
g.drawString((stps / 1000 * settings.avStep).toFixed(2) + ' KM', w/2, ha + 107);
|
||||
}
|
||||
|
||||
// at x,y width:wi thicknes:th
|
||||
function drawCalendar(x,y,wi,th,str) {
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(x, y, x + wi, y + wi);
|
||||
g.setColor(g.theme.bg);
|
||||
g.fillRect(x + th, y + th, x + wi - th, y + wi - th);
|
||||
g.setColor(g.theme.fg);
|
||||
|
||||
let hook_t = 6;
|
||||
// first calendar hook, one third in
|
||||
g.fillRect(x + (wi/3) - (th/2), y - hook_t, x + wi/3 + th - (th/2), y + hook_t);
|
||||
// second calendar hook, two thirds in
|
||||
g.fillRect(x + (2*wi/3) -(th/2), y - hook_t, x + 2*wi/3 + th - (th/2), y + hook_t);
|
||||
|
||||
g.setFontLECO1976Regular22();
|
||||
g.setFontAlign(0, 0);
|
||||
g.drawString(str, x + wi/2, y + wi/2 + th);
|
||||
}
|
||||
|
||||
function getSteps() {
|
||||
if (WIDGETS.wpedom !== undefined) {
|
||||
return WIDGETS.wpedom.getSteps();
|
||||
}
|
||||
return '0';
|
||||
}
|
||||
|
||||
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, 15000); // refresh every 15s
|
||||
draw();
|
||||
Bangle.setUI("clock");
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEw4kB78A/4ACj/fn8Rz/Or987+M38hv8Rn++z9Cn8y/9rj9Tr+diIAHiAxDgIOICpYADCywyNCxQYMSxouVGBIWODBDgRC7hGQJAwWRGAguSC7JICCyYXYJAQXWLyhICC94LKu8Gqk1gGD+AjQ//C6Ei2HS0AXQ/U2gEquFVC6Pyk8AlssmwXK3oGF+fi2Et0v2C4ONoAPFhsiwBfFhU0lvS2wXBsVUC4vSkwHF90Etlso0L6CSBkowEFwNgM5sImQwEFw4AJGAguQGAsQ6UvxAAQ2UloMYkQAU0MZmUjmYAQmUi+MRSowAM4UmiMRSowALhiKBC4MQGCIuBgAXCGCAuCgIXBiLGCFyQXCGAJINsToBC4cQgGMI50AIwYwCACAWEGAQAOFwowRCwwwPFw4wPCxAYNCxRJLCxYxKCxwyGORI"))
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,48 @@
|
|||
(function(back) {
|
||||
const SETTINGS_FILE = "pebbleDistance.json";
|
||||
|
||||
// initialize with default settings...
|
||||
let s = {'bg': '#0f0', 'color': 'Green', 'avStep': 0.75};
|
||||
|
||||
// ...and overwrite them with any saved values
|
||||
// This way saved values are preserved if a new version adds more settings
|
||||
const storage = require('Storage');
|
||||
let settings = storage.readJSON(SETTINGS_FILE, 1) || s;
|
||||
const saved = settings || {};
|
||||
for (const key in saved) {
|
||||
s[key] = saved[key];
|
||||
}
|
||||
|
||||
function save() {
|
||||
settings = s;
|
||||
storage.write(SETTINGS_FILE, settings);
|
||||
}
|
||||
|
||||
var color_options = ['Green','Orange','Cyan','Purple','Red','Blue'];
|
||||
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
|
||||
|
||||
E.showMenu({
|
||||
'': { 'title': 'Pebble Clock' },
|
||||
'< Back': back,
|
||||
'Color': {
|
||||
value: 0 | color_options.indexOf(s.color),
|
||||
min: 0, max: 5,
|
||||
format: v => color_options[v],
|
||||
onchange: v => {
|
||||
s.color = color_options[v];
|
||||
s.bg = bg_code[v];
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Step length': {
|
||||
value: 0.75 || s.avStep,
|
||||
min: 0.2,
|
||||
max: 1.5,
|
||||
step: 0.01,
|
||||
onchange : v => {
|
||||
s.avStep = v;
|
||||
save();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
After Width: | Height: | Size: 161 KiB |
|
@ -16,8 +16,8 @@ the ability to check the _exact_ time, hands free, without the impact on battery
|
|||
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.
|
||||
You can also override the system 12/24 hour setting just for this face here, since it's, well, a rather different experience than with numeric displays.
|
||||
|
||||
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.
|
||||
In some previous 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.
|
||||
|
||||
One other thing: there's some integration with system timers and alarms; they will show as small pips at the appropriate places
|
||||
in the day around the display. When they come within an hour, the pips turn to crosses relating to the minute hand, and the minute
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -40,3 +40,4 @@
|
|||
0.35: Change App/Widget settings to 'App Settings' so it fits on Bangle screen
|
||||
0.36: Added 'Utils' menu with helpful utilities for restoring Bangle.js
|
||||
0.37: Going into passkey menu now saves settings with passkey
|
||||
0.38: Restructed menus as per forum discussion
|
||||
|
|
|
@ -61,6 +61,37 @@ if (!settings) resetSettings();
|
|||
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
|
||||
|
||||
function showMainMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
/*LANG*/'Apps': ()=>showAppSettingsMenu(),
|
||||
/*LANG*/'Bluetooth': ()=>showBLEMenu(),
|
||||
/*LANG*/'System': ()=>showSystemMenu(),
|
||||
/*LANG*/'Alerts': ()=>showAlertsMenu(),
|
||||
/*LANG*/'Utils': ()=>showUtilMenu(),
|
||||
/*LANG*/'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() }
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
function showSystemMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'System' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'Theme': ()=>showThemeMenu(),
|
||||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||
/*LANG*/'Select Clock': ()=>showClockMenu(),
|
||||
/*LANG*/'Set Time': ()=>showSetTimeMenu()
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
function showAlertsMenu() {
|
||||
var beepMenuItem;
|
||||
if (BANGLEJS2) {
|
||||
beepMenuItem = {
|
||||
|
@ -91,12 +122,9 @@ function showMainMenu() {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': 'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
/*LANG*/'App Settings': ()=>showAppSettingsMenu(),
|
||||
/*LANG*/'BLE': ()=>showBLEMenu(),
|
||||
'': { 'title': 'Alerts' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'Beep': beepMenuItem,
|
||||
/*LANG*/'Vibration': {
|
||||
value: settings.vibrate,
|
||||
|
@ -119,23 +147,18 @@ function showMainMenu() {
|
|||
updateOptions();
|
||||
if ("qmsched" in WIDGETS) WIDGETS["qmsched"].draw();
|
||||
},
|
||||
},
|
||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||
/*LANG*/'Select Clock': ()=>showClockMenu(),
|
||||
/*LANG*/'Set Time': ()=>showSetTimeMenu(),
|
||||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||
/*LANG*/'Theme': ()=>showThemeMenu(),
|
||||
/*LANG*/'Utils': ()=>showUtilMenu(),
|
||||
/*LANG*/'Turn Off': ()=>{ if (Bangle.softOff) Bangle.softOff(); else Bangle.off() },
|
||||
}
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
}
|
||||
|
||||
|
||||
function showBLEMenu() {
|
||||
var hidV = [false, "kbmedia", "kb", "joy"];
|
||||
var hidN = ["Off", "Kbrd & Media", "Kbrd","Joystick"];
|
||||
E.showMenu({
|
||||
'': { 'title': 'Bluetooth' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'Make Connectable': ()=>makeConnectable(),
|
||||
'BLE': {
|
||||
|
@ -190,7 +213,7 @@ function showThemeMenu() {
|
|||
}
|
||||
var m = E.showMenu({
|
||||
'':{title:'Theme'},
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'Dark BW': ()=>{
|
||||
upd({
|
||||
fg:cl("#fff"), bg:cl("#000"),
|
||||
|
@ -335,7 +358,7 @@ function showWhitelistMenu() {
|
|||
function showLCDMenu() {
|
||||
const lcdMenu = {
|
||||
'': { 'title': 'LCD' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'LCD Brightness': {
|
||||
value: settings.brightness,
|
||||
min: 0.1,
|
||||
|
@ -447,7 +470,7 @@ function showLCDMenu() {
|
|||
function showLocaleMenu() {
|
||||
const localemenu = {
|
||||
'': { 'title': 'Locale' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'Time Zone': {
|
||||
value: settings.timezone,
|
||||
min: -11,
|
||||
|
@ -551,7 +574,7 @@ function showClockMenu() {
|
|||
'': {
|
||||
'title': 'Select Clock',
|
||||
},
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
};
|
||||
clockApps.forEach((app, index) => {
|
||||
var label = app.name;
|
||||
|
@ -578,7 +601,7 @@ function showSetTimeMenu() {
|
|||
'': { 'title': 'Set Time' },
|
||||
'< Back': function () {
|
||||
setTime(d.getTime() / 1000);
|
||||
showMainMenu();
|
||||
showSystemMenu();
|
||||
},
|
||||
'Hour': {
|
||||
value: d.getHours(),
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.1: Initial release
|
||||
0.2: Fixed launcher image
|
|
@ -1,3 +1,3 @@
|
|||
Displays an image. I use this app to show my vaccination certificate.
|
||||
The image is read from the file "showimage.user.img".
|
||||
The image is read from the file "showimg.user.img".
|
||||
Returns to watch face after 60s/button push.
|
||||
|
|
|
@ -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,4 @@
|
|||
0.01: Initial import.
|
||||
0.07: Add swipe to change screens.
|
||||
1.06: Misc memory and screen optimisations.
|
||||
1.10: ...
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -1 +1 @@
|
|||
require("heatshrink").decompress(atob("AH4A/ACXd7vQC6vUpoBBDaQXEDaQXIDZwXMAIQZHC4R6BAAIZJDAwXIDY4XHAAodJ7oXMDpQXSAAiRHhoWN7zFLDY/e9ve9zeMhvQCIIBFC5ARIC5oVNC5EOCpwABC4vuCZYXPCIwXOCJAAFC5gAJ8AXFCpwuHgDjCFqQXC6lN6gbFf5gXEAInd6AXVDYndhoXKBoIbMC5QZLC44AFDpIXNDpQXdhoYMAAbwIC6oZQbxhOKC5gbKC6BUGC6oA/AHgA=="))
|
||||
require("heatshrink").decompress(atob("mEw4UA///sH8ov+8GyJf4AIgt8BZV9voNIBYQNIBYgNGBYwMEBYNVqoMEoALGBoYLDBQILCAQVQBYoOEBZIABBYUAgILGsBiEBodWy2gN4soywACBYcI1QJDBYoJFBYkCBQ2qBYUKBIoLHBAQLHBAYACBYwAEwALBgwKG1S/DC4wWCa4Y3Efa19mALKvrLDfY7XGBwjvVBYjuHfYgLLBg4LEAAMVBZQNEBZBPCBZQA+A"))
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
0.02: New App!
|
||||
0.03: Improved messages and added Celsius sign
|
||||
0.04: Make temperature value readable on smaller screens
|
||||
0.05: Use temperature from current locale
|
||||
Update every 10s, average last 5 readings
|
||||
Changes based on #1092
|
||||
|
|
|
@ -1,13 +1,27 @@
|
|||
// history of temperature readings
|
||||
var history = [];
|
||||
|
||||
|
||||
// When we get temperature...
|
||||
function onTemperature(p) {
|
||||
g.reset(1).clearRect(0,24,g.getWidth(),g.getHeight());
|
||||
var rect = Bangle.appRect;
|
||||
g.reset(1).clearRect(rect.x, rect.y, rect.x2, rect.y2);
|
||||
g.setFont("6x8",2).setFontAlign(0,0);
|
||||
var x = g.getWidth()/2;
|
||||
var y = g.getHeight()/2 + 10;
|
||||
var x = (rect.x+rect.x2)/2;
|
||||
var y = (rect.y+rect.y2)/2 + 10;
|
||||
g.drawString("Temperature:", x, y - 45);
|
||||
g.setFontVector(g.getWidth() > 200 ? 70 : 40).setFontAlign(0,0);
|
||||
g.drawString(p.temperature.toFixed(1) + " °C", x, y);
|
||||
g.setFontVector(g.getWidth() > 200 ? 70 : 50).setFontAlign(0,0);
|
||||
|
||||
// Average the last 5 temperature readings
|
||||
while (history.length>4) history.shift();
|
||||
history.push(p.temperature);
|
||||
var avrTemp = E.sum(history) / history.length;
|
||||
// Draw the temperature
|
||||
var t = require('locale').temp(avrTemp).replace("'","°");
|
||||
g.drawString(t, x, y);
|
||||
}
|
||||
|
||||
// Gets the temperature in the most accurate way (pressure sensor or inbuilt thermistor)
|
||||
function drawTemperature() {
|
||||
if (Bangle.getPressure) {
|
||||
Bangle.getPressure().then(onTemperature);
|
||||
|
@ -18,11 +32,10 @@ function drawTemperature() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
setInterval(function() {
|
||||
drawTemperature();
|
||||
}, 20000);
|
||||
drawTemperature();
|
||||
}, 10000);
|
||||
E.showMessage("Reading temperature...");
|
||||
drawTemperature();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
|
After Width: | Height: | Size: 2.4 KiB |
|
@ -293,9 +293,9 @@ Bangle.on('swipe', dir => {
|
|||
else next();
|
||||
});
|
||||
|
||||
// close launcher when lcd is off
|
||||
Bangle.on('lcdPower', on => {
|
||||
if(!on) return load();
|
||||
// close launcher when screen is locked
|
||||
Bangle.on('lock', on => {
|
||||
if(on) return load();
|
||||
});
|
||||
|
||||
if (process.env.HWVERSION == 1) {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
0.01: New watch face
|
||||
0.02: Use Bangle.setUI for button/launcher handling
|
||||
0.03: Bangle.js 2 support
|
|
@ -1,3 +0,0 @@
|
|||
0.1: New watch face
|
||||
0.2: Use Bangle.setUI for button/launcher handling
|
||||
0.3: Bangle.js 2 support
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*/
|
||||
|
||||
var fs = require("fs");
|
||||
var heatshrink = require("../core/lib/heatshrink");
|
||||
var acorn;
|
||||
try {
|
||||
acorn = require("acorn");
|
||||
|
@ -59,6 +60,7 @@ const STORAGE_KEYS = ['name', 'url', 'content', 'evaluate', 'noOverwite', 'suppo
|
|||
const DATA_KEYS = ['name', 'wildcard', 'storageFile', 'url', 'content', 'evaluate'];
|
||||
const FORBIDDEN_FILE_NAME_CHARS = /[,;]/; // used as separators in appid.info
|
||||
const VALID_DUPLICATES = [ '.tfmodel', '.tfnames' ];
|
||||
const GRANDFATHERED_ICONS = ["s7clk", "snek", "astral", "alpinenav", "slomoclock", "arrow", "pebble", "rebble"];
|
||||
|
||||
function globToRegex(pattern) {
|
||||
const ESCAPE = '.*+-?^${}()|[]\\';
|
||||
|
@ -175,6 +177,23 @@ apps.forEach((app,appIdx) => {
|
|||
for (const key in file) {
|
||||
if (!STORAGE_KEYS.includes(key)) ERROR(`App ${app.id} file ${file.name} has unknown key ${key}`);
|
||||
}
|
||||
// warn if JS icon is the wrong size
|
||||
if (file.name == app.id+".img") {
|
||||
let icon;
|
||||
let match = fileContents.match(/E\.toArrayBuffer\(atob\(\"([^"]*)\"\)\)/);
|
||||
if (match) icon = Buffer.from(match[1], 'base64');
|
||||
else {
|
||||
match = fileContents.match(/require\(\"heatshrink\"\)\.decompress\(\s*atob\(\s*\"([^"]*)\"\s*\)\s*\)/);
|
||||
if (match) icon = heatshrink.decompress(Buffer.from(match[1], 'base64'));
|
||||
else ERROR(`JS icon ${file.name} does not match the pattern 'require("heatshrink").decompress(atob("..."))'`);
|
||||
}
|
||||
if (match) {
|
||||
if (icon[0] > 48 || icon[0] < 24 || icon[1] > 48 || icon[1] < 24) {
|
||||
if (GRANDFATHERED_ICONS.includes(app.id)) WARN(`JS icon ${file.name} should be 48x48px (or slightly under) but is instead ${icon[0]}x${icon[1]}px`);
|
||||
else ERROR(`JS icon ${file.name} should be 48x48px (or slightly under) but is instead ${icon[0]}x${icon[1]}px`);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
let dataNames = [];
|
||||
(app.data||[]).forEach((data)=>{
|
||||
|
|
|
@ -11,7 +11,7 @@ if (window.location.host=="banglejs.com") {
|
|||
'This is not the official Bangle.js App Loader - you can try the <a href="https://banglejs.com/apps/">Official Version</a> here.';
|
||||
}
|
||||
|
||||
var RECOMMENDED_VERSION = "2v10";
|
||||
var RECOMMENDED_VERSION = "2v11";
|
||||
// could check http://www.espruino.com/json/BANGLEJS.json for this
|
||||
|
||||
// We're only interested in Bangles
|
||||
|
|