initial Elapsed Time Clock commit

Update metadata.json

Add files via upload

Update README.md

Update README.md

Update README.md

Add files via upload

Update metadata.json

Update app.js

Update app.js

Update settings.js

Update app-icon.js

Update metadata.json

Update metadata.json

Update README.md

Add files via upload

Create metadata.json
pull/3291/head
paul-arg 2024-03-23 16:56:11 +01:00 committed by Paul Arguillère
parent 31d24d77d0
commit a99f21aab7
10 changed files with 538 additions and 0 deletions

1
apps/elapsed_t/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: New App!

22
apps/elapsed_t/README.md Normal file
View File

@ -0,0 +1,22 @@
# Elapsed Time Clock
A clock that calculates the time difference between now (in blue) and any given target date (in red).
The results is show in years, months, days, hours, minutes, seconds. To save battery life, the seconds are shown only when the watch is unlocked, or can be disabled entirely.
The time difference is positive if the target date is in the past and negative if it is in the future.
![Screenshot 1](screenshot1.png)
![Screenshot 2](screenshot2.png)
![Screenshot 3](screenshot3.png)
# Settings
## Time and date formats:
- time can be shown in 24h or in AM/PM format
- date can be shown in DD/MM/YYYY, MM/DD/YYYY or YYYY-MM-DD format
## Display years and months
You can select if the difference is shown with years, months and days, or just days.
# TODO
- add the option to set an alarm to the target date
- add an offset to said alarm (e.g. x hours/days... before/after)

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwcA/4A/AH8kyVJARAQE/YRLn4RD/IRT5cs2QCEEbQgFAQYjIrMlAQwjR5JHIsv2pNkz3JsgjKl/yEAO/I5l/+REBz/7I5f/EYf/I5Vf//2rNlz//8gjJAgIjE/hHIy7xEAAQjIDoIAG+RHHCA///wjHCJIjHMoI1HEY+zCI6zJv4dCFIX9R5PPR4vsEZNJCILXC/77JyXLn4jD/b7KpMnI4fZBARHHpcsEYW2AQIjKARBHIDoICECJIjRpZKCAQYjbCMH/CJVLCAgA/AHYA=="))

438
apps/elapsed_t/app.js Normal file
View File

