feat: add large clock

pull/373/head
Francesco Bedussi 2020-05-03 21:12:28 +02:00
parent 51bf1d4ae4
commit a15a049b94
8 changed files with 326 additions and 0 deletions

View File

@ -1550,5 +1550,36 @@
{"name":"hidjoystick.app.js","url":"app.js"},
{"name":"hidjoystick.img","url":"app-icon.js","evaluate":true}
]
},
{
"id": "largeclock",
"name": "Large Clock",
"icon": "largeclock.png",
"version": "0.01",
"description": "A readable and informational digital watch, with date, seconds and moon phase",
"readme": "README.md",
"tags": "clock",
"type": "clock",
"allow_emulator": true,
"storage": [
{
"name": "largeclock.app.js",
"url": "largeclock.js"
},
{
"name": "largeclock.img",
"url": "largeclock-icon.js",
"evaluate": true
},
{
"name": "largeclock.settings.js",
"url": "settings.js"
},
{
"name": "largeclock.json",
"url": "largeclock.json",
"evaluate": true
}
]
}
]

View File

@ -0,0 +1 @@
0.01: Init

19
apps/largeclock/README.md Normal file
View File

@ -0,0 +1,19 @@
# Large clock
A readable and informational digital watch, with date, seconds and moon phase and with programmable BTN1 & BTN3
## Features
- Readable
- Informative: hours, minutes, secondsa, date, year and moon phase
- Pairs nicely with any other apps: in setting > large clock any installed app can be assigned to BTN1 and BTN3 in order to open it easily directly from the watch, without the hassle of passing trough the launcher. For example BTN1 can be assigned to alarm and BTN3 to chronometer.
## How to use it
- The clock can be used as any other one, if you like it just set it as the default clock app in settings > select clock
- In setting > large clock you can select which app is to be open by BTN1 and BTN3
## Credits
- The clock face is heavily inspired by Big Clock byJeffmer https://jeffmer.github.io/JeffsBangleAppsDev/
- The moon phase is basically the one from the widget https://github.com/espruino/BangleApps/tree/master/apps/widmp

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwwhC/AH4ArmYAQCwkDC6MwFyowFC/4XKnGIAAIQFBAWDC5INCBwggEEIYXdxAODnAYCAYIgDDAQXECoIrDE4YrEBwYX/C/4X/C/4X8BwIKBAAM4DgQDBBAQDBBAIXFE4QOCA4QrCAAQHCC7wODCwYhEEAYXGACAX/C5cDCyMwC4YwSCwgA/AH4AlA="))

View File

