Merge branch 'master' into widbars

master
Gordon Williams 2021-12-01 09:00:08 +00:00 committed by GitHub
commit a9530f64ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 614 additions and 22 deletions

View File

@ -872,7 +872,7 @@
"id": "widbatpc",
"name": "Battery Level Widget (with percentage)",
"shortName": "Battery Widget",
"version": "0.13",
"version": "0.14",
"description": "Show the current battery level and charging status in the top right of the clock, with charge percentage",
"icon": "widget.png",
"type": "widget",
@ -4106,9 +4106,10 @@
"id": "pastel",
"name": "Pastel Clock",
"shortName": "Pastel",
"version": "0.07",
"description": "A Configurable clock with custom fonts and background",
"version": "0.08",
"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"},
"screenshots": [{"url":"screenshot_pastel.png"}],
"type": "clock",
"tags": "clock",
@ -4433,7 +4434,7 @@
"shortName": "AuthWatch",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}],
"version": "0.01",
"version": "0.02",
"description": "Google Authenticator compatible tool.",
"tags": "tool",
"interface": "interface.html",
@ -4571,6 +4572,73 @@
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"widbars.wid.js","url":"widget.js"}
]
},
{
"id":"a_speech_timer",
"name":"A Speech Timer",
"icon": "app.png",
"version":"1.00",
"description": "A timer designed to help keeping your speeches and presentations to time.",
"tags": "tool,timer",
"readme":"README.md",
"supports":["BANGLEJS2"],
"storage": [
{"name":"a_speech_timer.app.js","url":"app.js"},
{"name":"a_speech_timer.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "sensible",
"name": "SensiBLE",
"shortName": "SensiBLE",
"version": "0.02",
"description": "Collect, display and advertise real-time sensor data.",
"icon": "sensible.png",
"type": "app",
"tags": "tool,sensors",
"supports" : [ "BANGLEJS2" ],
"allow_emulator": true,
"readme": "README.md",
"storage": [
{ "name": "sensible.app.js", "url": "sensible.js" },
{ "name": "sensible.img", "url": "sensible-icon.js", "evaluate": true }
]
},
{ "id": "mylocation",
"name": "My Location",
"shortName":"My Location",
"icon": "mylocation.png",
"type": "app",
"screenshots": [{"url":"screenshot_1.png"}],
"version":"0.01",
"description": "Sets and stores the lat and long of your preferred City or it can be set from the GPS. mylocation.json can be used by other apps that need your main location lat and lon. See README",
"readme": "README.md",
"tags": "tool,utility",
"supports": ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"mylocation.app.js","url":"mylocation.app.js"},
{"name":"mylocation.img","url":"mylocation.icon.js","evaluate": true }
],
"data": [
{"name":"mylocation.json"}
]
},
{
"id": "pebble",
"name": "Pebble Clock",
"shortName": "Pebble",
"version": "0.01",
"description": "A pebble style clock to keep the rebellion going",
"readme": "README.md",
"icon": "pebble.png",
"screenshots": [{"url":"pebble_screenshot.png"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"pebble.app.js","url":"pebble.app.js"},
{"name":"pebble.img","url":"pebble.icon.js","evaluate":true}
]
}
]

View File

@ -0,0 +1 @@
1.00: Release (2021/12/01)

View File

