Merge pull request #3 from espruino/master

updating my fork
pull/675/head
dapgo 2021-02-19 20:18:24 +01:00 committed by GitHub
commit 8d51f20a7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 851 additions and 26 deletions

View File

@ -65,7 +65,7 @@
{ "id": "locale",
"name": "Languages",
"icon": "locale.png",
"version":"0.08",
"version":"0.09",
"description": "Translations for different countries",
"tags": "tool,system,locale,translate",
"type": "locale",
@ -213,6 +213,19 @@
{"name":"wclock.img","url":"clock-word-icon.js","evaluate":true}
]
},
{ "id": "slidingtext",
"name": "Sliding Clock",
"icon": "slidingtext.png",
"version":"0.01",
"description": "Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported",
"tags": "clock",
"type":"clock",
"allow_emulator":true,
"storage": [
{"name":"slidingtext.app.js","url":"slidingtext.js"},
{"name":"slidingtext.img","url":"slidingtext-icon.js","evaluate":true}
]
},
{ "id": "imgclock",
"name": "Image background clock",
"shortName":"Image Clock",
@ -269,6 +282,30 @@
{"name":"clock2x3.img","url":"clock2x3-icon.js","evaluate":true}
]
},
{ "id": "geissclk",
"name": "Geiss Clock",
"icon": "clock.png",
"version":"0.02",
"description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation",
"tags": "clock",
"type":"clock",
"storage": [
{"name":"geissclk.app.js","url":"clock.js"},
{"name":"geissclk.precompute.js","url":"precompute.js"},
{"name":"geissclk.img","url":"clock-icon.js","evaluate":true}
],
"data": [
{"name":"geissclk.0.map"},
{"name":"geissclk.1.map"},
{"name":"geissclk.2.map"},
{"name":"geissclk.3.map"},
{"name":"geissclk.4.map"},
{"name":"geissclk.5.map"},
{"name":"geissclk.0.pal"},
{"name":"geissclk.1.pal"},
{"name":"geissclk.2.pal"}
]
},
{ "id": "trex",
"name": "T-Rex",
"icon": "trex.png",
@ -2785,7 +2822,7 @@
"name": "Walkers Clock",
"shortName":"Walkers Clock",
"icon": "walkersclock48.png",
"version":"0.01",
"version":"0.02",
"description": "A larg font watch, displays steps, can switch GPS on/off, displays grid reference",
"type":"clock",
"tags": "clock, gps, tools, outdoors",

2
apps/geissclk/ChangeLog Normal file
View File

@ -0,0 +1,2 @@
0.01: New App!
0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEw4X/AoMF/3G/2m/wOCmoaKmtAAgVQBYsFoECwALMAA0JBZQABBYIbBBaVUBYMVBYkBIIILDgEKGYUBqkA6oLB6sAlQLDqsAioLBCAOq4AkCqtAivCBYMC1YwDitQgtCwNUhWsOwlUBYVQlWwJAlVgtSxNQFwnwGANBqWFqQuE1QwBqNewtKFwgLBGANWytq2EFWoU6gWBqoLBFwNVYIZZBqtXyurioLF1W1q/W1tVPIIxE1NV72VqpTDDAleEIgADnWq3qgBTIYADvQuBAAVUBYla1XVBYYbDyt632qd40Fqulquq0ALFOwO16t6BYpxBvWX6tW0aoDXwNU1XXNQMD1T7CAwPC1abBegPqFodQdQILCO4hxBlWxq2FoqaFBYOBcYNxQggLCToOBqAwBTgwLCiqoGgtCJ4L0BBYsVBYRmBBY0CwEUJ4IwFBYkVBYsMBYI0CJAwLDAA4L2A="))

151
apps/geissclk/clock.js Normal file
View File

@ -0,0 +1,151 @@
var W = 79, H = 64;
/*var compiled = E.compiledC(`
// void transl(int, int, int )
int transl(unsigned char *map, unsigned char *imgfrom, unsigned char *imgto) {
int n = 0;
const int W = 79;
const int H = 64;
for (int y=0;y<H;y++)
for (int x=0;x<W;x++) {
int d = map[n];
int nx = (x<<3) + ((d )&0x0F) - 8;
int ny = (y<<3) + ((d>>4)&0x0F) - 8;
int ax = nx&7;
int ay = ny&7;
int a = (nx>>3) + ((ny>>3)*W);
int c = 0;
if (a>=0 && a<(W*H-(W+1))) {
c = imgfrom[a]*(8-ax)*(8-ay) +
imgfrom[a+1]*(ax)*(8-ay) +
imgfrom[a+W]*(8-ax)*(ay) +
imgfrom[a+W+1]*(ax)*(ay);
c = (c>>6) - 4;
if (c<0) c=0;
}
imgto[n] = c;
n++;
}
}
`);*/
var compiled = (function(){
var bin=atob("Len3TwAnT/BPCPsAAJMI+wfzAOsDCdMYAZMAJhn4BkAAnQTwDwMD68YDBesUFAg7CDzdEE/q5AoI+wpaQfJvNapFItgB6woOA/AHAxH4CsCe+AGww/EIBQX7DPwD+wvMnvhPsATwBwQF+wv1ZUPE8QgKCvsMXJ74UFBrQwT7A8SkEQQ8JOrkdADgACQBm5xVATZPLsLRATdAL7bRA7C96PCPAAA=");
return {
transl:E.nativeCall(1, "void(int, int, int )", bin),
};
})();
//require("Font5x9Numeric7Seg").add(Graphics);
Graphics.prototype.setFont5x9Numeric7Seg = function() {
this.setFontCustom(atob("AAAAAAAAAAIAAAQCAQAAAd0BgMBdwAAAAAAAdwAB0RiMRcAAAERiMRdwAcAQCAQdwAcERiMRBwAd0RiMRBwAAEAgEAdwAd0RiMRdwAcERiMRdwAFAAd0QiEQdwAdwRCIRBwAd0BgMBAAABwRCIRdwAd0RiMRAAAd0QiEQAAAAAAAAAA="), 32, atob("BgAAAAAAAAAAAAAAAAYCAAYGBgYGBgYGBgYCAAAAAAAABgYGBgYG"), 9);
}
// Allocate the data
var dataa = new Uint8Array(W*H);
var datab = new Uint8Array(W*H);
var map = new Uint8Array(W*H);
var pal = new Uint16Array(256);
var PALETTES = 3;
var MAPS = 6;
// If we're missing any maps, compute them!
(function() {
var files = require("Storage").list(/^geissclk/);
var allOk = true;
for (var n=0;n<PALETTES;n++)
if (!files.includes("geissclk."+n+".pal"))
allOk = false;
for (var n=0;n<MAPS;n++)
if (!files.includes("geissclk."+n+".map"))
allOk = false;
if (!allOk)
eval(require("Storage").read("geissclk.precompute.js"));
})();
function randomPalette() {
var n = (0|Math.random()*200000) % PALETTES;
var p = new Uint8Array(pal.buffer);
p.set(require("Storage").readArrayBuffer("geissclk."+n+".pal"));
}
function randomMap() {
var n = (0|Math.random()*200000) % MAPS;
map.set(require("Storage").readArrayBuffer("geissclk."+n+".map"));
}
randomPalette();
randomMap();
// Get the address
var addra = E.getAddressOf(dataa,true);
if (!addra) throw new Error("Not a Flat String");
var addrb = E.getAddressOf(datab,true);
if (!addrb) throw new Error("Not a Flat String");
var addrmap = E.getAddressOf(map,true);
if (!addrmap) throw new Error("Not a Flat String");
var gfx = Graphics.createArrayBuffer(W,H,8);
gfx.buffer = dataa.buffer;
var im = {
width:W, height:H, bpp:8,
palette: pal,
buffer : dataa.buffer
};
var lastSeconds = -1;
function iterate() { "ram"
var d = new Date();
var time = require("locale").time(d,1);
var seconds = d.getSeconds().toString().padStart(2,0);
t = addra; addra = addrb; addrb = t;
t = dataa; dataa = datab; datab = t;
if (seconds!=lastSeconds) {
lastSeconds = seconds;
im.buffer = datab.buffer;
gfx.buffer = datab.buffer;
} else {
im.buffer = dataa.buffer;
gfx.buffer = dataa.buffer;
}
var x,y,n,t = getTime()/10;
var amt = 100*Bangle.getAccel().diff;
for (var i=0;i<amt;i++) {
//x = Math.round((W/2) + 20*Math.sin(t));
//y = Math.round((H/2) + 20*Math.cos(t));
//t += 0.628;
x = 1+(Math.random()*(W-2))|0;
y = 1+(Math.random()*(H-2))|0;
dataa[x + y*W] = 240;
}
compiled.transl(addrmap, addra, addrb);
x = 8;
gfx.setFont("5x9Numeric7Seg",2);
gfx.drawString(time, x, 20);
gfx.setFont("5x9Numeric7Seg");
gfx.drawString(seconds, x+55, 30);
// firmwares pre-2v09 wouldn't accelerate a 3x blit if it went right to the RHS - hence we're 79px not 80
g.drawImage(im,1,24,{scale:3});
}
Bangle.on('lcdPower',function(on) {
if (animInterval) {
clearInterval(animInterval);
animInterval = undefined;
}
if (on) {
randomMap();
randomPalette();
iterate();
animInterval = setInterval(iterate, 50);
}
});
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
iterate();
animInterval = setInterval(iterate, 50);
setWatch(Bangle.showLauncher, BTN2, {repeat:false,edge:"falling"});

BIN
apps/geissclk/clock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

108
apps/geissclk/precompute.js Normal file
View File

@ -0,0 +1,108 @@
// PALETTES ---------------------------
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n0 / 3");
(function() { // fire
for (var i=0;i<256;i++) {
var r = Math.min(i*6,240);
var g = Math.min(i*3,240);
var b = E.clip((i-192)*8,0,240);
pal[i] = (b>>3) | ((g&0xFC)<<3) | ((r&0xF8)<<8);
}pal[255] = 65535;
})()
require("Storage").write("geissclk.0.pal",pal.buffer);
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n1 / 3");
(function() { // gunge
for (var i=0;i<256;i++) {
var r = 0;
var g = Math.min(i*3,255);
var b = Math.min(i,255);
pal[i] = (b>>3) | ((g&0xFC)<<3) | ((r&0xF8)<<8);
}pal[255] = 65535;
})()
require("Storage").write("geissclk.1.pal",pal.buffer);
E.showMessage("Precomputing\npalettes\n\nPlease wait...\n2 / 3");
(function() { // rainbow
for (var i=0;i<256;i++) {
var cl = E.HSBtoRGB((48+i)/128,1,Math.min(i/16,0.9),true);
var r = cl[0];
var g = cl[1];
var b = cl[2];
pal[i] = (b>>3) | ((g&0xFC)<<3) | ((r&0xF8)<<8);
}pal[255] = 65535;pal[255] = 65535;
})()
require("Storage").write("geissclk.2.pal",pal.buffer);
// MAPS ----------------------------------------------
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n0 / 5");
// straight out
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) {
var dx = x-(W/2);
var dy = y-(H/2);
var d = Math.sqrt(dx*dx + dy*dy);
var s = -2 + Math.sin(d*0.5);
dx = dx*s/d;
dy = dy*s/d;
map[n++] = ((dx*2 + 8) & 0x0F) | (((dy*2 + 8) & 0x0F)<<4);
}
}})();
require("Storage").write("geissclk.0.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n1 / 5");
// ripple out
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) {
var dx = x-(W/2);
var dy = y-(H/2);
var d = Math.sqrt(dx*dx + dy*dy);
var s = -2 + Math.sin(d*0.5)/2;
dx = dx*s/d;
dy = dy*s/d;
map[n++] = ((dx*3 + 8) & 0x0F) | (((dy*3 + 8) & 0x0F)<<4);
}
}})();
require("Storage").write("geissclk.1.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n2 / 5");
// twisty outwards
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) {
var dx = x-(W/2);
var dy = y-(H/2);
var d = Math.sqrt(dx*dx + dy*dy);
dx = (-dx/d) + (((y&7)>3)?0.5:-0.5);
dy = (-dy/d) + (((x&7)>3)?0.5:-0.5);
map[n++] = ((dx*3 + 8) & 0x0F) | (((dy*3 + 8) & 0x0F)<<4);
}
}})()
require("Storage").write("geissclk.2.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n3 / 5");
// spiral
(function() { "ram"; var n = 0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) {
var dx = x-(W/2);
var dy = y-(H/2);
var d = Math.sqrt(dx*dx + dy*dy);
var cx = (2*dy-dx)/(2*d);
var cy = (-2*dx-dy)/(2*d);
map[n++] = ((cx*3 + 8) & 0x0F) | (((cy*3 + 8) & 0x0F)<<4);
}
}})();
require("Storage").write("geissclk.3.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n4 / 5");
// blur down
(function() { "ram"; var n=0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) {
map[n++] = 136 - 6*16 + (y&1)*8-4;
}
}})()
require("Storage").write("geissclk.4.map",map);
E.showMessage("Precomputing\nmaps\n\nPlease wait...\n5 / 5");
// twisty
(function() { "ram"; var n=0; for (var y=0;y<H;y++) {
for (var x=0;x<W;x++) {
dx = Math.sin(y*0.2);
dy = Math.cos(x*0.2);
map[n++] = ((dx*6 + 8) & 0x0F) | (((dy*6 + 8) & 0x0F)<<4);
}
}})()
require("Storage").write("geissclk.5.map",map)
E.showMessage("Finished!");