@ -0,0 +1,198 @@
const REFRESH_RATE = 1000;
let interval;
let lastMoonPhase;
let lastMinutes;
const moonR = 12;
const moonX = 215;
const moonY = 50;
const settings = require("Storage").readJSON("largeclock.json", 1);
const BTN1app = settings.BTN1;
const BTN3app = settings.BTN3;
console.log("BTN1app", BTN1app);
console.log("BTN3app", BTN3app);
function drawMoon(d) {
const BLACK = 0,
MOON = 0x41f,
MC = 29.5305882,
NM = 694039.09;
var moon = {
// reset
0: () => {
g.setColor(BLACK).fillRect(
moonX - moonR,
moonY - moonR,
moonX + moonR,
moonY + moonR
);
},
// new moon
1: () => {
moon[0]();
g.setColor(MOON).drawCircle(moonX, moonY, moonR);
},
// 1/4 ascending
2: () => {
moon[3]();
g.setColor(BLACK).fillEllipse(
moonX - moonR / 2,
moonY - moonR,
moonX + moonR / 2,
moonY + moonR
);
},
// 1/2 ascending
3: () => {
moon[0]();
g.setColor(MOON)
.fillCircle(moonX, moonY, moonR)
.setColor(BLACK)
.fillRect(moonX, moonY - moonR, moonX + moonR + moonR, moonY + moonR);
},
// 3/4 ascending
4: () => {
moon[7]();
g.setColor(MOON).fillEllipse(
moonX - moonR / 2,
moonY - moonR,
moonX + moonR / 2,
moonY + moonR
);
},
// Full moon
5: () => {
moon[0]();
g.setColor(MOON).fillCircle(moonX, moonY, moonR);
},
// 3/4 descending
6: () => {
moon[3]();
g.setColor(MOON).fillEllipse(
moonX - moonR / 2,
moonY - moonR,
moonX + moonR / 2,
moonY + moonR
);
},
// 1/2 descending
7: () => {
moon[0]();
g.setColor(MOON)
.fillCircle(moonX, moonY, moonR)
.setColor(BLACK)
.fillRect(moonX - moonR, moonY - moonR, moonX, moonY + moonR);
},
// 1/4 descending
8: () => {
moon[7]();
g.setColor(BLACK).fillEllipse(
moonX - moonR / 2,
moonY - moonR,
moonX + moonR / 2,
moonY + moonR
);
}
};
function moonPhase(d) {
var tmp,
month = d.getMonth(),
year = d.getFullYear(),
day = d.getDate();
if (month < 3) {
year--;
month += 12;
}
tmp = (365.25 * year + 30.6 * ++month + day - NM) / MC;
return Math.round((tmp - (tmp | 0)) * 7 + 1);
}
const currentMoonPhase = moonPhase(d);
if (currentMoonPhase != lastMoonPhase) {
moon[currentMoonPhase]();
lastMoonPhase = currentMoonPhase;
}
}
function drawTime(d) {
const da = d.toString().split(" ");
const time = da[4].substr(0, 5).split(":");
const dow = da[0];
const month = da[1];
const day = da[2];
const year = da[3];
const hours = time[0];
const minutes = time[1];
const seconds = d.getSeconds();
if (minutes != lastMinutes) {
g.clearRect(0, 24, moonX - moonR - 10, 239);
g.setColor(1, 1, 1);
g.setFontAlign(-1, -1);
g.setFont("Vector", 100);
g.drawString(hours, 50, 24, true);
g.setColor(1, 50, 1);
g.drawString(minutes, 50, 135, true);
g.setFont("Vector", 20);
g.setRotation(3);
g.drawString(`${dow} ${day} ${month}`, 50, 15, true);
g.drawString(year, 75, 205, true);
lastMinutes = minutes;
}
g.setRotation(0);
g.setFont("Vector", 20);
g.setColor(1, 1, 1);
g.setFontAlign(0, -1);
g.clearRect(200, 210, 240, 240);
g.drawString(seconds, 215, 215);
}
function drawClockFace() {
const d = new Date();
drawTime(d);
drawMoon(d);
}
Bangle.on("lcdPower", function(on) {
if (on) {
g.clear();
Bangle.drawWidgets();
drawClockFace();
interval = setInterval(drawClockFace, REFRESH_RATE);
} else {
clearInterval(interval);
lastMinutes = undefined;
lastMoonPhase = undefined;
}
});
Bangle.setLCDMode();
// Show launcher when middle button pressed
clearWatch();
setWatch(Bangle.showLauncher, BTN2, { repeat: false, edge: "falling" });
setWatch(
function() {
load(BTN1app);
},
BTN1,
{ repeat: false, edge: "rising" }
);
setWatch(
function() {
load(BTN3app);
},
BTN3,
{ repeat: false, edge: "rising" }
);
g.clear();
clearInterval();
drawClockFace();
interval = setInterval(drawClockFace, REFRESH_RATE);
Bangle.loadWidgets();
Bangle.drawWidgets();

View File

@ -0,0 +1,4 @@
{
"BTN1": "timer.app.js",
"BTN3": "calendar.app.js"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

View File

@ -0,0 +1,72 @@
(function(back) {
const s = require("Storage");
const apps = s
.list(/\.info$/)
.map(app => {
var a = s.readJSON(app, 1);
return (
a && {
n: a.name,
t: a.type,
src: a.src
}
);
})
.filter(app => app && (app.t == "app" || app.t == "clock" || !app.t))
.map(a => {
return { n: a.n, src: a.src };
});
apps.sort((a, b) => {
if (a.n < b.n) return -1;
if (a.n > b.n) return 1;
return 0;
});
const settings = s.readJSON("largeclock.json", 1) || {
BTN1: "",
BTN3: ""
};
function showApps(btn) {
function format(v) {
return v === settings[btn] ? "*" : "";
}
function onchange(v) {
settings[btn] = v;
s.write("largeclock.json", settings);
}
const btnMenu = {
"": {
title: `Apps for ${btn}`
},
"< Back": () => E.showMenu(mainMenu)
};
if (apps.length > 0) {
for (let i = 0; i < apps.length; i++) {
btnMenu[apps[i].n] = {
value: apps[i].src,
format: format,
onchange: onchange
};
}
} else {
btnMenu["...No Apps..."] = {
value: undefined,
format: () => "",
onchange: () => {}
};
}
return E.showMenu(btnMenu);
}
const mainMenu = {
"": { title: "Large Clock Settings" },
"< Back": back,
"BTN1 app": () => showApps("BTN1"),
"BTN3 app": () => showApps("BTN3")
};
E.showMenu(mainMenu);
});