Merge branch 'master' into new_alarm
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"aclock.app.js","url":"clock-analog.js"},
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
0.06: fixes #1271 - wrong settings name
|
||||
when weekday name and calendar weeknumber are on then display is <weekday short> #<calweek>
|
||||
week is buffered until date or timezone changes
|
||||
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
||||
0.07: align default settings with app.js (otherwise the initial displayed settings will be confusing to users)
|
||||
0.08: fixed calendar weeknumber not shortened to two digits
|
|
@ -99,7 +99,7 @@ function updateState() {
|
|||
}
|
||||
|
||||
function isoStr(date) {
|
||||
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).substr(-2) + "-" + ("0" + date.getDate()).substr(-2);
|
||||
return date.getFullYear() + "-" + ("0" + (date.getMonth() + 1)).slice(-2) + "-" + ("0" + date.getDate()).slice(-2);
|
||||
}
|
||||
|
||||
var calWeekBuffer = [false,false,false]; //buffer tz, date, week no (once calculated until other tz or date is requested)
|
||||
|
@ -140,7 +140,7 @@ function draw() {
|
|||
g.setFontAlign(0, 0).setFont("Anton").drawString(timeStr, x, y); // draw time
|
||||
if (secondsScreen) {
|
||||
y += 65;
|
||||
var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).substr(-2);
|
||||
var secStr = (secondsWithColon ? ":" : "") + ("0" + date.getSeconds()).slice(-2);
|
||||
if (doColor())
|
||||
g.setColor(0, 0, 1);
|
||||
g.setFont("AntonSmall");
|
||||
|
@ -193,7 +193,7 @@ function draw() {
|
|||
if (calWeek || weekDay) {
|
||||
var dowcwStr = "";
|
||||
if (calWeek)
|
||||
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).substring(-2);
|
||||
dowcwStr = " #" + ("0" + ISO8601calWeek(date)).slice(-2);
|
||||
if (weekDay)
|
||||
dowcwStr = require("locale").dow(date, calWeek ? 1 : 0) + dowcwStr; //weekDay e.g. Monday or weekDayShort #<calWeek> e.g. Mon #01
|
||||
else //week #01
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "antonclk",
|
||||
"name": "Anton Clock",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||
"readme":"README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
# Spelling bee game
|
||||
|
||||
Word finding game inspired by the NYT spelling bee. Find as many words with 4 or more letters (must include the
|
||||
letter at the center of the 'hive') as you can.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
- tap on letters to type out word
|
||||
- swipe left to delete last letter
|
||||
- swipe right to enter; the word will turn blue while it is being checked against the internal dictionary; once
|
||||
checked, it will turn red if the word is invalid, does not contain the central letter or has been guessed before or
|
||||
will turn green if it is a valid word; in the latter case, points will be awarded
|
||||
- swipe down to shuffle the 6 outer letters
|
||||
- swipe up to view a list of already guessed words; tap on any of them to return to the regular game.
|
||||
|
||||
|
||||
## Scoring
|
||||
|
||||
The number of correctly guessed words is displayed on the bottom left, the score on the bottom right. A single point
|
||||
is awarded for a 4 letter word, or the number of letters if longer. A pangram is a word that contains all 7 letters at
|
||||
least once and yields an additional 7 points. Each game contains at least one pangram.
|
||||
|
||||
|
||||
## Technical remarks
|
||||
The game uses an internal dictionary consisting of a newline separated list of English words ('bee.words', using the '2of12inf' word list).
|
||||
The dictionary is fairly large (~700kB of flash space) and thus requires appropriate space on the watch and will make installing the app somewhat
|
||||
slow. Because of its size it cannot be compressed (heatshrink needs to hold the compressed/uncompressed data in memory).
|
||||
In order to make checking the validity of a guessed word faster an index file ('bee_lindex.json') is installed with
|
||||
the app that facilitates faster word lookups. This index file is specific to the dictionary file used. If one were to
|
||||
replace the dictionary file with a different version (e.g. a different language) the index file has to be regenerated. The easiest
|
||||
way to do so is to delete (via the Web IDE or the fileman app on the watch) the file 'bee_lindex.json' - it will be regenerated (and saved,
|
||||
i.e. it only happens once) on app startup automatically, a process that takes roughly 30 seconds.
|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A/AE2JAAIKHnc7DyNPp4vRGAwuBGB4sBAAQvSGIovPFqYvHGAYvDGBYsGGhwvGGIQvEGBQnDMYhkNGBAvOvQABqyRTF5GJr4wLFwQACX6IwLsowJLYMrldVGAQvTsoADGBITD0YvDldPF6n+F4gyGGAdP5nMF4KKBGDJZDGI7EBcoOiGAK7DGAQvYRogxEr1Pp9VMAiSBBILBWeJIxCromBMAQwDAAZfTGBQyCxOCGAIvBGIV/F7AwMAAOIp95GAYACFqoyQMAIwGF7QADEQd5FgIADqvGF8DnEAAIvFGIWjF8CFE0QwHAAQudAAK0EGBQuecw3GqpemYIxiCGIa8cF4wwHdTwvJp9/F82jGA9VMQovf5jkHGIwvg4wvIAAgvg5miF9wwNF8QABF9QwF0YuoF4oxCqoulGBAAB42i0QvjGBPMF0gwIFswwHF1IA/AH4A/AH4AL"))
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,193 @@
|
|||
|
||||
const S = require("Storage");
|
||||
var letters = [];
|
||||
var letterIdx = [];
|
||||
|
||||
var centers = [];
|
||||
|
||||
var word = '';
|
||||
|
||||
var foundWords = [];
|
||||
var score = 0;
|
||||
|
||||
var intervalID = -1;
|
||||
|
||||
function prepareLetterIdx () {
|
||||
"compile"
|
||||
var li = [0];
|
||||
if (S.read("bee_lindex.json")!==undefined) li = S.readJSON("bee_lindex.json"); // check for cached index
|
||||
else {
|
||||
for (var i=1; i<26; ++i) {
|
||||
var prefix = String.fromCharCode(97+i%26);
|
||||
console.log(prefix);
|
||||
li.push(S.read('bee.words').indexOf("\n"+prefix, li[i-1])+1);
|
||||
}
|
||||
li.push(S.read('bee.words').length);
|
||||
S.writeJSON("bee_lindex.json", li);
|
||||
}
|
||||
for (var i=0; i<26; ++i) letterIdx[i] = S.read("bee.words", li[i], li[i+1]-li[i]);
|
||||
}
|
||||
|
||||
function findWord (w) {
|
||||
"compile"
|
||||
var ci = w.charCodeAt(0)-97;
|
||||
var f = letterIdx[ci].indexOf(w);
|
||||
if (f>=0 && letterIdx[ci][f+w.length]=="\n") return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function isPangram(w) {
|
||||
var ltrs = '';
|
||||
for (var i=0; i<w.length; ++i) if (ltrs.indexOf(w[i])===-1) ltrs += w[i];
|
||||
return ltrs.length==7;
|
||||
}
|
||||
|
||||
function checkWord (w) {
|
||||
if (w.indexOf(String.fromCharCode(97+letters[0]))==-1) return false; // does it contain central letter?
|
||||
if (foundWords.indexOf(w)>=0) return false; // already found
|
||||
if (findWord(w)) {
|
||||
foundWords.push(w);
|
||||
if (w.length==4) score++;
|
||||
else score += w.length;
|
||||
if (isPangram(w)) score += 7;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getHexPoly(cx, cy, r, a) {
|
||||
var p = [];
|
||||
for (var i=0; i<6; ++i) p.push(cx+r*Math.sin((i+a)/3*Math.PI), cy+r*Math.cos((i+a)/3*Math.PI));
|
||||
return p;
|
||||
}
|
||||
|
||||
function drawHive() {
|
||||
w = g.getWidth();
|
||||
h = g.getHeight();
|
||||
const R = w/3.3;
|
||||
centers = getHexPoly(w/2, h/2+10, R, 0);
|
||||
centers.push(w/2, h/2+10);
|
||||
g.clear();
|
||||
g.setFont("Vector", w/7).setFontAlign(0, 0, 0);
|
||||
g.setColor(g.theme.fg);
|
||||
for (var i=0; i<6; ++i) {
|
||||
g.drawPoly(getHexPoly(centers[2*i], centers[2*i+1], 0.9*R/Math.sqrt(3), 0.5), {closed:true});
|
||||
g.drawString(String.fromCharCode(65+letters[i+1]), centers[2*i]+2, centers[2*i+1]+2);
|
||||
}
|
||||
g.setColor(1, 1, 0).fillPoly(getHexPoly(w/2, h/2+10, 0.9*R/Math.sqrt(3), 0.5));
|
||||
g.setColor(0).drawString(String.fromCharCode(65+letters[0]), w/2+2, h/2+10+2);
|
||||
}
|
||||
|
||||
function shuffleLetters(qAll) {
|
||||
for (var i=letters.length-1; i > 0; i--) {
|
||||
var j = (1-qAll) + Math.floor(Math.random()*(i+qAll));
|
||||
var temp = letters[i];
|
||||
letters[i] = letters[j];
|
||||
letters[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
function pickLetters() {
|
||||
var ltrs = "";
|
||||
while (ltrs.length!==7) {
|
||||
ltrs = [];
|
||||
var j = Math.floor(26*Math.random());
|
||||
var i = Math.floor((letterIdx[j].length-10)*Math.random());
|
||||
while (letterIdx[j][i]!="\n" && i<letterIdx[j].length) ++i;
|
||||
if (i<letterIdx[j].length-1) {
|
||||
++i;
|
||||
while (letterIdx[j][i]!=="\n") {
|
||||
var c = letterIdx[j][i];
|
||||
if (ltrs.indexOf(c)===-1) ltrs += c;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var i=0; i<7; ++i) letters.push(ltrs.charCodeAt(i)-97);
|
||||
shuffleLetters(1);
|
||||
}
|
||||
|
||||
function drawWord(c) {
|
||||
g.clearRect(0, 0, g.getWidth()-1, 19);
|
||||
g.setColor(c).setFont("Vector", 20).setFontAlign(0, 0, 0).drawString(word, g.getWidth()/2, 11).flip();
|
||||
}
|
||||
|
||||
function touchHandler(e, x) {
|
||||
var hex = 0;
|
||||
var hex_d = 1e6;
|
||||
for (var i=0; i<7; ++i) {
|
||||
var d = (x.x-centers[2*i])*(x.x-centers[2*i]) + (x.y-centers[2*i+1])*(x.y-centers[2*i+1]);
|
||||
if (d < hex_d) {
|
||||
hex_d = d;
|
||||
hex = i+1;
|
||||
}
|
||||
}
|
||||
hex = hex%7;
|
||||
if (word.length <= 15) word += String.fromCharCode(letters[hex]+65);
|
||||
drawWord(g.theme.fg);
|
||||
}
|
||||
|
||||
function drawScore() {
|
||||
g.setColor(g.theme.fg).setFont("Vector", 20).setFontAlign(0, 0, 0);
|
||||
g.clearRect(0, g.getHeight()-22, 60, g.getHeight()-1);
|
||||
g.clearRect(g.getWidth()-60, g.getHeight()-22, g.getWidth(), g.getHeight()-1);
|
||||
g.drawString(foundWords.length.toString(), 30, g.getHeight()-11);
|
||||
g.drawString(score.toString(), g.getWidth()-30, g.getHeight()-11);
|
||||
}
|
||||
|
||||
function wordFound (c) {
|
||||
word = "";
|
||||
drawWord(g.theme.fg);
|
||||
drawScore();
|
||||
clearInterval(intervalID);
|
||||
intervalID = -1;
|
||||
}
|
||||
|
||||
function swipeHandler(d, e) {
|
||||
if (d==-1 && word.length>0) {
|
||||
word = word.slice(0, -1);
|
||||
drawWord(g.theme.fg);
|
||||
}
|
||||
if (d==1 && word.length>=4) {
|
||||
drawWord("#00f");
|
||||
drawWord((checkWord(word.toLowerCase()) ? "#0f0" : "#f00"));
|
||||
if (intervalID===-1) intervalID = setInterval(wordFound, 800);
|
||||
}
|
||||
if (e===1) {
|
||||
shuffleLetters(0);
|
||||
drawHive();
|
||||
drawScore();
|
||||
drawWord(g.theme.fg);
|
||||
}
|
||||
if (e===-1 && foundWords.length>0) showWordList();
|
||||
}
|
||||
|
||||
function showWordList() {
|
||||
Bangle.removeListener("touch", touchHandler);
|
||||
Bangle.removeListener("swipe", swipeHandler);
|
||||
E.showScroller({
|
||||
h : 20, c : foundWords.length,
|
||||
draw : (idx, r) => {
|
||||
g.clearRect(r.x,r.y,r.x+r.w-1,r.y+r.h-1).setFont("6x8:2");
|
||||
g.setColor(isPangram(foundWords[idx])?'#0f0':g.theme.fg).drawString(foundWords[idx].toUpperCase(),r.x+10,r.y+4);
|
||||
},
|
||||
select : (idx) => {
|
||||
setInterval(()=> {
|
||||
E.showScroller();
|
||||
drawHive();
|
||||
drawScore();
|
||||
drawWord(g.theme.fg);
|
||||
Bangle.on("touch", touchHandler);
|
||||
Bangle.on("swipe", swipeHandler);
|
||||
clearInterval();
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
prepareLetterIdx();
|
||||
pickLetters();
|
||||
drawHive();
|
||||
drawScore();
|
||||
Bangle.on("touch", touchHandler);
|
||||
Bangle.on("swipe", swipeHandler);
|
|
@ -0,0 +1 @@
|
|||
[0,41048,80445,152390,198606,228714,257919,279071,303726,337982,343582,348026,367246,404452,419780,438696,496250,499697,544600,624304,659085,680996,691270,708186,708341,709916,710883]
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,16 @@
|
|||
{ "id": "bee",
|
||||
"name": "Bee",
|
||||
"shortName":"Bee",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "Spelling bee",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"tags": "game,text",
|
||||
"storage": [
|
||||
{"name":"bee.app.js","url":"bee.app.js"},
|
||||
{"name":"bee.words","url":"bee_words_2of12"},
|
||||
{"name":"bee_lindex.json","url":"bee_lindex.json"},
|
||||
{"name":"bee.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"berlin-clock-screenshot.png"}],
|
||||
"storage": [
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
0.01: New App
|
||||
0.02: app keeps track of statistics now
|
|
@ -21,7 +21,8 @@ function buttonPushed(b) {
|
|||
layout.bt1.bgCol = wordle.keyColors.Z||g.theme.bg;
|
||||
layout.bt2.label = "<del>";
|
||||
layout.bt4.label = "<ent>";
|
||||
layout.bt3.label = layout.bt5.label = " ";
|
||||
layout.bt3.label = " ";
|
||||
layout.bt5.label = "<stat>";
|
||||
layout.bt6.label = "<";
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +31,10 @@ function buttonPushed(b) {
|
|||
if (b!=6) {
|
||||
if ((keyStateIdx<=5 || b<=1) && inp.length<5) inp += String.fromCharCode(b+(keyStateIdx-1)*5+64);
|
||||
else if (layout.input.label.length>0 && b==2) inp = inp.slice(0,-1);
|
||||
if (keyStateIdx==6 && b==5) {
|
||||
wordle.drawStats();
|
||||
return;
|
||||
}
|
||||
layout.input.label = inp;
|
||||
}
|
||||
layout = getKeyLayout(inp);
|
||||
|
@ -82,6 +87,7 @@ class Wordle {
|
|||
this.word = this.words.slice(i, i+5).toUpperCase();
|
||||
}
|
||||
console.log(this.word);
|
||||
this.stats = require("Storage").readJSON("bordlestats.json") || {'1':0, '2':0, '3':0, '4':0, '5':0, '6':0, 'p':0, 'w':0, 's':0, 'ms':0};
|
||||
}
|
||||
render(clear) {
|
||||
h = g.getHeight();
|
||||
|
@ -109,7 +115,7 @@ class Wordle {
|
|||
layout = getKeyLayout("");
|
||||
wordle.render(true);
|
||||
});
|
||||
return 3;
|
||||
return 1;
|
||||
}
|
||||
this.guesses.push(w);
|
||||
this.nGuesses++;
|
||||
|
@ -130,13 +136,39 @@ class Wordle {
|
|||
this.guessColors[this.nGuesses].push(col);
|
||||
}
|
||||
if (correct==5) {
|
||||
E.showAlert("The word is\n"+this.word, "You won in "+(this.nGuesses+1)+" guesses!").then(function(){load();});
|
||||
return 1;
|
||||
}
|
||||
if (this.nGuesses==5) {
|
||||
E.showAlert("The word was\n"+this.word, "You lost!").then(function(){load();});
|
||||
E.showAlert("The word is\n"+this.word, "You won in "+(this.nGuesses+1)+" guesses!").then(function(){
|
||||
wordle.stats['p']++; wordle.stats['w']++; wordle.stats['s']++; wordle.stats[wordle.nGuesses+1]++;
|
||||
if (wordle.stats['s']>wordle.stats['ms']) wordle.stats['ms'] = wordle.stats['s'];
|
||||
require("Storage").writeJSON("bordlestats.json", wordle.stats);
|
||||
wordle.drawStats();
|
||||
});
|
||||
return 2;
|
||||
}
|
||||
if (this.nGuesses==5) {
|
||||
E.showAlert("The word was\n"+this.word, "You lost!").then(function(){
|
||||
wordle.stats['p']++; wordle.stats['s'] = 0;
|
||||
require("Storage").writeJSON("bordlestats.json", wordle.stats);
|
||||
wordle.drawStats();
|
||||
});
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
drawStats() {
|
||||
E.showMessage(" ", "Statistics");
|
||||
var max = 1;
|
||||
for (i=1; i<=6; ++i) if (max<this.stats[i]) max = this.stats[i];
|
||||
var h = g.getHeight();
|
||||
var w = g.getWidth();
|
||||
g.setColor('#00f').setFontVector((h-40)/8).setFontAlign(-1, 0, 0);
|
||||
for (i=1; i<=6; ++i) {
|
||||
tw = this.stats[i]*(w-24)/max;
|
||||
g.setColor("#00f").fillRect(20, 52+(i-1)*(h-52)/6+2, 20+tw, 52+i*(h-52)/6-2);
|
||||
g.setColor("#fff").drawString(i.toString(), 1, 52+(i-0.5)*(h-52)/6);
|
||||
g.drawString(this.stats[i].toString(), tw>20 ? 25 : 25+tw, 52+(i-0.5)*(h-52)/6);
|
||||
}
|
||||
g.setFontVector((h-40)/9).setColor("#fff").drawString("P:"+this.stats["p"]+" W:"+this.stats["w"]+" S:"+this.stats["s"]+" M:"+this.stats["ms"], 4, 34);
|
||||
Bangle.setUI();
|
||||
Bangle.on("touch", (e) => { load(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "Bordle",
|
||||
"shortName":"Bordle",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "Bangle version of a popular word search game",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"screenshots": [{"url":"screenshot_calculator.png"}],
|
||||
"tags": "app,tool",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"calculator.app.js","url":"app.js"},
|
||||
{"name":"calculator.img","url":"calculator-icon.js","evaluate":true}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock,cli,command,bash,shell",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"cliock.app.js","url":"app.js"},
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
0.03: fix metadata.json to allow setting as clock
|
||||
0.04: added heart rate which is switched on when cycled to it through up/down touch on rhs
|
||||
0.05: changed text to uppercase, just looks better, removed colons on text
|
||||
0.06: better contrast for light theme, use fg color instead of dithered for ring
|
||||
|
|
|
@ -28,5 +28,6 @@ See [#1248](https://github.com/espruino/BangleApps/issues/1248)
|
|||
|
||||
## Screenshots
|
||||

|
||||

|
||||
|
||||
It is worth looking at the real thing though as the screenshot does not do it justice.
|
||||
It is worth looking at the real thing though as the screenshots do not do it justice.
|
||||
|
|
|
@ -41,10 +41,17 @@ Graphics.prototype.setFontRoboto20 = function(scale) {
|
|||
};
|
||||
|
||||
function assignPalettes() {
|
||||
// palette for 0-40%
|
||||
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
|
||||
// palette for 50-100%
|
||||
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]);
|
||||
if (g.theme.dark) {
|
||||
// palette for 0-40%
|
||||
pal1 = new Uint16Array([g.theme.bg, g.toColor(settings.gy), g.toColor(settings.fg), g.toColor("#00f")]);
|
||||
// palette for 50-100%
|
||||
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.toColor(settings.gy), g.toColor("#00f")]);
|
||||
} else {
|
||||
// palette for 0-40%
|
||||
pal1 = new Uint16Array([g.theme.bg, g.theme.fg, g.toColor(settings.fg), g.toColor("#00f")]);
|
||||
// palette for 50-100%
|
||||
pal2 = new Uint16Array([g.theme.bg, g.toColor(settings.fg), g.theme.fg, g.toColor("#00f")]);
|
||||
}
|
||||
}
|
||||
|
||||
function setSmallFont20() {
|
||||
|
@ -109,10 +116,10 @@ function updateSunRiseSunSet(now, lat, lon, line){
|
|||
const infoData = {
|
||||
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 ' + getSteps() },
|
||||
ID_BATT: { calc: () => 'Battery ' + E.getBattery() + '%' },
|
||||
ID_SR: { calc: () => 'SUNRISE ' + sunRise },
|
||||
ID_SS: { calc: () => 'SUNSET ' + sunSet },
|
||||
ID_STEP: { calc: () => 'STEPS ' + getSteps() },
|
||||
ID_BATT: { calc: () => 'BATTERY ' + E.getBattery() + '%' },
|
||||
ID_HRM: { calc: () => hrmCurrent }
|
||||
};
|
||||
|
||||
|
@ -225,7 +232,7 @@ function drawSteps() {
|
|||
setSmallFont();
|
||||
g.setFontAlign(0,0);
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString('Steps ' + getSteps(), w/2, (3*h/4) - 4);
|
||||
g.drawString('STEPS ' + getSteps(), w/2, (3*h/4) - 4);
|
||||
drawingSteps = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
{ "id": "daisy",
|
||||
"name": "Daisy",
|
||||
"version":"0.05",
|
||||
"version":"0.06",
|
||||
"dependencies": {"mylocation":"app"},
|
||||
"description": "A clock based on the Pastel clock with large ring guage for steps",
|
||||
"description": "A beautiful digital clock with large ring guage, idle timer and a cyclic information line that includes, day, date, steps, battery, sunrise and sunset times",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"screenshots": [{"url":"screenshot_daisy2.jpg"}],
|
||||
"screenshots": [{"url":"screenshot_daisy3.png"}],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"daisy.app.js","url":"app.js"},
|
||||
|
|
Before Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"id": "doztime",
|
||||
"name": "Dozenal Time",
|
||||
"shortName": "Dozenal Time",
|
||||
"name": "Dozenal Digital Time",
|
||||
"shortName": "Dozenal Digital",
|
||||
"version": "0.05",
|
||||
"description": "A dozenal Holocene calendar and dozenal diurnal clock",
|
||||
"description": "A dozenal Holocene calendar and dozenal diurnal digital clock",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"ffcniftyb.app.js","url":"app.js"},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"floralclk.app.js","url":"app.js"},
|
||||
|
|
|
@ -4,4 +4,6 @@
|
|||
0.04: Bug fix score reset after Game Over, new icon
|
||||
0.05: Chevron marker on the randomly added square
|
||||
0.06: Fixed issue 1609 added a message popup state handler to control unwanted screen redraw
|
||||
0.07: Optimized the mover algorithm for efficiency (work in progress)
|
||||
0.07: Optimized the mover algorithm for efficiency (work in progress)
|
||||
0.08: Bug fix at end of the game with victorious splash and glorious orchestra
|
||||
0.09: Added settings menu, removed symbol selection button (*), added highscore reset
|
|
@ -1,7 +1,21 @@
|
|||
const debugMode = 'off'; // valid values are: off, test, production, development
|
||||
let settings = Object.assign({
|
||||
// default values
|
||||
maxUndoLevels: 4,
|
||||
charIndex: 0,
|
||||
clockMode: true,
|
||||
debugMode: false,
|
||||
}, require('Storage').readJSON("game1024.settings.json", true) || {});
|
||||
|
||||
const clockMode = settings.clockMode!==undefined ? settings.clockMode : true;
|
||||
const debugMode = settings.debugMode!==undefined ? settings.debugMode : false; // #settings -- valid values are: true or false
|
||||
const maxUndoLevels = settings.maxUndoLevels!==undefined ? settings.maxUndoLevels : 4; // #settings
|
||||
const charIndex = settings.charIndex!==undefined ? settings.charIndex : 0; // #settings -- plain numbers on the grid
|
||||
|
||||
delete settings; // remove unneeded settings from memory
|
||||
|
||||
const middle = {x:Math.floor(g.getWidth()/2)-20, y: Math.floor(g.getHeight()/2)};
|
||||
const rows = 4, cols = 4;
|
||||
const borderWidth = 6;
|
||||
const rows = 4, cols = 4; // #settings
|
||||
const borderWidth = 6;
|
||||
const sqWidth = (Math.floor(Bangle.appRect.w - 48) / rows) - borderWidth;
|
||||
const cellColors = [{bg:'#00FFFF', fg: '#000000'},
|
||||
{bg:'#FF00FF', fg: '#000000'}, {bg:'#808000', fg: '#FFFFFF'}, {bg:'#0000FF', fg: '#FFFFFF'}, {bg:'#008000', fg: '#FFFFFF'},
|
||||
|
@ -13,12 +27,8 @@ const cellChars = [
|
|||
['0','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'],
|
||||
['0','I', 'II', 'III', 'IV', 'V', 'VI', 'VII','VIII', 'IX', 'X']
|
||||
];
|
||||
// const numInitialCells = 2;
|
||||
const maxUndoLevels = 4;
|
||||
const noExceptions = true;
|
||||
let charIndex = 0; // plain numbers on the grid
|
||||
const themeBg = g.theme.bg;
|
||||
|
||||
const themeBg = g.theme.bg;
|
||||
|
||||
const scores = {
|
||||
currentScore: 0,
|
||||
|
@ -78,12 +88,12 @@ const snapshot = {
|
|||
updCounter: function() {
|
||||
this.counter = ++this.counter > this.interval ? 0 : this.counter;
|
||||
},
|
||||
dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0, charIndex: charIndex},
|
||||
dump: {gridsize: rows * cols, expVals: [], score: 0, highScore: 0},
|
||||
write: function() {
|
||||
require("Storage").writeJSON(this.snFileName, this.dump);
|
||||
},
|
||||
read: function () {
|
||||
let sn = require("Storage").readJSON(this.snFileName, noExceptions);
|
||||
let sn = require("Storage").readJSON(this.snFileName, true);
|
||||
if ((typeof sn == "undefined") || (sn.gridsize !== rows * cols)) {
|
||||
require("Storage").writeJSON(this.snFileName, this.dump);
|
||||
return false;
|
||||
|
@ -101,7 +111,6 @@ const snapshot = {
|
|||
});
|
||||
this.dump.score = scores.currentScore;
|
||||
this.dump.highScore = scores.highScore;
|
||||
this.dump.charIndex = charIndex;
|
||||
},
|
||||
make: function () {
|
||||
this.updCounter();
|
||||
|
@ -118,7 +127,7 @@ const snapshot = {
|
|||
});
|
||||
scores.currentScore = this.dump.score ? this.dump.score : 0;
|
||||
scores.highScore = this.dump.highScore ? this.dump.highScore : 0 ;
|
||||
charIndex = this.dump.charIndex ? this.dump.charIndex : 0 ;
|
||||
if (this.dump.hasOwnProperty('charIndex')) delete this.dump.charIndex; // depricated in v0.09
|
||||
}
|
||||
},
|
||||
reset: function () {
|
||||
|
@ -129,12 +138,11 @@ const snapshot = {
|
|||
}
|
||||
this.dump.score = 0;
|
||||
this.dump.highScore = scores.highScore;
|
||||
this.dump.charIndex = charIndex;
|
||||
this.write();
|
||||
debug(() => console.log("reset D U M P E D!", this.dump));
|
||||
}
|
||||
};
|
||||
const btnAtribs = {x: 134, w: 42, h: 42, fg:'#C0C0C0', bg:'#800000'};
|
||||
const btnAtribs = {x: 134, w: 42, h: 50, fg:'#C0C0C0', bg:'#800000'};
|
||||
const buttons = {
|
||||
all: [],
|
||||
draw: function () {
|
||||
|
@ -162,6 +170,7 @@ const buttons = {
|
|||
*/
|
||||
|
||||
const mover = {
|
||||
gameWon: false,
|
||||
direction: {
|
||||
up: {name: 'up', step: 1, innerBegin: 0, innerEnd: rows-1, outerBegin: 0, outerEnd: cols-1, iter: rows -1,
|
||||
sqIndex: function (i,o) {return i*(cols) + o;}, sqNextIndex: function (i,o) {return i < rows -1 ? (i+1)*(cols) + o : -1;}
|
||||
|
@ -313,7 +322,7 @@ class Cell {
|
|||
}
|
||||
drawBg() {
|
||||
debug(()=>console.log("Drawbg!!"));
|
||||
if (this.isRndm == true) {
|
||||
if (this.isRndm) {
|
||||
debug(()=>console.log('Random: (ax)', this.ax));
|
||||
g.setColor(this.getColor(this.expVal).bg)
|
||||
.fillRect(this.x0, this.y0, this.x1, this.y1)
|
||||
|
@ -364,7 +373,7 @@ class Cell {
|
|||
this.isRndm = true;
|
||||
}
|
||||
drawRndmIndicator(){
|
||||
if (this.isRndm == true) {
|
||||
if (this.isRndm) {
|
||||
debug(()=>console.log('Random: (ax)', this.ax));
|
||||
g.setColor(this.getColor(0).bg)
|
||||
.fillPoly(this.ax,this.ay,this.bx,this.by,this.cx,this.cy);
|
||||
|
@ -373,8 +382,9 @@ class Cell {
|
|||
}
|
||||
|
||||
function undoGame() {
|
||||
g.clear();
|
||||
if (scores.lastScores.length > 0) {
|
||||
|
||||
if (scores.lastScores.length) {
|
||||
g.clear();
|
||||
allSquares.forEach(sq => {
|
||||
sq.popFromUndo();
|
||||
sq.drawBg();
|
||||
|
@ -385,9 +395,9 @@ function undoGame() {
|
|||
buttons.draw();
|
||||
updUndoLvlIndex();
|
||||
snapshot.make();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
function addToUndo() {
|
||||
allSquares.forEach(sq => {
|
||||
|
@ -397,7 +407,7 @@ function addToUndo() {
|
|||
}
|
||||
function addToScore (val) {
|
||||
scores.add(val);
|
||||
if (val == 10) messageYouWin();
|
||||
if (val == 10) mover.gameWon = true;
|
||||
}
|
||||
function createGrid () {
|
||||
let cn =0;
|
||||
|
@ -421,15 +431,30 @@ function messageGameOver () {
|
|||
.drawString("O V E R !", middle.x+12, middle.y+25);
|
||||
}
|
||||
function messageYouWin () {
|
||||
g.setColor("#1a0d00")
|
||||
const c = (g.theme.dark) ? {"fg": "#FFFFFF", "bg": "#808080"} : {"fg": "#FF0000", "bg": "#000000"};
|
||||
g.setColor(c.bg)
|
||||
.setFont12x20(2)
|
||||
.setFontAlign(0,0,0)
|
||||
.drawString("YOU HAVE", middle.x+18, middle.y-24)
|
||||
.drawString("W O N ! !", middle.x+18, middle.y+24);
|
||||
g.setColor("#FF0808")
|
||||
g.setColor(c.fg)
|
||||
.drawString("YOU HAVE", middle.x+17, middle.y-25)
|
||||
.drawString("W O N ! !", middle.x+17, middle.y+25);
|
||||
Bangle.buzz(200, 1);
|
||||
for (let r=0;r<4;r++){
|
||||
Bangle.buzz(200,0.2)
|
||||
.then((result) => {
|
||||
Bangle.buzz(200,0.5)
|
||||
.then((result)=>{
|
||||
Bangle.buzz(200,0.8)
|
||||
.then((result)=>{
|
||||
Bangle.buzz(200,1)
|
||||
.then((result)=>{
|
||||
Bangle.buzz(500,0);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
function makeRandomNumber () {
|
||||
return Math.ceil(2*Math.random());
|
||||
|
@ -471,8 +496,8 @@ function initGame() {
|
|||
drawGrid();
|
||||
scores.draw();
|
||||
buttons.draw();
|
||||
// Clock mode allows short-press on button to exit
|
||||
Bangle.setUI("clock");
|
||||
// #settings Clock mode allows short-press on button to exit
|
||||
if(clockMode) Bangle.setUI("clock");
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
@ -491,8 +516,8 @@ function drawPopUp(message,cb) {
|
|||
rDims.x+10, rDims.y2-40
|
||||
]);
|
||||
buttons.all.forEach(btn => {btn.disable();});
|
||||
const btnYes = new Button('yes', rDims.x+16, rDims.y2-80, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true);
|
||||
const btnNo = new Button('no', rDims.x2-80, rDims.y2-80, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true);
|
||||
const btnYes = new Button('yes', rDims.x+16, rDims.y2-88, 54, btnAtribs.h, 'YES', btnAtribs.fg, btnAtribs.bg, cb, true);
|
||||
const btnNo = new Button('no', rDims.x2-80, rDims.y2-88, 54, btnAtribs.h, 'NO', btnAtribs.fg, btnAtribs.bg, cb, true);
|
||||
btnYes.draw();
|
||||
btnNo.draw();
|
||||
g.setColor('#000000');
|
||||
|
@ -527,6 +552,7 @@ function handlePopUpClicks(btn) {
|
|||
function resetGame() {
|
||||
g.clear();
|
||||
scores.reset();
|
||||
mover.gameWon=false;
|
||||
allSquares.forEach(sq => {sq.setExpVal(0);sq.removeUndo();sq.setRndmFalse();});
|
||||
addRandomNumber();
|
||||
addRandomNumber();
|
||||
|
@ -543,14 +569,8 @@ function resetGame() {
|
|||
* @param {function} func function to call like console.log()
|
||||
*/
|
||||
const debug = (func) => {
|
||||
switch (debugMode) {
|
||||
case "development":
|
||||
if (typeof func === 'function') {
|
||||
func();
|
||||
}
|
||||
break;
|
||||
case "off":
|
||||
default: break;
|
||||
if (debugMode) {
|
||||
if (typeof func === 'function') func();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -653,6 +673,12 @@ function runGame(dir){
|
|||
debug(() => console.log("G A M E O V E R !!"));
|
||||
snapshot.reset();
|
||||
messageGameOver();
|
||||
} else {
|
||||
if (mover.gameWon) {
|
||||
debug(() => console.log("Y O U H A V E W O N !!"));
|
||||
snapshot.reset();
|
||||
messageYouWin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -667,13 +693,9 @@ function updUndoLvlIndex() {
|
|||
.drawString(scores.lastScores.length, x, y);
|
||||
}
|
||||
}
|
||||
function incrCharIndex() {
|
||||
charIndex++;
|
||||
if (charIndex >= cellChars.length) charIndex = 0;
|
||||
drawGrid();
|
||||
}
|
||||
|
||||
buttons.add(new Button('undo', btnAtribs.x, 25, btnAtribs.w, btnAtribs.h, 'U', btnAtribs.fg, btnAtribs.bg, undoGame, true));
|
||||
buttons.add(new Button('chars', btnAtribs.x, 71, btnAtribs.w, 31, '*', btnAtribs.fg, btnAtribs.bg, function(){incrCharIndex();}, true));
|
||||
|
||||
buttons.add(new Button('restart', btnAtribs.x, 106, btnAtribs.w, btnAtribs.h, 'R', btnAtribs.fg, btnAtribs.bg, function(){drawPopUp('Do you want\nto restart?',handlePopUpClicks);}, true));
|
||||
|
||||
initGame();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "game1024",
|
||||
"name": "1024 Game",
|
||||
"shortName" : "1024 Game",
|
||||
"version": "0.07",
|
||||
"version": "0.09",
|
||||
"icon": "game1024.png",
|
||||
"screenshots": [ {"url":"screenshot.png" } ],
|
||||
"readme":"README.md",
|
||||
|
@ -12,6 +12,7 @@
|
|||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"game1024.app.js","url":"app.js"},
|
||||
{"name":"game1024.settings.js","url":"settings.js"},
|
||||
{"name":"game1024.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
(function(back) {
|
||||
var FILE = "game1024.settings.json";
|
||||
var scoreFile = "game1024.json";
|
||||
// Load settings
|
||||
var settings = Object.assign({
|
||||
maxUndoLevels: 5,
|
||||
charIndex: 0,
|
||||
clockMode: true,
|
||||
debugMode: false,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
var symbols = ["1 2 3 ...", "A B C ...", "I II III..."];
|
||||
var settingsMenu = {
|
||||
"" : { "title" : "1024 Game" },
|
||||
"< Back" : () => back(),
|
||||
"Symbols": {
|
||||
value: 0|settings.charIndex,
|
||||
min:0,max:symbols.length-1,
|
||||
format: v=>symbols[v],
|
||||
onchange: v=> { settings.charIndex=v; writeSettings();}
|
||||
}
|
||||
,
|
||||
"Undo levels:": {
|
||||
value: 0|settings.maxUndoLevels, // 0| converts undefined to 0
|
||||
min: 0, max: 9,
|
||||
onchange: v => {
|
||||
settings.maxUndoLevels = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Exit press:": {
|
||||
value: !settings.debugMode, // ! converts undefined to true
|
||||
format: v => v?"short":"long",
|
||||
onchange: v => {
|
||||
settings.debugMode = v;
|
||||
writeSettings();
|
||||
},
|
||||
},
|
||||
"Debug mode:": {
|
||||
value: !!settings.debugMode, // !! converts undefined to false
|
||||
format: v => v?"On":"Off",
|
||||
onchange: v => {
|
||||
settings.debugMode = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Reset Highscore": () => {
|
||||
E.showPrompt('Reset Highscore?').then((v) => {
|
||||
let delay = 50;
|
||||
if (v) {
|
||||
delay = 500;
|
||||
let sF = require("Storage").readJSON(scoreFile, true);
|
||||
if (typeof sF !== "undefined") {
|
||||
E.showMessage('Resetting');
|
||||
sF.highScore = 0;
|
||||
require("Storage").writeJSON(scoreFile, sF);
|
||||
} else {
|
||||
E.showMessage('No highscore!');
|
||||
}
|
||||
}
|
||||
setTimeout(() => E.showMenu(settingsMenu), delay);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Show the menu
|
||||
E.showMenu(settingsMenu);
|
||||
})
|
|
@ -8,6 +8,7 @@
|
|||
"tags": "clock",
|
||||
"screenshots": [{"url":"bangle1-high-contrast-clock-screenshot.png"}],
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"hcclock.app.js","url":"hcclock.app.js"},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"bangle1-impercise-word-clock-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"intclock.app.js","url":"app.js"},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"icon": "intervals.png",
|
||||
"tags": "",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"intervals.app.js","url":"intervals.app.js"},
|
||||
{"name":"intervals.img","url":"intervals-icon.js","evaluate":true}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"tags": "tool,system,ios,apple,messages,notifications",
|
||||
"dependencies": {"messages":"app"},
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"ios.app.js","url":"app.js"},
|
||||
{"name":"ios.img","url":"app-icon.js","evaluate":true},
|
||||
|
|
|
@ -41,3 +41,5 @@
|
|||
0.26: Setting to auto-open music
|
||||
0.27: Add 'mark all read' option to popup menu (fix #1624)
|
||||
0.28: Option to auto-unlock the watch when a new message arrives
|
||||
0.29: Fix message list overwrites on Bangle.js 1 (fix #1642)
|
||||
0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel)
|
||||
|
|
|
@ -82,31 +82,45 @@ function getNegImage() {
|
|||
return atob("FhaBADAAMeAB78AP/4B/fwP4/h/B/P4D//AH/4AP/AAf4AB/gAP/AB/+AP/8B/P4P4fx/A/v4B//AD94AHjAAMA=");
|
||||
}
|
||||
/*
|
||||
* icons should be 24x24px with 1bpp colors and transparancy
|
||||
* icons should be 24x24px with 1bpp colors and 'Transparency to Color'
|
||||
* http://www.espruino.com/Image+Converter
|
||||
*/
|
||||
function getMessageImage(msg) {
|
||||
if (msg.img) return atob(msg.img);
|
||||
var s = (msg.src||"").toLowerCase();
|
||||
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
|
||||
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
|
||||
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
|
||||
if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
|
||||
if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA");
|
||||
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA");
|
||||
if (s=="facebook") return getFBIcon();
|
||||
if (s=="gmail") return getNotificationImage();
|
||||
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
|
||||
if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
|
||||
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
|
||||
if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA==");
|
||||
if (s=="gmail") return getNotificationImage();
|
||||
if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
|
||||
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
|
||||
if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44");
|
||||
if (s=="mail") return getNotificationImage();
|
||||
if (s=="messenger") return getFBIcon();
|
||||
if (s=="outlook mail") return getNotificationImage();
|
||||
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
|
||||
if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA=");
|
||||
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
|
||||
if (s=="post & dhl") return atob("GBgBAPgAE/5wMwZ8NgN8NgP4NgP4HgP4HgPwDwfgD//AB/+AAf8AAAAABs7AHcdgG4MwAAAAGESAFESAEkSAEnyAEkSAFESAGETw");
|
||||
if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA");
|
||||
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
|
||||
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
|
||||
if (s=="sms message") return getNotificationImage();
|
||||
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
|
||||
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
|
||||
if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA");
|
||||
if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA");
|
||||
if (s=="telegram") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
|
||||
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
|
||||
if (s=="to do") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA");
|
||||
if (s=="twitch") return atob("GBgBH//+P//+P//+eAAGeAAGeAAGeDGGeDOGeDOGeDOGeDOGeDOGeDOGeAAOeAAOeAAcf4/4f5/wf7/gf//Af/+AA/AAA+AAAcAA");
|
||||
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
|
||||
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
|
||||
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
|
||||
if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
|
||||
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
|
||||
if (msg.id=="back") return getBackImage();
|
||||
return getNotificationImage();
|
||||
|
@ -115,28 +129,38 @@ function getMessageImageCol(msg,def) {
|
|||
return {
|
||||
// generic colors, using B2-safe colors
|
||||
"alarm": "#fff",
|
||||
"calendar": "#f00",
|
||||
"mail": "#ff0",
|
||||
"music": "#f0f",
|
||||
"phone": "#0f0",
|
||||
"sms message": "#0ff",
|
||||
// brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos)
|
||||
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
|
||||
"bibel": "#54342c",
|
||||
"discord": "#738adb",
|
||||
"facebook": "#4267b2",
|
||||
"gmail": "#ea4335",
|
||||
"google home": "#fbbc05",
|
||||
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
|
||||
"hangouts": "#1ba261",
|
||||
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
|
||||
"instagram": "#dd2a7b",
|
||||
"liferando": "#ee5c00",
|
||||
"messenger": "#0078ff",
|
||||
"nina": "#e57004",
|
||||
"outlook mail": "#0072c6",
|
||||
"post & dhl": "#f2c101",
|
||||
"signal": "#00f",
|
||||
"skype": "#00aff0",
|
||||
"slack": "#e51670",
|
||||
"threema": "#000",
|
||||
"snapchat": "#ff0",
|
||||
"teams": "#464eb8",
|
||||
"telegram": "#0088cc",
|
||||
"threema": "#000",
|
||||
"to do": "#3999e5",
|
||||
"twitch": "#6441A4",
|
||||
"twitter": "#1da1f2",
|
||||
"whatsapp": "#4fce5d",
|
||||
"wordfeud": "#e7d3c7",
|
||||
"youtube": "#f00",
|
||||
}[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg);
|
||||
}
|
||||
|
||||
|
@ -457,6 +481,7 @@ function checkMessages(options) {
|
|||
var msg = MESSAGES[idx-1];
|
||||
if (msg && msg.new) g.setBgColor(g.theme.bgH).setColor(g.theme.fgH);
|
||||
else g.setColor(g.theme.fg);
|
||||
g.clearRect(r.x,r.y,r.x+r.w, r.y+r.h);
|
||||
if (idx==0) msg = {id:"back", title:"< Back"};
|
||||
if (!msg) return;
|
||||
var x = r.x+2, title = msg.title, body = msg.body;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.28",
|
||||
"version": "0.30",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "numerals,clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle1-numerals-screenshot.png"}],
|
||||
"storage": [
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"screenshots": [{"url":"bangle1-pipboy-themed-clock-screenshot.png"}],
|
||||
"storage": [
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"type": "boot",
|
||||
"tags": "system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"screenshots": [{"url":"pro-menu-screenshot.png"}],
|
||||
"storage": [
|
||||
{"name":"promenu.boot.js","url":"boot.js","supports": ["BANGLEJS"]},
|
||||
|
|
|
@ -10,4 +10,5 @@
|
|||
0.09: Fix broken start/stop if recording not enabled (fix #1561)
|
||||
0.10: Don't allow the same setting to be chosen for 2 boxes (fix #1578)
|
||||
0.11: Notifications fixes
|
||||
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
||||
0.12: Fix for recorder not stopping at end of run. Bug introduced in 0.11
|
||||
0.13: Revert #1578 (stop duplicate entries) as with 2v12 menus it causes other boxes to be wiped (fix #1643)
|
||||
|
|
|
@ -14,7 +14,8 @@ the red `STOP` in the bottom right turns to a green `RUN`.
|
|||
shown will increase, even if you are standing still.
|
||||
* `TIME` - the elapsed time for your run
|
||||
* `PACE` - the number of minutes it takes you to run a given distance, configured in settings (default 1km) **based on your run so far**
|
||||
* `HEART` - Your heart rate
|
||||
* `HEART (BPM)` - Your current heart rate
|
||||
* `Max BPM` - Your maximum heart rate reached during the run
|
||||
* `STEPS` - Steps since you started exercising
|
||||
* `CADENCE` - Steps per second based on your step rate *over the last minute*
|
||||
* `GPS` - this is green if you have a GPS lock. GPS is turned on automatically
|
||||
|
@ -35,7 +36,7 @@ Under `Settings` -> `App` -> `Run` you can change settings for this app.
|
|||
record GPS/HRM/etc data every time you start a run?
|
||||
* `Pace` is the distance that pace should be shown over - 1km, 1 mile, 1/2 Marathon or 1 Marathon
|
||||
* `Boxes` leads to a submenu where you can configure what is shown in each of the 6 boxes on the display.
|
||||
Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence".
|
||||
Available stats are "Time", "Distance", "Steps", "Heart (BPM)", "Max BPM", "Pace (avg)", "Pace (curr)", "Speed", and "Cadence".
|
||||
Any box set to "-" will display no information.
|
||||
* Box 1 is the top left (defaults to "Distance")
|
||||
* Box 2 is the top right (defaults to "Time")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "run",
|
||||
"name": "Run",
|
||||
"version":"0.12",
|
||||
"version":"0.13",
|
||||
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
||||
"icon": "app.png",
|
||||
"tags": "run,running,fitness,outdoors,gps",
|
||||
|
|
|
@ -42,11 +42,6 @@
|
|||
value: Math.max(statsIDs.indexOf(settings[boxID]),0),
|
||||
format: v => statsList[v].name,
|
||||
onchange: v => {
|
||||
for (var i=1;i<=6;i++)
|
||||
if (settings["B"+i]==statsIDs[v]) {
|
||||
settings["B"+i]="";
|
||||
boxMenu["Box "+i].value=0;
|
||||
}
|
||||
settings[boxID] = statsIDs[v];
|
||||
saveSettings();
|
||||
},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "app",
|
||||
"tags": "",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"score.app.js","url":"score.app.js"},
|
||||
{"name":"score.settings.js","url":"score.settings.js"},
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"showimg.app.js","url":"app.js"},
|
||||
{"name":"showimg.img","url":"app-icon.js","evaluate":true}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"shortName": "MonoClock",
|
||||
"icon": "app.png",
|
||||
"screenshots": [{ "url": "screenshot0.png" }, {"url": "screenshot1.png" }],
|
||||
"version": "0.04",
|
||||
"version": "0.06",
|
||||
"description": "A simple watchface based on my stylised monogram.",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
1.0: Initial release on the app repository for Bangle.js 1 and 2
|
|
@ -0,0 +1,23 @@
|
|||
# Stardate Clock
|
||||
|
||||
A clock face displaying a stardate along with a "standard" digital/analog clock
|
||||
in LCARS design.
|
||||
|
||||
That design has been made popular by various Star Trek shows. Credits for the
|
||||
original LCARS designs go to Michael Okuda, copyrights are owned by Paramount Global,
|
||||
usage of that type of design is permitted freely for non-profit use cases.
|
||||
The Bangle.js version has been created by Robert Kaiser <kairo@kairo.at>.
|
||||
|
||||
The stardate concept used leans on the shows released from the late 80s onward
|
||||
by using 1000 units per Earth year, but to apply this more cleanly, this split
|
||||
is applied exactly. Also, to give more relationship to the shows and
|
||||
incidentally make values look similar to those depicted there, the zero point
|
||||
is set to the first airing of the original 'Star Trek' series in the US on
|
||||
Thursday, September 8, 1966, at 8:30 p.m. Eastern Time.
|
||||
|
||||
The clock face supports Bangle.js 1 and 2 with some compromises (e.g. the
|
||||
colors will look best on Bangle.js 1, the font sizes will look best on
|
||||
Bangle.js 2).
|
||||
|
||||
Any tap on the diaply while unlocked switches the "standard" Earth-style clock
|
||||
between digital and analog display.
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwgkiAA0gDRwX/C/4X/C5MO9wBDgnkAIMAAQQXKAItehwECAQIXK8gBEIQIeDC5YAF8EAAIIECC48hE4oYCAogXIkQvHDIgCBiQXHiCPFAIaaCgECJBChDAIsOU4RIJbJwwIDIVEABIYBMJAXOAC8SmYAHmHdABJfBCxAXNCpEyRxoWVgETC46+OkYXHRpxGWC5EwBQMBkQDBiK4DKQMBiAXKNQMggQ2CgI7CkcgC5UjicwkYXCgUxmakBC5kCmERC4MiAoMjgMTC50AC4KYCkcAgYXRPgJFBC6YABgYEBC6iQBC6cRgMgL6ikBR4IXOiR3EX4IXPAgTXDBgIXNgUiiClCAAMikIKBC5YAMC64AXogAGoAX/C6w"))
|
|
@ -0,0 +1,362 @@
|
|||
// Stardate clock face, by KaiRo.at, 2021-2022
|
||||
|
||||
var redrawClock = true;
|
||||
var clockface = "digital";
|
||||
|
||||
// note: Bangle.js 1 has 240x240x16, 2 has 176x176x3 screen
|
||||
var bpp = g.getBPP ? g.getBPP() : 16;
|
||||
|
||||
// Load fonts
|
||||
Graphics.prototype.setFontAntonio27 = function(scale) {
|
||||
// Actual height 23 (23 - 1)
|
||||
g.setFontCustom(atob("AAAAAAGAAAAwAAAGAAAAwAAAGAAAAwAAAAAAAAAAAAAAAAAADAAAA4AAAHAAAAAAAAAAAAAAAAAAAAAA4AAB/AAD/4AH/4AP/wAf/gAD/AAAeAAAAAAAAAAAAA///AP//+D///4eAAPDgAA4cAAHD///4P//+A///gAAAAAAAAAAAAAAYAAAHAAAA4AAAOAAAD///4f///D///4AAAAAAAAAAAAAAAAAAAAAAA/gD4P8B/D/g/4cAfzDgP4Yf/8DD/+AYP/ADAGAAAAAAAAAAAAHwD8B+AfwfwD/DgMA4cDgHDgeA4f///B/3/wH8P8AAAAAAAAAAAAOAAAPwAAP+AAP/wAf8OAf4BwD///4f///D///4AABwAAAGAAAAAAAAAAAAAAD/4Pwf/h/D/4P4cMAHDjgA4cf//Dh//4cH/8AAAAAAAAAAAAAAH//8B///wf///Dg4A4cHAHDg4A4f3//B+f/wHh/8AAAAAAAAAAAAAAcAAADgAA4cAD/DgH/4cH//Dv/4Af/gAD/gAAfAAADgAAAAAAAAAAAAH4f8B///wf///Dg8A4cDAHDg8A4f///B///wH8/8AAAAAAAAAAAAAAH/h4B/+Pwf/5/DgHA4cA4HDgHA4f///B///wH//8AAAAAAAAAAAAAAAAAAAHgeAA8DwAHgeAAAAAAAAAA"), 45, atob("CQcKDAsMDAwMDAwMDAc="), 27+(scale<<8)+(1<<16));
|
||||
};
|
||||
Graphics.prototype.setFontAntonio42 = function(scale) {
|
||||
// Actual height 36 (36 - 1)
|
||||
g.setFontCustom(atob("AAAAAAAAAAAAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAHgAAAAAHgAAAAAHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAfgAAAAH/gAAAB//gAAAf//gAAH//4AAB//+AAAf//gAAH//4AAAf/+AAAAf/gAAAAf4AAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAA////gAH////+AP/////Af/////gf/////gfAAAAPgeAAAAHgeAAAAHgfAAAAPgf/////gf/////gP/////AH////+AB////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4AAAAAB4AAAAAB4AAAAADwAAAAAHwAAAAAP/////gf/////gf/////gf/////gf/////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8AAPgH/8AD/gP/8AP/gP/8A//gf/8B//gfAAH/ngeAAf+HgeAB/4HgfAH/gHgf//+AHgP//4AHgH//wAHgD/+AAHgAPgAAAAAAAAAAAAAAAAAAAAAAAAAA+AAfwAH+AAf+AP+AAf/AP+AAf/Af+AAf/gfADwAPgeADwAHgeADwAHgfAH4APgf///h/gf/////AP/+///AH/+f/+AB/4H/4AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAA/gAAAAH/gAAAB//gAAAP//gAAB//HgAAf/wHgAD/8AHgAf/AAHgAf/////gf/////gf/////gf/////gf/////gAAAAHgAAAAAHgAAAAAHAAAAAAAAAAAAAAAAAAAAAAAf//gP8Af//gP+Af//gP/Af//gP/gf/+AAfgeB8AAHgeB4AAHgeB8AAHgeB////geB////geA////AeAf//+AAAD//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf///gAD////8AH/////AP/////Af/////gfAPgAfgeAPAAHgeAPAAHgeAPAAHgf+PgAPgf+P///gP+H///AH+H//+AB+B//8AAAAD8AAAAAAAAAAAAAAAAAAAAAAAeAAAAAAeAAAAAAeAAAAPgeAAAP/geAAD//geAA///geAH///geB///+AeP//4AAe//8AAAf//AAAAf/wAAAAf+AAAAAfwAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAB/wH/4AH/8f/+AP/////Af/////gf/////geAH4APgeADgAHgeADgAHgeAHwAHgf/////gf/////gP/////AH/8//+AB/wH/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//gPgAH//4P+AP//8P/Af//+P/AfwB+P/geAAeAPgeAAeAHgeAAeAHgfAAeAPgf/////gP/////AP/////AH////8AA////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4APgAAH4AfgAAH4AfgAAH4AfgAAH4AfgAAD4APgAAAAAAAAAAAAAAA="), 45, atob("DgsPEhESEhISEhISEgo="), 42+(scale<<8)+(1<<16));
|
||||
};
|
||||
const fontName = "Antonio27";
|
||||
const fontNameLarge = "Antonio42";
|
||||
const fontSize = 1;
|
||||
const fontSizeLarge = 1;
|
||||
const fontHeightLarge = 42 * fontSizeLarge;
|
||||
|
||||
// LCARS dimensions
|
||||
if (g.getWidth() < 200) { // Bangle.js 2
|
||||
const baseUnit1 = 3;
|
||||
const baseUnit2 = 2;
|
||||
const baseUnit3 = 7;
|
||||
}
|
||||
else {
|
||||
const baseUnit1 = 5;
|
||||
const baseUnit2 = 3;
|
||||
const baseUnit3 = 10;
|
||||
}
|
||||
const widgetsHeight = 24;
|
||||
const sbarWid = baseUnit3 * 5;
|
||||
const hbarHt = baseUnit1;
|
||||
const outRad = baseUnit1 * 5;
|
||||
const inRad = outRad - hbarHt;
|
||||
const gap = baseUnit2;
|
||||
const divisionPos = baseUnit3 * 8;
|
||||
const sbarGapPos = baseUnit3 * 15;
|
||||
const lowerTop = divisionPos+gap+1;
|
||||
|
||||
// Star Trek famously premiered on Thursday, September 8, 1966, at 8:30 p.m.
|
||||
// See http://www.startrek.com/article/what-if-the-original-star-trek-had-debuted-on-friday-nights
|
||||
const gSDBase = new Date("September 8, 1966 20:30:00 EST");
|
||||
const sdatePosBottom = divisionPos - hbarHt - 1;
|
||||
const sdatePosRight = g.getWidth() - baseUnit2;
|
||||
const sdateDecimals = 1;
|
||||
const secondsPerYear = 86400 * 365.2425;
|
||||
const sdateDecFactor = Math.pow(10, sdateDecimals);
|
||||
|
||||
const clockAreaLeft = sbarWid + inRad / 2;
|
||||
const clockAreaTop = lowerTop + hbarHt + inRad / 2;
|
||||
const clockWid = g.getWidth() - clockAreaLeft;
|
||||
const clockHt = g.getHeight() - clockAreaTop;
|
||||
|
||||
const ctimePosTop = clockAreaTop + baseUnit1 * 5;
|
||||
const ctimePosCenter = clockAreaLeft + clockWid / 2;
|
||||
const cdatePosTop = ctimePosTop + fontHeightLarge;
|
||||
const cdatePosCenter = clockAreaLeft + clockWid / 2;
|
||||
|
||||
const clockCtrX = Math.floor(clockAreaLeft + clockWid / 2);
|
||||
const clockCtrY = Math.floor(clockAreaTop + clockHt / 2);
|
||||
const analogRad = Math.floor(Math.min(clockWid, clockHt) / 2);
|
||||
|
||||
const analogMainLineLength = baseUnit1 * 2;
|
||||
const analogSubLineLength = baseUnit1;
|
||||
|
||||
const analogHourHandLength = analogRad / 2;
|
||||
const analogMinuteHandLength = analogRad - analogMainLineLength / 2;
|
||||
|
||||
const colorBg = "#000000";
|
||||
const colorTime = "#9C9CFF";
|
||||
const colorDate = "#A09090";
|
||||
const colorStardate = "#FFCF00";
|
||||
// On low-bpp devices (Bangle.js 2), use basic colors for analog clock.
|
||||
const colorHours = bpp > 3 ? "#9C9CFF" : "#00FF00";
|
||||
const colorSeconds = bpp > 3 ? "#E7ADE7" : "#FFFF00";
|
||||
const colorHands = bpp > 3 ? "#A09090" : "#00FFFF";
|
||||
const colorLCARSGray = "#A09090";
|
||||
const colorLCARSOrange = "#FF9F00";
|
||||
const colorLCARSPink = "#E7ADE7";
|
||||
const colorLCARSPurple = "#A06060";
|
||||
const colorLCARSBrown = "#C09070";
|
||||
// More colors: teal #008484, yellow FFCF00, purple #6050B0
|
||||
|
||||
var lastSDateString;
|
||||
var lastTimeStringToMin;
|
||||
var lastTimeStringSec;
|
||||
var lastDateString;
|
||||
var lastAnalogDate;
|
||||
|
||||
function updateStardate() {
|
||||
var curDate = new Date();
|
||||
|
||||
// Note that the millisecond division and the 1000-unit multiplier cancel each other out.
|
||||
var sdateval = (curDate - gSDBase) / secondsPerYear;
|
||||
|
||||
var sdatestring = (Math.floor(sdateval * sdateDecFactor) / sdateDecFactor).toFixed(sdateDecimals);
|
||||
|
||||
// Reset the state of the graphics library.
|
||||
g.reset();
|
||||
g.setBgColor(colorBg);
|
||||
// Set Font
|
||||
g.setFont(fontName, fontSize);
|
||||
if (lastSDateString) {
|
||||
// Clear the area where we want to draw the time.
|
||||
//g.setBgColor("#FF6600"); // for debugging
|
||||
g.clearRect(sdatePosRight - g.stringWidth(lastSDateString) - 1,
|
||||
sdatePosBottom - g.getFontHeight(),
|
||||
sdatePosRight,
|
||||
sdatePosBottom);
|
||||
}
|
||||
// Draw the current stardate.
|
||||
g.setColor(colorStardate);
|
||||
g.setFontAlign(1, 1, 0); // Align following string to bottom right.
|
||||
g.drawString(sdatestring, sdatePosRight, sdatePosBottom);
|
||||
lastSDateString = sdatestring;
|
||||
|
||||
// Schedule next when an update to the last decimal is due.
|
||||
var mstonextUpdate = (Math.ceil(sdateval * sdateDecFactor) / sdateDecFactor - sdateval) * secondsPerYear;
|
||||
if (redrawClock) {
|
||||
setTimeout(updateStardate, mstonextUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
function updateConventionalTime() {
|
||||
var curDate = new Date();
|
||||
|
||||
if (clockface == "digital") {
|
||||
drawDigitalClock(curDate);
|
||||
}
|
||||
else {
|
||||
drawAnalogClock(curDate);
|
||||
}
|
||||
|
||||
// Schedule next when an update to the last second is due.
|
||||
var mstonextUpdate = Math.ceil(curDate / 1000) * 1000 - curDate;
|
||||
if (redrawClock) {
|
||||
setTimeout(updateConventionalTime, mstonextUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
function drawDigitalClock(curDate) {
|
||||
var timestringToMin = ("0" + curDate.getHours()).substr(-2) + ":"
|
||||
+ ("0" + curDate.getMinutes()).substr(-2) + ":";
|
||||
var timestringSec = ("0" + curDate.getSeconds()).substr(-2);
|
||||
var datestring = "" + curDate.getFullYear() + "-"
|
||||
+ ("0" + (curDate.getMonth() + 1)).substr(-2) + "-"
|
||||
+ ("0" + curDate.getDate()).substr(-2);
|
||||
|
||||
// Reset the state of the graphics library.
|
||||
g.reset();
|
||||
g.setBgColor(colorBg);
|
||||
// Set Font
|
||||
g.setFont(fontNameLarge, fontSizeLarge);
|
||||
var ctimePosLeft = ctimePosCenter - g.stringWidth("12:34:56") / 2;
|
||||
if (ctimePosLeft + g.stringWidth("00:00:00") > g.getWidth()) {
|
||||
ctimePosLeft = g.getWidth() - g.stringWidth("00:00:00");
|
||||
}
|
||||
g.setColor(colorTime);
|
||||
if (timestringToMin != lastTimeStringToMin) {
|
||||
if (lastTimeStringToMin) {
|
||||
// Clear the area where we want to draw the time.
|
||||
//g.setBgColor("#FF6600"); // for debugging
|
||||
g.clearRect(ctimePosLeft,
|
||||
ctimePosTop,
|
||||
ctimePosLeft + g.stringWidth(lastTimeStringToMin) + 1,
|
||||
ctimePosTop + g.getFontHeight());
|
||||
}
|
||||
// Draw the current time.
|
||||
g.drawString(timestringToMin, ctimePosLeft, ctimePosTop);
|
||||
lastTimeStringToMin = timestringToMin;
|
||||
}
|
||||
var ctimePosLeftSec = ctimePosLeft + g.stringWidth(timestringToMin);
|
||||
if (lastTimeStringSec) {
|
||||
// Clear the area where we want to draw the seconds.
|
||||
//g.setBgColor("#FF6600"); // for debugging
|
||||
g.clearRect(ctimePosLeftSec,
|
||||
ctimePosTop,
|
||||
ctimePosLeftSec + g.stringWidth(lastTimeStringSec) + 1,
|
||||
ctimePosTop + g.getFontHeight());
|
||||
}
|
||||
// Draw the current seconds.
|
||||
g.drawString(timestringSec, ctimePosLeftSec, ctimePosTop);
|
||||
lastTimeStringSec = timestringSec;
|
||||
|
||||
if (datestring != lastDateString) {
|
||||
// Set Font
|
||||
g.setFont(fontName, fontSize);
|
||||
var cdatePosLeft = cdatePosCenter - g.stringWidth("1234-56-78") / 2;
|
||||
if (lastDateString) {
|
||||
// Clear the area where we want to draw the time.
|
||||
//g.setBgColor("#FF6600"); // for debugging
|
||||
g.clearRect(cdatePosLeft,
|
||||
cdatePosTop,
|
||||
cdatePosLeft + g.stringWidth(lastDateString) + 1,
|
||||
cdatePosTop + g.getFontHeight());
|
||||
}
|
||||
// Draw the current date.
|
||||
g.setColor(colorDate);
|
||||
//g.setFontAlign(0, -1, 0); // Align following string to bottom right.
|
||||
g.drawString(datestring, cdatePosLeft, cdatePosTop);
|
||||
lastDateString = datestring;
|
||||
}
|
||||
}
|
||||
|
||||
function drawLine(x1, y1, x2, y2, color) {
|
||||
g.setColor(color);
|
||||
// On high-bpp devices, use anti-aliasing. Low-bpp (Bangle.js 2) doesn't clear nicely with AA.
|
||||
if (bpp > 3 && g.drawLineAA) {
|
||||
g.drawLineAA(x1, y1, x2, y2);
|
||||
}
|
||||
else {
|
||||
g.drawLine(x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
function clearLine(x1, y1, x2, y2) {
|
||||
drawLine(x1, y1, x2, y2, colorBg);
|
||||
}
|
||||
|
||||
function drawAnalogClock(curDate) {
|
||||
// Reset the state of the graphics library.
|
||||
g.reset();
|
||||
g.setBgColor(colorBg);
|
||||
// Init variables for drawing any seconds we have not drawn.
|
||||
// If minute changed, we'll set for the full wheel below.
|
||||
var firstDrawSecond = lastAnalogDate ? lastAnalogDate.getSeconds() + 1 : curDate.getSeconds();
|
||||
var lastDrawSecond = curDate.getSeconds();
|
||||
if (!lastAnalogDate || curDate.getMinutes() != lastAnalogDate.getMinutes()) {
|
||||
// Draw the main hour lines.
|
||||
//g.setColor("#9C9CFF");
|
||||
//g.drawCircle(clockCtrX, clockCtrY, analogRad);
|
||||
for (let i = 0; i < 60; i = i + 15) {
|
||||
let edgeX = clockCtrX + analogRad * Math.sin(i * Math.PI / 30);
|
||||
let edgeY = clockCtrY - analogRad * Math.cos(i * Math.PI / 30);
|
||||
let innerX = clockCtrX + (analogRad - analogMainLineLength) * Math.sin(i * Math.PI / 30);
|
||||
let innerY = clockCtrY - (analogRad - analogMainLineLength) * Math.cos(i * Math.PI / 30);
|
||||
drawLine(edgeX, edgeY, innerX, innerY, colorHours);
|
||||
}
|
||||
// Set for drawing the full second wheel.
|
||||
firstDrawSecond = 0;
|
||||
lastDrawSecond = 59;
|
||||
}
|
||||
// Draw the second wheel, or the parts of it that we haven't done yet.
|
||||
for (let i = firstDrawSecond; i <= lastDrawSecond; i++) {
|
||||
let edgeX = clockCtrX + analogRad * Math.sin(i * Math.PI / 30);
|
||||
let edgeY = clockCtrY - analogRad * Math.cos(i * Math.PI / 30);
|
||||
let innerX = clockCtrX + (analogRad - analogSubLineLength) * Math.sin(i * Math.PI / 30);
|
||||
let innerY = clockCtrY - (analogRad - analogSubLineLength) * Math.cos(i * Math.PI / 30);
|
||||
if (i <= curDate.getSeconds()) {
|
||||
drawLine(edgeX, edgeY, innerX, innerY, colorSeconds);
|
||||
}
|
||||
else if (i % 5 == 0) {
|
||||
drawLine(edgeX, edgeY, innerX, innerY, colorHours);
|
||||
}
|
||||
else {
|
||||
clearLine(edgeX, edgeY, innerX, innerY);
|
||||
}
|
||||
}
|
||||
if (lastAnalogDate) {
|
||||
// Clear previous hands.
|
||||
if (curDate.getMinutes() != lastAnalogDate.getMinutes()) {
|
||||
// Clear hour hand.
|
||||
let HhAngle = (lastAnalogDate.getHours() + lastAnalogDate.getMinutes() / 60) * Math.PI / 6;
|
||||
let HhEdgeX = clockCtrX + analogHourHandLength * Math.sin(HhAngle);
|
||||
let HhEdgeY = clockCtrY - analogHourHandLength * Math.cos(HhAngle);
|
||||
clearLine(HhEdgeX, HhEdgeY, clockCtrX, clockCtrY);
|
||||
// Clear minute hand.
|
||||
let MhEdgeX = clockCtrX + analogMinuteHandLength * Math.sin(lastAnalogDate.getMinutes() * Math.PI / 30);
|
||||
let MhEdgeY = clockCtrY - analogMinuteHandLength * Math.cos(lastAnalogDate.getMinutes() * Math.PI / 30);
|
||||
clearLine(MhEdgeX, MhEdgeY, clockCtrX, clockCtrY);
|
||||
}
|
||||
}
|
||||
if (!lastAnalogDate || curDate.getMinutes() != lastAnalogDate.getMinutes()) {
|
||||
// Draw hour hand.
|
||||
let HhAngle = (curDate.getHours() + curDate.getMinutes() / 60) * Math.PI / 6;
|
||||
let HhEdgeX = clockCtrX + analogHourHandLength * Math.sin(HhAngle);
|
||||
let HhEdgeY = clockCtrY - analogHourHandLength * Math.cos(HhAngle);
|
||||
drawLine(HhEdgeX, HhEdgeY, clockCtrX, clockCtrY, colorHands);
|
||||
// Draw minute hand.
|
||||
let MhEdgeX = clockCtrX + analogMinuteHandLength * Math.sin(curDate.getMinutes() * Math.PI / 30);
|
||||
let MhEdgeY = clockCtrY - analogMinuteHandLength * Math.cos(curDate.getMinutes() * Math.PI / 30);
|
||||
drawLine(MhEdgeX, MhEdgeY, clockCtrX, clockCtrY, colorHands);
|
||||
}
|
||||
lastAnalogDate = curDate;
|
||||
}
|
||||
|
||||
function switchClockface() {
|
||||
if (clockface == "digital") {
|
||||
clockface = "analog";
|
||||
}
|
||||
else {
|
||||
clockface = "digital";
|
||||
}
|
||||
// Clear whole lower area.
|
||||
g.clearRect(clockAreaLeft,clockAreaTop,g.getWidth(),g.getHeight());
|
||||
lastTimeStringToMin = undefined;
|
||||
lastTimeStringSec = undefined;
|
||||
lastDateString = undefined;
|
||||
lastAnalogDate = undefined;
|
||||
}
|
||||
|
||||
// Clear the screen once, at startup.
|
||||
g.setBgColor(colorBg);
|
||||
g.clear();
|
||||
// Draw LCARS borders.
|
||||
// Upper section: rounded corner.
|
||||
g.setColor(colorLCARSGray);
|
||||
g.fillCircle(outRad, divisionPos - outRad, outRad);
|
||||
g.fillRect(outRad, divisionPos - outRad, sbarWid + inRad, divisionPos);
|
||||
g.fillRect(outRad, divisionPos - hbarHt, sbarWid + outRad, divisionPos); // div bar stub
|
||||
g.fillRect(0, 0, sbarWid, divisionPos - outRad); // side bar
|
||||
g.setColor(colorBg); // blocked out areas of corner
|
||||
g.fillCircle(sbarWid + inRad + 1, divisionPos - hbarHt - inRad - 1, inRad);
|
||||
g.fillRect(sbarWid + 1, divisionPos - outRad * 2, sbarWid + outRad, divisionPos - hbarHt - inRad);
|
||||
// upper division bar
|
||||
g.setColor(colorLCARSPurple);
|
||||
g.fillRect(sbarWid + outRad + gap + 1, divisionPos - hbarHt, g.getWidth(), divisionPos);
|
||||
// Lower section: rounded corner.
|
||||
g.setColor(colorLCARSPink);
|
||||
g.fillCircle(outRad, lowerTop + outRad, outRad);
|
||||
g.fillRect(outRad, lowerTop, sbarWid + inRad, lowerTop + outRad);
|
||||
g.fillRect(outRad, lowerTop, sbarWid + outRad, lowerTop + hbarHt); // div bar stub
|
||||
g.fillRect(0, lowerTop + outRad, sbarWid, sbarGapPos); // side bar
|
||||
g.setColor(colorBg); // blocked out areas of corner
|
||||
g.fillCircle(sbarWid + inRad + 1, lowerTop + hbarHt + inRad + 1, inRad);
|
||||
g.fillRect(sbarWid + 1, lowerTop + hbarHt + inRad, sbarWid + outRad, lowerTop + outRad * 2);
|
||||
// lower division bar
|
||||
g.setColor(colorLCARSOrange);
|
||||
g.fillRect(sbarWid + outRad + gap + 1, lowerTop, g.getWidth(), lowerTop + hbarHt);
|
||||
// second color of side bar
|
||||
g.setColor(colorLCARSBrown);
|
||||
g.fillRect(0, sbarGapPos + gap + 1, sbarWid, g.getHeight());
|
||||
// Draw immediately at first.
|
||||
updateStardate();
|
||||
updateConventionalTime();
|
||||
// Make sure widgets can be shown.
|
||||
//g.setColor("#FF0000"); g.fillRect(0, 0, g.getWidth(), widgetsHeight); // debug
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
// Show launcher on button press as usual for a clock face
|
||||
Bangle.setUI("clock", Bangle.showLauncher);
|
||||
// Stop updates when LCD is off, restart when on
|
||||
Bangle.on('lcdPower', on => {
|
||||
if (on) {
|
||||
redrawClock = true;
|
||||
// Draw immediately to kick things off.
|
||||
updateStardate();
|
||||
updateConventionalTime();
|
||||
}
|
||||
else {
|
||||
redrawClock = false;
|
||||
}
|
||||
});
|
||||
Bangle.on('touch', button => {
|
||||
// button == 1 is left, 2 is right
|
||||
switchClockface();
|
||||
});
|
After Width: | Height: | Size: 594 B |
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"id": "stardateclock",
|
||||
"name":"Stardate Clock",
|
||||
"shortName":"Stardate Clock",
|
||||
"description": "A clock displaying a stardate along with a 'standard' digital/analog clock in LCARS design",
|
||||
"version":"1.0",
|
||||
"icon": "app.png",
|
||||
"type":"clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS", "BANGLEJS2"],
|
||||
"allow_emulator": true,
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name": "stardateclock.app.js","url": "app.js"},
|
||||
{"name": "stardateclock.img","url": "app-icon.js","evaluate": true}
|
||||
],
|
||||
"screenshots": [
|
||||
{"url": "screenshot1.png"},
|
||||
{"url": "screenshot2.png"},
|
||||
{"url": "screenshot3.png"},
|
||||
{"url": "screenshot4.png"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 5.2 KiB |
|
@ -7,6 +7,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"sunclock.app.js","url":"app.js"},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"supmariodark.app.js","url":"supmariodark.js"},
|
||||
{"name":"supmariodark.img","url":"supmariodark-icon.js","evaluate":true},
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "bootloader",
|
||||
"tags": "tool",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"touchmenu.boot.js","url":"touchmenu.boot.js"}
|
||||
]
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"waveclk.app.js","url":"app.js"},
|
||||
|
|
|
@ -15,6 +15,7 @@ print(ExStats.getList());
|
|||
{name: "Distance", id:"dist"},
|
||||
{name: "Steps", id:"step"},
|
||||
{name: "Heart (BPM)", id:"bpm"},
|
||||
{name: "Max BPM", id:"maxbpm"},
|
||||
{name: "Pace (avr)", id:"pacea"},
|
||||
{name: "Pace (current)", id:"pacec"},
|
||||
{name: "Cadence", id:"caden"},
|
||||
|
@ -72,6 +73,7 @@ var state = {
|
|||
// cadence // steps per minute adjusted if <1 minute
|
||||
// BPM // beats per minute
|
||||
// BPMage // how many seconds was BPM set?
|
||||
// maxBPM // The highest BPM reached while active
|
||||
// Notifies: 0 for disabled, otherwise how often to notify in meters, seconds, or steps
|
||||
notify: {
|
||||
dist: {
|
||||
|
@ -159,6 +161,10 @@ Bangle.on("HRM", function(h) {
|
|||
if (h.confidence>=60) {
|
||||
state.BPM = h.bpm;
|
||||
state.BPMage = 0;
|
||||
if (state.maxBPM < h.bpm) {
|
||||
state.maxBPM = h.bpm;
|
||||
if (stats["maxbpm"]) stats["maxbpm"].emit("changed",stats["maxbpm"]);
|
||||
}
|
||||
if (stats["bpm"]) stats["bpm"].emit("changed",stats["bpm"]);
|
||||
}
|
||||
});
|
||||
|
@ -170,6 +176,7 @@ exports.getList = function() {
|
|||
{name: "Distance", id:"dist"},
|
||||
{name: "Steps", id:"step"},
|
||||
{name: "Heart (BPM)", id:"bpm"},
|
||||
{name: "Max BPM", id:"maxbpm"},
|
||||
{name: "Pace (avg)", id:"pacea"},
|
||||
{name: "Pace (curr)", id:"pacec"},
|
||||
{name: "Speed", id:"speed"},
|
||||
|
@ -230,6 +237,14 @@ exports.getStats = function(statIDs, options) {
|
|||
getString : function() { return state.BPM||"--" },
|
||||
};
|
||||
}
|
||||
if (statIDs.includes("maxbpm")) {
|
||||
needHRM = true;
|
||||
stats["maxbpm"]={
|
||||
title : "Max BPM",
|
||||
getValue : function() { return state.maxBPM; },
|
||||
getString : function() { return state.maxBPM||"--" },
|
||||
};
|
||||
}
|
||||
if (statIDs.includes("pacea")) {
|
||||
needGPS = true;
|
||||
stats["pacea"]={
|
||||
|
@ -299,6 +314,7 @@ exports.getStats = function(statIDs, options) {
|
|||
state.curSpeed = 0;
|
||||
state.BPM = 0;
|
||||
state.BPMage = 0;
|
||||
state.maxBPM = 0;
|
||||
state.notify = options.notify;
|
||||
if (options.notify.dist.increment > 0) {
|
||||
state.notify.dist.next = state.distance + options.notify.dist.increment;
|
||||
|
|