BangleApps/apps/clockcal/app.js

340 lines
11 KiB
JavaScript

Bangle.setUI("clock");
Bangle.loadWidgets();
var s = Object.assign({
CAL_ROWS: 4, //total number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets.
CAL_ROWS_PRIOR: 0, //number of calendar rows.(weeks) that show above the current week
BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually
MODE24: true, //24h mode vs 12h mode
FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su
REDSUN: true, // Use red color for sunday?
REDSAT: true, // Use red color for saturday?
DRAGDOWN: "[AI:messg]",
DRAGRIGHT: "[AI:music]",
DRAGLEFT: "[ignore]",
DRAGUP: "[calend.]"
}, require('Storage').readJSON("clockcal.json", true) || {});
const h = g.getHeight();
const w = g.getWidth();
const CELL_W = w / 7;
const CELL2_W = w / 8;//full calendar
const CELL_H = 15;
const CAL_Y = h - s.CAL_ROWS * CELL_H;
const DEBUG = false;
var state = "watch";
var monthOffset = 0;
// FIXME: These variables should maybe be defined inside relevant functions below. The linter complained they were not defined (i.e. they were added to global scope if I understand correctly).
let dayInterval;
let secondInterval;
let minuteInterval;
let newmonth;
let bottomrightY;
let bottomrightX;
let rMonth;
let dimSeconds;
/*
* Calendar features
*/
function drawFullCalendar(monthOffset) {
const addMonths = function (_d, _am) {
let ay = 0, m = _d.getMonth(), y = _d.getFullYear();
while ((m + _am) > 11) { ay++; _am -= 12; }
while ((m + _am) < 0) { ay--; _am += 12; }
let n = new Date(_d.getTime());
n.setMonth(m + _am);
n.setFullYear(y + ay);
return n;
};
monthOffset = (typeof monthOffset == "undefined") ? 0 : monthOffset;
state = "calendar";
var start = Date().getTime();
const months = ['Jan.', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec.'];
const monthclr = ['#0f0', '#f0f', '#00f', '#ff0', '#0ff', '#fff'];
if (typeof dayInterval !== "undefined") clearTimeout(dayInterval);
if (typeof secondInterval !== "undefined") clearTimeout(secondInterval);
if (typeof minuteInterval !== "undefined") clearTimeout(minuteInterval);
var d = addMonths(Date(), monthOffset);
let tdy = Date().getDate() + "." + Date().getMonth();
newmonth = false;
let c_y = 0;
g.reset();
g.setBgColor(0);
g.clear();
var prevmonth = addMonths(d, -1);
const today = prevmonth.getDate();
var rD = new Date(prevmonth.getTime());
rD.setDate(rD.getDate() - (today - 1));
const dow = (s.FIRSTDAY + rD.getDay()) % 7;
rD.setDate(rD.getDate() - dow);
var rDate = rD.getDate();
bottomrightY = c_y - 3;
let clrsun = s.REDSUN ? '#f00' : '#fff';
let clrsat = s.REDSUN ? '#f00' : '#fff';
var fg = [clrsun, '#fff', '#fff', '#fff', '#fff', '#fff', clrsat];
for (var y = 1; y <= 11; y++) {
bottomrightY += CELL_H;
bottomrightX = -2;
for (var x = 1; x <= 7; x++) {
bottomrightX += CELL2_W;
rMonth = rD.getMonth();
rDate = rD.getDate();
if (tdy == rDate + "." + rMonth) {
caldrawToday(rDate);
} else if (rDate == 1) {
caldrawFirst(rDate);
} else {
caldrawNormal(rDate, fg[rD.getDay()]);
}
if (newmonth && x == 7) {
caldrawMonth(rDate, monthclr[rMonth % 6], months[rMonth], rD);
}
rD.setDate(rDate + 1);
}
}
delete addMonths;
if (DEBUG) console.log("Calendar performance (ms):" + (Date().getTime() - start));
}
function caldrawMonth(rDate, c, m, rD) {
g.setColor(c);
g.setFont("Vector", 18);
g.setFontAlign(-1, 1, 1);
let drawyear = ((rMonth % 11) == 0) ? String(rD.getFullYear()).substr(-2) : "";
g.drawString(m + drawyear, bottomrightX, bottomrightY - CELL_H, 1);
newmonth = false;
}
function caldrawToday(rDate) {
g.setFont("Vector", 16);
g.setFontAlign(1, 1);
g.setColor('#0f0');
g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2);
g.setColor('#000');
g.drawString(rDate, bottomrightX, bottomrightY);
}
function caldrawFirst(rDate) {
g.flip();
g.setFont("Vector", 16);
g.setFontAlign(1, 1);
bottomrightY += 3;
newmonth = true;
g.setColor('#0ff');
g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2);
g.setColor('#000');
g.drawString(rDate, bottomrightX, bottomrightY);
}
function caldrawNormal(rDate, c) {
g.setFont("Vector", 16);
g.setFontAlign(1, 1);
g.setColor(c);
g.drawString(rDate, bottomrightX, bottomrightY);//100
}
function drawMinutes() {
if (DEBUG) console.log("|-->minutes");
var d = new Date();
var hours = s.MODE24 ? d.getHours().toString().padStart(2, ' ') : ((d.getHours() + 24) % 12 || 12).toString().padStart(2, ' ');
var minutes = d.getMinutes().toString().padStart(2, '0');
var textColor = NRF.getSecurityStatus().connected ? '#fff' : '#f00';
var size = 50;
var clock_x = (w - 20) / 2;
if (dimSeconds) {
size = 65;
clock_x = 4 + (w / 2);
}
g.setBgColor(0);
g.setColor(textColor);
g.setFont("Vector", size);
g.setFontAlign(0, 1);
g.drawString(hours + ":" + minutes, clock_x, CAL_Y - 10, 1);
var nextminute = (61 - d.getSeconds());
if (typeof minuteInterval !== "undefined") clearTimeout(minuteInterval);
minuteInterval = setTimeout(drawMinutes, nextminute * 1000);
}
function drawSeconds() {
if (DEBUG) console.log("|--->seconds");
var d = new Date();
g.setColor();
g.fillRect(w - 31, CAL_Y - 36, w - 3, CAL_Y - 19);
g.setBgColor(0);
g.setColor('#fff');
g.setFont("Vector", 24);
g.setFontAlign(1, 1);
g.drawString(" " + d.getSeconds().toString().padStart(2, '0'), w, CAL_Y - 13);
if (typeof secondInterval !== "undefined") clearTimeout(secondInterval);
if (!dimSeconds) secondInterval = setTimeout(drawSeconds, 1000);
}
function drawWatch() {
if (DEBUG) console.log("DRAWWATCH");
monthOffset = 0;
state = "watch";
var d = new Date();
g.reset();
g.setBgColor(0);
g.clear();
drawMinutes();
if (!dimSeconds) drawSeconds();
const dow = (s.FIRSTDAY + d.getDay()) % 7; //MO=0, SU=6
const today = d.getDate();
var rD = new Date(d.getTime());
rD.setDate(rD.getDate() - dow - s.CAL_ROWS_PRIOR * 7);
var rDate = rD.getDate();
g.setFontAlign(1, 1);
for (var y = 1; y <= s.CAL_ROWS; y++) {
for (var x = 1; x <= 7; x++) {
bottomrightX = x * CELL_W - 2;
bottomrightY = y * CELL_H + CAL_Y;
g.setFont("Vector", 16);
var fg = ((s.REDSUN && rD.getDay() == 0) || (s.REDSAT && rD.getDay() == 6)) ? '#f00' : '#fff';
if (y == s.CAL_ROWS_PRIOR + 1 && today == rDate) {
g.setColor('#0f0');
g.fillRect(bottomrightX - CELL_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2);
g.setColor('#000');
g.drawString(rDate, bottomrightX, bottomrightY);
}
else {
g.setColor(fg);
g.drawString(rDate, bottomrightX, bottomrightY);
}
rD.setDate(rDate + 1);
rDate = rD.getDate();
}
}
Bangle.drawWidgets();
var nextday = (3600 * 24) - (d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds() + 1);
if (DEBUG) console.log("Next Day:" + (nextday / 3600));
if (typeof dayInterval !== "undefined") clearTimeout(dayInterval);
dayInterval = setTimeout(drawWatch, nextday * 1000);
if (DEBUG) console.log("ended DRAWWATCH. next refresh in " + nextday + "s");
}
function BTevent() {
drawMinutes();
if (s.BUZZ_ON_BT) {
var interval = (NRF.getSecurityStatus().connected) ? 100 : 500;
Bangle.buzz(interval);
setTimeout(function () { Bangle.buzz(interval); }, interval * 3);
}
}
function action(a) {
g.reset();
if (typeof secondInterval !== "undefined") clearTimeout(secondInterval);
if (DEBUG) console.log("action:" + a);
state = "unknown";
console.log("state -> unknown");
let l;
switch (a) {
case "[ignore]":
drawWatch();
break;
case "[calend.]":
drawFullCalendar();
break;
case "[AI:music]":
l = require("Storage").list(RegExp("music.*app.js"));
if (l.length > 0) {
load(l[0]);
} else E.showAlert("Music app not found", "Not found").then(drawWatch);
break;
case "[AI:messg]":
l = require("Storage").list(RegExp("message.*app.js"));
if (l.length > 0) {
load(l[0]);
} else E.showAlert("Message app not found", "Not found").then(drawWatch);
break;
case "[AI:agenda]":
l = require("Storage").list(RegExp("agenda.*app.js"));
if (l.length > 0) {
load(l[0]);
} else E.showAlert("Agenda app not found", "Not found").then(drawWatch);
break;
default:
l = require("Storage").list(RegExp(a + ".app.js"));
if (l.length > 0) {
load(l[0]);
} else E.showAlert(a + ": App not found", "Not found").then(drawWatch);
break;
}
}
function input(dir) {
Bangle.buzz(100, 1);
if (DEBUG) console.log("swipe:" + dir);
switch (dir) {
case "r":
if (state == "calendar") {
drawWatch();
} else {
action(s.DRAGRIGHT);
}
break;
case "l":
if (state == "calendar") {
drawWatch();
} else {
action(s.DRAGLEFT);
}
break;
case "d":
if (state == "calendar") {
monthOffset--;
drawFullCalendar(monthOffset);
} else {
action(s.DRAGDOWN);
}
break;
case "u":
if (state == "calendar") {
monthOffset++;
drawFullCalendar(monthOffset);
} else {
action(s.DRAGUP);
}
break;
default:
if (state == "calendar") {
drawWatch();
}
break;
}
}
let drag;
Bangle.on("drag", e => {
if (!drag) {
drag = { x: e.x, y: e.y };
} else if (!e.b) {
const dx = e.x - drag.x, dy = e.y - drag.y;
var dir = "t";
if (Math.abs(dx) > Math.abs(dy) + 20) {
dir = (dx > 0) ? "r" : "l";
} else if (Math.abs(dy) > Math.abs(dx) + 20) {
dir = (dy > 0) ? "d" : "u";
}
drag = null;
input(dir);
}
});
//register events
Bangle.on('lock', locked => {
if (typeof secondInterval !== "undefined") clearTimeout(secondInterval);
dimSeconds = locked; //dim seconds if lock=on
drawWatch();
});
NRF.on('connect', BTevent);
NRF.on('disconnect', BTevent);
dimSeconds = Bangle.isLocked();
drawWatch();
setWatch(function() {
if (state == "watch") {
Bangle.showLauncher()
} else if (state == "calendar") {
drawWatch();
}
}, BTN1, {repeat:true, edge:"falling"});