Merge branch 'espruino:master' into master
|
@ -2,3 +2,4 @@ apps/animclk/V29.LBM.js
|
|||
apps/banglerun/rollup.config.js
|
||||
apps/schoolCalendar/fullcalendar/main.js
|
||||
apps/authentiwatch/qr_packed.js
|
||||
*.test.js
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
7x7DotsClock
|
||||
|
||||
by Peter Kuppelwieser
|
||||
|
||||
*/
|
||||
|
||||
let settings = Object.assign({ swupApp: "",swdownApp: "", swleftApp: "", swrightApp: ""}, require("Storage").readJSON("7x7dotsclock.json", true) || {});
|
||||
|
||||
// position on screen
|
||||
var Xs = 0, Ys = 30,Xe = 175, Ye=175;
|
||||
//const Xs = 0, Ys = 0,Xe = 175, Ye=175;
|
||||
var SegH = (Ye-Ys)/2,SegW = (Xe-Xs)/2;
|
||||
var Dx = SegW/14, Dy = SegH/16;
|
||||
|
||||
const hColor = [1,1,1];
|
||||
const mColor = [0.3,0.3,1];
|
||||
const bColor = [0.2,0.2,0.2];
|
||||
|
||||
const Font = [
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
],
|
||||
[
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0],
|
||||
],
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
],
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,1,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
],
|
||||
[
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,0,1,1,0,0],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,0,0,1,1,0,0],
|
||||
[0,0,0,1,1,0,0]
|
||||
],
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
],
|
||||
[
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
],
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,1,1]
|
||||
],
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
],
|
||||
[
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,0,0,0,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,1],
|
||||
[0,0,0,0,0,1,1]
|
||||
],
|
||||
];
|
||||
|
||||
// Global Vars
|
||||
var dho = -1, eho = -1, dmo = -1, emo = -1;
|
||||
|
||||
|
||||
function drawHSeg(x1,y1,x2,y2,Num,dColor,Size) {
|
||||
g.setColor(0,0,0);
|
||||
g.fillRect(x1, y1, x2, y2);
|
||||
for (let i = 1; i < 8; i++) {
|
||||
for (let j = 1; j < 8; j++) {
|
||||
if (Font[Num][j-1][i-1] == 1) {
|
||||
g.setColor(dColor[0],dColor[1],dColor[2]);
|
||||
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,Size);
|
||||
} else {
|
||||
g.setColor(bColor[0],bColor[1],bColor[2]);
|
||||
g.fillCircle(x1+Dx+(i-1)*(x2-x1)/7,y1+Dy+(j-1)*(y2-y1)/7,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function drawSSeg(x1,y1,x2,y2,Num,dColor,Size) {
|
||||
for (let i = 1; i < 8; i++) {
|
||||
for (let j = 1; j < 8; j++) {
|
||||
if (Font[Num][j-1][i-1] == 1) {
|
||||
g.setColor(dColor[0],dColor[1],dColor[2]);
|
||||
g.fillCircle(x1+(i-1)*(x2-x1)/7,y1+(j-1)*(y2-y1)/7,Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function ShowSecons() {
|
||||
g.setColor(1,1,1);
|
||||
g.fillRect((Xe-Xs) / 2 - 14 + Xs -3,
|
||||
(Ye-Ys) / 2 - 7 + Ys -3,
|
||||
(Xe-Xs) / 2 + 14 + Xs +1,
|
||||
(Ye-Ys) / 2 + 7 + Ys +1);
|
||||
|
||||
|
||||
drawSSeg( (Xe-Xs) / 2 - 14 + Xs -1,
|
||||
(Ye-Ys) / 2 - 7 + Ys ,
|
||||
(Xe-Xs) / 2 + Xs -1,
|
||||
(Ye-Ys) / 2 + 7 + Ys,
|
||||
ds,mColor,1);
|
||||
|
||||
drawSSeg( (Xe-Xs) / 2 + Xs +1,
|
||||
(Ye-Ys) / 2 - 7 + Ys,
|
||||
(Xe-Xs) / 2 + 14 + Xs +1,
|
||||
(Ye-Ys) / 2 + 7 + Ys,
|
||||
es,mColor,1);
|
||||
|
||||
}
|
||||
|
||||
function draw() {
|
||||
// work out how to display the current time
|
||||
var d = new Date();
|
||||
var h = d.getHours(), m = d.getMinutes(), s = d.getSeconds();
|
||||
|
||||
|
||||
dh = Math.floor(h/10);
|
||||
eh = h - dh * 10;
|
||||
|
||||
dm = Math.floor(m/10);
|
||||
em = m - dm * 10;
|
||||
|
||||
ds = Math.floor(s/10);
|
||||
es = s - ds * 10;
|
||||
|
||||
|
||||
// Reset the state of the graphics library
|
||||
g.reset();
|
||||
if (dh != dho) {
|
||||
g.setColor(1,1,1);
|
||||
drawHSeg(Xs, Ys, Xs+SegW, Ys+SegH,dh,hColor,4);
|
||||
dho = dh;
|
||||
}
|
||||
|
||||
if (eh != eho) {
|
||||
g.setColor(1,1,1);
|
||||
drawHSeg(Xs+SegW+Dx, Ys, Xs+SegW*2, Ys+SegH,eh,hColor,4);
|
||||
eho = eh;
|
||||
}
|
||||
|
||||
if (dm != dmo) {
|
||||
g.setColor(0.3,0.3,1);
|
||||
drawHSeg(Xs, Ys+SegH+Dy, Xs+SegW, Ys+SegH*2,dm,mColor,4);
|
||||
dmo = dm;
|
||||
}
|
||||
|
||||
if (em != emo) {
|
||||
g.setColor(0.3,0.3,1);
|
||||
drawHSeg(Xs+SegW+Dx, Ys+SegH+Dy, Xs+SegW*2, Ys+SegH*2,em,mColor,4);
|
||||
emo = em;
|
||||
}
|
||||
|
||||
if (!Bangle.isLocked()) ShowSecons();
|
||||
|
||||
}
|
||||
|
||||
|
||||
function actions(v){
|
||||
if(BTN1.read() === true) {
|
||||
print("BTN pressed");
|
||||
Bangle.showLauncher();
|
||||
}
|
||||
|
||||
if(v==-1){
|
||||
print("up swipe event");
|
||||
if(settings.swupApp != "") load(settings.swupApp);
|
||||
print(settings.swupApp);
|
||||
} else if(v==1) {
|
||||
print("down swipe event");
|
||||
if(settings.swdownApp != "") load(settings.swdownApp);
|
||||
print(settings.swdownApp);
|
||||
} else {
|
||||
print("touch event");
|
||||
}
|
||||
}
|
||||
|
||||
// Get Messages status
|
||||
var messages = require("Storage").readJSON("messages.json",1)||[];
|
||||
|
||||
//var BTconnected = NRF.getSecurityStatus().connected;
|
||||
//NRF.on('connect',BTconnected = NRF.getSecurityStatus().connected);
|
||||
//NRF.on('disconnect',BTconnected = NRF.getSecurityStatus().connected);
|
||||
|
||||
|
||||
function drawWidgeds() {
|
||||
|
||||
//Bluetooth
|
||||
//print(BluetoothDevice.connected);
|
||||
var x1Bt = 160;
|
||||
var y1Bt = 0;
|
||||
var x2Bt = x1Bt + 30;
|
||||
var y2Bt = y2Bt;
|
||||
|
||||
if (NRF.getSecurityStatus().connected)
|
||||
g.setColor((g.getBPP()>8) ? "#07f" : (g.theme.dark ? "#0ff" : "#00f"));
|
||||
else
|
||||
g.setColor(g.theme.dark ? "#666" : "#999");
|
||||
g.drawImage(atob("CxQBBgDgFgJgR4jZMawfAcA4D4NYybEYIwTAsBwDAA=="),x1Bt,y1Bt);
|
||||
|
||||
|
||||
//Battery
|
||||
//print(E.getBattery());
|
||||
//print(Bangle.isCharging());
|
||||
|
||||
var x1B = 130;
|
||||
var y1B = 2;
|
||||
var x2B = x1B + 20;
|
||||
var y2B = y1B + 15;
|
||||
|
||||
g.setColor(g.theme.bg);
|
||||
g.clearRect(x1B,y1B,x2B,y2B);
|
||||
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawRect(x1B,y1B,x2B,y2B);
|
||||
g.fillRect(x1B,y1B,x1B+(E.getBattery()*(x2B-x1B)/100),y2B);
|
||||
g.fillRect(x2B,y1B+(y2B-y1B)/2-3,x2B+4,y1B+(y2B-y1B)/2+3);
|
||||
|
||||
|
||||
|
||||
//Messages
|
||||
|
||||
var x1M = 100;
|
||||
var y1M = y1B;
|
||||
var x2M = x1M + 30;
|
||||
var y2M = y2B;
|
||||
|
||||
if (messages.some(m=>m.new)) {
|
||||
g.setColor(g.theme.fg);
|
||||
g.fillRect(x1M,y1M,x2M,y2M);
|
||||
g.setColor(g.theme.bg);
|
||||
g.drawLine(x1M,y1M,x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2);
|
||||
g.drawLine(x1M+(x2M-x1M)/2,y1M+(y2M-y1M)/2,x2M,y1M);
|
||||
}
|
||||
|
||||
var strDow = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
|
||||
var d = new Date();
|
||||
var dow = d.getDay(),day = d.getDate(), month = d.getMonth() + 1, year = d.getFullYear();
|
||||
|
||||
print(strDow[dow] + ' ' + day + '.' + month + ' ' + year);
|
||||
|
||||
g.setFontAlign(-1, -1,0);
|
||||
g.setFont("Vector", 20);
|
||||
g.drawString(strDow[dow] + ' ' + day, 0, 0, true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function SetFull(on) {
|
||||
dho = -1; eho = -1; dmo = -1; emo = -1;
|
||||
g.clear();
|
||||
|
||||
if (on === true) {
|
||||
Ys = 0;
|
||||
Bangle.setUI("clock");
|
||||
Bangle.on('swipe', function(direction) { });
|
||||
|
||||
} else {
|
||||
Ys = 30;
|
||||
Bangle.setUI("updown",actions);
|
||||
Bangle.on('swipe', function(direction) {
|
||||
switch (direction) {
|
||||
case 1:
|
||||
print("swipe left event");
|
||||
if(settings.swleftApp != "") load(settings.swleftApp);
|
||||
print(settings.swleftApp);
|
||||
break;
|
||||
case -1:
|
||||
print("swipe right event");
|
||||
if(settings.swrightApp != "") load(settings.swrightApp);
|
||||
print(settings.swrightApp);
|
||||
break;
|
||||
default:
|
||||
print("swipe undefined event");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SegH = (Ye-Ys)/2;
|
||||
Dy = SegH/16;
|
||||
|
||||
draw();
|
||||
|
||||
if (on != true) {
|
||||
//Bangle.loadWidgets();
|
||||
//Bangle.drawWidgets();
|
||||
drawWidgeds();
|
||||
}
|
||||
}
|
||||
|
||||
Bangle.on('lock', function(on) {
|
||||
SetFull(on);
|
||||
});
|
||||
|
||||
|
||||
SetFull(Bangle.isLocked());
|
||||
|
||||
var secondInterval = setInterval(draw, 1000);
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwkEBAkTmEzkAHDmcjmQBBmcTmICCgMAiMAkE/+P/mEQgMQgH/n/zAIP/l/yA4QvXC4kDkEjFgIACkcSmMTkMyBoQHBI4kvI6wXBn8wA4c/mfzl8y+cfEoIaBVa5HBAAMQF4UgIoIBBBgJNBAwQ3BkfygSnJSQIUBkECiBoCL48DmCPFAA6PCX40jX4hYEU4LNBX4JHIkBHCBgJHBianKj8wO4IvHgSnBmJ3CHYqGCABcRcYTXLAA5KCFAJfCC4KnDX4anNgUgiSnMkQQBO5hvCl8yO4pHEd4oyBH4QBBU5TXHkcimUTkLXFL44HEiTbBO4MhBoQHBI4KECR45HGBoIFBU4y/BC4c/mYXGMQJHFiBHLEAIHCf5gAKhWg1UB0IEBjUA0MB0EAjQKCiANCCQOg0cxmcSmWjU4MqmcDmSnDBASkBmejCQIXFmYXEmYXHicyhRLC0AEBAIJFBAIIFCBAYHDF65fXR66vImUCnS8IkeinUBgERgEgcIMBgRHDBgLvCBYMQmcjBYIAHfwL7JiQLBichkcSnUSO4MhI4MxI5MSmMjPgMinCnCkRHGIgJHFiUgkUalUCAgMRkUCkIvIkUSkMC0EiBxAAI0UKkBHCkCPDgA+CI5Z3BmYPBAB53CV4MSEgcSiCnOR4cyR5JQEgBHCC4I0BC4UjC4MCxQXGF4IlBxRHB0UAlUK0BMBkIEBI5ILB0ZHBF4czlTXHI4mjCQIXOH4KnDC4MKgGqgGgAgIBBIoJHJBoQ="))
|
|
@ -0,0 +1,65 @@
|
|||
(function(back) {
|
||||
|
||||
let settings = Object.assign({ swupApp: "",swdownApp: "", swleftApp: "", swrightApp: ""}, require("Storage").readJSON("7x7dotsclock.json", true) || {});
|
||||
|
||||
|
||||
function showMainMenu() {
|
||||
const mainMenu = {
|
||||
"": {"title": "7x7 Dots Clock Settings"},
|
||||
"< Back": ()=>load(),
|
||||
"sw-up": ()=>showSelAppMenu("swupApp"),
|
||||
"sw-down": ()=>showSelAppMenu("swdownApp"),
|
||||
"sw-left": ()=>showSelAppMenu("swleftApp"),
|
||||
"sw-right": ()=>showSelAppMenu("swrightApp")
|
||||
|
||||
};
|
||||
|
||||
E.showMenu(mainMenu);
|
||||
}
|
||||
|
||||
function setSetting(key,value) {
|
||||
print("call " + key + " = " + value);
|
||||
settings[key] = value;
|
||||
|
||||
print("storing settings 7x7dotsclock.json");
|
||||
storage.write('7x7dotsclock.json', settings);
|
||||
}
|
||||
|
||||
|
||||
function showSelAppMenu(key) {
|
||||
var Apps = require("Storage").list(/\.info$/)
|
||||
.map(app => {var a=storage.readJSON(app, 1);return (
|
||||
a&&a.name != "Launcher"
|
||||
&& a&&a.name != "Bootloader"
|
||||
&& a&&a.type != "clock"
|
||||
&& a&&a.type !="widget"
|
||||
)?a:undefined})
|
||||
.filter(app => app) // filter out any undefined apps
|
||||
.sort((a, b) => a.sortorder - b.sortorder);
|
||||
const SelAppMenu = {
|
||||
'': {
|
||||
'title': /*LANG*/'Select App',
|
||||
},
|
||||
'< Back': ()=>showMainMenu(),
|
||||
};
|
||||
Apps.forEach((app, index) => {
|
||||
var label = app.name;
|
||||
if (settings[key] === app.src) {
|
||||
label = "* " + label;
|
||||
}
|
||||
SelAppMenu[label] = () => {
|
||||
if (settings[key] !== app.src) {
|
||||
setSetting(key,app.src);
|
||||
showMainMenu();
|
||||
}
|
||||
};
|
||||
});
|
||||
if (Apps.length === 0) {
|
||||
SelAppMenu[/*LANG*/"No Apps Found"] = () => { };
|
||||
}
|
||||
return E.showMenu(SelAppMenu);
|
||||
}
|
||||
|
||||
showMainMenu();
|
||||
|
||||
})
|
|
@ -0,0 +1 @@
|
|||
0.01: Initial version for upload
|
|
@ -0,0 +1,17 @@
|
|||
# 7x7 dots clock
|
||||
|
||||

|
||||
|
||||
looks best with dark theme so far
|
||||
|
||||
* A Clock with big numbers made of 7x7 dots
|
||||
* system widgeds ar not (yet) supported
|
||||
* when screen is locked it shows hours and minutes in full screen mode
|
||||
|
||||

|
||||
|
||||
* when screen is unlocked it shows additional info: bluetooth, battery, new message, date and seconds
|
||||
* you can configure a app per swipe direction
|
||||
* when swiping the configured apps are launced
|
||||
* button press opens launcher
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 26 KiB |
|
@ -0,0 +1,17 @@
|
|||
{ "id": "7x7dotsclock",
|
||||
"name": "7x7 Dots Clock",
|
||||
"shortName":"7x7 Dots Clock",
|
||||
"version":"0.01",
|
||||
"description": "A clock with a big 7x7 dots Font",
|
||||
"icon": "dotsfontclock.png",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"7x7dotsclock.app.js","url":"7x7dotsclock.app.js"},
|
||||
{"name":"7x7dotsclock.settings.js","url":"7x7dotsclock.settings.js"},
|
||||
{"name":"7x7dotsclock.img","url":"7x7dotsclock.img.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"7x7dotsclock.json"}]
|
||||
}
|
|
@ -7,4 +7,5 @@
|
|||
when weekday name "On": weekday name is cut at 6th position and .#<week num> is added
|
||||
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
|
||||
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)
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "antonclk",
|
||||
"name": "Anton Clock",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "A clock using the bold Anton font, optionally showing seconds and date in ISO-8601 format.",
|
||||
"readme":"README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
},
|
||||
"< Back": () => back(),
|
||||
"Seconds...": () => E.showMenu(secmenu),
|
||||
"Date": stringInSettings("dateOnMain", ["Short", "Long", "ISO8601"]),
|
||||
"Date": stringInSettings("dateOnMain", ["Long", "Short", "ISO8601"]),
|
||||
"Show Weekday": {
|
||||
value: (settings.weekDay !== undefined ? settings.weekDay : true),
|
||||
format: v => v ? "On" : "Off",
|
||||
|
@ -56,7 +56,7 @@
|
|||
}
|
||||
},
|
||||
"Uppercase": {
|
||||
value: (settings.upperCase !== undefined ? settings.upperCase : false),
|
||||
value: (settings.upperCase !== undefined ? settings.upperCase : true),
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => {
|
||||
settings.upperCase = v;
|
||||
|
@ -81,7 +81,7 @@
|
|||
"< Back": () => E.showMenu(mainmenu),
|
||||
"Show": stringInSettings("secondsMode", ["Never", "Unlocked", "Always"]),
|
||||
"With \":\"": {
|
||||
value: (settings.secondsWithColon !== undefined ? settings.secondsWithColon : false),
|
||||
value: (settings.secondsWithColon !== undefined ? settings.secondsWithColon : true),
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => {
|
||||
settings.secondsWithColon = v;
|
||||
|
@ -89,14 +89,14 @@
|
|||
}
|
||||
},
|
||||
"Color": {
|
||||
value: (settings.secondsColoured !== undefined ? settings.secondsColoured : false),
|
||||
value: (settings.secondsColoured !== undefined ? settings.secondsColoured : true),
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => {
|
||||
settings.secondsColoured = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
"Date": stringInSettings("dateOnSecs", ["No", "Year", "Weekday"])
|
||||
"Date": stringInSettings("dateOnSecs", ["Year", "Weekday", "No"])
|
||||
};
|
||||
|
||||
// Actually display the menu
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New game! BTN4- Hit card, BTN5- Stand
|
||||
0.02: ignore buttons on pauses
|
||||
0.02: Ignore buttons on pauses
|
||||
0.03: Support Bangle.js 2
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
var Clubs = require("heatshrink").decompress(atob("j0ewcBkmSpICipEAiQLHwA3BBY8gBQMEEA1AJwQgGyAKChILGBQUCFgxwDJpEAO5AVCII44CAQI1GAAg1GAAZQCWxCDEAAqJBQYQAFRIJWCAApcCR4YADPoRWCgQdBPopfCwAdBTw47BcBAvBU44vDfBDUIRIbUHATuQ"));
|
||||
|
||||
var Spades = require("heatshrink").decompress(atob("j0ewcBkmSpICuoALJIQILHpAKBJQ+QLIUJBYsgMoY1GBQcCBYmAPgkSEBEAgggIKApBDIg4KFHAZiCAAgsDBQw4DFitJFhQ4FTwplBgRoCSQoRBBYJ6EF4jgUwDUHAVOQA=="));
|
||||
|
||||
var Hearts = require("heatshrink").decompress(atob("j0ewY96gMkyAEByVIBQcSpILBhMkBYkEyQLBAQYKCCIQLEEwQgCBYuAEBFJkBBCBYw4CEA44CgQLHIYQsHLJsAEBJEHSQhxENwQADMQoAEKAdAWowLCYJESXggAFGowA/AAQ"));
|
||||
|
||||
var Diamonds = require("heatshrink").decompress(atob("j0ewY1ykgKJhIKJiVIEBOSoAKHpILBBQ+SBYOQBIsBCgILBwAKEgQgCAQIKEggICAQMgKwgUDAQI1GBY4IFLgoLGJpGSPoo4EMoxNIMoqSHiR6HLgizIPoLgfAFA"));
|
||||
|
||||
var deck = [];
|
||||
var player = {Hand:[]};
|
||||
var computer = {Hand:[]};
|
||||
var ctx = {ready:true};
|
||||
|
||||
function createDeck() {
|
||||
var suits = ["Spades", "Hearts", "Diamonds", "Clubs"];
|
||||
var values = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
|
||||
|
||||
var dck = [];
|
||||
for (var i = 0 ; i < values.length; i++) {
|
||||
for(var x = 0; x < suits.length; x++) {
|
||||
dck.push({ Value: values[i], Suit: suits[x] });
|
||||
}
|
||||
}
|
||||
return dck;
|
||||
}
|
||||
|
||||
function shuffle(a) {
|
||||
var j, x, i;
|
||||
for (i = a.length - 1; i > 0; i--) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
x = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = x;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function EndGameMessdage(msg){
|
||||
ctx.ready = false;
|
||||
g.clearRect(0,160,176,176);
|
||||
g.setColor(255,255,255);
|
||||
g.fillRect(0,160,176,176);
|
||||
g.setColor(0,0,0);
|
||||
g.drawString(msg, 12, 155);
|
||||
setTimeout(function(){
|
||||
startGame();
|
||||
}, 2500);
|
||||
|
||||
}
|
||||
|
||||
function hitMe() {
|
||||
if (!ctx.ready) return;
|
||||
player.Hand.push(deck.pop());
|
||||
renderOnScreen(1);
|
||||
var playerWeight = calcWeight(player.Hand, 0);
|
||||
|
||||
if(playerWeight == 21)
|
||||
EndGameMessdage('WINNER');
|
||||
else if(playerWeight > 21)
|
||||
EndGameMessdage('LOSER');
|
||||
}
|
||||
|
||||
function calcWeight(hand, hideCard) {
|
||||
if(hideCard === 1) {
|
||||
if (hand[0].Value == "J" || hand[0].Value == "Q" || hand[0].Value == "K")
|
||||
return "10 +";
|
||||
else if (hand[0].Value == "A")
|
||||
return "11 +";
|
||||
else
|
||||
return parseInt(hand[0].Value) +" +";
|
||||
}
|
||||
else {
|
||||
var weight = 0;
|
||||
for(i=0; i<hand.length; i++){
|
||||
if (hand[i].Value == "J" || hand[i].Value == "Q" || hand[i].Value == "K") {
|
||||
weight += 10;
|
||||
}
|
||||
else if (hand[i].Value == "A") {
|
||||
weight += 1;
|
||||
}
|
||||
else
|
||||
weight += parseInt(hand[i].Value);
|
||||
}
|
||||
|
||||
// Find count of aces because it may be 11 or 1
|
||||
var numOfAces = hand.filter(function(x){ return x.Value === "A"; }).length;
|
||||
for (var j = 0; j < numOfAces; j++) {
|
||||
if (weight + 10 <= 21) {
|
||||
weight +=10;
|
||||
}
|
||||
}
|
||||
return weight;
|
||||
}
|
||||
}
|
||||
|
||||
function stand(){
|
||||
if (!ctx.ready) return;
|
||||
ctx.ready = false;
|
||||
function sleepFor( sleepDuration ){
|
||||
console.log("Sleeping...");
|
||||
var now = new Date().getTime();
|
||||
while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
|
||||
}
|
||||
|
||||
renderOnScreen(0);
|
||||
var playerWeight = calcWeight(player.Hand, 0);
|
||||
var bangleWeight = calcWeight(computer.Hand, 0);
|
||||
|
||||
while(bangleWeight<17){
|
||||
sleepFor(500);
|
||||
computer.Hand.push(deck.pop());
|
||||
renderOnScreen(0);
|
||||
bangleWeight = calcWeight(computer.Hand, 0);
|
||||
}
|
||||
|
||||
if (bangleWeight == playerWeight)
|
||||
EndGameMessdage('TIES');
|
||||
else if(playerWeight==21 || bangleWeight > 21 || bangleWeight < playerWeight)
|
||||
EndGameMessdage('WINNER');
|
||||
else if(bangleWeight > playerWeight)
|
||||
EndGameMessdage('LOOSER');
|
||||
}
|
||||
|
||||
function renderOnScreen(HideCard) {
|
||||
const fontName = "6x8";
|
||||
|
||||
g.clear(); // clear screen
|
||||
g.reset(); // default draw styles
|
||||
g.setFont(fontName, 1);
|
||||
|
||||
g.setColor(255,255,255);
|
||||
g.fillRect(Bangle.appRect);
|
||||
g.setColor(0,0,0);
|
||||
|
||||
g.drawString('Hit', 176/4-10, 160);
|
||||
g.drawString('Stand', 176/4+176/2-10, 160);
|
||||
|
||||
g.setFont(fontName, 3);
|
||||
for(i=0; i<computer.Hand.length; i++){
|
||||
g.drawImage(eval(computer.Hand[i].Suit), i*40, -1);
|
||||
if(i == 1 && HideCard == 1)
|
||||
g.drawString("?", i*40+8, 30);
|
||||
else
|
||||
g.drawString(computer.Hand[i].Value, i*40+8, 30);
|
||||
}
|
||||
g.setFont(fontName, 2);
|
||||
g.drawString('AI has '+ calcWeight(computer.Hand, HideCard), 5, 55);
|
||||
|
||||
g.setFont(fontName, 3);
|
||||
for(i=0; i<player.Hand.length; i++){
|
||||
g.drawImage(eval(player.Hand[i].Suit), i*40, 83);
|
||||
g.drawString(player.Hand[i].Value, i*40+8, 110);
|
||||
}
|
||||
g.setFont(fontName, 2);
|
||||
g.drawString('You have ' + calcWeight(player.Hand, 0), 5, 133);
|
||||
}
|
||||
|
||||
function dealHands() {
|
||||
player.Hand= [];
|
||||
computer.Hand= [];
|
||||
ctx.ready = false;
|
||||
|
||||
setTimeout(function(){
|
||||
player.Hand.push(deck.pop());
|
||||
renderOnScreen(0);
|
||||
}, 500);
|
||||
|
||||
setTimeout(function(){
|
||||
computer.Hand.push(deck.pop());
|
||||
renderOnScreen(1);
|
||||
}, 1000);
|
||||
|
||||
setTimeout(function(){
|
||||
player.Hand.push(deck.pop());
|
||||
renderOnScreen(1);
|
||||
}, 1500);
|
||||
|
||||
setTimeout(function(){
|
||||
computer.Hand.push(deck.pop());
|
||||
renderOnScreen(1);
|
||||
ctx.ready = true;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function startGame(){
|
||||
deck = createDeck();
|
||||
deck = shuffle(deck);
|
||||
dealHands();
|
||||
}
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
var left = parseInt(g.getWidth() * 0.2);
|
||||
var right = g.getWidth() - left;
|
||||
|
||||
var is_left = e.x < left;
|
||||
var is_right = e.x > right;
|
||||
|
||||
if(is_left){
|
||||
hitMe();
|
||||
|
||||
} else if(is_right){
|
||||
stand();
|
||||
}
|
||||
});
|
||||
setWatch(startGame, BTN1, {repeat:true, edge:"falling"});
|
||||
|
||||
startGame();
|
|
@ -2,15 +2,16 @@
|
|||
"id": "blackjack",
|
||||
"name": "Black Jack game",
|
||||
"shortName": "Black Jack game",
|
||||
"version": "0.02",
|
||||
"version": "0.03",
|
||||
"description": "Simple implementation of card game Black Jack",
|
||||
"icon": "blackjack.png",
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS"],
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"screenshots": [{"url":"bangle1-black-jack-game-screenshot.png"}],
|
||||
"allow_emulator": true,
|
||||
"storage": [
|
||||
{"name":"blackjack.app.js","url":"blackjack.app.js"},
|
||||
{"name":"blackjack.app.js","url":"blackjack.app.js","supports": ["BANGLEJS"]},
|
||||
{"name":"blackjack.app.js","url":"appb2.js","supports": ["BANGLEJS2"]},
|
||||
{"name":"blackjack.img","url":"blackjack-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
0.03: Tweak for more efficient rendering, and firmware 2v06
|
||||
0.04: Work with themes, smaller screens
|
||||
0.05: Adjust hand lengths to be within 'tick' points
|
||||
0.06: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
|
||||
|
|
|
@ -129,14 +129,6 @@ Bangle.on('lcdPower', (on) => {
|
|||
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();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "boldclk",
|
||||
"name": "Bold Clock",
|
||||
"version": "0.05",
|
||||
"version": "0.06",
|
||||
"description": "Simple, readable and practical clock",
|
||||
"icon": "bold_clock.png",
|
||||
"screenshots": [{"url":"screenshot_bold.png"}],
|
||||
|
|
|
@ -4,3 +4,4 @@
|
|||
0.22: Changed timing code, original "Nunito" Font is back!
|
||||
0.23: Customizer! Unused fonts no longer take up precious memory.
|
||||
0.24: Added previews to the customizer.
|
||||
0.25: Fixed a bug that would let widgets change the color of the clock.
|
||||
|
|
|
@ -10,6 +10,7 @@ if (settings.fontIndex==undefined) {
|
|||
function draw() {
|
||||
var date = new Date();
|
||||
// Draw day of the week
|
||||
g.reset();
|
||||
g.setFont("Teletext10x18Ascii");
|
||||
g.clearRect(0,138,g.getWidth()-1,176);
|
||||
g.setFontAlign(0,1).drawString(require("locale").dow(date).toUpperCase(),g.getWidth()/2,g.getHeight()-18);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "contourclock",
|
||||
"name": "Contour Clock",
|
||||
"shortName" : "Contour Clock",
|
||||
"version":"0.24",
|
||||
"version":"0.25",
|
||||
"icon": "app.png",
|
||||
"description": "A Minimalist clockface with large Digits. Now with more fonts!",
|
||||
"screenshots" : [{"url":"cc-screenshot-1.png"},{"url":"cc-screenshot-2.png"}],
|
||||
|
|
|
@ -7,3 +7,5 @@
|
|||
0.07: Fix "previous" button image
|
||||
0.08: Fix scrolling title background color
|
||||
0.09: Move event listener from widget to boot code, stops music from showing up in messages
|
||||
0.10: Simplify touch events
|
||||
Remove date+time
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
If you have an Android phone with Gadgetbridge, this app allows you to view
|
||||
and control music playback.
|
||||
|
||||
| Bangle.js 1 | Bangle.js 2 |
|
||||
|:-------------------------------------------|:-------------------------------------------|
|
||||
|  |  |
|
||||
| Bangle.js 1 | Bangle.js 2 |
|
||||
|:---------------------------------------------------------|:---------------------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
Download the [latest Gadgetbridge for Android here](https://f-droid.org/packages/nodomain.freeyourgadget.gadgetbridge/).
|
||||
|
||||
|
@ -14,7 +15,6 @@ Download the [latest Gadgetbridge for Android here](https://f-droid.org/packages
|
|||
* Dynamic colors based on Track/Artist/Album name
|
||||
* Scrolling display for long titles
|
||||
* Automatic start when music plays
|
||||
* Time and date display
|
||||
|
||||
## Settings
|
||||
|
||||
|
@ -40,9 +40,7 @@ Disable double/triple pressing Middle Button: always simply toggle play/pause.
|
|||
* Button 3 (*Bangle.js 1*): Volume down
|
||||
|
||||
### Touch
|
||||
* Left: Pause/previous song
|
||||
* Right: Next song/resume
|
||||
* Center: Toggle play/pause
|
||||
* Touch: Toggle play/pause
|
||||
* Swipe left/right: Next/previous song
|
||||
* Swipe up/down (*Bangle.js 2*): Volume up/down
|
||||
|
||||
|
|
|
@ -10,15 +10,15 @@ const BANGLE2 = process.env.HWVERSION===2;
|
|||
|
||||
/**
|
||||
* @param {string} text
|
||||
* @return {number} Maximum font size to make text fit on screen
|
||||
* @param {number} w Width to fit text in
|
||||
* @return {number} Maximum font size to make text fit
|
||||
*/
|
||||
function fitText(text) {
|
||||
function fitText(text, w) {
|
||||
if (!text.length) {
|
||||
return Infinity;
|
||||
}
|
||||
// make a guess, then shrink/grow until it fits
|
||||
const w = Bangle.appRect.w,
|
||||
test = (s) => g.setFont("Vector", s).stringWidth(text);
|
||||
const test = (s) => g.setFont("Vector", s).stringWidth(text);
|
||||
let best = Math.floor(100*w/test(100));
|
||||
if (test(best)===w) { // good guess!
|
||||
return best;
|
||||
|
@ -106,7 +106,7 @@ function rTitle(l) {
|
|||
rScroller(l); // already scrolling
|
||||
return;
|
||||
}
|
||||
let size = fitText(l.label);
|
||||
let size = fitText(l.label, l.w);
|
||||
if (size<l.h/2) {
|
||||
// the title is too long: start the scroller
|
||||
scrollStart();
|
||||
|
@ -119,7 +119,7 @@ function rTitle(l) {
|
|||
* @param l
|
||||
*/
|
||||
function rInfo(l) {
|
||||
let size = fitText(l.label);
|
||||
let size = fitText(l.label, l.w);
|
||||
if (size>l.h) {
|
||||
size = l.h;
|
||||
}
|
||||
|
@ -182,23 +182,17 @@ function makeUI() {
|
|||
type: "v", c: [
|
||||
{
|
||||
type: "h", fillx: 1, c: [
|
||||
{id: "time", type: "txt", label: "88:88", valign: -1, halign: -1, font: "8%", bgCol: g.theme.bg},
|
||||
{fillx: 1},
|
||||
{id: "num", type: "txt", label: "88:88", valign: -1, halign: 1, font: "12%", bgCol: g.theme.bg},
|
||||
BANGLE2 ? {} : {id: "up", type: "txt", label: " +", font: "6x8:2"},
|
||||
{id: "num", type: "txt", label: "", valign: -1, halign: -1, font: "12%", bgCol: g.theme.bg},
|
||||
BANGLE2 ? {} : {id: "up", type: "txt", label: " +", halign: 1, font: "6x8:2"},
|
||||
],
|
||||
},
|
||||
{id: "title", type: "custom", label: "", fillx: 1, filly: 2, offset: null, font: "Vector:20%", render: rTitle, bgCol: g.theme.bg},
|
||||
{id: "artist", type: "custom", label: "", fillx: 1, filly: 1, size: 30, render: rInfo, bgCol: g.theme.bg},
|
||||
{id: "album", type: "custom", label: "", fillx: 1, filly: 1, size: 20, render: rInfo, bgCol: g.theme.bg},
|
||||
{height: 10},
|
||||
{
|
||||
type: "h", c: [
|
||||
{width: 3},
|
||||
{id: "prev", type: "custom", height: 15, width: 15, icon: "previous", render: rIcon, bgCol: g.theme.bg},
|
||||
{id: "date", type: "txt", halign: 0, valign: 1, label: "", font: "8%", fillx: 1, bgCol: g.theme.bg},
|
||||
{id: "next", type: "custom", height: 15, width: 15, icon: "next", render: rIcon, bgCol: g.theme.bg},
|
||||
BANGLE2 ? {width: 3} : {id: "down", type: "txt", label: " -", font: "6x8:2"},
|
||||
{id: "album", type: "custom", label: "", fillx: 1, filly: 1, size: 20, render: rInfo, bgCol: g.theme.bg},
|
||||
BANGLE2 ? {} : {id: "down", type: "txt", label: " -", font: "6x8:2"},
|
||||
],
|
||||
},
|
||||
{height: 10},
|
||||
|
@ -211,20 +205,6 @@ function makeUI() {
|
|||
// Self-repeating timeouts
|
||||
///////////////////////
|
||||
|
||||
// Clock
|
||||
let tock = -1;
|
||||
function tick() {
|
||||
if (!BANGLE2 && !Bangle.isLCDOn()) {
|
||||
return;
|
||||
}
|
||||
const now = new Date();
|
||||
if (now.getHours()*60+now.getMinutes()!==tock) {
|
||||
drawDateTime();
|
||||
tock = now.getHours()*60+now.getMinutes();
|
||||
}
|
||||
setTimeout(tick, 1000); // we only show minute precision anyway
|
||||
}
|
||||
|
||||
// Fade out while paused and auto closing
|
||||
let fade = null;
|
||||
function fadeOut() {
|
||||
|
@ -271,40 +251,12 @@ function scrollStop() {
|
|||
////////////////////
|
||||
// Drawing functions
|
||||
////////////////////
|
||||
/**
|
||||
* Draw date and time
|
||||
*/
|
||||
function drawDateTime() {
|
||||
const now = new Date();
|
||||
const l = require("locale");
|
||||
const is12 = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
||||
if (is12) {
|
||||
const d12 = new Date(now.getTime());
|
||||
const hour = d12.getHours();
|
||||
if (hour===0) {
|
||||
d12.setHours(12);
|
||||
} else if (hour>12) {
|
||||
d12.setHours(hour-12);
|
||||
}
|
||||
layout.time.label = l.time(d12, true)+l.meridian(now);
|
||||
} else {
|
||||
layout.time.label = l.time(now, true);
|
||||
}
|
||||
layout.date.label = require("locale").date(now, true);
|
||||
layout.render();
|
||||
}
|
||||
|
||||
function drawControls() {
|
||||
let l = layout;
|
||||
if (BANGLE2) return;
|
||||
const cc = a => (a ? "#f00" : "#0f0"); // control color: red for active, green for inactive
|
||||
if (!BANGLE2) {
|
||||
l.up.col = cc("volumeup" in tCommand);
|
||||
l.down.col = cc("volumedown" in tCommand);
|
||||
}
|
||||
l.prev.icon = (stat==="play") ? "pause" : "previous";
|
||||
l.prev.col = cc("prev" in tCommand || "pause" in tCommand);
|
||||
l.next.icon = (stat==="play") ? "next" : "play";
|
||||
l.next.col = cc("next" in tCommand || "play" in tCommand);
|
||||
layout.up.col = cc("volumeup" in tCommand);
|
||||
layout.down.col = cc("volumedown" in tCommand);
|
||||
layout.render();
|
||||
}
|
||||
|
||||
|
@ -339,6 +291,7 @@ function info(info) {
|
|||
layout.album.col = infoColor("album");
|
||||
layout.artist.col = infoColor("artist");
|
||||
layout.num.label = formatNum(info);
|
||||
layout.update();
|
||||
layout.render();
|
||||
rTitle(layout.title); // force redraw of title, or scroller might break
|
||||
// reset auto exit interval
|
||||
|
@ -473,37 +426,16 @@ function sendCommand(command) {
|
|||
drawControls();
|
||||
}
|
||||
|
||||
// touch/swipe: navigation
|
||||
function togglePlay() {
|
||||
sendCommand(stat==="play" ? "pause" : "play");
|
||||
}
|
||||
function pausePrev() {
|
||||
sendCommand(stat==="play" ? "pause" : "previous");
|
||||
}
|
||||
function nextPlay() {
|
||||
sendCommand(stat==="play" ? "next" : "play");
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup touch+swipe for Bangle.js 1
|
||||
*/
|
||||
function touch1() {
|
||||
Bangle.on("touch", side => {
|
||||
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
|
||||
switch(side) {
|
||||
case 1:
|
||||
pausePrev();
|
||||
break;
|
||||
case 2:
|
||||
nextPlay();
|
||||
break;
|
||||
default:
|
||||
togglePlay();
|
||||
break;
|
||||
}
|
||||
});
|
||||
Bangle.on("touch", togglePlay);
|
||||
Bangle.on("swipe", dir => {
|
||||
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
|
||||
sendCommand(dir===1 ? "previous" : "next");
|
||||
});
|
||||
}
|
||||
|
@ -511,16 +443,7 @@ function touch1() {
|
|||
* Setup touch+swipe for Bangle.js 2
|
||||
*/
|
||||
function touch2() {
|
||||
Bangle.on("touch", (side, xy) => {
|
||||
const ar = Bangle.appRect;
|
||||
if (xy.x<ar.x+ar.w/3) {
|
||||
pausePrev();
|
||||
} else if (xy.x>ar.x+ar.w*2/3) {
|
||||
nextPlay();
|
||||
} else {
|
||||
togglePlay();
|
||||
}
|
||||
});
|
||||
Bangle.on("touch", togglePlay);
|
||||
// swiping
|
||||
let drag;
|
||||
Bangle.on("drag", e => {
|
||||
|
@ -595,7 +518,6 @@ function startWatches() {
|
|||
function start() {
|
||||
makeUI();
|
||||
startWatches();
|
||||
tick();
|
||||
startEmulator();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
"id": "gbmusic",
|
||||
"name": "Gadgetbridge Music Controls",
|
||||
"shortName": "Music Controls",
|
||||
"version": "0.09",
|
||||
"version": "0.10",
|
||||
"description": "Control the music on your Gadgetbridge-connected phone",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot_v1.png"},{"url":"screenshot_v2.png"}],
|
||||
"screenshots": [{"url":"screenshot_v1_d.png"},{"url":"screenshot_v1_l.png"},
|
||||
{"url":"screenshot_v2_d.png"},{"url":"screenshot_v2_l.png"}],
|
||||
"type": "app",
|
||||
"tags": "tools,bluetooth,gadgetbridge,music",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
|
|
Before Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1 @@
|
|||
0.01: Release
|
|
@ -0,0 +1,17 @@
|
|||
# Info
|
||||
|
||||
A very simple app that shows information on 3 different screens.
|
||||
Go to the next screen via tab right, go to the previous screen
|
||||
via tab left and reload the data via tab in the middle of the
|
||||
screen. Very useful if combined with pattern launcher ;)
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
## Contributors
|
||||
- [David Peer](https://github.com/peerdavid).
|
||||
|
||||
## Thanks To
|
||||
<a href="https://www.flaticon.com/free-icons/info" title="info icons">Info icons created by Freepik - Flaticon</a>
|
|
@ -0,0 +1,108 @@
|
|||
var s = require("Storage");
|
||||
const locale = require('locale');
|
||||
var ENV = process.env;
|
||||
var W = g.getWidth(), H = g.getHeight();
|
||||
var screen = 0;
|
||||
const maxScreen = 2;
|
||||
|
||||
function getVersion(file) {
|
||||
var j = s.readJSON(file,1);
|
||||
var v = ("object"==typeof j)?j.version:false;
|
||||
return v?((v?"v"+v:"Unknown")):"NO ";
|
||||
}
|
||||
|
||||
|
||||
function drawData(name, value, y){
|
||||
g.drawString(name, 5, y);
|
||||
g.drawString(value, 100, y);
|
||||
}
|
||||
|
||||
function getSteps(){
|
||||
try{
|
||||
return Bangle.getHealthStatus("day").steps;
|
||||
} catch(e) {
|
||||
return ">= 2v12";
|
||||
}
|
||||
}
|
||||
|
||||
function getBpm(){
|
||||
try{
|
||||
return Math.round(Bangle.getHealthStatus("day").bpm) + "bpm";
|
||||
} catch(e) {
|
||||
return ">= 2v12";
|
||||
}
|
||||
}
|
||||
|
||||
function drawInfo() {
|
||||
g.reset().clearRect(Bangle.appRect);
|
||||
var h=18, y = h;//-h;
|
||||
|
||||
// Header
|
||||
g.setFont("Vector", h+2).setFontAlign(0,-1);
|
||||
g.drawString("--==|| INFO ||==--", W/2, 0);
|
||||
g.setFont("Vector",h).setFontAlign(-1,-1);
|
||||
|
||||
// Dynamic data
|
||||
if(screen == 0){
|
||||
drawData("Steps", getSteps(), y+=h);
|
||||
drawData("HRM", getBpm(), y+=h);
|
||||
drawData("Battery", E.getBattery() + "%", y+=h);
|
||||
drawData("Voltage", E.getAnalogVRef().toFixed(2) + "V", y+=h);
|
||||
drawData("IntTemp.", locale.temp(parseInt(E.getTemperature())), y+=h);
|
||||
}
|
||||
|
||||
if(screen == 1){
|
||||
drawData("Charging?", Bangle.isCharging() ? "Yes" : "No", y+=h);
|
||||
drawData("Bluetooth", NRF.getSecurityStatus().connected ? "Conn." : "Disconn.", y+=h);
|
||||
drawData("GPS", Bangle.isGPSOn() ? "On" : "Off", y+=h);
|
||||
drawData("Compass", Bangle.isCompassOn() ? "On" : "Off", y+=h);
|
||||
drawData("HRM", Bangle.isHRMOn() ? "On" : "Off", y+=h);
|
||||
}
|
||||
|
||||
// Static data
|
||||
if(screen == 2){
|
||||
drawData("Firmw.", ENV.VERSION, y+=h);
|
||||
drawData("Boot.", getVersion("boot.info"), y+=h);
|
||||
drawData("Settings", getVersion("setting.info"), y+=h);
|
||||
drawData("Storage", "", y+=h);
|
||||
drawData(" Total", ENV.STORAGE>>10, y+=h);
|
||||
drawData(" Free", require("Storage").getFree()>>10, y+=h);
|
||||
}
|
||||
|
||||
if(Bangle.isLocked()){
|
||||
g.setFont("Vector",h-2).setFontAlign(-1,-1);
|
||||
g.drawString("Locked", 0, H-h+2);
|
||||
}
|
||||
|
||||
g.setFont("Vector",h-2).setFontAlign(1,-1);
|
||||
g.drawString((screen+1) + "/3", W, H-h+2);
|
||||
}
|
||||
|
||||
drawInfo();
|
||||
setWatch(_=>load(), BTN1);
|
||||
|
||||
Bangle.on('touch', function(btn, e){
|
||||
var left = parseInt(g.getWidth() * 0.3);
|
||||
var right = g.getWidth() - left;
|
||||
var isLeft = e.x < left;
|
||||
var isRight = e.x > right;
|
||||
|
||||
if(isRight){
|
||||
screen = (screen + 1) % (maxScreen+1);
|
||||
}
|
||||
|
||||
if(isLeft){
|
||||
screen -= 1;
|
||||
screen = screen < 0 ? maxScreen : screen;
|
||||
}
|
||||
|
||||
drawInfo();
|
||||
});
|
||||
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
drawInfo();
|
||||
});
|
||||
|
||||
Bangle.loadWidgets();
|
||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||
// Bangle.drawWidgets();
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwcBkmSpICDBwcJBYwCDpAhFggRJGg8SCI+ABgU//gSDCI4JBj//AAX4JRAIBg4QDAAPgBIJWGgIQFAAI+BLglAgEPCI/wEgJoEgYQHAAPANwhWFAApcBCIWQgAQJAAMAgSMDCJiSCwB6GQA6eCn5TFL4q5BUgIRF/wuBv4RGkCeGO4IREUgMBCJCVGCISwIWw0BYRLIICLBHHCJRrGCIQIFR44I5LIoRaPpARcdIwRJfYMBCJuACKUkgE/a5f8gEJCJD7FCIeAg78FAAvggFJCIMACJZOBCIOQCJsCCIOSgEfCBP4gESCIZTFOIwRDoDIGaguSCIVIgCkFTwcAggRDpIYBQAx6BgAOCAQYIBLghWBTwQRFFgIABXIIFDBwgCDBYQAENAYCFLgIAEKwpKIIhA="))
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "info",
|
||||
"name": "Info",
|
||||
"version": "0.01",
|
||||
"description": "An application that displays information such as battery level, steps etc.",
|
||||
"icon": "info.png",
|
||||
"type": "app",
|
||||
"tags": "tool",
|
||||
"readme": "README.md",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"screenshots": [
|
||||
{"url":"screenshot_1.png"},
|
||||
{"url":"screenshot_2.png"},
|
||||
{"url":"screenshot_3.png"}],
|
||||
"storage": [
|
||||
{"name":"info.app.js","url":"info.app.js"},
|
||||
{"name":"info.img","url":"info.icon.js","evaluate":true}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 3.4 KiB |
|
@ -15,7 +15,7 @@
|
|||
0.10: Respect the 'new' attribute if it was set from iOS integrations
|
||||
0.11: Open app when touching the widget (Bangle.js 2 only)
|
||||
0.12: Extra app-specific notification icons
|
||||
New animated notifcationicon (instead of large blinking 'MESSAGES')
|
||||
New animated notification icon (instead of large blinking 'MESSAGES')
|
||||
Added screenshots
|
||||
0.13: Add /*LANG*/ comments for internationalisation
|
||||
Add 'Delete All' option to message options
|
||||
|
@ -31,3 +31,6 @@
|
|||
0.19: Use a larger font for message text if it'll fit
|
||||
0.20: Allow tapping on the body to show a scrollable view of the message and title in a bigger font (fix #1405, #1031)
|
||||
0.21: Improve list readability on dark theme
|
||||
0.22: Add Home Assistant icon
|
||||
0.22: Allow repeat to be switched Off, so there is no buzzing repetition.
|
||||
Also gave the widget a pixel more room to the right
|
|
@ -83,6 +83,7 @@ function getMessageImage(msg) {
|
|||
if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
|
||||
if (s=="facebook") return getFBIcon();
|
||||
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==");
|
||||
|
@ -114,6 +115,7 @@ function getMessageImageCol(msg,def) {
|
|||
"facebook": "#4267b2",
|
||||
"gmail": "#ea4335",
|
||||
"google home": "#fbbc05",
|
||||
"home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
|
||||
"hangouts": "#1ba261",
|
||||
"instagram": "#dd2a7b",
|
||||
"messenger": "#0078ff",
|
||||
|
@ -123,7 +125,7 @@ function getMessageImageCol(msg,def) {
|
|||
"telegram": "#0088cc",
|
||||
"twitter": "#1da1f2",
|
||||
"whatsapp": "#4fce5d",
|
||||
"wordfeud": "#dcc8bd",
|
||||
"wordfeud": "#e7d3c7",
|
||||
}[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.21",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge",
|
||||
"version": "0.22",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge/Android",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "tool,system",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
if (settings.vibrate===undefined) settings.vibrate=".";
|
||||
if (settings.repeat===undefined) settings.repeat=4;
|
||||
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
|
||||
settings.maxUnreadTimeout=240;
|
||||
return settings;
|
||||
}
|
||||
function updateSetting(setting, value) {
|
||||
|
@ -13,7 +14,6 @@
|
|||
}
|
||||
|
||||
var vibPatterns = [/*LANG*/"Off", ".", "-", "--", "-.-", "---"];
|
||||
var currentVib = settings().vibrate;
|
||||
var mainmenu = {
|
||||
"" : { "title" : /*LANG*/"Messages" },
|
||||
"< Back" : back,
|
||||
|
@ -27,13 +27,13 @@
|
|||
},
|
||||
/*LANG*/'Repeat': {
|
||||
value: settings().repeat,
|
||||
min: 2, max: 10,
|
||||
format: v => v+"s",
|
||||
min: 0, max: 10,
|
||||
format: v => v?v+"s":/*LANG*/"Off",
|
||||
onchange: v => updateSetting("repeat", v)
|
||||
},
|
||||
/*LANG*/'Unread timer': {
|
||||
value: settings().unreadTimeout,
|
||||
min: 0, max: 240, step : 10,
|
||||
min: 0, max: settings().maxUnreadTimeout, step : 10,
|
||||
format: v => v?v+"s":/*LANG*/"Off",
|
||||
onchange: v => updateSetting("unreadTimeout", v)
|
||||
},
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
WIDGETS["messages"]={area:"tl", width:0, iconwidth:23,
|
||||
WIDGETS["messages"]={area:"tl", width:0, iconwidth:24,
|
||||
draw:function() {
|
||||
Bangle.removeListener('touch', this.touch);
|
||||
if (!this.width) return;
|
||||
var c = (Date.now()-this.t)/1000;
|
||||
g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+this.iconwidth);
|
||||
g.drawImage((c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y);
|
||||
//if (c<60) Bangle.setLCDPower(1); // keep LCD on for 1 minute
|
||||
let settings = require('Storage').readJSON("messages.settings.json", true) || {};
|
||||
console.log("dingen ", typeof(settings.repeat), settings.repeat)
|
||||
if (settings.repeat===undefined) settings.repeat = 4;
|
||||
if (c<120 && (Date.now()-this.l)>settings.repeat*1000) {
|
||||
this.l = Date.now();
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
0.01: Initial release
|
||||
0.02: Optional fullscreen mode
|
||||
0.03: Optional show lock status via color
|
||||
0.04: Ensure that widgets are always hidden in fullscreen mode
|
|
@ -4,8 +4,8 @@
|
|||
|---------------------------------|--------------------------------------|
|
||||
| <center>Neon X</center> | <center>Neon IO X</center> |
|
||||
|
||||
This is a clock based on Pebble's Neon X and Neon IO X watchfaces by Sam Jerichow.
|
||||
Can be switched between in the Settings menu, which can be accessed through
|
||||
This is a clock based on Pebble's Neon X and Neon IO X watchfaces by Sam Jerichow.
|
||||
Can be switched between in the Settings menu, which can be accessed through
|
||||
the app/widget settings menu of the Bangle.js
|
||||
|
||||
## Settings
|
||||
|
@ -14,7 +14,14 @@ the app/widget settings menu of the Bangle.js
|
|||
Activate the Neon IO X clock look, a bit hard to read until one gets used to it.
|
||||
|
||||
### Thickness
|
||||
The thickness of watch lines, from 1 to 5.
|
||||
The thickness of watch lines, from 1 to 6.
|
||||
|
||||
### Date on touch
|
||||
Shows the current date as DD MM on touch and reverts back to time after 5 seconds or with another touch.
|
||||
|
||||
### Fullscreen
|
||||
Shows the watchface in fullscreen mode.
|
||||
Note: In fullscreen mode, widgets are hidden, but still loaded.
|
||||
|
||||
### Show lock status
|
||||
If enabled, color changes when unlocked to detect the lock state easily.
|
|
@ -2,7 +2,7 @@
|
|||
"id": "neonx",
|
||||
"name": "Neon X & IO X Clock",
|
||||
"shortName": "Neon X Clock",
|
||||
"version": "0.01",
|
||||
"version": "0.04",
|
||||
"description": "Pebble Neon X & Neon IO X for Bangle.js",
|
||||
"icon": "neonx.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -8,6 +8,19 @@
|
|||
* Created: February 2022
|
||||
*/
|
||||
|
||||
let settings = {
|
||||
thickness: 4,
|
||||
io: 0,
|
||||
showDate: 1,
|
||||
fullscreen: false,
|
||||
showLock: false,
|
||||
};
|
||||
let saved_settings = require('Storage').readJSON('neonx.json', 1) || settings;
|
||||
for (const key in saved_settings) {
|
||||
settings[key] = saved_settings[key]
|
||||
}
|
||||
|
||||
|
||||
const digits = {
|
||||
0:[[15,15,85,15,85,85,15,85,15,15]],
|
||||
1:[[85,15,85,85]],
|
||||
|
@ -21,6 +34,7 @@ const digits = {
|
|||
9:[[15,50,15,15,85,15,85,85,15,85]],
|
||||
};
|
||||
|
||||
|
||||
const colors = {
|
||||
x: [
|
||||
["#FF00FF", "#00FFFF"],
|
||||
|
@ -31,16 +45,14 @@ const colors = {
|
|||
["#00FF00", "#00FFFF"]
|
||||
]
|
||||
};
|
||||
|
||||
const is12hour = (require("Storage").readJSON("setting.json",1)||{})["12hour"]||false;
|
||||
const screenWidth = g.getWidth();
|
||||
const screenHeight = g.getHeight();
|
||||
const halfWidth = screenWidth / 2;
|
||||
const scale = screenWidth / 240;
|
||||
const REFRESH_RATE = 10E3;
|
||||
|
||||
let interval = 0;
|
||||
let showingDate = false;
|
||||
|
||||
|
||||
function drawLine(poly, thickness){
|
||||
for (let i = 0; i < poly.length; i = i + 2){
|
||||
if (poly[i + 2] === undefined) {
|
||||
|
@ -58,34 +70,35 @@ function drawLine(poly, thickness){
|
|||
}
|
||||
}
|
||||
|
||||
let settings = require('Storage').readJSON('neonx.json', 1);
|
||||
|
||||
if (!settings) {
|
||||
settings = {
|
||||
thickness: 4,
|
||||
io: 0,
|
||||
showDate: 1
|
||||
};
|
||||
}
|
||||
|
||||
function drawClock(num){
|
||||
let tx, ty;
|
||||
|
||||
if(settings.fullscreen){
|
||||
g.clearRect(0,0,screenWidth,screenHeight);
|
||||
} else {
|
||||
g.clearRect(0,24,240,240);
|
||||
}
|
||||
|
||||
for (let x = 0; x <= 1; x++) {
|
||||
for (let y = 0; y <= 1; y++) {
|
||||
const current = ((y + 1) * 2 + x - 1);
|
||||
let newScale = scale;
|
||||
|
||||
g.setColor(colors[settings.io ? 'io' : 'x'][y][x]);
|
||||
let xc = settings.showLock && !Bangle.isLocked() ? Math.abs(x-1) : x;
|
||||
let c = colors[settings.io ? 'io' : 'x'][y][xc];
|
||||
g.setColor(c);
|
||||
|
||||
if (!settings.io) {
|
||||
tx = (x * 100 + 18) * newScale;
|
||||
ty = (y * 100 + 32) * newScale;
|
||||
newScale *= settings.fullscreen ? 1.20 : 1.0;
|
||||
let dx = settings.fullscreen ? 0 : 18
|
||||
tx = (x * 100 + dx) * newScale;
|
||||
ty = (y * 100 + dx*2) * newScale;
|
||||
} else {
|
||||
newScale = 0.33 + current * 0.4;
|
||||
newScale = 0.33 + current * (settings.fullscreen ? 0.48 : 0.4);
|
||||
|
||||
tx = (halfWidth - 139) * newScale + halfWidth;
|
||||
ty = (halfWidth - 139) * newScale + halfWidth + 12;
|
||||
tx = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 2 : 0);
|
||||
ty = (halfWidth - 139) * newScale + halfWidth + (settings.fullscreen ? 2 : 12);
|
||||
}
|
||||
|
||||
for (let i = 0; i < digits[num[y][x]].length; i++) {
|
||||
|
@ -95,59 +108,81 @@ function drawClock(num){
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function draw(date){
|
||||
queueDraw();
|
||||
|
||||
// Depending on the settings, we clear all widgets or draw those.
|
||||
if(settings.fullscreen){
|
||||
for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
|
||||
} else {
|
||||
Bangle.drawWidgets();
|
||||
}
|
||||
|
||||
// Now lets draw the time/date
|
||||
let d = new Date();
|
||||
let l1, l2;
|
||||
|
||||
showingDate = date;
|
||||
|
||||
if (date) {
|
||||
setUpdateInt(0);
|
||||
|
||||
l1 = ('0' + (new Date()).getDate()).substr(-2);
|
||||
l2 = ('0' + ((new Date()).getMonth() + 1)).substr(-2);
|
||||
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
setTimeout(_ => {
|
||||
draw();
|
||||
setUpdateInt(1);
|
||||
}, 5000);
|
||||
} else {
|
||||
l1 = ('0' + (d.getHours() % (is12hour ? 12 : 24))).substr(-2);
|
||||
l2 = ('0' + d.getMinutes()).substr(-2);
|
||||
}
|
||||
|
||||
g.clearRect(0,24,240,240);
|
||||
|
||||
drawClock([l1, l2]);
|
||||
}
|
||||
|
||||
function setUpdateInt(set){
|
||||
if (interval) {
|
||||
clearInterval(interval);
|
||||
}
|
||||
|
||||
if (set) {
|
||||
interval = setInterval(draw, REFRESH_RATE);
|
||||
}
|
||||
/*
|
||||
* Draw watch face
|
||||
*/
|
||||
var drawTimeout;
|
||||
function queueDraw() {
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = setTimeout(function() {
|
||||
drawTimeout = undefined;
|
||||
draw();
|
||||
}, 60000 - (Date.now() % 60000));
|
||||
}
|
||||
|
||||
g.clear(1);
|
||||
|
||||
Bangle.setUI("clock");
|
||||
|
||||
setUpdateInt(1);
|
||||
draw();
|
||||
|
||||
/*
|
||||
* Event handlers
|
||||
*/
|
||||
if (settings.showDate) {
|
||||
Bangle.on('touch', () => draw(!showingDate));
|
||||
}
|
||||
|
||||
Bangle.on('lcdPower', function(on){
|
||||
if (on){
|
||||
if (drawTimeout) clearTimeout(drawTimeout);
|
||||
drawTimeout = undefined;
|
||||
|
||||
if (on) {
|
||||
draw();
|
||||
setUpdateInt(1);
|
||||
} else setUpdateInt(0);
|
||||
}
|
||||
});
|
||||
|
||||
Bangle.on('lock', function(isLocked) {
|
||||
draw();
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
* Draw first time
|
||||
*/
|
||||
g.clear(1);
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
draw();
|
|
@ -7,7 +7,9 @@
|
|||
neonXSettings = {
|
||||
thickness: 4,
|
||||
io: 0,
|
||||
showDate: 1
|
||||
showDate: 1,
|
||||
fullscreen: false,
|
||||
showLock: false,
|
||||
};
|
||||
|
||||
updateSettings();
|
||||
|
@ -17,7 +19,7 @@
|
|||
|
||||
if (!neonXSettings) resetSettings();
|
||||
|
||||
let thicknesses = [1, 2, 3, 4, 5];
|
||||
let thicknesses = [1, 2, 3, 4, 5, 6];
|
||||
|
||||
const menu = {
|
||||
"" : { "title":"Neon X & IO"},
|
||||
|
@ -48,7 +50,23 @@
|
|||
neonXSettings.showDate = v;
|
||||
updateSettings();
|
||||
}
|
||||
}
|
||||
},
|
||||
'Fullscreen': {
|
||||
value: false | neonXSettings.fullscreen,
|
||||
format: () => (neonXSettings.fullscreen ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
neonXSettings.fullscreen = !neonXSettings.fullscreen;
|
||||
updateSettings();
|
||||
},
|
||||
},
|
||||
'Show lock': {
|
||||
value: false | neonXSettings.showLock,
|
||||
format: () => (neonXSettings.showLock ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
neonXSettings.showLock = !neonXSettings.showLock;
|
||||
updateSettings();
|
||||
},
|
||||
},
|
||||
};
|
||||
E.showMenu(menu);
|
||||
})
|
||||
|
|
|
@ -16,3 +16,4 @@
|
|||
0.14: incorporated lazybones idle timer, configuration settings to come
|
||||
0.15: fixed tendancy for mylocation to default to London
|
||||
added setting to enable/disable idle timer warning
|
||||
0.16: make check_idle boolean setting work properly with new B2 menu
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "pastel",
|
||||
"name": "Pastel Clock",
|
||||
"shortName": "Pastel",
|
||||
"version": "0.15",
|
||||
"version": "0.16",
|
||||
"description": "A Configurable clock with custom fonts, background and weather display. Has a cyclic information line that includes, day, date, battery, sunrise and sunset times",
|
||||
"icon": "pastel.png",
|
||||
"dependencies": {"mylocation":"app","weather":"app"},
|
||||
|
|
|
@ -38,38 +38,28 @@
|
|||
},
|
||||
},
|
||||
'Show Grid': {
|
||||
value: s.grid,
|
||||
format: () => (s.grid ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.grid = !s.grid;
|
||||
value: !!s.grid,
|
||||
format: v => v ? /*LANG*/"Yes":/*LANG*/"No",
|
||||
onchange: v => {
|
||||
s.grid = v;
|
||||
save();
|
||||
},
|
||||
},
|
||||
'Show Weather': {
|
||||
value: s.weather,
|
||||
format: () => (s.weather ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.weather = !s.weather;
|
||||
value: !!s.weather,
|
||||
format: v => v ? /*LANG*/"Yes":/*LANG*/"No",
|
||||
onchange: v => {
|
||||
s.weather = v;
|
||||
save();
|
||||
},
|
||||
},
|
||||
// for use when the new menu system goes live
|
||||
/*
|
||||
'Idle Warning': {
|
||||
value: s.idle_check,
|
||||
onchange : v => {
|
||||
value: !!s.idle_check,
|
||||
format: v => v ? /*LANG*/"Yes":/*LANG*/"No",
|
||||
onchange: v => {
|
||||
s.idle_check = v;
|
||||
save();
|
||||
},
|
||||
},
|
||||
*/
|
||||
'Idle Warning': {
|
||||
value: s.idle_check,
|
||||
format: () => (s.idle_check ? 'Yes' : 'No'),
|
||||
onchange: () => {
|
||||
s.idle_check = !s.idle_check;
|
||||
save();
|
||||
},
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4A/ABl3ABQVJg4WLC/QWMC/4X/U7NVqIXTgtVC4MXC6QWBAAQYLCxQAGqByJIoQAKC8IWMJBIuNGBIXvCxxgIC/4XH1QAO0AX/C/4X/C/4X/C6Q"))
|
|
@ -0,0 +1,56 @@
|
|||
function end(){
|
||||
clearInterval(m);
|
||||
clearWatch(w);
|
||||
gfx.clear();
|
||||
gfx.setColor(1,0,0);
|
||||
gfx.setFont("Vector30");
|
||||
gfx.drawString('Game over!\n Score: '+score+'\nPress BTN1', gfx.getWidth()*0.15,gfx.getHeight()*0.4);
|
||||
setWatch(function(){init();}, BTN1);
|
||||
}
|
||||
function scrollX(){
|
||||
gfx.clearRect(0,gfx.getHeight()*(1/4),gfx.getWidth(),0);
|
||||
gfx.scroll(0,gfx.getHeight()/4);
|
||||
score++;
|
||||
if(typeof(m) != undefined && score>0){
|
||||
clearInterval(m);
|
||||
m = setInterval(scrollY,Math.abs(100/score+15-0.1*score));}
|
||||
gfx.setColor(1,1,1);
|
||||
gfx.drawString(score,gfx.getWidth()*(4.2/5),gfx.getHeight()*(0.5/5));
|
||||
gfx.setColor(Math.random(),Math.random(),Math.random());
|
||||
gfx.setColor(col[0],col[1],col[2]);
|
||||
gfx.fillRect(colm[0],colm[1],colm[2],colm[3]);
|
||||
col = [Math.random(),Math.random(),Math.random()];
|
||||
gfx.setColor(col[0],col[1],col[2]);
|
||||
block[0] = gfx.getWidth();
|
||||
}
|
||||
function scrollY(){
|
||||
block[0] -= 2;
|
||||
block[2] = block[0]+colm[2]-colm[0];
|
||||
gfx.clearRect(block[2], block[1], gfx.getWidth(), block[3]);
|
||||
gfx.fillRect(block[0],block[1],block[2],block[3]);
|
||||
if(block[2]<colm[0])end();
|
||||
}
|
||||
function coldet(){
|
||||
if(block[0]<colm[2]){
|
||||
gfx.clearRect(block[0],block[1],block[2],block[3]);
|
||||
if(colm[2]>block[2] && colm[0]<block[2])colm[2]=block[2];
|
||||
if(colm[0]<block[0] && block[0]<colm[2])colm[0]=block[0];
|
||||
scrollX();
|
||||
}else{end();}
|
||||
}
|
||||
function init(){
|
||||
gfx = Graphics.getInstance();
|
||||
col = [Math.random(),Math.random(),Math.random()];
|
||||
gfx.clear();
|
||||
colm = [gfx.getWidth()*(1/5),gfx.getHeight()*(3/4),gfx.getWidth()*(4/5),gfx.getHeight()/2];
|
||||
block = [gfx.getWidth(),gfx.getHeight()/4,gfx.getWidth(),gfx.getHeight()/2];
|
||||
score = -3;
|
||||
gfx.setFont("Vector15");
|
||||
gfx.fillPoly(colm);
|
||||
scrollX();
|
||||
scrollX();
|
||||
scrollX();
|
||||
w = setWatch(coldet, BTN1, {repeat:true});
|
||||
m = setInterval(scrollY,110);
|
||||
}
|
||||
init();
|
After Width: | Height: | Size: 510 B |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "pie",
|
||||
"name": "In this game you need to make highest pie",
|
||||
"shortName":"Pie maker",
|
||||
"version":"0.01",
|
||||
"description": "In this game you will be making pie out of different pieces",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"tags": "game",
|
||||
"supports" : ["BANGLEJS"],
|
||||
"storage": [
|
||||
{"name":"pie.app.js","url":"app.js"},
|
||||
{"name":"pie.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -5,3 +5,4 @@
|
|||
0.04: Use the exstats module, and make what is displayed configurable
|
||||
0.05: exstats updated so update 'distance' label is updated, option for 'speed'
|
||||
0.06: Add option to record a run using the recorder app automatically
|
||||
0.07: Fix crash if an odd number of active boxes are configured (fix #1473)
|
||||
|
|
|
@ -62,14 +62,14 @@ for (var i=0;i<statIDs.length;i+=2) {
|
|||
var sa = exs.stats[statIDs[i+0]];
|
||||
var sb = exs.stats[statIDs[i+1]];
|
||||
lc.push({ type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontHeading, label:sa.title.toUpperCase(), fillx:1, col:headingCol },
|
||||
{type:"txt", font:fontHeading, label:sb.title.toUpperCase(), fillx:1, col:headingCol }
|
||||
sa?{type:"txt", font:fontHeading, label:sa.title.toUpperCase(), fillx:1, col:headingCol }:{},
|
||||
sb?{type:"txt", font:fontHeading, label:sb.title.toUpperCase(), fillx:1, col:headingCol }:{}
|
||||
]}, { type:"h", filly:1, c:[
|
||||
{type:"txt", font:fontValue, label:sa.getString(), id:sa.id, fillx:1 },
|
||||
{type:"txt", font:fontValue, label:sb.getString(), id:sb.id, fillx:1 }
|
||||
sa?{type:"txt", font:fontValue, label:sa.getString(), id:sa.id, fillx:1 }:{},
|
||||
sb?{type:"txt", font:fontValue, label:sb.getString(), id:sb.id, fillx:1 }:{}
|
||||
]});
|
||||
sa.on('changed', e=>layout[e.id].label = e.getString());
|
||||
sb.on('changed', e=>layout[e.id].label = e.getString());
|
||||
if (sa) sa.on('changed', e=>layout[e.id].label = e.getString());
|
||||
if (sb) sb.on('changed', e=>layout[e.id].label = e.getString());
|
||||
}
|
||||
// At the bottom put time/GPS state/etc
|
||||
lc.push({ type:"h", filly:1, c:[
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "run",
|
||||
"name": "Run",
|
||||
"version":"0.06",
|
||||
"version":"0.07",
|
||||
"description": "Displays distance, time, steps, cadence, pace and more for runners.",
|
||||
"icon": "app.png",
|
||||
"tags": "run,running,fitness,outdoors,gps",
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
0.01: first release
|
||||
0.02: Adjust for touch events outside of screen g dimensions
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "stopwatch",
|
||||
"name": "Stopwatch Touch",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "A touch based stop watch for Bangle JS 2",
|
||||
"icon": "stopwatch.png",
|
||||
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"}],
|
||||
|
|
|
@ -185,17 +185,27 @@ resetBtn.setImage(pause_img);
|
|||
|
||||
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
var x = xy.x;
|
||||
var y = xy.y;
|
||||
|
||||
// adjust for outside the dimension of the screen
|
||||
// http://forum.espruino.com/conversations/371867/#comment16406025
|
||||
if (y > h) y = h;
|
||||
if (y < 0) y = 0;
|
||||
if (x > w) x = w;
|
||||
if (x < 0) x = 0;
|
||||
|
||||
// not running, and reset
|
||||
if (!running && tCurrent == tTotal && bigPlayPauseBtn.check(xy.x, xy.y)) return;
|
||||
if (!running && tCurrent == tTotal && bigPlayPauseBtn.check(x, y)) return;
|
||||
|
||||
// paused and hit play
|
||||
if (!running && tCurrent != tTotal && smallPlayPauseBtn.check(xy.x, xy.y)) return;
|
||||
if (!running && tCurrent != tTotal && smallPlayPauseBtn.check(x, y)) return;
|
||||
|
||||
// paused and press reset
|
||||
if (!running && tCurrent != tTotal && resetBtn.check(xy.x, xy.y)) return;
|
||||
if (!running && tCurrent != tTotal && resetBtn.check(x, y)) return;
|
||||
|
||||
// must be running
|
||||
if (running && bigPlayPauseBtn.check(xy.x, xy.y)) return;
|
||||
if (running && bigPlayPauseBtn.check(x, y)) return;
|
||||
});
|
||||
|
||||
// Stop updates when LCD is off, restart when on
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,9 @@
|
|||
# Terminal clock
|
||||
|
||||
A clock displayed as a terminal cli.
|
||||
It can display :
|
||||
- time
|
||||
- date
|
||||
- hrm
|
||||
- activity
|
||||
- steps
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwghC/AHsHu93uAX/C+wACsAaTC/4Xiu1mswXRCYNnDQQxTgwX/C8QABuAaTg4X/C7Z3Xa/4Xds1ms4XUCgV2DYIXUsBdTC/4AEg4VCC61wIiYX/C74A/AGIA=="))
|
|
@ -0,0 +1,138 @@
|
|||
var locale = require("locale");
|
||||
var fontColor = g.theme.dark ? "#0f0" : "#000";
|
||||
var startY = 24;
|
||||
var paddingY = 2;
|
||||
var font6x8At4Size = 32;
|
||||
var font6x8At2Size = 18;
|
||||
var heartRate = 0;
|
||||
|
||||
|
||||
function setFontSize(pos){
|
||||
if(pos == 1)
|
||||
g.setFont("6x8", 4);
|
||||
else
|
||||
g.setFont("6x8", 2);
|
||||
}
|
||||
|
||||
function clearField(pos){
|
||||
var yStartPos = startY +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos-1) +
|
||||
font6x8At2Size * Math.max(0, pos-2);
|
||||
var yEndPos = startY +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos) +
|
||||
font6x8At2Size * Math.max(0, pos-1);
|
||||
g.clearRect(0, yStartPos, 240, yEndPos);
|
||||
}
|
||||
|
||||
function clearWatchIfNeeded(now){
|
||||
if(now.getMinutes() % 10 == 0)
|
||||
g.clearRect(0, startY, 240, 240);
|
||||
}
|
||||
|
||||
function drawLine(line, pos){
|
||||
setFontSize(pos);
|
||||
var yPos = startY +
|
||||
paddingY * (pos - 1) +
|
||||
font6x8At4Size * Math.min(1, pos-1) +
|
||||
font6x8At2Size * Math.max(0, pos-2);
|
||||
g.drawString(line, 5, yPos, true);
|
||||
}
|
||||
|
||||
function drawTime(now, pos){
|
||||
var h = now.getHours();
|
||||
var m = now.getMinutes();
|
||||
var time = ">" + (""+h).substr(-2) + ":" + ("0"+m).substr(-2);
|
||||
drawLine(time, pos);
|
||||
}
|
||||
|
||||
function drawDate(now, pos){
|
||||
var dow = locale.dow(now, 1);
|
||||
var date = locale.date(now, 1).substr(0,6) + locale.date(now, 1).substr(-2);
|
||||
var locale_date = ">" + dow + " " + date;
|
||||
drawLine(locale_date, pos);
|
||||
}
|
||||
|
||||
function drawInput(now, pos){
|
||||
clearField(pos);
|
||||
drawLine(">", pos);
|
||||
}
|
||||
|
||||
function drawStepCount(pos){
|
||||
var health = Bangle.getHealthStatus("day");
|
||||
var steps_formated = ">Steps: " + health.steps;
|
||||
drawLine(steps_formated, pos);
|
||||
}
|
||||
|
||||
function drawHRM(pos){
|
||||
clearField(pos);
|
||||
if(heartRate != 0)
|
||||
drawLine(">HR: " + parseInt(heartRate), pos);
|
||||
else
|
||||
drawLine(">HR: unknown", pos);
|
||||
}
|
||||
|
||||
function drawActivity(pos){
|
||||
clearField(pos);
|
||||
var health = Bangle.getHealthStatus('last');
|
||||
var steps_formated = ">Activity: " + parseInt(health.movement/10);
|
||||
drawLine(steps_formated, pos);
|
||||
}
|
||||
|
||||
|
||||
function draw(){
|
||||
var curPos = 1;
|
||||
g.reset();
|
||||
g.setFontAlign(-1, -1);
|
||||
g.setColor(fontColor);
|
||||
var now = new Date();
|
||||
clearWatchIfNeeded(now); // mostly to not have issues when changing days
|
||||
drawTime(now, curPos);
|
||||
curPos++;
|
||||
if(settings.showDate){
|
||||
drawDate(now, curPos);
|
||||
curPos++;
|
||||
}
|
||||
if(settings.showHRM){
|
||||
drawHRM(curPos);
|
||||
curPos++;
|
||||
}
|
||||
if(settings.showActivity){
|
||||
drawActivity(curPos);
|
||||
curPos++;
|
||||
}
|
||||
if(settings.showStepCount){
|
||||
drawStepCount(curPos);
|
||||
curPos++;
|
||||
}
|
||||
drawInput(now, curPos);
|
||||
}
|
||||
|
||||
|
||||
Bangle.on('HRM',function(hrmInfo) {
|
||||
if(hrmInfo.confidence >= settings.HRMinConfidence)
|
||||
heartRate = hrmInfo.bpm;
|
||||
});
|
||||
|
||||
|
||||
// Clear the screen once, at startup
|
||||
g.clear();
|
||||
// load the settings
|
||||
var settings = Object.assign({
|
||||
// default values
|
||||
HRMinConfidence: 50,
|
||||
showDate: true,
|
||||
showHRM: true,
|
||||
showActivity: true,
|
||||
showStepCount: true,
|
||||
}, require('Storage').readJSON("terminalclock.json", true) || {});
|
||||
// draw immediately at first
|
||||
draw();
|
||||
// Show launcher when middle button pressed
|
||||
Bangle.setUI("clock");
|
||||
// Load widgets
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
var secondInterval = setInterval(draw, 10000);
|
After Width: | Height: | Size: 1022 B |
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"id": "terminalclock",
|
||||
"name": "Terminal Clock",
|
||||
"shortName":"Terminal Clock",
|
||||
"description": "A terminal cli like clock displaying multiple sensor data",
|
||||
"version":"0.01",
|
||||
"icon": "app.png",
|
||||
"type": "clock",
|
||||
"tags": "clock",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name": "terminalclock.app.js","url": "app.js"},
|
||||
{"name": "terminalclock.settings.js","url": "settings.js"},
|
||||
{"name": "terminalclock.img","url": "app-icon.js","evaluate": true}
|
||||
],
|
||||
"data": [
|
||||
{"name": "terminalclock.json"}
|
||||
],
|
||||
"screenshots": [
|
||||
{"url": "screenshot1.png"},
|
||||
{"url": "screenshot2.png"}
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
|
@ -0,0 +1,61 @@
|
|||
(function(back) {
|
||||
var FILE = "terminalclock.json";
|
||||
// Load settings
|
||||
var settings = Object.assign({
|
||||
HRMinConfidence: 50,
|
||||
showDate: true,
|
||||
showHRM: true,
|
||||
showActivity: true,
|
||||
showStepCount: true,
|
||||
}, require('Storage').readJSON(FILE, true) || {});
|
||||
|
||||
function writeSettings() {
|
||||
require('Storage').writeJSON(FILE, settings);
|
||||
}
|
||||
|
||||
// Show the menu
|
||||
E.showMenu({
|
||||
"" : { "title" : "Terminal Clock" },
|
||||
"< Back" : () => back(),
|
||||
'HR confidence': {
|
||||
value: 50|settings.HRMinConfidence, // 0| converts undefined to 0
|
||||
min: 0, max: 100,
|
||||
onchange: v => {
|
||||
settings.HRMinConfidence = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show date': {
|
||||
value: !!settings.showDate,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => {
|
||||
settings.showDate = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show HRM': {
|
||||
value: !!settings.showHRM,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => {
|
||||
settings.showHRM = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show Activity': {
|
||||
value: !!settings.showActivity,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => {
|
||||
settings.showActivity = v;
|
||||
writeSettings();
|
||||
}
|
||||
},
|
||||
'Show Steps': {
|
||||
value: !!settings.showStepCount,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => {
|
||||
settings.showStepCount = v;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
|
@ -0,0 +1,10 @@
|
|||
0.01: Initial creation of the clock face time and calendar
|
||||
0.02: Feature Request #1154 and some findings...
|
||||
-> get rendered time from optimisations
|
||||
-> *BATT SAFE* only update once a minute instead of once a second
|
||||
-> *RAM optimized* clean code, corrected minute update (timout, no intervall)
|
||||
-> locale: weekday name (first two characters) from locale
|
||||
-> added settings to render cal view begin day (-1: today, 0:sunday, 1:monday [default])
|
||||
0.03: a lot of more settings for outline, colors and highlights
|
||||
0.04: finalized README, fixed settings cancel, fixed border-setting
|
||||
0.05: bugfix: default settings
|
|
@ -0,0 +1,22 @@
|
|||
# Calendar Clock
|
||||
|
||||
## Features
|
||||
Shows the
|
||||
* Date
|
||||
* Time (hh:mm) - respecting 12/24 (uses locale string)
|
||||
* 3 weeks calendar view (last,current and next week)
|
||||
|
||||
### The settings menu
|
||||
Calendar View can be customized
|
||||
* < Save: Exist and save the current settings
|
||||
* Show date: Choose if and how the date is displayed: none, locale (default), monthfull or monthshort.yearshort #weeknum with 0 prefixed
|
||||
* Start wday: Set day of week start. Values: 0=Sunday, 1=Monday,...,6=Saturday or -1=Relative to today (default 0: Sunday)
|
||||
* Su color: Set Sundays color. Values: none (default), red, green or blue
|
||||
* Border: show or none (default)
|
||||
* Submenu Today settings - choose how today is highlighted
|
||||
* < Back:
|
||||
* Color: none, red (default), green or blue
|
||||
* Marker: Outline today graphically. Values: none (default), circle, rect(angle)
|
||||
* Mrk.Color: Circle/rectangle color: red (default), green or blue
|
||||
* Mrk.Size: Circle/rectangle thickness in pixel: min:1, max: 10, default:3
|
||||
* < Cancel: Exit and no change. Nevertheless missing default settings and superflous settings will be removed and saved.
|
|
@ -1,13 +1,16 @@
|
|||
{ "id": "timecal",
|
||||
"name": "TimeCal",
|
||||
"shortName":"TimeCal",
|
||||
"version":"0.05",
|
||||
"description": "TimeCal shows the date/time along with a 3 week calendar",
|
||||
"icon": "icon.png",
|
||||
"version":"0.01",
|
||||
"description": "TimeCal shows the Time along with a 3 week calendar",
|
||||
"tags": "clock",
|
||||
"type": "clock",
|
||||
"tags": "clock,calendar",
|
||||
"supports":["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"allow_emulator":true,
|
||||
"storage": [
|
||||
{"name":"timecal.app.js","url":"timecal.app.js"}
|
||||
{"name":"timecal.app.js","url":"timecal.app.js"},
|
||||
{"name":"timecal.settings.js","url":"timecal.settings.js"}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,798 @@
|
|||
//Clock renders date, time and pre,current,next week calender view
|
||||
class TimeCalClock{
|
||||
DATE_FONT_SIZE(){ return 20; }
|
||||
TIME_FONT_SIZE(){ return 40; }
|
||||
|
||||
/**
|
||||
* @param{Date} date optional the date (e.g. for testing)
|
||||
* @param{Settings} settings optional settings to use e.g. for testing
|
||||
*/
|
||||
constructor(date, settings){
|
||||
if (date)
|
||||
this.date=date;
|
||||
|
||||
if (settings)
|
||||
this._settings = settings;
|
||||
else
|
||||
this._settings = require("Storage").readJSON("timecal.settings.json", 1) || {};
|
||||
|
||||
const defaults = {
|
||||
shwDate:1, //0:none, 1:locale, 2:month, 3:monthshort.year #week
|
||||
|
||||
wdStrt:0, //identical to getDay() 0->Su, 1->Mo, ... //Issue #1154: weekstart So/Mo, -1 for use today
|
||||
|
||||
tdyNumClr:3, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
tdyMrkr:0, //0:none, 1:circle, 2:rectangle, 3:filled
|
||||
tdyMrkClr:2, //1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
tdyMrkPxl:3, //px
|
||||
|
||||
suClr:1, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
//phColor:"#E00", //public holiday
|
||||
|
||||
calBrdr:false
|
||||
};
|
||||
for (const k in this._settings) if (!defaults.hasOwnProperty(k)) delete this._settings[k]; //remove invalid settings
|
||||
for (const k in defaults) if(!this._settings.hasOwnProperty(k)) this._settings[k] = defaults[k]; //assign missing defaults
|
||||
|
||||
g.clear();
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
this.centerX = Bangle.appRect.w/2;
|
||||
this.nrgb = [g.theme.fg, "#E00", "#0E0", "#00E"]; //fg, r ,g , b
|
||||
|
||||
this.ABR_DAY=[];
|
||||
if (require("locale") && require("locale").dow)
|
||||
for (let d=0; d<=6; d++) {
|
||||
var refDay=new Date();
|
||||
refDay.setFullYear(1972);
|
||||
refDay.setMonth(0);
|
||||
refDay.setDate(2+d);
|
||||
this.ABR_DAY.push(require("locale").dow(refDay));
|
||||
|
||||
}
|
||||
else
|
||||
this.ABR_DAY=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Object} current settings object
|
||||
*/
|
||||
settings(){
|
||||
return this._settings;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run forest run
|
||||
**/
|
||||
draw(){
|
||||
this.drawTime();
|
||||
|
||||
if (this.TZOffset===undefined || this.TZOffset!==d.getTimezoneOffset())
|
||||
this.drawDateAndCal();
|
||||
}
|
||||
|
||||
/**
|
||||
* draw given or current time from date
|
||||
* overwatch timezone changes
|
||||
* schedules itself to update
|
||||
*/
|
||||
drawTime(){
|
||||
d=this.date ? this.date : new Date();
|
||||
const Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10;
|
||||
|
||||
d=d?d :new Date();
|
||||
|
||||
g.setFontAlign(0, -1).setFont("Vector", this.TIME_FONT_SIZE()).setColor(g.theme.fg)
|
||||
.clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7)
|
||||
.drawString(("0" + require("locale").time(d, 1)).slice(-5), this.centerX, Y, true);
|
||||
//.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7); //DEV-Option
|
||||
|
||||
setTimeout(this.draw.bind(this), 60000-(d.getSeconds()*1000)-d.getMilliseconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* draws given date and cal
|
||||
* @param{Date} d provide date or uses today
|
||||
*/
|
||||
drawDateAndCal(){
|
||||
d=this.date ? this.date : new Date();
|
||||
|
||||
this.TZOffset=d.getTimezoneOffset();
|
||||
this.drawDate();
|
||||
this.drawCal();
|
||||
|
||||
if (this.tOutD) //abort exisiting
|
||||
clearTimeout(this.tOutD);
|
||||
this.tOutD=setTimeout(this.drawDateAndCal.bind(this), 86400000-(d.getHours()*24*60*1000)-(d.getMinutes()*60*1000)-d.getSeconds()-d.getMilliseconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* draws given date as defiend in settings
|
||||
*/
|
||||
drawDate(){
|
||||
d=this.date ? this.date : new Date();
|
||||
|
||||
const FONT_SIZE=20;
|
||||
const Y=Bangle.appRect.y;
|
||||
var render=false;
|
||||
var dateStr = "";
|
||||
if (this.settings().shwDate>0) { //skip if exactly -none
|
||||
const dateSttngs = ["","l","M","m.Y #W"];
|
||||
for (let c of dateSttngs[this.settings().shwDate]) { //add part as configured
|
||||
switch (c){
|
||||
case "l":{ //locale
|
||||
render=true;
|
||||
dateStr+=require("locale").date(d,1);
|
||||
break;
|
||||
}
|
||||
case "m":{ //month e.g. Jan.
|
||||
render=true;
|
||||
dateStr+=require("locale").month(d,1);
|
||||
break;
|
||||
}
|
||||
case "M":{ //month e.g. January
|
||||
render=true;
|
||||
dateStr+=require("locale").month(d,0);
|
||||
break;
|
||||
}
|
||||
case "y":{ //year e.g. 22
|
||||
render=true;
|
||||
dateStr+=d.getFullYear().slice(-2);
|
||||
break;
|
||||
}
|
||||
case "Y":{ //year e.g. 2022
|
||||
render=true;
|
||||
dateStr+=d.getFullYear();
|
||||
break;
|
||||
}
|
||||
case "w":{ //week e.g. #2
|
||||
dateStr+=(this.ISO8601calWeek(d));
|
||||
break;
|
||||
}
|
||||
case "W":{ //week e.g. #02
|
||||
dateStr+=("0"+this.ISO8601calWeek(d)).slice(-2);
|
||||
break;
|
||||
}
|
||||
default: //append c
|
||||
dateStr+=c;
|
||||
render=dateStr.length>0;
|
||||
break; //noop
|
||||
}
|
||||
}
|
||||
}
|
||||
if (render){
|
||||
g.setFont("Vector", FONT_SIZE).setColor(g.theme.fg).setFontAlign(0, -1).clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3).drawString(dateStr,this.centerX,Y);
|
||||
}
|
||||
//g.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3); //DEV-Option
|
||||
}
|
||||
|
||||
/**
|
||||
* draws calender week view (-1,0,1) for given date
|
||||
*/
|
||||
drawCal(){
|
||||
d=this.date ? this.date : new Date();
|
||||
|
||||
const DAY_NAME_FONT_SIZE=10;
|
||||
const CAL_Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3;
|
||||
const CAL_AREA_H=Bangle.appRect.h-CAL_Y+24; //+24: top widegtes only
|
||||
const CELL_W=Bangle.appRect.w/7; //cell width
|
||||
const CELL_H=(CAL_AREA_H-DAY_NAME_FONT_SIZE)/3; //cell heigth
|
||||
const DAY_NUM_FONT_SIZE=Math.min(CELL_H-1,15); //size down, max 15
|
||||
|
||||
g.setFont("Vector", DAY_NAME_FONT_SIZE).setColor(g.theme.fg).setFontAlign(-1, -1).clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H);
|
||||
|
||||
//draw grid & Headline
|
||||
const dNames = this.ABR_DAY.map((a) => a.length<=2 ? a : a.substr(0, 2)); //force shrt 2
|
||||
for(var dNo=0; dNo<dNames.length; dNo++){
|
||||
const dIdx=this.settings().wdStrt>=0 ? (dNo+this.settings().wdStrt)%7 : (dNo+d.getDay()+4)%7;
|
||||
const dName=dNames[dIdx];
|
||||
if(dNo>0)
|
||||
g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1);
|
||||
|
||||
if (dIdx==0) g.setColor(this.nrgb[this.settings().suClr]); //sunday maybe colorize txt
|
||||
g.drawString(dName, dNo*CELL_W+(CELL_W-g.stringWidth(dName))/2+2, CAL_Y+1).setColor(g.theme.fg);
|
||||
}
|
||||
|
||||
var nextY=CAL_Y+DAY_NAME_FONT_SIZE;
|
||||
|
||||
for(i=0; i<3; i++){
|
||||
const y=nextY+i*CELL_H;
|
||||
g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y);
|
||||
}
|
||||
|
||||
g.setFont("Vector", DAY_NUM_FONT_SIZE);
|
||||
|
||||
//write days
|
||||
const tdyDate=d.getDate();
|
||||
const days=this.settings().wdStrt>=0 ? 7+((7+d.getDay()-this.settings().wdStrt)%7) : 10;//start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days
|
||||
var rD=new Date(d.getTime());
|
||||
rD.setDate(rD.getDate()-days);
|
||||
var rDate=rD.getDate();
|
||||
for(var y=0; y<3; y++){
|
||||
for(var x=0; x<dNames.length; x++){
|
||||
if(rDate===tdyDate){ //today
|
||||
g.setColor(this.nrgb[this.settings().tdyMrkClr]); //today marker color or fg color
|
||||
switch(this.settings().tdyMrkr){ //0:none, 1:circle, 2:rectangle, 3:filled
|
||||
case 1:
|
||||
for(m=1; m<=this.settings().tdyMrkPxl&&m<CELL_H-1&&m<CELL_W-1; m++)
|
||||
g.drawCircle(x*CELL_W+(CELL_W/2)+1, nextY+(CELL_H*y)+(CELL_H/2)+1, Math.min((CELL_W-m)/2, (CELL_H-m)/2)-2);
|
||||
break;
|
||||
case 2:
|
||||
for(m=1; m<=this.settings().tdyMrkPxl&&m<CELL_H-1&&m<CELL_W-1; m++)
|
||||
g.drawRect(x*CELL_W+m, nextY+CELL_H+m, x*CELL_W+CELL_W-m, nextY+CELL_H+CELL_H-m);
|
||||
break;
|
||||
case 3:
|
||||
g.fillRect(x*CELL_W+1, nextY+CELL_H+1, x*CELL_W+CELL_W-1, nextY+CELL_H+CELL_H-1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g.setColor(this.nrgb[this.settings().tdyNumClr]); //today color or fg color
|
||||
}else if(this.settings().suClr && rD.getDay()==0){ //sundays
|
||||
g.setColor(this.nrgb[this.settings().suClr]);
|
||||
}else{ //default
|
||||
g.setColor(g.theme.fg);
|
||||
}
|
||||
g.drawString(rDate, x*CELL_W+((CELL_W-g.stringWidth(rDate))/2)+2, nextY+((CELL_H-DAY_NUM_FONT_SIZE+2)/2)+(CELL_H*y));
|
||||
rD.setDate(rDate+1);
|
||||
rDate=rD.getDate();
|
||||
}
|
||||
}
|
||||
if (this.settings().calBrdr) {
|
||||
g.setColor(g.theme.fg).drawRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H-1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates current ISO8601 week number e.g. 2
|
||||
* @param{Date} date for the date
|
||||
* @returns{Number}} e.g. 2
|
||||
*/
|
||||
ISO8601calWeek(date){ //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
|
||||
var tdt = new Date(date.valueOf());
|
||||
var dayn = (date.getDay() + 6) % 7;
|
||||
tdt.setDate(tdt.getDate() - dayn + 3);
|
||||
var firstThursday = tdt.valueOf();
|
||||
tdt.setMonth(0, 1);
|
||||
if (tdt.getDay() !== 4){
|
||||
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
|
||||
}
|
||||
return Number(1 + Math.ceil((firstThursday - tdt) / 604800000));
|
||||
}
|
||||
}
|
||||
|
||||
//*************************************************************************************
|
||||
//*************************************************************************************
|
||||
//*************************************************************************************
|
||||
//Copy ABOVE the src code of clock-app class and load via espruino WEB IDE
|
||||
//*************************************************************************************
|
||||
//*************************************************************************************
|
||||
//*************************************************************************************
|
||||
|
||||
/**
|
||||
* Severity for logging
|
||||
*/
|
||||
const LogSeverity={
|
||||
DEBUG: 5,
|
||||
INFO: 4,
|
||||
WARNING: 3,
|
||||
ERROR: 2,
|
||||
EXCEPTION: 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Exception: Mandatory Field not provided
|
||||
*/
|
||||
class EmptyMandatoryError extends Error{
|
||||
/**
|
||||
* Create Exception
|
||||
* @param {String} name of the field
|
||||
* @param {*} given data e.g. an object
|
||||
* @param {*} expected *optional* an working example
|
||||
*/
|
||||
constructor(name, given, expected) {
|
||||
this.field = name;
|
||||
this.got = given;
|
||||
this.message = "Missing mandatory '"+ name +"'. given '"+JSON.stringify(given)+"'";
|
||||
if (expected) {
|
||||
this.message+= " != expected: '"+JSON.stringify(expected)+"'";
|
||||
this.sample = expected;
|
||||
}
|
||||
Error(this.message);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception: Invalid Function
|
||||
*/
|
||||
class InvalidMethodName extends Error{
|
||||
/**
|
||||
* Create Exception
|
||||
* @param {String} name of the field
|
||||
* @param {*} given data e.g. an object
|
||||
* @param {*} expected *optional* an working example
|
||||
*/
|
||||
constructor(className, methodName) {
|
||||
this.class = className;
|
||||
this.method = methodName;
|
||||
this.message = "Function '"+methodName+"' not found in '"+className+"'";
|
||||
Error(this.message);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* All Test Masterclass
|
||||
*/
|
||||
class Test{
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* Test Settings - use this if you want e.g. test draw/render function(s)
|
||||
*/
|
||||
class TestSetting extends Test{
|
||||
TEST_SETTING_SAMPLE() {
|
||||
return {
|
||||
setting: "<settingName>",
|
||||
cases: [
|
||||
{
|
||||
value: "required,<settingValue>",
|
||||
beforeTxt: "optional,<textToDisplayBeforeTest>",
|
||||
beforeExpression: "optional,<expressionExpectedTrue>",
|
||||
afterText: "optional,<textToDisplayAfterTest>",
|
||||
afterExpression: "optional,<expressionExpectedTrue>"
|
||||
}
|
||||
],
|
||||
constructorParams: ["optional: <cpar1>","|TEST_SETTINGS|","..."], //TEST_SETTINGS will be replcaed with each current {setting: case}
|
||||
functionNames: ["required, <function under test>", "..."],
|
||||
functionParams: ["optional: <fpar1>","|TEST_SETTINGS|","..."]
|
||||
};
|
||||
}
|
||||
|
||||
constructor(data){
|
||||
|
||||
this._validate(data);
|
||||
|
||||
this.setting = data.setting;
|
||||
this.cases = data.cases.map((entry) => {
|
||||
return {
|
||||
value: entry.value,
|
||||
beforeTxt: entry.beforeTxt||"",
|
||||
beforeExpression: entry.beforeExpression||true,
|
||||
afterTxt: entry.afterTxt||"",
|
||||
afterExpression: entry.afterExpression||true
|
||||
};
|
||||
});
|
||||
this.constructorParams = data.constructorParams;
|
||||
this.functionNames = data.functionNames;
|
||||
this.functionParams = data.functionParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* validates the given data config
|
||||
*/
|
||||
_validate(data){
|
||||
//validate given config
|
||||
if (!data.setting) throw new EmptyMandatoryError("setting", data, this.TEST_SETTING_SAMPLE());
|
||||
if (!(data.cases instanceof Array) || data.cases.length==0) throw new EmptyMandatoryError("cases", data, this.TEST_SETTING_SAMPLE());
|
||||
if (!(data.functionNames instanceof Array) || data.functionNames==0) throw new EmptyMandatoryError("functionNames", data, this.TEST_SETTING_SAMPLE());
|
||||
|
||||
data.cases.forEach((entry,idx) => {
|
||||
if (entry.value === undefined) throw new EmptyMandatoryError("cases["+idx+"].value", entry, this.TEST_SETTING_SAMPLE());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* Testing a Bangle object
|
||||
*/
|
||||
class BangleTestRunner{
|
||||
/**
|
||||
* create for ObjClass
|
||||
* @param {Class} objClass
|
||||
* @param {LogSeverity} minSeverity to Log
|
||||
*/
|
||||
constructor(objClass, minSeverity){
|
||||
this.TESTCASE_MSG_BEFORE_TIMEOUT = 1000; //5s
|
||||
this.TESTCASE_RUN_TIMEOUT = 1000; //5s
|
||||
this.TESTCASE_MSG_AFTER_TIMEOUT = 1000; //5s
|
||||
|
||||
this.oClass = objClass;
|
||||
this.minSvrty = minSeverity;
|
||||
this.tests = [];
|
||||
|
||||
this.currentCaseNum = this.currentTestNum = this.currentTest = this.currentCase = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a Setting Test, return instance for chaining
|
||||
* @param {TestSetting}
|
||||
*/
|
||||
addTestSettings(sttngs) {
|
||||
this.tests.push(new TestSetting(sttngs));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execution of all tests
|
||||
*/
|
||||
execute() {
|
||||
this._init();
|
||||
while (this._nextTest()) {
|
||||
this._beforeTest();
|
||||
while (this._nextCase()) {
|
||||
this._beforeCase();
|
||||
this._runCase();
|
||||
this._afterCase();
|
||||
}
|
||||
this._afterTest();
|
||||
this._firstCase();
|
||||
}
|
||||
this._exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* global prepare - before all test
|
||||
*/
|
||||
_init() {
|
||||
console.log(this._nowTime(), ">>init");
|
||||
this.currentTestNum=-1;
|
||||
this.currentCaseNum=-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* before each test
|
||||
*/
|
||||
_beforeTest() {
|
||||
console.log(this._nowTime(), ">>test #" + this.currentTestNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* befor each testcase
|
||||
*/
|
||||
_beforeCase() {
|
||||
console.log(this.currentTest);
|
||||
console.log(this._nowTime(), ">>case #" + this.currentTestNum + "." + this.currentCaseNum + "/" + (this.currentTest.cases.length-1));
|
||||
if (this.currentTest instanceof TestSetting)
|
||||
console.log(this.currentTest.setting+"="+this.currentCase.value+"/n"+(this.currentCase.beforeTxt ? "#"+this.currentCase.beforeTxt : ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* testcase runner
|
||||
*/
|
||||
_runCase() {
|
||||
console.log(this._nowTime(), ">>running...");
|
||||
var returns = [];
|
||||
this.currentTest.functionNames.forEach((fName) => {
|
||||
var settings={}; settings[this.currentTest.setting] = this.currentCase.value;
|
||||
var cParams = this.currentTest.constructorParams||[];
|
||||
cParams = cParams.map((v) => (v && v instanceof String && v==="|TEST_SETTINGS|") ? settings : v);//replace settings in call params
|
||||
var fParams = this.currentTest.functionParams||[];
|
||||
fParams = fParams.map((v) => (v && v instanceof String && v==="|TEST_SETTINGS|") ? settings : v);//replace settings in call params
|
||||
|
||||
var creatorFunc = new Function("console.log('Constructor params:', arguments); return new " + this.oClass + "(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9])"); //prepare spwan arguments[0],arguments[1]
|
||||
let instance = creatorFunc.call(this.oClass, cParams[0], cParams[1], cParams[2], cParams[3], cParams[4], cParams[5], cParams[6], cParams[7], cParams[8], cParams[9]); //spwan
|
||||
|
||||
console.log(">>"+this.oClass+"["+fName+"]()");
|
||||
|
||||
console.log('Instance:', instance);
|
||||
console.log('Function params:', fParams);
|
||||
returns.push(instance[fName](fParams[0], fParams[1], fParams[2], fParams[3], fParams[4], fParams[5], fParams[6], fParams[7], fParams[8], fParams[9])); //run method and store result
|
||||
g.dump();
|
||||
console.log("<<"+this.oClass+"["+fName+"]()");
|
||||
});
|
||||
console.log(this._nowTime(), "<<...running");
|
||||
}
|
||||
|
||||
/**
|
||||
* after each testcase
|
||||
*/
|
||||
_afterCase() {
|
||||
if (this.currentTest instanceof TestSetting)
|
||||
if (this.currentCase.afterTxt.length>0)
|
||||
console.log("++EXPECTED:" + this.currentCase.afterTxt + "EXPECTED++");
|
||||
console.log(this._nowTime(), "<<case #" + this.currentTestNum + "." + this.currentCaseNum + "/" + (this.currentTest.cases.length-1));
|
||||
}
|
||||
|
||||
/**
|
||||
* after each test
|
||||
*/
|
||||
_afterTest() {
|
||||
console.log(this._nowTime(), "<<test #" + this.currentTestNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* after all tests
|
||||
*/
|
||||
_exit() {
|
||||
console.log(this._nowTime(), "<<exit");
|
||||
}
|
||||
|
||||
/**
|
||||
* delays for x seconds
|
||||
* @param {Number} sec to delay
|
||||
*/
|
||||
_delay(sec) {
|
||||
return new Promise(resolve => setTimeout(resolve, sec));
|
||||
}
|
||||
|
||||
_waits(sec) {
|
||||
this._delay(1).then();
|
||||
}
|
||||
|
||||
_log() {
|
||||
|
||||
}
|
||||
|
||||
_nextTest() {
|
||||
if (this.currentTestNum>=-1 && (this.currentTestNum+1)<this.tests.length) {
|
||||
this.currentTestNum++; this.currentTest = this.tests[this.currentTestNum];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_firstCase() {
|
||||
this.currentCaseNum=-1;
|
||||
}
|
||||
|
||||
_nextCase() {
|
||||
if (this.currentCaseNum>=-1 && (this.currentCaseNum+1)<this.currentTest.cases.length) {
|
||||
this.currentCaseNum++; this.currentCase = this.currentTest.cases[this.currentCaseNum];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_nowTime() {
|
||||
d = new Date();
|
||||
return(("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2) + ":" + ("0" + d.getSeconds()).slice(-2) + "." + ("00" + d.getMilliseconds()).slice(-3));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* TEST all Settings
|
||||
*/
|
||||
new BangleTestRunner("TimeCalClock", LogSeverity.INFO)
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "shwDate",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"No date display?", afterTxt: "top area should be 'emtpy'" },
|
||||
{ value: 1, beforeTxt:"Locale date display?", afterTxt: "date should be 06/05/1234" },
|
||||
{ value: 2, beforeTxt:"Month longname?", afterTxt: "date should be June" },
|
||||
{ value: 3, beforeTxt:"Monthshort yearshort #week", afterTxt: "date should be Jun.34 #23" }
|
||||
],
|
||||
constructorParams: [new Date(1234,5,6,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawDate"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"Week start Sunday?" , afterTxt: "Calendar first day is Sunday" },
|
||||
{ value: 1, beforeTxt:"Week start Monday?" , afterTxt: "Calendar first day is Monday"},
|
||||
{ value: 2, beforeTxt:"Week start Tuesday?" , afterTxt: "Calendar first day is Tuesday"},
|
||||
{ value: 3, beforeTxt:"Week start Wednesday?" , afterTxt: "Calendar first day is Wednesday"},
|
||||
{ value: 4, beforeTxt:"Week start Thursday?" , afterTxt: "Calendar first day is Thursday"},
|
||||
{ value: 5, beforeTxt:"Week start Friday?" , afterTxt: "Calendar first day is Friday"},
|
||||
{ value: 6, beforeTxt:"Week start Saturday?" , afterTxt: "Calendar first day is Saturday"},
|
||||
],
|
||||
constructorParams: [new Date(1234,5,3,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"Week start Sunday?" , afterTxt: "Calendar first day is Sunday" },
|
||||
{ value: 1, beforeTxt:"Week start Monday?" , afterTxt: "Calendar first day is Monday"},
|
||||
{ value: 2, beforeTxt:"Week start Tuesday?" , afterTxt: "Calendar first day is Tuesday"},
|
||||
{ value: 3, beforeTxt:"Week start Wednesday?" , afterTxt: "Calendar first day is Wednesday"},
|
||||
{ value: 4, beforeTxt:"Week start Thursday?" , afterTxt: "Calendar first day is Thursday"},
|
||||
{ value: 5, beforeTxt:"Week start Friday?" , afterTxt: "Calendar first day is Friday"},
|
||||
{ value: 6, beforeTxt:"Week start Saturday?" , afterTxt: "Calendar first day is Saturday"},
|
||||
],
|
||||
constructorParams: [new Date(1234,5,4,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"Week start Sunday?" , afterTxt: "Calendar first day is Sunday" },
|
||||
{ value: 1, beforeTxt:"Week start Monday?" , afterTxt: "Calendar first day is Monday"},
|
||||
{ value: 2, beforeTxt:"Week start Tuesday?" , afterTxt: "Calendar first day is Tuesday"},
|
||||
{ value: 3, beforeTxt:"Week start Wednesday?" , afterTxt: "Calendar first day is Wednesday"},
|
||||
{ value: 4, beforeTxt:"Week start Thursday?" , afterTxt: "Calendar first day is Thursday"},
|
||||
{ value: 5, beforeTxt:"Week start Friday?" , afterTxt: "Calendar first day is Friday"},
|
||||
{ value: 6, beforeTxt:"Week start Saturday?" , afterTxt: "Calendar first day is Saturday"},
|
||||
],
|
||||
constructorParams: [new Date(1234,5,5,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"Week start Sunday?" , afterTxt: "Calendar first day is Sunday" },
|
||||
{ value: 1, beforeTxt:"Week start Monday?" , afterTxt: "Calendar first day is Monday"},
|
||||
{ value: 2, beforeTxt:"Week start Tuesday?" , afterTxt: "Calendar first day is Tuesday"},
|
||||
{ value: 3, beforeTxt:"Week start Wednesday?" , afterTxt: "Calendar first day is Wednesday"},
|
||||
{ value: 4, beforeTxt:"Week start Thursday?" , afterTxt: "Calendar first day is Thursday"},
|
||||
{ value: 5, beforeTxt:"Week start Friday?" , afterTxt: "Calendar first day is Friday"},
|
||||
{ value: 6, beforeTxt:"Week start Saturday?" , afterTxt: "Calendar first day is Saturday"},
|
||||
],
|
||||
constructorParams: [new Date(1234,5,6,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"Week start Sunday?" , afterTxt: "Calendar first day is Sunday" },
|
||||
{ value: 1, beforeTxt:"Week start Monday?" , afterTxt: "Calendar first day is Monday"},
|
||||
{ value: 2, beforeTxt:"Week start Tuesday?" , afterTxt: "Calendar first day is Tuesday"},
|
||||
{ value: 3, beforeTxt:"Week start Wednesday?" , afterTxt: "Calendar first day is Wednesday"},
|
||||
{ value: 4, beforeTxt:"Week start Thursday?" , afterTxt: "Calendar first day is Thursday"},
|
||||
{ value: 5, beforeTxt:"Week start Friday?" , afterTxt: "Calendar first day is Friday"},
|
||||
{ value: 6, beforeTxt:"Week start Saturday?" , afterTxt: "Calendar first day is Saturday"},
|
||||
],
|
||||
constructorParams: [new Date(1234,5,7,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: 0, beforeTxt:"Week start Sunday?" , afterTxt: "Calendar first day is Sunday" },
|
||||
{ value: 1, beforeTxt:"Week start Monday?" , afterTxt: "Calendar first day is Monday"},
|
||||
{ value: 2, beforeTxt:"Week start Tuesday?" , afterTxt: "Calendar first day is Tuesday"},
|
||||
{ value: 3, beforeTxt:"Week start Wednesday?" , afterTxt: "Calendar first day is Wednesday"},
|
||||
{ value: 4, beforeTxt:"Week start Thursday?" , afterTxt: "Calendar first day is Thursday"},
|
||||
{ value: 5, beforeTxt:"Week start Friday?" , afterTxt: "Calendar first day is Friday"},
|
||||
{ value: 6, beforeTxt:"Week start Saturday?" , afterTxt: "Calendar first day is Saturday"},
|
||||
],
|
||||
constructorParams: [new Date(1234,5,8,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Sunday in mid?" , afterTxt: "Calendar focus today: Sunday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,3,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Monday in mid?" , afterTxt: "Calendar focus today: Monday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,4,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Tuesday in mid?" , afterTxt: "Calendar focus today: Tuesday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,5,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Wednesday in mid?" , afterTxt: "Calendar focus today: Wednesday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,6,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Thursday in mid?" , afterTxt: "Calendar focus today: Thursday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,7,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Friday in mid?" , afterTxt: "Calendar focus today: Friday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,8,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
.addTestSettings({
|
||||
setting: "wdStrt",
|
||||
cases: [
|
||||
{ value: -1, beforeTxt:"Saturday in mid?" , afterTxt: "Calendar focus today: Saturday" },
|
||||
],
|
||||
constructorParams: [new Date(1234,5,9,7,8,9),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "tdyNumClr",
|
||||
cases: [
|
||||
{ value: 1, beforeTxt:"Today color: red?" , afterTxt: "Today is marked red" },
|
||||
{ value: 2, beforeTxt:"Today color: green?" , afterTxt: "Today is marked green" },
|
||||
{ value: 3, beforeTxt:"Today color: blue?" , afterTxt: "Today is marked blue" },
|
||||
],
|
||||
constructorParams: [new Date(),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "tdyMrkr",
|
||||
cases: [
|
||||
{ value: 1, beforeTxt:"Today highlight cricle?" , afterTxt: "Today circled." },
|
||||
{ value: 2, beforeTxt:"Today highlight rectangle?" , afterTxt: "Today rectangled." },
|
||||
{ value: 3, beforeTxt:"Today highlight filled?" , afterTxt: "Today filled." },
|
||||
],
|
||||
constructorParams: [new Date(),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "suClr",
|
||||
cases: [
|
||||
{ value: 1, beforeTxt:"Sundays color: red?" , afterTxt: "Sundays are red" },
|
||||
{ value: 2, beforeTxt:"Sundays color: green?" , afterTxt: "Sundays are green" },
|
||||
{ value: 3, beforeTxt:"Sundays color: blue?" , afterTxt: "Sundays are blue" },
|
||||
],
|
||||
constructorParams: [new Date(),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
|
||||
/*
|
||||
.addTestSettings({
|
||||
setting: "calBrdr",
|
||||
cases: [
|
||||
{ value: false, beforeTxt:"Calendar without border?" , afterTxt: "No outer border." },
|
||||
{ value: true, beforeTxt:"Calendar with border?" , afterTxt: "Outer border." },
|
||||
],
|
||||
constructorParams: [new Date(),"|TEST_SETTINGS|"],
|
||||
functionNames: ["drawCal"],
|
||||
functionParams: [],
|
||||
})
|
||||
*/
|
||||
.execute();
|
|
@ -1,94 +1,274 @@
|
|||
var center = g.getWidth() / 2;
|
||||
var lastDayDraw;
|
||||
var lastTimeDraw;
|
||||
//Clock renders date, time and pre,current,next week calender view
|
||||
class TimeCalClock{
|
||||
DATE_FONT_SIZE(){ return 20; }
|
||||
TIME_FONT_SIZE(){ return 40; }
|
||||
|
||||
/**
|
||||
* @param{Date} date optional the date (e.g. for testing)
|
||||
* @param{Settings} settings optional settings to use e.g. for testing
|
||||
*/
|
||||
constructor(date, settings){
|
||||
if (date)
|
||||
this.date=date;
|
||||
|
||||
var fontColor = g.theme.fg;
|
||||
var accentColor = "#FF0000";
|
||||
var locale = require("locale");
|
||||
if (settings)
|
||||
this._settings = settings;
|
||||
else
|
||||
this._settings = require("Storage").readJSON("timecal.settings.json", 1) || {};
|
||||
|
||||
function loop() {
|
||||
var d = new Date();
|
||||
var cleared = false;
|
||||
if(lastDayDraw != d.getDate()){
|
||||
lastDayDraw = d.getDate();
|
||||
drawDate(d);
|
||||
drawCal(d);
|
||||
}
|
||||
|
||||
if(lastTimeDraw != d.getMinutes() || cleared){
|
||||
lastTimeDraw = d.getMinutes();
|
||||
drawTime(d);
|
||||
}
|
||||
}
|
||||
function drawTime(d){
|
||||
var hour = ("0" + d.getHours()).slice(-2);
|
||||
var min = ("0" + d.getMinutes()).slice(-2);
|
||||
g.setFontAlign(0,-1,0);
|
||||
g.setFont("Vector",40);
|
||||
g.setColor(fontColor);
|
||||
g.clearRect(0,50,g.getWidth(),90);
|
||||
g.drawString(hour + ":" + min,center,50);
|
||||
}
|
||||
function drawDate(d){
|
||||
var day = ("0" + d.getDate()).slice(-2);
|
||||
var month = ("0" + d.getMonth()).slice(-2);
|
||||
var dateStr = locale.date(d,1);
|
||||
g.clearRect(0,24,g.getWidth(),44);
|
||||
g.setFont("Vector",20);
|
||||
g.setColor(fontColor);
|
||||
g.setFontAlign(0,-1,0);
|
||||
g.drawString(dateStr,center,24);
|
||||
}
|
||||
const defaults = {
|
||||
shwDate:1, //0:none, 1:locale, 2:month, 3:monthshort.year #week
|
||||
|
||||
wdStrt:0, //identical to getDay() 0->Su, 1->Mo, ... //Issue #1154: weekstart So/Mo, -1 for use today
|
||||
|
||||
tdyNumClr:3, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
tdyMrkr:0, //0:none, 1:circle, 2:rectangle, 3:filled
|
||||
tdyMrkClr:2, //1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
tdyMrkPxl:3, //px
|
||||
|
||||
suClr:1, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
//phColor:"#E00", //public holiday
|
||||
|
||||
calBrdr:false
|
||||
};
|
||||
for (const k in this._settings) if (!defaults.hasOwnProperty(k)) delete this._settings[k]; //remove invalid settings
|
||||
for (const k in defaults) if(!this._settings.hasOwnProperty(k)) this._settings[k] = defaults[k]; //assign missing defaults
|
||||
|
||||
g.clear();
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
this.centerX = Bangle.appRect.w/2;
|
||||
this.nrgb = [g.theme.fg, "#E00", "#0E0", "#00E"]; //fg, r ,g , b
|
||||
|
||||
this.ABR_DAY=[];
|
||||
if (require("locale") && require("locale").dow)
|
||||
for (let d=0; d<=6; d++) {
|
||||
var refDay=new Date();
|
||||
refDay.setFullYear(1972);
|
||||
refDay.setMonth(0);
|
||||
refDay.setDate(2+d);
|
||||
this.ABR_DAY.push(require("locale").dow(refDay));
|
||||
|
||||
function drawCal(d){
|
||||
var calStart = 101;
|
||||
var cellSize = g.getWidth() / 7;
|
||||
var halfSize = cellSize / 2;
|
||||
g.clearRect(0,calStart,g.getWidth(),g.getHeight());
|
||||
g.drawLine(0,calStart,g.getWidth(),calStart);
|
||||
var days = ["Mo","Tu","We","Th","Fr","Sa","Su"];
|
||||
g.setFont("Vector",10);
|
||||
g.setColor(fontColor);
|
||||
g.setFontAlign(-1,-1,0);
|
||||
for(var i = 0; i < days.length;i++){
|
||||
g.drawString(days[i],i*cellSize+5,calStart -11);
|
||||
if(i!=0){
|
||||
g.drawLine(i*cellSize,calStart,i*cellSize,g.getHeight());
|
||||
}
|
||||
}
|
||||
var cellHeight = (g.getHeight() -calStart ) / 3;
|
||||
for(var i = 0;i < 3;i++){
|
||||
var starty = calStart + i * cellHeight;
|
||||
g.drawLine(0,starty,g.getWidth(),starty);
|
||||
}
|
||||
|
||||
g.setFont("Vector",15);
|
||||
|
||||
var dayOfWeek = d.getDay();
|
||||
var dayRem = d.getDay() - 1;
|
||||
if(dayRem <0){
|
||||
dayRem = 0;
|
||||
}
|
||||
|
||||
var start = new Date();
|
||||
start.setDate(start.getDate()-(7+dayRem));
|
||||
g.setFontAlign(0,-1,0);
|
||||
for (var y = 0;y < 3; y++){
|
||||
for(var x = 0;x < 7; x++){
|
||||
if(start.getDate() === d.getDate()){
|
||||
g.setColor(accentColor);
|
||||
}else{
|
||||
g.setColor(fontColor);
|
||||
}
|
||||
g.drawString(start.getDate(),x*cellSize +(cellSize / 2) + 2,calStart+(cellHeight*y) + 5);
|
||||
start.setDate(start.getDate()+1);
|
||||
else
|
||||
this.ABR_DAY=["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Object} current settings object
|
||||
*/
|
||||
settings(){
|
||||
return this._settings;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run forest run
|
||||
**/
|
||||
draw(){
|
||||
this.drawTime();
|
||||
|
||||
if (this.TZOffset===undefined || this.TZOffset!==d.getTimezoneOffset())
|
||||
this.drawDateAndCal();
|
||||
}
|
||||
|
||||
/**
|
||||
* draw given or current time from date
|
||||
* overwatch timezone changes
|
||||
* schedules itself to update
|
||||
*/
|
||||
drawTime(){
|
||||
d=this.date ? this.date : new Date();
|
||||
const Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10;
|
||||
|
||||
d=d?d :new Date();
|
||||
|
||||
g.setFontAlign(0, -1).setFont("Vector", this.TIME_FONT_SIZE()).setColor(g.theme.fg)
|
||||
.clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7)
|
||||
.drawString(("0" + require("locale").time(d, 1)).slice(-5), this.centerX, Y, true);
|
||||
//.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+this.TIME_FONT_SIZE()-7); //DEV-Option
|
||||
|
||||
setTimeout(this.draw.bind(this), 60000-(d.getSeconds()*1000)-d.getMilliseconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* draws given date and cal
|
||||
* @param{Date} d provide date or uses today
|
||||
*/
|
||||
drawDateAndCal(){
|
||||
d=this.date ? this.date : new Date();
|
||||
|
||||
this.TZOffset=d.getTimezoneOffset();
|
||||
this.drawDate();
|
||||
this.drawCal();
|
||||
|
||||
if (this.tOutD) //abort exisiting
|
||||
clearTimeout(this.tOutD);
|
||||
this.tOutD=setTimeout(this.drawDateAndCal.bind(this), 86400000-(d.getHours()*24*60*1000)-(d.getMinutes()*60*1000)-d.getSeconds()-d.getMilliseconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* draws given date as defiend in settings
|
||||
*/
|
||||
drawDate(){
|
||||
d=this.date ? this.date : new Date();
|
||||
|
||||
const FONT_SIZE=20;
|
||||
const Y=Bangle.appRect.y;
|
||||
var render=false;
|
||||
var dateStr = "";
|
||||
if (this.settings().shwDate>0) { //skip if exactly -none
|
||||
const dateSttngs = ["","l","M","m.Y #W"];
|
||||
for (let c of dateSttngs[this.settings().shwDate]) { //add part as configured
|
||||
switch (c){
|
||||
case "l":{ //locale
|
||||
render=true;
|
||||
dateStr+=require("locale").date(d,1);
|
||||
break;
|
||||
}
|
||||
case "m":{ //month e.g. Jan.
|
||||
render=true;
|
||||
dateStr+=require("locale").month(d,1);
|
||||
break;
|
||||
}
|
||||
case "M":{ //month e.g. January
|
||||
render=true;
|
||||
dateStr+=require("locale").month(d,0);
|
||||
break;
|
||||
}
|
||||
case "y":{ //year e.g. 22
|
||||
render=true;
|
||||
dateStr+=d.getFullYear().slice(-2);
|
||||
break;
|
||||
}
|
||||
case "Y":{ //year e.g. 2022
|
||||
render=true;
|
||||
dateStr+=d.getFullYear();
|
||||
break;
|
||||
}
|
||||
case "w":{ //week e.g. #2
|
||||
dateStr+=(this.ISO8601calWeek(d));
|
||||
break;
|
||||
}
|
||||
case "W":{ //week e.g. #02
|
||||
dateStr+=("0"+this.ISO8601calWeek(d)).slice(-2);
|
||||
break;
|
||||
}
|
||||
default: //append c
|
||||
dateStr+=c;
|
||||
render=dateStr.length>0;
|
||||
break; //noop
|
||||
}
|
||||
}
|
||||
}
|
||||
if (render){
|
||||
g.setFont("Vector", FONT_SIZE).setColor(g.theme.fg).setFontAlign(0, -1).clearRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3).drawString(dateStr,this.centerX,Y);
|
||||
}
|
||||
//g.drawRect(Bangle.appRect.x, Y, Bangle.appRect.x2, Y+FONT_SIZE-3); //DEV-Option
|
||||
}
|
||||
|
||||
/**
|
||||
* draws calender week view (-1,0,1) for given date
|
||||
*/
|
||||
drawCal(){
|
||||
d=this.date ? this.date : new Date();
|
||||
|
||||
const DAY_NAME_FONT_SIZE=10;
|
||||
const CAL_Y=Bangle.appRect.y+this.DATE_FONT_SIZE()+10+this.TIME_FONT_SIZE()+3;
|
||||
const CAL_AREA_H=Bangle.appRect.h-CAL_Y+24; //+24: top widegtes only
|
||||
const CELL_W=Bangle.appRect.w/7; //cell width
|
||||
const CELL_H=(CAL_AREA_H-DAY_NAME_FONT_SIZE)/3; //cell heigth
|
||||
const DAY_NUM_FONT_SIZE=Math.min(CELL_H-1,15); //size down, max 15
|
||||
|
||||
g.setFont("Vector", DAY_NAME_FONT_SIZE).setColor(g.theme.fg).setFontAlign(-1, -1).clearRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H);
|
||||
|
||||
//draw grid & Headline
|
||||
const dNames = this.ABR_DAY.map((a) => a.length<=2 ? a : a.substr(0, 2)); //force shrt 2
|
||||
for(var dNo=0; dNo<dNames.length; dNo++){
|
||||
const dIdx=this.settings().wdStrt>=0 ? (dNo+this.settings().wdStrt)%7 : (dNo+d.getDay()+4)%7;
|
||||
const dName=dNames[dIdx];
|
||||
if(dNo>0)
|
||||
g.drawLine(dNo*CELL_W, CAL_Y, dNo*CELL_W, CAL_Y+CAL_AREA_H-1);
|
||||
|
||||
if (dIdx==0) g.setColor(this.nrgb[this.settings().suClr]); //sunday maybe colorize txt
|
||||
g.drawString(dName, dNo*CELL_W+(CELL_W-g.stringWidth(dName))/2+2, CAL_Y+1).setColor(g.theme.fg);
|
||||
}
|
||||
|
||||
var nextY=CAL_Y+DAY_NAME_FONT_SIZE;
|
||||
|
||||
for(i=0; i<3; i++){
|
||||
const y=nextY+i*CELL_H;
|
||||
g.drawLine(Bangle.appRect.x, y, Bangle.appRect.x2, y);
|
||||
}
|
||||
|
||||
g.setFont("Vector", DAY_NUM_FONT_SIZE);
|
||||
|
||||
//write days
|
||||
const tdyDate=d.getDate();
|
||||
const days=this.settings().wdStrt>=0 ? 7+((7+d.getDay()-this.settings().wdStrt)%7) : 10;//start day (week before=7 days + days in this week realtive to week start) or fixed 7+3 days
|
||||
var rD=new Date(d.getTime());
|
||||
rD.setDate(rD.getDate()-days);
|
||||
var rDate=rD.getDate();
|
||||
for(var y=0; y<3; y++){
|
||||
for(var x=0; x<dNames.length; x++){
|
||||
if(rDate===tdyDate){ //today
|
||||
g.setColor(this.nrgb[this.settings().tdyMrkClr]); //today marker color or fg color
|
||||
switch(this.settings().tdyMrkr){ //0:none, 1:circle, 2:rectangle, 3:filled
|
||||
case 1:
|
||||
for(m=1; m<=this.settings().tdyMrkPxl&&m<CELL_H-1&&m<CELL_W-1; m++)
|
||||
g.drawCircle(x*CELL_W+(CELL_W/2)+1, nextY+(CELL_H*y)+(CELL_H/2)+1, Math.min((CELL_W-m)/2, (CELL_H-m)/2)-2);
|
||||
break;
|
||||
case 2:
|
||||
for(m=1; m<=this.settings().tdyMrkPxl&&m<CELL_H-1&&m<CELL_W-1; m++)
|
||||
g.drawRect(x*CELL_W+m, nextY+CELL_H+m, x*CELL_W+CELL_W-m, nextY+CELL_H+CELL_H-m);
|
||||
break;
|
||||
case 3:
|
||||
g.fillRect(x*CELL_W+1, nextY+CELL_H+1, x*CELL_W+CELL_W-1, nextY+CELL_H+CELL_H-1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
g.setColor(this.nrgb[this.settings().tdyNumClr]); //today color or fg color
|
||||
}else if(this.settings().suClr && rD.getDay()==0){ //sundays
|
||||
g.setColor(this.nrgb[this.settings().suClr]);
|
||||
}else{ //default
|
||||
g.setColor(g.theme.fg);
|
||||
}
|
||||
g.drawString(rDate, x*CELL_W+((CELL_W-g.stringWidth(rDate))/2)+2, nextY+((CELL_H-DAY_NUM_FONT_SIZE+2)/2)+(CELL_H*y));
|
||||
rD.setDate(rDate+1);
|
||||
rDate=rD.getDate();
|
||||
}
|
||||
}
|
||||
if (this.settings().calBrdr) {
|
||||
g.setColor(g.theme.fg).drawRect(Bangle.appRect.x, CAL_Y, Bangle.appRect.x2, CAL_Y+CAL_AREA_H-1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* calculates current ISO8601 week number e.g. 2
|
||||
* @param{Date} date for the date
|
||||
* @returns{Number}} e.g. 2
|
||||
*/
|
||||
ISO8601calWeek(date){ //copied from: https://gist.github.com/IamSilviu/5899269#gistcomment-3035480
|
||||
var tdt = new Date(date.valueOf());
|
||||
var dayn = (date.getDay() + 6) % 7;
|
||||
tdt.setDate(tdt.getDate() - dayn + 3);
|
||||
var firstThursday = tdt.valueOf();
|
||||
tdt.setMonth(0, 1);
|
||||
if (tdt.getDay() !== 4){
|
||||
tdt.setMonth(0, 1 + ((4 - tdt.getDay()) + 7) % 7);
|
||||
}
|
||||
return Number(1 + Math.ceil((firstThursday - tdt) / 604800000));
|
||||
}
|
||||
}
|
||||
|
||||
g.clear();
|
||||
Bangle.setUI("clock");
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
loop();
|
||||
setInterval(loop,1000);
|
||||
timeCalClock = new TimeCalClock(); timeCalClock.draw();
|
||||
|
||||
//hook on settime to redraw immediatly
|
||||
var _setTime = setTime;
|
||||
var setTime = function(t) {
|
||||
_setTime(t);
|
||||
timeCalClock.draw(true);
|
||||
};
|
|
@ -0,0 +1,109 @@
|
|||
// Settings menu for Time calendar clock
|
||||
(function(exit) {
|
||||
ABR_DAY = require("locale") && require("locale").abday ? require("locale").abday : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
||||
|
||||
var FILE = "timecal.settings.json";
|
||||
|
||||
const DEFAULTS = {
|
||||
shwDate:1, //0:none, 1:locale, 2:month, 3:monthshort.year #week
|
||||
|
||||
wdStrt:0, //identical to getDay() 0->Su, 1->Mo, ... //Issue #1154: weekstart So/Mo, -1 for use today
|
||||
|
||||
tdyNumClr:3, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
tdyMrkr:0, //0:none, 1:circle, 2:rectangle, 3:filled
|
||||
tdyMrkClr:2, //1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
tdyMrkPxl:3, //px
|
||||
|
||||
suClr:1, //0:fg, 1:red=#E00, 2:green=#0E0, 3:blue=#00E
|
||||
//phColor:"#E00", //public holiday
|
||||
|
||||
calBrdr:false
|
||||
};
|
||||
validSttngs = require("Storage").readJSON(FILE, 1) || {};
|
||||
for (const k in validSttngs) if (!DEFAULTS.hasOwnProperty(k)) delete this.validSttngs[k]; //remove invalid settings
|
||||
for (const k in DEFAULTS) if(!validSttngs.hasOwnProperty(k)) validSttngs[k] = DEFAULTS[k]; //assign missing defaults fixed
|
||||
|
||||
var chngdSttngs = Object.assign({}, validSttngs);
|
||||
|
||||
var saveExitSettings = () => {
|
||||
require('Storage').writeJSON(FILE, chngdSttngs);
|
||||
exit();
|
||||
};
|
||||
|
||||
var cancelExitSettings = () => {
|
||||
require('Storage').writeJSON(FILE, validSttngs);
|
||||
exit();
|
||||
};
|
||||
|
||||
var showMainMenu = () => {
|
||||
E.showMenu({
|
||||
"": {
|
||||
"title": "TimeCal "+ /*LANG*/"settings"
|
||||
},
|
||||
/*LANG*/"< Save": () => saveExitSettings(),
|
||||
/*LANG*/"Show date": {
|
||||
value: chngdSttngs.shwDate,
|
||||
min: 0, max: 3,
|
||||
format: v => [/*LANG*/"none", /*LANG*/"locale", /*LANG*/"M", /*LANG*/"m.Y #W"][v],
|
||||
onchange: v => chngdSttngs.shwDate = v
|
||||
},
|
||||
/*LANG*/"Start wday": {
|
||||
value: chngdSttngs.wdStrt,
|
||||
min: -1, max: 6,
|
||||
format: v => v>=0 ? ABR_DAY[v] : /*LANG*/"today",
|
||||
onchange: v => chngdSttngs.wdStrt = v
|
||||
},
|
||||
/*LANG*/"Su color": {
|
||||
value: chngdSttngs.suClr,
|
||||
min: 0, max: 3,
|
||||
format: v => [/*LANG*/"none", /*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v],
|
||||
onchange: v => chngdSttngs.suClr = v
|
||||
},
|
||||
/*LANG*/"Border": {
|
||||
value: chngdSttngs.calBrdr,
|
||||
format: v => v ? /*LANG*/"show" : /*LANG*/"none",
|
||||
onchange: v => chngdSttngs.calBrdr = v
|
||||
},
|
||||
/*LANG*/"Today settings": () => {
|
||||
showTodayMenu();
|
||||
},
|
||||
/*LANG*/"< Cancel": () => cancelExitSettings()
|
||||
});
|
||||
};
|
||||
|
||||
var showTodayMenu = () => {
|
||||
E.showMenu({
|
||||
"": {
|
||||
"title": /*LANG*/"Today settings"
|
||||
},
|
||||
"< Back": () => showMainMenu(),
|
||||
/*LANG*/"Color": {
|
||||
value: chngdSttngs.tdyNumClr,
|
||||
min: 0, max: 3,
|
||||
format: v => [/*LANG*/"none", /*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v],
|
||||
onchange: v => chngdSttngs.tdyNumClr = v
|
||||
},
|
||||
/*LANG*/"Marker": {
|
||||
value: chngdSttngs.tdyMrkr,
|
||||
min: 0, max: 3,
|
||||
format: v => [/*LANG*/"none", /*LANG*/"circle", /*LANG*/"rectangle", /*LANG*/"filled"][v],
|
||||
onchange: v => chngdSttngs.tdyMrkr = v
|
||||
},
|
||||
/*LANG*/"Mrk.Color": {
|
||||
value: chngdSttngs.tdyMrkClr,
|
||||
min: 0, max: 2,
|
||||
format: v => [/*LANG*/"red", /*LANG*/"green", /*LANG*/"blue"][v],
|
||||
onchange: v => chngdSttngs.tdyMrkClr = v
|
||||
},
|
||||
/*LANG*/"Mrk.Size": {
|
||||
value: chngdSttngs.tdyMrkPxl,
|
||||
min: 1, max: 10,
|
||||
format: v => v+"px",
|
||||
onchange: v => chngdSttngs.tdyMrkPxl = v
|
||||
},
|
||||
/*LANG*/"< Cancel": () => cancelExitSettings()
|
||||
});
|
||||
};
|
||||
|
||||
showMainMenu();
|
||||
});
|
|
@ -1 +1,3 @@
|
|||
0.01: New Widget!
|
||||
0.02: Battery bar turns yellow on charge
|
||||
Memory status bar does not trigger garbage collect
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "widbars",
|
||||
"name": "Bars Widget",
|
||||
"version": "0.01",
|
||||
"version": "0.02",
|
||||
"description": "Display several measurements as vertical bars.",
|
||||
"icon": "icon.png",
|
||||
"screenshots": [{"url":"screenshot.png"}],
|
||||
|
|
|
@ -42,19 +42,25 @@
|
|||
if (top) g .clearRect(x,y, x+w-1,y+top-1); // erase above bar
|
||||
if (f) g.setColor(col).fillRect(x,y+top, x+w-1,y+h-1); // even for f=0.001 this is still 1 pixel high
|
||||
}
|
||||
let batColor='#0f0';
|
||||
function draw() {
|
||||
g.reset();
|
||||
const x = this.x, y = this.y,
|
||||
m = process.memory();
|
||||
m = process.memory(false);
|
||||
let b=0;
|
||||
// ==HRM== bar(x+(w*b++),y,'#f00'/*red */,bpm/200); // >200 seems very unhealthy; if we have no valid bpm this will just be empty space
|
||||
// ==Temperature== bar(x+(w*b++),y,'#ff0'/*yellow */,E.getTemperature()/50); // you really don't want to wear a watch that's hotter than 50°C
|
||||
bar(x+(w*b++),y,g.theme.dark?'#0ff':'#00f'/*cyan/blue*/,1-(require('Storage').getFree() / process.env.STORAGE));
|
||||
bar(x+(w*b++),y,'#f0f'/*magenta*/,m.usage/m.total);
|
||||
bar(x+(w*b++),y,'#0f0'/*green */,E.getBattery()/100);
|
||||
bar(x+(w*b++),y,batColor,E.getBattery()/100);
|
||||
}
|
||||
|
||||
let redraw;
|
||||
Bangle.on('charging', function(charging) {
|
||||
batColor=charging?'#ff0':'#0f0';
|
||||
WIDGETS["bars"].draw();
|
||||
});
|
||||
|
||||
Bangle.on('lcdPower', on => {
|
||||
if (redraw) clearInterval(redraw)
|
||||
redraw = undefined;
|
||||
|
|
2
core
|
@ -1 +1 @@
|
|||
Subproject commit bd894bfdcee8293c97763de2b4d105a6b6e5415e
|
||||
Subproject commit a7a80a13fa187a4ff5f89669992babca2d95812c
|
|
@ -202,7 +202,6 @@ window.addEventListener('load', (event) => {
|
|||
}
|
||||
|
||||
var selectLang = document.getElementById("settings-lang");
|
||||
console.log(languages);
|
||||
languages.forEach(lang => {
|
||||
selectLang.innerHTML += `<option value="${lang.code}" ${SETTINGS.language==lang.code?"selected":""}>${lang.name} (${lang.code})</option>`;
|
||||
});
|
||||
|
|
|
@ -232,7 +232,7 @@ Layout.prototype.render = function (l) {
|
|||
|
||||
function render(l) {"ram"
|
||||
g.reset();
|
||||
if (l.col) g.setColor(l.col);
|
||||
if (l.col!==undefined) g.setColor(l.col);
|
||||
if (l.bgCol!==undefined) g.setBgColor(l.bgCol).clearRect(l.x,l.y,l.x+l.w-1,l.y+l.h-1);
|
||||
cb[l.type](l);
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ Layout.prototype.render = function (l) {
|
|||
x,y+4
|
||||
], bg = l.selected?g.theme.bgH:g.theme.bg2;
|
||||
g.setColor(bg).fillPoly(poly).setColor(l.selected ? g.theme.fgH : g.theme.fg2).drawPoly(poly);
|
||||
if (l.col) g.setColor(l.col);
|
||||
if (l.col!==undefined) g.setColor(l.col);
|
||||
if (l.src) g.setBgColor(bg).drawImage("function"==typeof l.src?l.src():l.src, l.x + 10 + (0|l.pad), l.y + 8 + (0|l.pad));
|
||||
else g.setFont("6x8",2).setFontAlign(0,0,l.r).drawString(l.label,l.x+l.w/2,l.y+l.h/2);
|
||||
}, "img":function(l){
|
||||
|
|
|
@ -207,7 +207,6 @@ exports.getStats = function(statIDs, options) {
|
|||
};
|
||||
}
|
||||
if (statIDs.includes("caden")) {
|
||||
needGPS = true;
|
||||
stats["caden"]={
|
||||
title : "Cadence",
|
||||
getValue : function() { return state.stepsPerMin; },
|
||||
|
|