@ -0,0 +1,438 @@
const APP_NAME = "elapsed_t";
const COLOUR_BLACK = 0x0;
const COLOUR_DARK_GREY = 0x4208; // same as: g.setColor(0.25, 0.25, 0.25)
const COLOUR_GREY = 0x8410; // same as: g.setColor(0.5, 0.5, 0.5)
const COLOUR_LIGHT_GREY = 0xc618; // same as: g.setColor(0.75, 0.75, 0.75)
const COLOUR_RED = 0xf800; // same as: g.setColor(1, 0, 0)
const COLOUR_BLUE = 0x001f; // same as: g.setColor(0, 0, 1)
const COLOUR_YELLOW = 0xffe0; // same as: g.setColor(1, 1, 0)
const COLOUR_LIGHT_CYAN = 0x87ff; // same as: g.setColor(0.5, 1, 1)
const COLOUR_DARK_YELLOW = 0x8400; // same as: g.setColor(0.5, 0.5, 0)
const COLOUR_DARK_CYAN = 0x0410; // same as: g.setColor(0, 0.5, 0.5)
const COLOUR_ORANGE = 0xfc00; // same as: g.setColor(1, 0.5, 0)
const SCREEN_WIDTH = g.getWidth();
const SCREEN_HEIGHT = g.getHeight();
const BIG_FONT_SIZE = 38;
const SMALL_FONT_SIZE = 22;
var arrowFont = atob("BwA4AcAOAHADgBwA4McfOf3e/+P+D+A+AOA="); // contains only the > character
var now = new Date();
var settings = Object.assign({
// default values
displaySeconds: true,
displayMonthsYears: true,
dateFormat: 0,
time24: true
}, require('Storage').readJSON(APP_NAME + ".settings.json", true) || {});
var temp_displaySeconds = settings.displaySeconds;
var data = Object.assign({
// default values
target: {
isSet: false,
Y: now.getFullYear(),
M: now.getMonth() + 1, // Month is zero-based, so add 1
D: now.getDate(),
h: now.getHours(),
m: now.getMinutes(),
s: now.getSeconds()
}
}, require('Storage').readJSON(APP_NAME + ".data.json", true) || {});
function writeData() {
require('Storage').writeJSON(APP_NAME + ".data.json", data);
}
function writeSettings() {
require('Storage').writeJSON(APP_NAME + ".settings.json", settings);
temp_displaySeconds = settings.temp_displaySeconds;
}
let inMenu = false;
Bangle.on('touch', function (zone, e) {
if (!inMenu) {
if (drawTimeout) clearTimeout(drawTimeout);
E.showMenu(menu);
inMenu = true;
}
});
function pad2(number) {
return (String(number).padStart(2, '0'));
}
function formatDateTime(date, dateFormat, time24, showSeconds) {
var formattedDateTime = {
date: "",
time: ""
};
var DD = pad2(date.getDate());
var MM = pad2(date.getMonth() + 1); // Month is zero-based
var YYYY = date.getFullYear();
var h = date.getHours();
var hh = pad2(date.getHours());
var mm = pad2(date.getMinutes());
var ss = pad2(date.getSeconds());
switch (dateFormat) {
case 0:
formattedDateTime.date = `${DD}/${MM}/${YYYY}`;
break;
case 1:
formattedDateTime.date = `${MM}/${DD}/${YYYY}`;
break;
case 2:
formattedDateTime.date = `${YYYY}-${MM}-${DD}`;
break;
default:
formattedDateTime.date = `${YYYY}-${MM}-${DD}`;
break;
}
if (time24) {
formattedDateTime.time = `${hh}:${mm}${showSeconds ? `:${ss}` : ''}`;
} else {
var ampm = (h >= 12 ? 'PM' : 'AM');
var h_ampm = h % 12;
h_ampm = (h_ampm == 0 ? 12 : h_ampm);
formattedDateTime.time = `${h_ampm}:${mm}${showSeconds ? `:${ss}` : ''}${ampm}`;
}
return formattedDateTime;
}
function howManyDaysInMonth(month, year) {
return new Date(year, month, 0).getDate();
}
function handleExceedingDay() {
var maxDays = howManyDaysInMonth(data.target.M, data.target.Y);
menu.Day.max = maxDays;
if (data.target.D > maxDays) {
menu.Day.value = maxDays;
data.target.D = maxDays;
}
}
function setTarget(set) {
if (set) {
target = new Date(
data.target.Y,
data.target.M - 1,
data.target.D,
data.target.h,
data.target.m,
data.target.s
);
data.target.isSet = true;
} else {
target = new Date();
Object.assign(
data,
{
target: {
isSet: false,
Y: now.getFullYear(),
M: now.getMonth() + 1, // Month is zero-based, so add 1
D: now.getDate(),
h: now.getHours(),
m: now.getMinutes(),
s: now.getSeconds()
}
}
);
}
writeData();
}
var target;
setTarget(data.target.isSet);
var drawTimeout;
var queueMillis = 1000;
var menu = {
"": {
"title": "Set target",
back: function () {
E.showMenu();
Bangle.setUI("clock");
inMenu = false;
draw();
}
},
'Day': {
value: data.target.D,
min: 1, max: 31, wrap: true,
onchange: v => {
data.target.D = v;
}
},
'Month': {
value: data.target.M,
min: 1, max: 12, noList: true, wrap: true,
onchange: v => {
data.target.M = v;
handleExceedingDay();
}
},
'Year': {
value: data.target.Y,
min: 1900, max: 2100,
onchange: v => {
data.target.Y = v;
handleExceedingDay();
}
},
'Hours': {
value: data.target.h,
min: 0, max: 23, wrap: true,
onchange: v => {
data.target.h = v;
},
format: function (v) { return pad2(v); }
},
'Minutes': {
value: data.target.m,
min: 0, max: 59, wrap: true,
onchange: v => {
data.target.m = v;
},
format: function (v) { return pad2(v); }
},
'Seconds': {
value: 0,
min: 0, max: 59, wrap: true,
onchange: v => {
data.target.s = v;
},
format: function (v) { return pad2(v); }
},
'Save': function () {
E.showMenu();
inMenu = false;
Bangle.setUI("clock");
setTarget(true);
writeSettings();
temp_displaySeconds = settings.displaySeconds;
updateQueueMillis(settings.displaySeconds);
draw();
},
'Reset': function () {
E.showMenu();
inMenu = false;
Bangle.setUI("clock");
setTarget(false);
updateQueueMillis(settings.displaySeconds);
draw();
}
};
function queueDraw() {
if (drawTimeout) clearTimeout(drawTimeout);
var delay = queueMillis - (Date.now() % queueMillis);
if (queueMillis == 60000 && signIsNegative()) {
delay += 1000;
}
drawTimeout = setTimeout(function () {
drawTimeout = undefined;
draw();
}, delay);
}
function updateQueueMillis(displaySeconds) {
if (displaySeconds) {
queueMillis = 1000;
} else {
queueMillis = 60000;
}
}
Bangle.on('lock', function (on, reason) {
if (on) { // screen is locked
temp_displaySeconds = false;
updateQueueMillis(false);
draw();
} else { // screen is unlocked
temp_displaySeconds = settings.displaySeconds;
updateQueueMillis(temp_displaySeconds);
draw();
}
});
function signIsNegative() {
var now = new Date();
return (now < target);
}
function diffToTarget() {
var diff = {
sign: "+",
Y: "0",
M: "0",
D: "0",
hh: "00",
mm: "00",
ss: "00"
};
if (!data.target.isSet) {
return (diff);
}
var now = new Date();
diff.sign = now < target ? '-' : '+';
if (settings.displayMonthsYears) {
var start;
var end;
if (now > target) {
start = target;
end = now;
} else {
start = now;
end = target;
}
diff.Y = end.getFullYear() - start.getFullYear();
diff.M = end.getMonth() - start.getMonth();
diff.D = end.getDate() - start.getDate();
diff.hh = end.getHours() - start.getHours();
diff.mm = end.getMinutes() - start.getMinutes();
diff.ss = end.getSeconds() - start.getSeconds();
// Adjust negative differences
if (diff.ss < 0) {
diff.ss += 60;
diff.mm--;
}
if (diff.mm < 0) {
diff.mm += 60;
diff.hh--;
}
if (diff.hh < 0) {
diff.hh += 24;
diff.D--;
}
if (diff.D < 0) {
var lastMonthDays = new Date(end.getFullYear(), end.getMonth(), 0).getDate();
diff.D += lastMonthDays;
diff.M--;
}
if (diff.M < 0) {
diff.M += 12;
diff.Y--;
}
} else {
var timeDifference = target - now;
timeDifference = Math.abs(timeDifference);
// Calculate days, hours, minutes, and seconds
diff.D = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
diff.hh = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
diff.mm = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
diff.ss = Math.floor((timeDifference % (1000 * 60)) / 1000);
}
// add zero padding
diff.hh = pad2(diff.hh);
diff.mm = pad2(diff.mm);
diff.ss = pad2(diff.ss);
return diff;
}
function draw() {
var nowFormatted = formatDateTime(new Date(), settings.dateFormat, settings.time24, temp_displaySeconds);
var targetFormatted = formatDateTime(target, settings.dateFormat, settings.time24, true);
var diff = diffToTarget();
var diffYMD;
if (settings.displayMonthsYears)
diffYMD = `${diff.sign}${diff.Y}Y ${diff.M}M ${diff.D}D`;
else
diffYMD = `${diff.sign}${diff.D}D`;
var diff_hhmm = `${diff.hh}:${diff.mm}`;
g.clearRect(0, 24, SCREEN_WIDTH, SCREEN_HEIGHT);
//console.log("drawing");
let y = 24; //Bangle.getAppRect().y;
// draw current date
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(COLOUR_BLUE);
g.drawString(nowFormatted.date, 4, y);
y += SMALL_FONT_SIZE;
// draw current time
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(COLOUR_BLUE);
g.drawString(nowFormatted.time, 4, y);
y += SMALL_FONT_SIZE;
// draw arrow
g.setFontCustom(arrowFont, 62, 16, 13).setFontAlign(-1, -1).setColor(COLOUR_RED);
g.drawString(">", 4, y + 3);
if (data.target.isSet) {
// draw target date
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(COLOUR_RED);
g.drawString(targetFormatted.date, 4 + 16 + 6, y);
} else {
// draw NOT SET
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(COLOUR_RED);
g.drawString("NOT SET", 4 + 16 + 6, y);
}
y += SMALL_FONT_SIZE;
// draw target time
if (data.target.isSet) {
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(COLOUR_RED);
g.drawString(targetFormatted.time, 4, y);
}
y += SMALL_FONT_SIZE + 4;
// draw separator
g.setColor(COLOUR_BLACK);
g.drawLine(0, y - 4, SCREEN_WIDTH, y - 4);
// draw diffYMD
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(0, -1).setColor(COLOUR_BLACK);
g.drawString(diffYMD, SCREEN_WIDTH / 2, y);
y += SMALL_FONT_SIZE;
// draw diff_hhmm
g.setFont("Vector", BIG_FONT_SIZE).setFontAlign(0, -1).setColor(COLOUR_BLACK);
g.drawString(diff_hhmm, SCREEN_WIDTH / 2, y);
// draw diff_ss
if (temp_displaySeconds) {
g.setFont("Vector", SMALL_FONT_SIZE).setFontAlign(-1, -1).setColor(COLOUR_GREY);
g.drawString(diff.ss, SCREEN_WIDTH / 2 + 52, y + 13);
}
queueDraw();
}
Bangle.loadWidgets();
Bangle.drawWidgets();
Bangle.setUI("clock");
draw();