View File

@ -0,0 +1 @@
0.01: Initial Release

View File

@ -0,0 +1,18 @@
# Sliding Text Clock - See the time in different languages
Inspired by the Pebble sliding clock, old times are scrolled off the screen and new times on. You are also able to change language on the fly so you can see the time written in other languages using button 1. Currently only English, French and Japanese are supported
![](app.png)
## Usage
Use Button 1 (the top right button) to change the language
## Requests
[Reach out to Adrian](https://www.github.com/awkirk71) if you have feature requests or notice bugs.
## Creator
Made by [Adrian Kirk](https://www.github.com/awkirk71).

BIN
apps/slidingtext/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("lEowkE/4A/AH//+czCSKbBgXziYhKEQXxgMQgERgASIkf/CgM/+UBiISBCZAUBn4DBmcybAUTHZIUEmEQkcgl5gLJ4XygQCBj50MAYUQCZ3/HgXwkcyiZqBJxQoC+UD+cPXBIIBBQcwHIMBNwpNFDocQBoMwM4IUEn4kBMIZ3DAAMgQYoZCCYg6CVhKJFEQJyIn4VBkZrFDAgIJBxCqHO5DmJT4v/mQSIKxA+DDIIADCRJEDZgRCKIgjUHHJAPCPhprFExwSDJRgPDN5oUHCJ4A/AH4AIA=="))

View File

@ -0,0 +1,475 @@
/**
* Adrian Kirk 2021-02
* Sliding text clock inspired by the Pebble
* clock with the same name
*/
class ShiftText {
/**
* Class Responsible for shifting text around the screen
*
* This is a object that initializes itself with a position and
* text after which you can tell it where you want to move to
* using the moveTo method and it will smoothly move the text across
* at the selected frame rate and speed
*/
constructor(x,y,txt,font_name,
font_size,speed_x,speed_y,freq_millis, color){
this.x = x;
this.tgt_x = x;
this.init_x = x;
this.y = y;
this.tgt_y = y;
this.init_y = y;
this.txt = txt;
this.init_txt = txt;
this.font_name = font_name;
this.font_size = font_size;
this.speed_x = Math.abs(speed_x);
this.speed_y = Math.abs(speed_y);
this.freq_millis = freq_millis;
this.colour = color;
this.finished_callback=null;
this.timeoutId = null;
}
reset(){
this.hide();
this.x = this.init_x;
this.y = this.init_y;
this.txt = this.init_txt;
this.show();
if(this.timeoutId != null){
clearTimeout(this.timeoutId);
}
}
show() {
g.setFont(this.font_name,this.font_size);
g.setColor(this.colour[0],this.colour[1],this.colour[2]);
g.drawString(this.txt, this.x, this.y);
}
hide(){
g.setFont(this.font_name,this.font_size);
g.setColor(0,0,0);
g.drawString(this.txt, this.x, this.y);
}
setText(txt){
this.txt = txt;
}
setTextPosition(txt,x,y){
this.hide();
this.x = x;
this.y = y;
this.txt = txt;
this.show();
}
setTextXPosition(txt,x){
this.hide();
this.x = x;
this.txt = txt;
this.show();
}
setTextYPosition(txt,y){
this.hide();
this.y = y;
this.txt = txt;
this.show();
}
moveTo(new_x,new_y){
this.tgt_x = new_x;
this.tgt_y = new_y;
this._doMove();
}
moveToX(new_x){
this.tgt_x = new_x;
this._doMove();
}
moveToY(new_y){
this.tgt_y = new_y;
this._doMove();
}
onFinished(finished_callback){
this.finished_callback = finished_callback;
}
/**
* private internal method for directing the text move.
* It will see how far away we are from the target coords
* and move towards the target at the defined speed.
*/
_doMove(){
this.hide();
// move closer to the target in the x direction
diff_x = this.tgt_x - this.x;
finished_x = false;
if(Math.abs(diff_x) <= this.speed_x){
this.x = this.tgt_x;
finished_x = true;
} else {
if(diff_x > 0){
this.x += this.speed_x;
} else {
this.x -= this.speed_x;
}
}
// move closer to the target in the y direction
diff_y = this.tgt_y - this.y;
finished_y = false;
if(Math.abs(diff_y) <= this.speed_y){
this.y = this.tgt_y;
finished_y = true;
} else {
if(diff_y > 0){
this.y += this.speed_y;
} else {
this.y -= this.speed_y;
}
}
this.show();
this.timeoutId = null;
finished = finished_x & finished_y;
if(!finished){
this.timeoutId = setTimeout(this._doMove.bind(this), this.freq_millis);
} else if(this.finished_callback != null){
this.finished_callback.call();
this.finished_callback = null;
}
}
}
class DateFormatter {
/**
* A pure virtual class which all the other date formatters will
* inherit from.
* The name will be used to declare the date format when selected
* and the date formatDate methid will return the time formated
* to the lines of text on the screen
*/
name(){"no name";}
formatDate(date){
return ["","",""];
}
}
/**
* English date formatting
*/
// English String Numbers
const numberStr = ["ZERO","ONE", "TWO", "THREE", "FOUR", "FIVE",
"SIX", "SEVEN","EIGHT", "NINE", "TEN",
"ELEVEN", "TWELVE", "THIRTEEN", "FOURTEEN",
"FIFTEEN", "SIXTEEN", "SEVENTEEN", "EIGHTEEN",
"NINETEEN", "TWENTY"];
const tensStr = ["ZERO", "TEN", "TWENTY", "THIRTY", "FOURTY",
"FIFTY"];
function hoursToText(hours){
hours = hours % 12;
if(hours == 0){
hours = 12;
}
return numberStr[hours];
}
function numberToText(value){
word1 = '';
word2 = '';
if(value > 20){
tens = (value / 10 | 0);
word1 = tensStr[tens];
remainder = value - tens * 10;
if(remainder > 0){
word2 = numberStr[remainder];
}
} else if(value > 0) {
word1 = numberStr[value];
}
return [word1,word2];
}
class EnglishDateFormatter extends DateFormatter{
name(){return "English";}
formatDate(date){
hours_txt = hoursToText(date.getHours());
mins_txt = numberToText(date.getMinutes());
return [hours_txt,mins_txt[0],mins_txt[1]];
}
}
/**
* French date formatting
*/
const frenchNumberStr = [ "ZERO", "UNE", "DEUX", "TROIS", "QUATRE",
"CINQ", "SIX", "SEPT", "HUIT", "NEUF", "DIX",
"ONZE", "DOUZE", "TREIZE", "QUATORZE","QUINZE",
"SEIZE", "DIX SEPT", "DIX HUIT","DIX NEUF", "VINGT",
"VINGT ET UN", "VINGT DEUX", "VINGT TROIS",
"VINGT QUATRE", "VINGT CINQ", "VINGT SIX",
"VINGT SEPT", "VINGT HUIT", "VINGT NEUF"
];
function frenchHoursToText(hours){
hours = hours % 12;
if(hours == 0){
hours = 12;
}
return frenchNumberStr[hours];
}
function frenchHeures(hours){
if(hours % 12 == 1){
return 'HEURE';
} else {
return 'HEURES';
}
}
class FrenchDateFormatter extends DateFormatter {
constructor() {
super();
}
name(){return "French";}
formatDate(date){
hours = frenchHoursToText(date.getHours());
heures = frenchHeures(date.getHours());
mins = date.getMinutes();
if(mins == 0){
if(hours == 0){
return ["MINUIT", "",""];
} else if(hours == 12){
return ["MIDI", "",""];
} else {
return [hours, heures,""];
}
} else if(mins == 30){
return [hours, heures,'ET DEMIE'];
} else if(mins == 15){
return [hours, heures,'ET QUERT'];
} else if(mins == 45){
next_hour = date.getHours() + 1;
hours = frenchHoursToText(next_hour);
heures = frenchHeures(next_hour);
return [hours, heures,"MOINS",'LET QUERT'];
}
if(mins > 30){
to_mins = 60-mins;
mins_txt = frenchNumberStr[to_mins];
next_hour = date.getHours() + 1;
hours = frenchHoursToText(next_hour);
heures = frenchHeures(next_hour);
return [ hours, heures , "MOINS", mins_txt ];
} else {
mins_txt = frenchNumberStr[mins];
return [ hours, heures , mins_txt ];
}
}
}
/**
* Japanese date formatting
*/
const japaneseHourStr = [ "ZERO", "ICHII", "NI", "SAN", "YO",
"GO", "ROKU", "SHICHI", "HACHI", "KU", "JUU",
'JUU ICHI', 'JUU NI'];
const tensPrefixStr = [ "",
"JUU",
'NIJUU',
'SAN JUU',
'YON JUU',
'GO JUU'];
const japaneseMinuteStr = [ ["", "PUN"],
["IP","PUN" ],
["NI", "FUN"],
["SAN", "PUN"],
["YON","FUN"],
["GO", "HUN"],
["RO", "PUN"],
["NANA", "FUN"],
["HAP", "PUN"],
["KYU","FUN"],
["JUP", "PUN"]
];
function japaneseHoursToText(hours){
hours = hours % 12;
if(hours == 0){
hours = 12;
}
return japaneseHourStr[hours];
}
function japaneseMinsToText(mins){
if(mins == 0){
return ["",""];
} else if(mins == 30)
return ["HAN",""];
else {
units = mins % 10;
mins_txt = japaneseMinuteStr[units];
tens = mins /10 | 0;
if(tens > 0){
tens_txt = tensPrefixStr[tens];
return [tens_txt + ' ' + mins_txt[0], mins_txt[1]];
} else {
return [mins_txt[0], mins_txt[1]];
}
}
}
class JapaneseDateFormatter extends DateFormatter {
constructor() {
super();
}
name(){return "Japanese (Romanji)";}
formatDate(date){
hours_txt = japaneseHoursToText(date.getHours());
mins_txt = japaneseMinsToText(date.getMinutes());
return [hours_txt,"JI", mins_txt[0], mins_txt[1] ];
}
}
/**
* The Watch Display
*/
// a list of display rows
let row_displays = [
new ShiftText(240,60,'',"Vector",40,10,10,40,[1,1,1]),
new ShiftText(240,100,'',"Vector",20,10,10,50,[0.85,0.85,0.85]),
new ShiftText(240,120,'',"Vector",20,10,10,60,[0.85,0.85,0.85]),
new ShiftText(240,140,'',"Vector",20,10,10,70,[0.85,0.85,0.85])
];
// a list of the formatters to cycle through
let date_formatters = [
new EnglishDateFormatter(),
new FrenchDateFormatter(),
new JapaneseDateFormatter()
];
// current index of the date formatter to display
let date_formatter_idx = 0;
let date_formatter = date_formatters[date_formatter_idx];
// The small display at the top which announces the date format
let format_name_display = new ShiftText(55,0,'',"Vector",10,1,1,50,[1,1,1]);
function changeFormatter(){
date_formatter_idx += 1;
if(date_formatter_idx >= date_formatters.length){
date_formatter_idx = 0;
}
console.log("changing to formatter " + date_formatter_idx);
date_formatter = date_formatters[date_formatter_idx];
reset_clock();
draw_clock();
// now announce the formatter by name
format_name_display.setTextYPosition(date_formatter.name(),-10);
format_name_display.moveToY(15);
// and then move back
format_name_display.onFinished(
function(){
format_name_display.moveToY(-10);
}
);
}
function reset_clock(){
//console.log("reset_clock");
var i;
for (i = 0; i < row_displays.length; i++) {
row_displays[i].reset();
}
}
function draw_clock(){
//console.log("draw_clock");
date = new Date();
rows = date_formatter.formatDate(date);
var i;
for (i = 0; i < rows.length; i++) {
display = row_displays[i];
txt = rows[i];
display_row(display,txt);
}
// If the dateformatter has not returned enough
// rows then treat the reamining rows as empty
for (j = i; j < row_displays.length; j++) {
display = row_displays[j];
display_row(display,'');
}
//console.log(date);
}
function display_row(display,txt){
if(display.txt == ''){
display.setTextXPosition(txt,240);
display.moveToX(20);
} else if(txt != display.txt){
display.moveToX(-100);
display.onFinished(
function(){
display.setTextXPosition(txt,240);
display.moveToX(20);
}
);
} else {
display.setTextXPosition(txt,20);
}
}
// The interval reference for updating the clock
let intervalRef = null;
function clearTimers(){
if(intervalRef) {
clearInterval(intervalRef);
intervalRef = null;
}
}
function startTimers(){
let date = new Date();
let secs = date.getSeconds();
let nextMinuteStart = 60 - secs;
//console.log("scheduling clock draw in " + nextMinuteStart + " seconds");
setTimeout(scheduleDrawClock,nextMinuteStart * 1000);
draw_clock();
}
function scheduleDrawClock(){
//console.log("scheduleDrawClock");
if(intervalRef) clearTimers();
intervalRef = setInterval(draw_clock, 60*1000);
draw_clock();
}
Bangle.on('lcdPower', (on) => {
if (on) {
console.log("lcdPower: on");
Bangle.drawWidgets();
reset_clock();
startTimers();
} else {
console.log("lcdPower: off");
reset_clock();
clearTimers();
}
});
Bangle.on('faceUp',function(up){
//console.log("faceUp: " + up + " LCD: " + Bangle.isLCDOn());
if (up && !Bangle.isLCDOn()) {
//console.log("faceUp and LCD off");
clearTimers();
Bangle.setLCDPower(true);
}
});
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
startTimers();
// Show launcher when middle button pressed
setWatch(Bangle.showLauncher, BTN2,{repeat:false,edge:"falling"});
setWatch(changeFormatter, BTN1,{repeat:true,edge:"falling"});

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,2 @@
0.01: First version of the Walkers Clock
0.02: Fixed screen flicker

View File

@ -57,7 +57,5 @@ on the second line of the watch.
![](gps_alt.jpg)
## Future Enhancements
* Ability to turn on the Heart Rate monitor
* Ability to turn on the heart rate monitor and display the rate on the info line
* Maybe a simple stopwatch capability
* Fix the screen flicker

View File

@ -46,6 +46,10 @@ let gpsPowerState = false;
let infoMode = INFO_NONE;
let functionMode = FN_MODE_OFF;
let gpsDisplay = GDISP_OS;
let prevInfoStr = "clear";
let prevActivityStr = "clear";
let prevSteps = "clear";
let clearActivityArea = true;
let last_steps = undefined;
let firstPress = 0;
@ -66,7 +70,8 @@ function drawTime() {
var time = da[4].substr(0,5);
g.reset();
g.clearRect(0,24,239,239);
g.clearRect(0,Y_TIME, 239, Y_ACTIVITY - 1);
g.setColor(1,1,1); // white
g.setFontAlign(0, -1);
@ -81,15 +86,22 @@ function drawTime() {
g.drawString(time, g.getWidth()/2, Y_TIME);
}
function drawSteps() {
function drawActivity() {
var steps = getSteps();
if (!gpsPowerState && steps != prevSteps)
clearActivityArea = true;
prevSteps = steps;
if (clearActivityArea) {
g.clearRect(0, Y_ACTIVITY, 239, Y_MODELINE - 1);
clearActivityArea = false;
}
if (!gpsPowerState) {
g.setColor(0,255,0); // green
g.setFont("Vector", 60);
g.drawString(getSteps(), g.getWidth()/2, Y_ACTIVITY);
}
function drawActivity() {
if (!gpsPowerState) {
drawSteps();
return;
}
@ -116,6 +128,7 @@ function drawActivity() {
let os = OsGridRef.latLongToOsGrid(last_fix);
let ref = to_map_ref(6, os.easting, os.northing);
let speed;
let activityStr = "";
if (age < 0) age = 0;
g.setFontVector(40);
@ -123,18 +136,20 @@ function drawActivity() {
switch(gpsDisplay) {
case GDISP_OS:
g.drawString(ref, 120, Y_ACTIVITY, true);
activityStr = ref;
break;
case GDISP_SPEED:
speed = last_fix.speed;
speed = speed.toFixed(1);
g.drawString(speed + "kph", 120, Y_ACTIVITY, true);
activityStr = speed + "kph"
break;
case GDISP_ALT:
g.drawString(last_fix.alt + "m" , 120, Y_ACTIVITY, true);
activityStr = last_fix.alt + "m";
break;
}
g.clearRect(0, Y_ACTIVITY, 239, Y_MODELINE - 1);
g.drawString(activityStr, 120, Y_ACTIVITY);
g.setFont("6x8",2);
g.setColor(1,1,1);
g.drawString(age, 120, Y_ACTIVITY + 46);
@ -174,8 +189,6 @@ function drawInfo() {
let str = "";
let col = 0x07E0; // green
//console.log("drawInfo(), infoMode=" + infoMode + " funcMode=" + functionMode);
switch(functionMode) {
case FN_MODE_OFF:
break;
@ -218,6 +231,12 @@ function drawInfo() {
}
function drawModeLine(str,col) {
// check if we need to draw, avoid flicker
if (str == prevInfoStr)
return;
prevInfoStr = str;
drawModeLine(str,col);
g.setFont("6x8", 3);
g.setColor(col);
g.fillRect(0, Y_MODELINE - 3, 239, Y_MODELINE + 25);
@ -249,7 +268,7 @@ function changeInfoMode() {
}
functionMode = FN_MODE_OFF;
infoMode = INFO_NONE;
//drawInfo();
clearActivityArea = true;
return;
case FN_MODE_GDISP:
@ -281,7 +300,8 @@ function changeInfoMode() {
default:
infoMode = INFO_NONE;
}
//drawInfo();
clearActivityArea = true;
}
function changeFunctionMode() {
@ -331,11 +351,16 @@ function resetLastFix() {
function processFix(fix) {
last_fix.time = fix.time;
if (gpsState == GPS_TIME)
if (gpsState == GPS_TIME) {
gpsState = GPS_SATS;
clearActivityArea = true;
}
if (fix.fix) {
if (!last_fix.fix) Bangle.buzz(); // buzz on first position
if (!last_fix.fix) {
Bangle.buzz(); // buzz on first position
clearActivityArea = true;
}
gpsState = GPS_RUNNING;
last_fix = fix;
}
@ -508,7 +533,13 @@ drawAll();
Bangle.on('lcdPower',function(on) {
functionMode = FN_MODE_OFF;
infoMode = INFO_NONE;
if (on) drawAll();
if (on) {
prevInfoStr = "on"; // forces are redraw
drawAll();
} else {
prevInfoStr = "off"; // forces are redraw
drawInfo();
}
});
var click = setInterval(onTick, 5000);