2024-09-07 10:49:56 +00:00
|
|
|
// Load libraries
|
2024-09-03 21:12:02 +00:00
|
|
|
const storage = require("Storage");
|
2024-09-03 22:13:23 +00:00
|
|
|
const locale = require('locale');
|
2024-09-06 19:43:14 +00:00
|
|
|
const widget_utils = require('widget_utils');
|
2024-09-03 21:12:02 +00:00
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
// Define constants
|
2024-09-03 21:51:05 +00:00
|
|
|
const DATETIME_SPACING_HEIGHT = 5;
|
2024-09-07 10:49:56 +00:00
|
|
|
const TIME_HEIGHT = 8;
|
|
|
|
const DATE_HEIGHT = 8;
|
|
|
|
const BOTTOM_SPACING = 2;
|
2024-09-03 21:05:45 +00:00
|
|
|
|
2024-09-02 22:41:50 +00:00
|
|
|
const MINS_IN_HOUR = 60;
|
2024-09-06 20:45:18 +00:00
|
|
|
const MINS_IN_DAY = 24 * MINS_IN_HOUR;
|
2024-09-02 22:41:50 +00:00
|
|
|
|
|
|
|
const VARIANT_EXACT = 'exact';
|
|
|
|
const VARIANT_APPROXIMATE = 'approximate';
|
|
|
|
const VARIANT_HYBRID = 'hybrid';
|
|
|
|
|
2024-09-03 21:05:45 +00:00
|
|
|
const DEFAULTS_FILE = "dutchclock.default.json";
|
2024-09-02 22:41:50 +00:00
|
|
|
const SETTINGS_FILE = "dutchclock.json";
|
|
|
|
|
2024-09-03 21:05:45 +00:00
|
|
|
// Load settings
|
|
|
|
const settings = Object.assign(
|
2024-09-03 21:17:01 +00:00
|
|
|
storage.readJSON(DEFAULTS_FILE, true) || {},
|
2024-09-03 21:05:45 +00:00
|
|
|
storage.readJSON(SETTINGS_FILE, true) || {}
|
|
|
|
);
|
2024-09-02 22:41:50 +00:00
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
// Define global variables
|
|
|
|
const textBox = {};
|
2024-09-03 21:05:45 +00:00
|
|
|
let date, mins;
|
2024-09-02 22:41:50 +00:00
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
// Define functions
|
2024-09-02 22:41:50 +00:00
|
|
|
function initialize() {
|
2024-09-07 10:49:56 +00:00
|
|
|
// Reset the state of the graphics library
|
|
|
|
g.clear(true);
|
|
|
|
|
2024-09-06 21:39:29 +00:00
|
|
|
// Tell Bangle this is a clock
|
|
|
|
Bangle.setUI("clock");
|
2024-09-07 10:49:56 +00:00
|
|
|
|
2024-09-06 21:39:29 +00:00
|
|
|
// Load widgets
|
|
|
|
Bangle.loadWidgets();
|
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
// Show widgets, or not
|
|
|
|
if (settings.showWidgets) {
|
|
|
|
Bangle.drawWidgets();
|
|
|
|
} else {
|
|
|
|
widget_utils.swipeOn();
|
|
|
|
}
|
|
|
|
|
|
|
|
const dateTimeHeight = (settings.showDate || settings.showTime ? DATETIME_SPACING_HEIGHT : 0)
|
|
|
|
+ (settings.showDate ? DATE_HEIGHT : 0)
|
|
|
|
+ (settings.showTime ? TIME_HEIGHT : 0);
|
|
|
|
|
|
|
|
Object.assign(textBox, {
|
|
|
|
x: Bangle.appRect.x + Bangle.appRect.w / 2,
|
|
|
|
y: Bangle.appRect.y + (Bangle.appRect.h - dateTimeHeight) / 2,
|
|
|
|
w: Bangle.appRect.w - 2,
|
|
|
|
h: Bangle.appRect.h - dateTimeHeight
|
|
|
|
});
|
|
|
|
|
2024-09-02 22:41:50 +00:00
|
|
|
// draw immediately at first
|
|
|
|
tick();
|
|
|
|
|
|
|
|
// now check every second
|
|
|
|
let secondInterval = setInterval(tick, 1000);
|
|
|
|
// Stop updates when LCD is off, restart when on
|
|
|
|
|
|
|
|
Bangle.on('lcdPower',on=>{
|
|
|
|
if (secondInterval) clearInterval(secondInterval);
|
|
|
|
secondInterval = undefined;
|
|
|
|
if (on) {
|
|
|
|
secondInterval = setInterval(tick, 1000);
|
|
|
|
draw(); // draw immediately
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function tick() {
|
|
|
|
date = new Date();
|
2024-09-06 20:45:18 +00:00
|
|
|
const m = (date.getHours() * MINS_IN_HOUR + date.getMinutes()) % MINS_IN_DAY;
|
2024-09-02 22:41:50 +00:00
|
|
|
|
|
|
|
if (m !== mins) {
|
|
|
|
mins = m;
|
|
|
|
draw();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function draw() {
|
|
|
|
// work out how to display the current time
|
|
|
|
const timeLines = getTimeLines(mins);
|
2024-09-03 21:46:05 +00:00
|
|
|
const bottomLines = getBottomLines();
|
2024-09-02 22:41:50 +00:00
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
g.reset().clearRect(Bangle.appRect);
|
2024-09-03 23:10:59 +00:00
|
|
|
|
2024-09-02 22:41:50 +00:00
|
|
|
// draw the current time (4x size 7 segment)
|
|
|
|
setFont(timeLines);
|
|
|
|
|
|
|
|
g.setFontAlign(0,0); // align center top
|
2024-09-07 10:49:56 +00:00
|
|
|
g.drawString(timeLines.join("\n"), textBox.x, textBox.y, false);
|
2024-09-02 22:41:50 +00:00
|
|
|
|
2024-09-03 21:46:05 +00:00
|
|
|
if (bottomLines.length) {
|
|
|
|
// draw the time and/or date, in a normal font
|
|
|
|
g.setFont("6x8");
|
|
|
|
g.setFontAlign(0,1); // align center bottom
|
|
|
|
// pad the date - this clears the background if the date were to change length
|
2024-09-07 10:49:56 +00:00
|
|
|
g.drawString(bottomLines.join('\n'), Bangle.appRect.w / 2, Bangle.appRect.y2 - BOTTOM_SPACING, false);
|
2024-09-02 22:41:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function setFont(timeLines) {
|
2024-09-07 10:49:56 +00:00
|
|
|
const size = textBox.h / timeLines.length;
|
2024-09-02 22:41:50 +00:00
|
|
|
|
|
|
|
g.setFont("Vector", size);
|
|
|
|
|
|
|
|
let width = g.stringWidth(timeLines.join('\n'));
|
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
if (width > textBox.w) {
|
|
|
|
g.setFont("Vector", Math.floor(size * (textBox.w / width)));
|
2024-09-02 22:41:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-03 21:46:05 +00:00
|
|
|
function getBottomLines() {
|
|
|
|
const lines = [];
|
|
|
|
|
2024-09-03 21:51:05 +00:00
|
|
|
if (settings.showTime) {
|
2024-09-03 22:13:23 +00:00
|
|
|
lines.push(locale.time(date, 1));
|
2024-09-03 21:46:05 +00:00
|
|
|
}
|
|
|
|
|
2024-09-03 21:51:05 +00:00
|
|
|
if (settings.showDate) {
|
2024-09-03 23:10:59 +00:00
|
|
|
lines.push(locale.date(date));
|
2024-09-03 21:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
2024-09-03 23:10:59 +00:00
|
|
|
function getTimeLines(m) {
|
2024-09-02 22:41:50 +00:00
|
|
|
switch (settings.variant) {
|
|
|
|
case VARIANT_EXACT:
|
|
|
|
return getExactTimeLines(m);
|
|
|
|
case VARIANT_APPROXIMATE:
|
|
|
|
return getApproximateTimeLines(m);
|
|
|
|
case VARIANT_HYBRID:
|
|
|
|
return distanceFromNearest(15)(m) < 3
|
|
|
|
? getApproximateTimeLines(m)
|
|
|
|
: getExactTimeLines(m);
|
|
|
|
default:
|
|
|
|
console.warn(`Error in settings: unknown variant "${settings.variant}"`);
|
|
|
|
return getExactTimeLines(m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getExactTimeLines(m) {
|
2024-09-03 23:10:59 +00:00
|
|
|
if (m === 0) {
|
|
|
|
return ['middernacht'];
|
|
|
|
}
|
|
|
|
|
2024-09-02 22:41:50 +00:00
|
|
|
const hour = getHour(m);
|
|
|
|
const minutes = getMinutes(hour.offset);
|
|
|
|
|
|
|
|
const lines = minutes.concat(hour.lines);
|
|
|
|
if (lines.length === 1) {
|
|
|
|
lines.push('uur');
|
|
|
|
}
|
|
|
|
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getApproximateTimeLines(m) {
|
|
|
|
const roundMinutes = getRoundMinutes(m);
|
|
|
|
|
2024-09-03 23:10:59 +00:00
|
|
|
const lines = getExactTimeLines(roundMinutes.minutes);
|
2024-09-02 22:41:50 +00:00
|
|
|
|
|
|
|
return addApproximateDescription(lines, roundMinutes.offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getHour(minutes) {
|
|
|
|
const hours = ['twaalf', 'een', 'twee', 'drie', 'vier', 'vijf', 'zes', 'zeven', 'acht', 'negen', 'tien', 'elf'];
|
|
|
|
|
|
|
|
const h = Math.floor(minutes / MINS_IN_HOUR), m = minutes % MINS_IN_HOUR;
|
|
|
|
|
|
|
|
if (m <= 15) {
|
|
|
|
return {lines: [hours[h % 12]], offset: m};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m > 15 && m < 45) {
|
|
|
|
return {
|
|
|
|
lines: ['half', hours[(h + 1) % 12]],
|
|
|
|
offset: m - (MINS_IN_HOUR / 2)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return {lines: [hours[(h + 1) % 12]], offset: m - MINS_IN_HOUR};
|
|
|
|
}
|
|
|
|
|
|
|
|
function getMinutes(m) {
|
|
|
|
const minutes = ['', 'een', 'twee', 'drie', 'vier', 'vijf', 'zes', 'zeven', 'acht', 'negen', 'tien', 'elf', 'twaalf', 'dertien', 'veertien', 'kwart'];
|
|
|
|
|
|
|
|
if (m === 0) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return [minutes[Math.abs(m)], m > 0 ? 'over' : 'voor'];
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRoundMinutes(m) {
|
|
|
|
const nearest = roundTo(5)(m);
|
|
|
|
|
|
|
|
return {
|
2024-09-06 20:53:14 +00:00
|
|
|
minutes: nearest % MINS_IN_DAY,
|
2024-09-02 22:41:50 +00:00
|
|
|
offset: m - nearest
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function addApproximateDescription(lines, offset) {
|
|
|
|
if (offset === 0) {
|
|
|
|
return lines;
|
|
|
|
}
|
|
|
|
|
2024-09-03 23:10:59 +00:00
|
|
|
if (lines.length === 1 || lines[1] === 'uur') {
|
2024-09-03 21:57:14 +00:00
|
|
|
const singular = lines[0];
|
|
|
|
const plural = getPlural(singular);
|
2024-09-02 22:41:50 +00:00
|
|
|
return {
|
|
|
|
'-2': ['tegen', plural],
|
2024-09-03 21:57:14 +00:00
|
|
|
'-1': ['iets voor', singular],
|
2024-09-02 22:41:50 +00:00
|
|
|
'1': ['iets na', plural],
|
|
|
|
'2': ['even na', plural]
|
|
|
|
}[`${offset}`];
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
'-2': ['bijna'].concat(lines),
|
|
|
|
'-1': ['rond'].concat(lines),
|
|
|
|
'1': ['iets na'].concat(lines),
|
|
|
|
'2': lines.concat(['geweest'])
|
|
|
|
}[`${offset}`];
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPlural(h) {
|
|
|
|
return {
|
2024-09-03 23:10:59 +00:00
|
|
|
middernacht: 'middernacht',
|
2024-09-02 22:41:50 +00:00
|
|
|
een: 'enen',
|
|
|
|
twee: 'tweeën',
|
|
|
|
drie: 'drieën',
|
|
|
|
vijf: 'vijven',
|
|
|
|
zes: 'zessen',
|
|
|
|
elf: 'elven',
|
|
|
|
twaalf: 'twaalven'
|
|
|
|
}[h] || `${h}en`;
|
|
|
|
}
|
|
|
|
|
|
|
|
function distanceFromNearest(x) {
|
|
|
|
return n => Math.abs(n - roundTo(x)(n));
|
|
|
|
}
|
|
|
|
|
|
|
|
function roundTo(x) {
|
|
|
|
return n => Math.round(n / x) * x;
|
|
|
|
}
|
|
|
|
|
2024-09-07 10:49:56 +00:00
|
|
|
// Let's go
|
2024-09-03 21:05:45 +00:00
|
|
|
initialize();
|