BIN
apps/elapsed_t/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,21 @@
{
"id": "elapsed_t",
"name": "Elapsed Time Clock",
"shortName": "Elapsed Time",
"type": "clock",
"version":"0.01",
"description": "A clock that calculates the time difference between now and any given target date.",
"tags": ["clock", "tool"],
"src": "elapsed_t.app.js",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"elapsed_t.app.js","url":"app.js"},
{"name":"elapsed_t.settings.js","url":"settings.js"},
{"name":"elapsed_t.img","url":"app-icon.js","evaluate":true}
],
"data": [{"name":"elapsed_t.data.json"}],
"icon": "app.png",
"readme": "README.md",
"screenshots": [{ "url": "screenshot1.png" }, { "url": "screenshot2.png" }, { "url": "screenshot3.png" }],
"allow_emulator":true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -0,0 +1,55 @@
(function(back) {
var APP_NAME = "elapsed_t";
var FILE = APP_NAME + ".settings.json";
// Load settings
var settings = Object.assign({
// default values
displaySeconds: true,
displayMonthsYears: true,
dateFormat: 0,
time24: true
}, require('Storage').readJSON(APP_NAME + ".settings.json", true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
var dateFormats = ["DD/MM/YYYY", "MM/DD/YYYY", "YYYY-MM-DD"];
// Show the menu
E.showMenu({
"" : { "title" : "Elapsed Time" },
"< Back" : () => back(),
'Show\nseconds': {
value: !!settings.displaySeconds,
onchange: v => {
settings.displaySeconds = v;
writeSettings();
}
},
'Show months/\nyears': {
value: !!settings.displayMonthsYears,
onchange: v => {
settings.displayMonthsYears = v;
writeSettings();
}
},
'Time format': {
value: !!settings.time24,
onchange: v => {
settings.time24 = v;
writeSettings();
},
format: function (v) {return v ? "24h" : "AM/PM";}
},
'Date format': {
value: settings.dateFormat,
min: 0, max: 2, wrap: true,
onchange: v => {
settings.dateFormat = v;
writeSettings();
},
format: function (v) {return dateFormats[v];}
}
});
})