@ -0,0 +1,16 @@
# A Speech Timer
* A timer designed to help keeping your speeches and presentations to time
* Vibrates 1-2-3 times and changes screen color within the target time range.
* Example for a 5 to 7 minutes speech: vibrates once at 5:00 (green), twice at 6:00 (yellow), thrice at 7:00 (red).
* Use the buttons to start a timer
* Swipe left or right to choose different target times
* Touching the timer on the upper part of the screen locks (or unlocks) the buttons to prevent accidental changes
![](screenshot0.png)
![](screenshot1.png)
![](screenshot2.png)
![](screenshot3.png)
## Creator
[@alainsaas](https://github.com/alainsaas)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwgP//kAj//AAP5/+PApH7//PAonvAoXzAonj//nApHggEHAoWAgA5BAAJCCAoU/IYIFCv///w0CAonrv/HAoXLv+DAogLFgPeAoV+nlOAoV4/8+AoV79+eFIVzAof7u/v5xBCs4FL84FE//O74FBu4FB64FD73TAoNz/+eAoV5IIIFCvl8vwFCv8A/wFDO4IFFFIQFCGoSVFUIqtDh65D/1vYof+Y4LLDw7dD/0ndIYRCeoQFC/P/z/+i///oFBGoX8gEfAgI="))

173
apps/a_speech_timer/app.js Normal file
View File

@ -0,0 +1,173 @@
Graphics.prototype.setFontMichroma36 = function() {
g.setFontCustom(atob("AAAAAAAAAAAAAAAAeAAAAAeAAAAAeAAAAAeAAAAAAAAAAAAAAAAAAAAAAAGAAAAA+AAAAD+AAAAP+AAAA/8AAAD/wAAAf/AAAB/4AAAH/gAAAf+AAAB/4AAAH/gAAAf+AAAAfwAAAAfAAAAAcAAAAAAAAAAAAAAAAAAAAAAAA///AAD///wAH///4AP///8APwAD+APAAAeAeAAAeAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAPAeAAAeAPAAAeAPwAD+AP///8AH///4AD///wAA///AAAAAAAAAAAAAAAAAAAAAEAAAAAOAAAAAfAAAAA+AAAAB8AAAAD8AAAAH4AAAAPwAAAAPgAAAAfAAAAAf///+Af///+Af///+Af///+AAAAAAAAAAAAAAAAAAAAAAAAAA/Af+AD/A/+AH/B/+AP/D/+APwD4eAPADweAfADweAeADweAeADweAeADweAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAHgeAeAPgeAeAPAeAeAPAeAeAPAeAeAPAeAfAPAeAPw/AeAP/+AeAH/+AeAD/8AeAB/wAOAAAAAAAAAAAAAAAAAAAAAAAAAB8APgAD8AP4AH8AP8AP8AP8APgAB+AfAAAeAeAAAeAeAAAPAeAAAPAeAAAPAeAAAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAPAeAeAeAfAeAeAPx/h+AP///+AH///8AD///4AB/h/gAAAAAAAAAAAAAAAAAAAAAAeAAAAA/AAAAA/AAAAB/AAAAD/AAAAH/AAAAPvAAAAPPAAAAfPAAAA+PAAAB8PAAAD4PAAADwPAAAHwPAAAPgPAAAfAPAAA+APAAA8APAAB8APAAD4APAAHwAPAAPgAPAAPAAPAAfAAPAAf///+Af///+Af///+Af///+AAAAPAAAAAPAAAAAPAAAAAPAAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAf/8PgAf/8P4Af/8P8Af/8P8AeB4A+AeB4AeAeDwAeAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAPAeDwAfAeDwAeAeD4A+AeD+D+AeB//8AeB//4AeA//4AAAP/AAAAAAAAAAAAAAAAAAAAAAAAAAA///AAD///wAH///4AH///8AP4fB+APAeAeAfA8AeAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAfA8APAPA+AeAPgeAeAP8fh+AH8f/8AD8P/8AA8H/4AAAB/gAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAeAAAAAeAAAAAeAAAAAeAAAAAeAAACAeAAAGAeAAAOAeAAAeAeAAA+AeAAD+AeAAH8AeAAP4AeAAfwAeAA/gAeAB/AAeAD+AAeAP4AAeAfwAAeA/gAAeB/AAAeD+AAAeH8AAAefwAAAe/gAAAf/AAAAf+AAAAf8AAAAf4AAAAfgAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAMAAB+B/wAD/j/4AH/3/8AP///+AP//A+AfB+AeAeA+AeAeA+APAeA+APAeA+APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA8APAeA+APAeA+APAeA+APAeA+AOAeA+AeAPh/A+AP///+AP/3/8AH/3/8AB/D/wAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAD/4HAAH/8HwAP/+H4AP5/H8AfAfA8AeAPAeAeAPAeAeAPAeAeAHgfAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHgPAeAHAPAeAPAOAeAPAeAPAPAeAPwfB+AP///8AH///4AD///wAA///AAAAAAAAAAAAAAAAAAAAAAAAAAAB8DwAAB8HwAAB8HwAAB8DwAAAAAAAAAAAAA"), 46, atob("CBIkESMjJCMjIyMjCA=="), 36+(1<<8)+(1<<16));
};
Graphics.prototype.setFontMichroma16 = function(scale) {
g.setFontCustom(atob("AAAAGAAYAAAAGAB4A/APwD4AeADgAAAAAAA/8H/4YBjAGMAcwBzAHMAcwBzAHMAYYBh/+D/wAAAAABgAOABwAGAA//h/+AAAAAA4+Hn4YZjhmMOYw5jDmMMYwxjDGOMYYxh/GD4YAAAAADBwcHhgGOAYwBzHHMccxxzHHMcc5xhnGH/4PfAAAAAAAOAB4APgB2AGYAxgHGA4YDBgYGD/+P/4AOAAYAAAAAD+cP547BjsGOwc7BzsHOwc7BzsHOwY7zjv+APgAAAAAD/wf/hmGOYYxhzGHMYcxhzGHOYYZhh3uDP4AeAAAEAA4ADgAOAI4DjgeODw4eDjgOcA7gD8APgA8AAAAAAAAAA58H/4bxjmGMYcxhzGHMYcxhzGHOYYbxh/+DnwAAAAADxgfnBnOOMYwxjDHMMcwxzDHMMY4xhjOH/4P/AAAAAABnAGcAAA"), 46, atob("BAgQCBAQEBAQEBAQBA=="), 16+(scale<<8)+(1<<16));
};
function timeToString(duration) {
var hrs = ~~(duration / 3600);
var mins = ~~((duration % 3600) / 60);
var secs = ~~duration % 60;
var ret = "";
if (hrs > 0) {
ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
}
ret += "" + mins + ":" + (secs < 10 ? "0" : "");
ret += "" + secs;
return ret;
}
var newtimer_left_from = 60;
var newtimer_left_to = 2*60;
var newtimer_right_from = 5*60;
var newtimer_right_to = 7*60;
var current_from = 5*60;
var current_mid = 6*60;
var current_to = 7*60;
var current_value = 0;
var timerinterval;
var istimeron = false;
var islocked = false;
function countDown() {
current_value++;
draw();
if (current_value == current_from) {
Bangle.buzz(500);
} else if (current_value == current_mid) {
Bangle.buzz(400).then(()=>{
return new Promise(resolve=>setTimeout(resolve, 800));
}).then(()=>{
return Bangle.buzz(500);
});
} else if (current_value == current_to) {
Bangle.buzz(300).then(()=>{
return new Promise(resolve=>setTimeout(resolve, 600));
}).then(()=>{
Bangle.buzz(300).then(()=>{
return new Promise(resolve=>setTimeout(resolve, 600));
}).then(()=>{
return Bangle.buzz(500);
});
});
}
}
Bangle.on('touch',(touchside, touchdata)=>{
if (!islocked && istimeron && touchdata.y > (100+10)) {
Bangle.buzz(40);
istimeron = false;
clearInterval(timerinterval);
} else if (touchdata.y > 24 && touchdata.y < (100-10)) {
Bangle.buzz(40);
islocked = !islocked;
} else if (!islocked && touchdata.y > (100+10) && touchdata.x > 88 + 10) {
Bangle.buzz(40);
current_from = newtimer_right_from;
current_to = newtimer_right_to;
current_mid = (current_from + current_to) / 2;
current_value = 0;
if (timerinterval) clearInterval(timerinterval);
timerinterval = setInterval(countDown, 1000);
istimeron = true;
} else if (!islocked && touchdata.y > (100+10) && touchdata.x < 88 - 10) {
Bangle.buzz(40);
current_from = newtimer_left_from;
current_to = newtimer_left_to;
current_mid = (current_from + current_to) / 2;
current_value = 0;
if (timerinterval) clearInterval(timerinterval);
timerinterval = setInterval(countDown, 1000);
istimeron = true;
}
showInstructions = false;
draw();
});
Bangle.on('swipe',(swiperight, swipedown)=>{
console.log(swiperight);
console.log(swipedown);
if (swiperight == -1) {
if (newtimer_left_from >= 60) {
newtimer_left_from += 60;
newtimer_left_to += 60;
} else { // special case for 0:30 to 1:00
newtimer_left_from = 60;
newtimer_left_to = 120;
}
newtimer_right_from += 60;
newtimer_right_to += 60;
draw();
} else if (swiperight == 1) {
if (newtimer_left_from > 60) {
newtimer_left_from -= 60;
newtimer_left_to -= 60;
} else { // special case for 0:30 to 1:00
newtimer_left_from = 30;
newtimer_left_to = 60;
}
if (newtimer_right_from > 120) {
newtimer_right_from -= 60;
newtimer_right_to -= 60;
}
draw();
}
});
var drawTimeout;
var showInstructions = true;
function draw() {
g.reset();
if (current_value >= current_to) { g.setBgColor("#F00"); }
else if (current_value >= current_mid) { g.setBgColor("#FF0"); }
else if (current_value >= current_from) { g.setBgColor("#8F8"); }
g.clearRect(0,24,176,176);
g.reset();
g.setFontAlign(0, 0);
g.setFont("Michroma36").drawString(timeToString(current_value), 88, 62);
g.setFont("HaxorNarrow7x17");
g.drawString(timeToString(current_from), 44, 62+26);
g.drawString(timeToString(current_mid), 88, 62+26);
g.drawString(timeToString(current_to), 132, 62+26);
if (current_value >= current_from) { g.drawRect(44-1,62+26+9,44+1,62+26+9+1); }
if (current_value >= current_mid) { g.drawRect(88-1,62+26+9,88+1,62+26+9+1); }
if (current_value >= current_to) { g.drawRect(132-1,62+26+9,132+1,62+26+9+1); }
if (showInstructions) {
g.setFont("6x8").drawString("Tapping timer locks buttons", 88, 100+5);
g.setFont("6x8").drawString("<= Swipe to change time =>", 88, 168);
}
g.setColor(islocked ? "#444" : "#000");
g.setFont("Michroma16");
g.drawString(timeToString(newtimer_left_from), 44, 138-9);
g.drawString(timeToString(newtimer_left_to), 44, 138+9);
g.drawString(timeToString(newtimer_right_from), 132, 138-9);
g.drawString(timeToString(newtimer_right_to), 132, 138+9);
g.drawRect(0+8,138-24, 88-9+1, 138+22+1);
g.drawRect(0+8,138-24, 88-9, 138+22);
g.drawRect(88+8,138-24, 176-10+1, 138+22+1);
g.drawRect(88+8,138-24, 176-10, 138+22);
}
require("FontHaxorNarrow7x17").add(Graphics);
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
draw();

BIN
apps/a_speech_timer/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1 +1,2 @@
0.02: Fix JSON save format
0.01: First release

View File

@ -8,6 +8,7 @@ const algos = {
};
var tokens = require("Storage").readJSON("authentiwatch.json", true) || [];
tokens = tokens.data;
// QR Code Text
//
@ -257,7 +258,8 @@ function onSwipe(e) {
}
if (e == -1 && state.curtoken != -1 && tokens[state.curtoken].period <= 0) {
tokens[state.curtoken].period--;
require("Storage").writeJSON("authentiwatch.json", tokens);
let save={data:tokens,count:tokens.length};
require("Storage").writeJSON("authentiwatch.json", save);
state.nextTime = 0;
state.hide = 2;
draw();

View File

@ -322,7 +322,8 @@ function loadTokens() {
Puck.eval(`require('Storage').read(${JSON.stringify('authentiwatch.json')})`,data=>{
Util.hideModal();
try {
tokens = JSON.parse(data);
let load = JSON.parse(data);
tokens = load.data;
updateTokens();
} catch {
tokens = [];
@ -333,7 +334,8 @@ function loadTokens() {
*/
function saveTokens() {
Util.showModal('Saving...');
Puck.write(`\x10require('Storage').write(${JSON.stringify('authentiwatch.json')},${JSON.stringify(tokens)})\n`,()=>{
let save={data:tokens,count:tokens.length};
Puck.write(`\x10require('Storage').write(${JSON.stringify('authentiwatch.json')},${JSON.stringify(save)})\n`,()=>{
Util.hideModal();
});
}

View File

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

41
apps/mylocation/README.md Normal file
View File

@ -0,0 +1,41 @@
# My Location
*Sets and stores GPS lat and lon of your preferred city*
* Select one of the preset Cities or setup through the GPS
* Other Apps can read this information to do calculations based on location
* When the City shows ??? it means the location has been set through the GPS
## Example Code
const LOCATION_FILE = "mylocation.json";
let location;
// requires the myLocation app
function loadLocation() {
location = require("Storage").readJSON(LOCATION_FILE,1)||{"lat":51.5072,"lon":0.1276,"location":"London"};
}
## Screenshots
### Select one of the Preset Cities
* The presets are London, Newcastle, Edinbrough, Paris, New York, Tokyo
![](screenshot_1.png)
### Or select 'Set By GPS' to start the GPS
![](screenshot_2.png)
### While the GPS is running you will see:
![](screenshot_3.png)
### When a GPS fix is received you will see:
![](screenshot_4.png)
Written by: [Hugh Barney](https://github.com/hughbarney) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)

View File

@ -0,0 +1,75 @@
Bangle.loadWidgets();
Bangle.drawWidgets();
const SETTINGS_FILE = "mylocation.json";
let settings;
// initialize with default settings...
let s = {
'lat': 51.5072,
'lon': 0.1276,
'location': "London"
}
function loadSettings() {
settings = require('Storage').readJSON(SETTINGS_FILE, 1) || s;
}
function save() {
settings = s
require('Storage').write(SETTINGS_FILE, settings)
}
const locations = ["London", "Newcastle", "Edinburgh", "Paris", "New York", "Tokyo","???"];
const lats = [51.5072 ,54.9783 ,55.9533 ,48.8566 ,40.7128 ,35.6762, 0.0];
const lons = [-0.1276 ,-1.6178 ,-3.1883 ,2.3522 , -74.0060 ,139.6503, 0.0];
function setFromGPS() {
Bangle.on('GPS', (gps) => {
//console.log(".");
if (gps.fix === 0) return;
//console.log("fix from GPS");
s = {'lat': gps.lat, 'lon': gps.lon, 'location': '???' }
Bangle.buzz(1500); // buzz on first position
Bangle.setGPSPower(0);
save();
Bangle.setUI("updown", ()=>{ load() });
E.showPrompt("Location has been saved from the GPS fix",{
title:"Location Saved",
buttons : {"OK":1}
}).then(function(v) {
load(); // load default clock
});
});
Bangle.setGPSPower(1);
E.showMessage("Waiting for GPS fix. Place watch in the open. Could take 10 minutes. Long press to abort", "GPS Running");
Bangle.setUI("updown", undefined);
}
function showMainMenu() {
console.log("showMainMenu");
const mainmenu = {
'': { 'title': 'My Location' },
'<Back': ()=>{ load(); },
'City': {
value: 0 | locations.indexOf(s.location),
min: 0, max: 6,
format: v => locations[v],
onchange: v => {
if (v != 6) {
s.location = locations[v];
s.lat = lats[v];
s.lon = lons[v];
save();
}
}
},
'Set From GPS': ()=>{ setFromGPS(); }
}
return E.showMenu(mainmenu);
}
loadSettings();
showMainMenu();

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4UA///t/7j/P3/vB4cBqtVoAbHBQIABBQ0FBYdQBYsVBYdUERIkGHIQADHoguEGAwuEGAwKFBZg8DHQw8EBYNf/1Vq3/8oLDIwNf/Wpv//0oLG9Wq3/qBYJUCBYuqBaBqBBYW+BepHEBbybCBYP+BYSnErYLDyoLFAANq/r8Ga5T7MBZZUBAAhSCfhA6DBZhIGBQg8FHQg8GHQgwGFwowFBQwwDFwwLMlS7Bqta1AKEn2q1K1C1WgBYf/1WqBYIDB1QKCgYLC0taBYoXB/QICBY0//7vBAAQ8EEgIABCwwME9QVEA"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -3,5 +3,6 @@
0.03: Make it work with Gadgetbridge, Notifications fullscreen on a Bangle 2
0.04: Leave space at the bottom for Chrono widget, set back option at first option
0.05: Added 2 new fonts
0.06: COnverted fonts to font modules
0.06: Converted fonts to font modules
0.07: Added info line that cycles on BTN1/BTN3 (or vitual buttons on a bangle 2)
0.08: Added dependancy on MyLocation

View File

@ -1,6 +1,6 @@
# Pastel Clock
*a configurable clock with custom fonts and background*
*a configurable clock with custom fonts and background. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times*
* Designed specifically for Bangle 1 and Bangle 2
* A choice of 7 different custom fonts
@ -8,12 +8,13 @@
* Has a settings menu, change font, enable/disable the grid
* On Bangle 1 use BTN1,BTN3 to cycle through the info display (Date, ID, Batt %, Ram % etc)
* On Bangle 2 touch the top right/top left to cycle through the info display (Date, ID, Batt %, Ram % etc)
* Uses mylocation.json from MyLocation app to calculate sunrise and sunset times for your location
* Uses pedometer widget to get latest step count
* Dependant apps are installed when Pastel installs
I came up with the name Pastel due to the shade of the grid background.
## Creator
[Hugh Barney](https://github.com/hughbarney)
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/)
## Lato
![](screenshot_lato.png)

View File

@ -1,6 +1,9 @@
var SunCalc = require("https://raw.githubusercontent.com/mourner/suncalc/master/suncalc.js");
require("f_latosmall").add(Graphics);
const SETTINGS_FILE = "pastel.json";
const LOCATION_FILE = "mylocation.json";
let settings;
let location;
function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)||{};
@ -8,6 +11,29 @@ function loadSettings() {
settings.font = settings.font||"Lato";
}
// requires the myLocation app
function loadLocation() {
location = require("Storage").readJSON(LOCATION_FILE,1)||{"lat":51.5072,"lon":0.1276,"location":"London"};
}
function extractTime(d){
var h = d.getHours(), m = d.getMinutes();
return(("0"+h).substr(-2) + ":" + ("0"+m).substr(-2));
}
var sunRise = "00:00";
var sunSet = "00:00";
var drawCount = 0;
function updateSunRiseSunSet(now, lat, lon, line){
// get today's sunlight times for lat/lon
var times = SunCalc.getTimes(new Date(), lat, lon);
// format sunrise time from the Date object
sunRise = extractTime(times.sunrise);
sunSet = extractTime(times.sunset);
}
function loadFonts() {
// load font files based on settings.font
if (settings.font == "Architect")
@ -39,6 +65,8 @@ const infoData = {
ID_BLANK: { calc: () => '' },
ID_DATE: { calc: () => {var d = (new Date).toString().split(" "); return d[2] + ' ' + d[1] + ' ' + d[3];} },
ID_DAY: { calc: () => {var d = require("locale").dow(new Date).toLowerCase(); return d[0].toUpperCase() + d.substring(1);} },
ID_SR: { calc: () => 'Sunrise: ' + sunRise },
ID_SS: { calc: () => 'Sunset: ' + sunSet },
ID_STEP: { calc: () => 'Steps: ' + stepsWidget().getSteps() },
ID_BATT: { calc: () => 'Battery: ' + E.getBattery() + '%' },
ID_MEM: { calc: () => {var val = process.memory(); return 'Ram: ' + Math.round(val.usage*100/val.total) + '%';} },
@ -148,6 +176,10 @@ function draw() {
g.setFontLatoSmall();
g.setFontAlign(0, -1);
g.drawString((infoData[infoMode].calc()), w/2, h - 24 - 24);
if (drawCount % 3600 == 0)
updateSunRiseSunSet(new Date(), location.lat, location.lon);
drawCount++;
}
// Only update when display turns on
@ -169,6 +201,8 @@ Bangle.setUI("clockupdown", btn=> {
loadSettings();
loadFonts();
loadLocation();
g.clear();
var secondInterval = setInterval(draw, 1000);
draw();

1
apps/pebble/ChangeLog Normal file
View File

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

17
apps/pebble/README.md Normal file
View File

@ -0,0 +1,17 @@
# Pebble
*a Pebble style clock with configurable background color, to keep the revolution going*
* Designed specifically for Bangle 2
* A choice of 6 different background colous through its setting menu
* Supports the Light and Dark themes
* Uses pedometer widget to get latest step count
* Dependant apps are installed when Pebble installs
* Uses the whole screen, widgets are made invisible but still run in the background
* When battery is less than 30% main screen goes Red
![](pebble_screenshot.png)
![](pebble_screenshot2.png)
![](pebble_screenshot3.png)
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/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

116
apps/pebble/pebble.app.js Normal file
View File

@ -0,0 +1,116 @@
Graphics.prototype.setFontQahiri = function(scale) {
// Actual height 60 (60 - 1)
g.setFontCustom(atob("AAAAAAAAfAAAAAAAAAAAAP4AAAAAAAAAAAD/AAAAAAAAAAAB/wAAAAAAAAAAAf8AAAAAAAAAAAH/AAAAAAAAAAAA/wAAAAAAAAAAAH4AAAAAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAB8AAAAAAAAAAAD/gAAAAAAAAAAH/4AAAAAAAAAAP/8AAAAAAAAAAP/4AAAAAAAAAAf/4AAAAAAAAAA//wAAAAAAAAAB//gAAAAAAAAAD//AAAAAAAAAAD//AAAAAAAAAAH/+AAAAAAAAAAP/8AAAAAAAAAAf/4AAAAAAAAAAf/4AAAAAAAAAA//wAAAAAAAAAB//gAAAAAAAAAD//AAAAAAAAAAH/+AAAAAAAAAAH/+AAAAAAAAAAP/8AAAAAAAAAAH/4AAAAAAAAAAB/wAAAAAAAAAAAPwAAAAAAAAAAADgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD/AAAD/AAAAAAA/wAAA/wAAAAAAP8AAAP8AAAAAAD/AAAD/AAAAAAA/wAAA/wAAAAAAP8AAAP8AAAAAAD/AAAD/AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af///AAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af///AAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA///+A/wAAAAAAP///gP8AAAAAAD///4D/AAAAAAA///+A/wAAAAAAP///gP8AAAAAAD///4D/AAAAAAA///+A/wAAAAAAH///gH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAH/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///AAAAAAAAAAP//wAAAAAAAAAD//8AAAAAAAAAA///AAAAAAAAAAP//wAAAAAAAAAD//8AAAAAAAAAA///AAAAAAAAAAP//wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAH/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///+A/wAAAAAAP///gP8AAAAAAD///4D/AAAAAAA///+A/wAAAAAAP///gP8AAAAAAD///4D/AAAAAAA///+A/wAAAAAAP///gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af///AAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af///AAAAAAA/wH///wAAAAAAH8A///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af///AAAAAAA/wH///wAAAAAAP8B///8AAAAAAD/Af///AAAAAAA/wH///wAAAAAAH8A///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA/wAAAAAAAAAAAP8AAAAAAAAAAAD/AAAAAAAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAH/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAH/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///+A/wAAAAAAP///gP8AAAAAAD///4D/AAAAAAA///+A/wAAAAAAP///gP8AAAAAAD///4D/AAAAAAA///+A/wAAAAAAP///gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA/wH+A/wAAAAAAP8B/gP8AAAAAAD/Af4D/AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAP/////8AAAAAAD//////AAAAAAA//////wAAAAAAH/////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAfAAAAAAAAAfwAP4AAAAAAAAP8AD/AAAAAAAAD/gB/wAAAAAAAA/4Af8AAAAAAAAP+AH/AAAAAAAAD/AA/wAAAAAAAAfgAH4AAAAAAAABwAA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("DR0bDBsbGxsbGxsbDQ=="), 80+(scale<<8)+(1<<16));
}
const SETTINGS_FILE = "pebble.json";
let settings;
function loadSettings() {
settings = require("Storage").readJSON(SETTINGS_FILE,1)|| {'bg': '#0f0', 'color': 'Green'};
}
var img = require("heatshrink").decompress(atob("oFAwkGswA/AH4A/AH4A/AH4A/AFEAD74gdsAfBELlggMhD70iILsAiUAIKQRBgxAHgUiIKQQJUAMSD4JBQsBVBIAq/DEAJBCJ45VHkAxEDwKfDIIUREBq2BmcQCAQeCkczmRBEiAgND4MxSoYGBAAQgCAAohKDARhBG4IeDEAQ8BAA5fJABgpBgFDgEiQgJEHT4IeMmMBkMFAYJJDEQaYDiYfMkECiEEoEDBAX//8ykJsBD4MAWwIALiBeCqAyDn//BoYgBgAeMYAMhgE0CRIOBD58BkEEmCRKkEGD5szkUQqdASJUxD4MAgKBKmUigFEGJZgBAATODFw0CkEBmoOJAAQdB7owBOBDdCgbdED5fd6pRIgDdCeBkxD4fdeAgNEkMFmheLdgIfE6BgGmDdCoDdKDwYfD6gzGiBeBrpLHXYUQXIMgD4NND4SAFZgMRgAKBPwroBBYIeBIAL/CADESL4VmsAcWgMRkQeDAAMAkQAWMAQeCD4MSDqqdBDwgfBAC8GDwiAXDowA/AH4A/AH4A/AH4A/AEA"));
const h = g.getHeight();
const w = g.getWidth();
const ha = 2*h/5 - 8;
const h2 = 3*h/5 - 10;
const h3 = 7*h/8;
let batteryWarning = false;
function draw() {
let date = new Date();
let da = date.toString().split(" ");
//let timeStr = require("locale").time(date,1); // causes screen corruption ???
let timeStr = da[4].substr(0,5);
const t = 6;
// turn the warning on once we have dipped below 30%
if (E.getBattery() < 30)
batteryWarning = true;
// turn the warning off once we have dipped above 40%
if (E.getBattery() > 40)
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.setFont('Vector', 22);
g.setFontAlign(0, -1);
g.drawString(da[0], w/4, ha); // day of week
g.drawString(getSteps(), 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.setFontQahiri();
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, 10, { scale: 1 });
drawCalendar(((w/2) - 48)/2, 10, 48, 4, da[2]);
}
// 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.setFont('Vector', 22);
g.setFontAlign(0, 0);
g.drawString(str, x + wi/2 + th/2, y + wi/2 + th/2);
}
function getSteps() {
if (WIDGETS.wpedom !== undefined) {
return WIDGETS.wpedom.getSteps();
}
return '????';
}
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
*/
for (let wd of WIDGETS) {wd.draw=()=>{};}
loadSettings();
setInterval(draw, 15000); // refresh every 15s
draw();
Bangle.setUI("clock");

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("oFAwgNKiIAIFqofegIf/DAUzAAMyAwUQD60T/4ACD7Q/cPxIf/YCofcDhYiSXYYfuUZgf/D/4f/D6USkUgD/4fuogAID6vtDw/UD6vu6geF73kb6vuEAtN9wfYMIneD7JADDwIfaIAJdBD7YgBHwQfbAAgfkf6Qf/D/4feogAID6oAND/4f/iAdJD/4f/D/4fUDxYABD74iODiAftTZgfnYYczAAMyD7UT/4ACH/S+bD8DAKD9Y="))

BIN
apps/pebble/pebble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,38 @@
(function(back) {
const SETTINGS_FILE = "pebble.json";
// initialize with default settings...
let s = {'bg': '#0f0', 'color': 'Green'}
// ...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','Perple','Red','Blue'];
var bg_code = ['#0f0','#ff0','#0ff','#f0f','#f00','#00f'];
E.showMenu({
'': { 'title': 'Pebble Clock' },
'< Back': back,
'Colour': {
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();
},
}
});
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -10,3 +10,4 @@
0.11: Don't overwrite existing settings on app update
0.12: Fixed for Bangle 2
0.13: Fillbar setting added, see README
0.14: Fix drawing the bar when charging

View File

@ -5,12 +5,12 @@ Show the current battery level and charging status in the top right of the clock
Works with Bangle 1 and Bangle 2
When the fillbar setting is on the level colour will fill the entire
bar. This makes for an easier to read dsiplay when the charge is
bar. This makes for an easier to read display when the charge is
below 50%.
![](widbatpc.full.jpg)
When the fillbar setting is off the level colour will follow the battry percentage
When the fillbar setting is off the level colour will follow the battery percentage
![](widbatpc.part.jpg)

View File

@ -79,20 +79,20 @@
// else...
var s = 39;
var x = this.x, y = this.y;
const l = E.getBattery();
let xl = x+4+l*(s-12)/100;
// show bar full in the level color, as you cant see the color if the bar is too small
if (setting('fillbar'))
xl = x+4+100*(s-12)/100;
c = levelColor(l);
const l = E.getBattery(),
c = levelColor(l);
if (Bangle.isCharging() && setting('charger')) {
g.setColor(chargerColor()).drawImage(atob(
"DhgBHOBzgc4HOP////////////////////3/4HgB4AeAHgB4AeAHgB4AeAHg"),x,y);
x+=16;
}
let xl = x+4+l*(s-12)/100;
// show bar full in the level color, as you can't see the color if the bar is too small
if (setting('fillbar'))
xl = x+4+100*(s-12)/100;
g.setColor(g.theme.fg);
g.fillRect(x,y+2,x+s-4,y+21);
g.clearRect(x+2,y+4,x+s-6,y+19);