337 lines
12 KiB
JavaScript
Raw Normal View History

2016-03-24 17:19:32 +01:00
/* global Log, Module, moment, config */
/* Magic Mirror
* Module: Clock
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
2016-04-05 14:35:11 -04:00
Module.register("clock",{
2016-03-24 17:19:32 +01:00
// Module config defaults.
defaults: {
displayType: "digital", // options: digital, analog, both
2016-03-29 13:28:15 +02:00
timeFormat: config.timeFormat,
2016-03-24 17:19:32 +01:00
displaySeconds: true,
showPeriod: true,
showPeriodUpper: false,
clockBold: false,
showDate: true,
showWeek: false,
2017-03-17 14:32:01 +01:00
dateFormat: "dddd, LL",
/* specific to the analog clock */
analogSize: "200px",
analogFace: "simple", // options: 'none', 'simple', 'face-###' (where ### is 001 to 012 inclusive)
analogPlacement: "bottom", // options: 'top', 'bottom', 'left', 'right'
analogShowDate: "top", // options: false, 'top', or 'bottom'
secondsColor: "#888888",
timezone: null,
showSunTimes: false,
showMoonTimes: false,
lat: 47.630539,
lon: -122.344147,
2016-03-24 17:19:32 +01:00
},
// Define required scripts.
getScripts: function() {
return ["moment.js", "moment-timezone.js", "suncalc.js"];
2016-03-24 17:19:32 +01:00
},
// Define styles.
getStyles: function() {
return ["clock_styles.css"];
},
2016-03-24 17:19:32 +01:00
// Define start sequence.
start: function() {
2016-04-05 14:35:11 -04:00
Log.info("Starting module: " + this.name);
// Schedule update interval.
var self = this;
self.second = moment().second();
self.minute = moment().minute();
//Calculate how many ms should pass until next update depending on if seconds is displayed or not
var delayCalculator = function(reducedSeconds) {
var EXTRA_DELAY = 50; //Deliberate imperceptable delay to prevent off-by-one timekeeping errors
if (self.config.displaySeconds) {
return 1000 - moment().milliseconds() + EXTRA_DELAY;
} else {
return ((60 - reducedSeconds) * 1000) - moment().milliseconds() + EXTRA_DELAY;
}
};
//A recursive timeout function instead of interval to avoid drifting
var notificationTimer = function() {
self.updateDom();
//If seconds is displayed CLOCK_SECOND-notification should be sent (but not when CLOCK_MINUTE-notification is sent)
if (self.config.displaySeconds) {
self.second = moment().second();
if (self.second !== 0) {
self.sendNotification("CLOCK_SECOND", self.second);
setTimeout(notificationTimer, delayCalculator(0));
return;
}
}
//If minute changed or seconds isn't displayed send CLOCK_MINUTE-notification
self.minute = moment().minute();
self.sendNotification("CLOCK_MINUTE", self.minute);
setTimeout(notificationTimer, delayCalculator(0));
};
//Set the initial timeout with the amount of seconds elapsed as reducedSeconds so it will trigger when the minute changes
setTimeout(notificationTimer, delayCalculator(self.second));
2016-03-24 17:19:32 +01:00
// Set locale.
moment.locale(config.language);
2016-03-24 17:19:32 +01:00
},
// Override dom generator.
getDom: function() {
2016-03-24 17:19:32 +01:00
var wrapper = document.createElement("div");
/************************************
* Create wrappers for DIGITAL clock
*/
2016-03-24 17:19:32 +01:00
var dateWrapper = document.createElement("div");
var timeWrapper = document.createElement("div");
var secondsWrapper = document.createElement("sup");
2016-04-20 11:42:01 +02:00
var periodWrapper = document.createElement("span");
var sunWrapper = document.createElement("div");
var moonWrapper = document.createElement("div");
2019-06-05 10:23:58 +02:00
var weekWrapper = document.createElement("div");
2016-03-24 17:19:32 +01:00
// Style Wrappers
dateWrapper.className = "date normal medium";
timeWrapper.className = "time bright large light";
secondsWrapper.className = "dimmed";
sunWrapper.className = "sun dimmed small";
moonWrapper.className = "moon dimmed small";
2019-06-05 10:23:58 +02:00
weekWrapper.className = "week dimmed medium";
2016-05-10 16:18:35 +02:00
2016-03-24 17:19:32 +01:00
// Set content of wrappers.
2016-04-29 11:24:14 -04:00
// The moment().format("h") method has a bug on the Raspberry Pi.
2016-04-18 19:12:24 +02:00
// So we need to generate the timestring manually.
// See issue: https://github.com/MichMich/MagicMirror/issues/181
2016-05-10 16:18:35 +02:00
var timeString;
var now = moment();
this.lastDisplayedMinute = now.minute();
if (this.config.timezone) {
now.tz(this.config.timezone);
}
2017-02-07 23:51:13 +01:00
var hourSymbol = "HH";
2017-02-08 00:05:28 +01:00
if (this.config.timeFormat !== 24) {
hourSymbol = "h";
}
2017-02-07 23:51:13 +01:00
2016-04-29 11:24:14 -04:00
if (this.config.clockBold === true) {
2017-02-07 23:51:13 +01:00
timeString = now.format(hourSymbol + "[<span class=\"bold\">]mm[</span>]");
2016-04-29 11:24:14 -04:00
} else {
2017-02-07 23:51:13 +01:00
timeString = now.format(hourSymbol + ":mm");
2016-04-29 11:24:14 -04:00
}
2016-05-10 16:18:35 +02:00
if(this.config.showDate){
2017-03-17 14:32:01 +01:00
dateWrapper.innerHTML = now.format(this.config.dateFormat);
}
if (this.config.showWeek) {
weekWrapper.innerHTML = this.translate("WEEK", { weekNumber: now.week() });
}
2016-04-18 19:12:24 +02:00
timeWrapper.innerHTML = timeString;
secondsWrapper.innerHTML = now.format("ss");
2016-04-20 11:42:01 +02:00
if (this.config.showPeriodUpper) {
periodWrapper.innerHTML = now.format("A");
2016-04-20 11:42:01 +02:00
} else {
periodWrapper.innerHTML = now.format("a");
2016-04-20 11:42:01 +02:00
}
2016-03-24 17:19:32 +01:00
if (this.config.displaySeconds) {
timeWrapper.appendChild(secondsWrapper);
}
if (this.config.showPeriod && this.config.timeFormat !== 24) {
2016-04-20 11:42:01 +02:00
timeWrapper.appendChild(periodWrapper);
}
function formatTime(config, time) {
2020-02-13 08:35:09 +01:00
var formatString = hourSymbol + ":mm";
if (config.showPeriod && config.timeFormat !== 24) {
2020-02-13 08:35:09 +01:00
formatString += config.showPeriodUpper ? "A" : "a";
}
return moment(time).format(formatString);
}
if (this.config.showSunTimes) {
const sunTimes = SunCalc.getTimes(now, this.config.lat, this.config.lon);
const isVisible = now.isBetween(sunTimes.sunrise, sunTimes.sunset);
2020-01-18 09:50:43 -08:00
var nextEvent;
if (now.isBefore(sunTimes.sunrise)) {
nextEvent = sunTimes.sunrise;
} else if (now.isBefore(sunTimes.sunset)) {
nextEvent = sunTimes.sunset;
} else {
2020-02-13 08:35:09 +01:00
const tomorrowSunTimes = SunCalc.getTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
2020-01-18 09:50:43 -08:00
nextEvent = tomorrowSunTimes.sunrise;
}
const untilNextEvent = moment.duration(moment(nextEvent).diff(now));
2020-02-13 08:35:09 +01:00
const untilNextEventString = untilNextEvent.hours() + "h " + untilNextEvent.minutes() + "m";
sunWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-sun-o\" aria-hidden=\"true\"></i> " + untilNextEventString + "</span>" +
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunrise) + "</span>" +
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i>" + formatTime(this.config, sunTimes.sunset) + "</span>";
}
if (this.config.showMoonTimes) {
const moonIllumination = SunCalc.getMoonIllumination(now.toDate());
const moonTimes = SunCalc.getMoonTimes(now, this.config.lat, this.config.lon);
const moonRise = moonTimes.rise;
var moonSet;
if (moment(moonTimes.set).isAfter(moonTimes.rise)) {
moonSet = moonTimes.set;
} else {
2020-02-13 08:35:09 +01:00
const nextMoonTimes = SunCalc.getMoonTimes(now.clone().add(1, "day"), this.config.lat, this.config.lon);
moonSet = nextMoonTimes.set;
}
const isVisible = now.isBetween(moonRise, moonSet) || moonTimes.alwaysUp === true;
2020-03-08 16:23:27 +01:00
const illuminatedFractionString = Math.round(moonIllumination.fraction * 100) + "%";
moonWrapper.innerHTML = "<span class=\"" + (isVisible ? "bright" : "") + "\"><i class=\"fa fa-moon-o\" aria-hidden=\"true\"></i> " + illuminatedFractionString + "</span>" +
"<span><i class=\"fa fa-arrow-up\" aria-hidden=\"true\"></i> " + (moonRise ? formatTime(this.config, moonRise) : "...") + "</span>"+
"<span><i class=\"fa fa-arrow-down\" aria-hidden=\"true\"></i> " + (moonSet ? formatTime(this.config, moonSet) : "...") + "</span>";
}
/****************************************************************
* Create wrappers for ANALOG clock, only if specified in config
*/
if (this.config.displayType !== "digital") {
// If it isn't 'digital', then an 'analog' clock was also requested
// Calculate the degree offset for each hand of the clock
var now = moment();
if (this.config.timezone) {
now.tz(this.config.timezone);
}
var second = now.seconds() * 6,
minute = now.minute() * 6 + second / 60,
hour = ((now.hours() % 12) / 12) * 360 + 90 + minute / 12;
// Create wrappers
var clockCircle = document.createElement("div");
clockCircle.className = "clockCircle";
clockCircle.style.width = this.config.analogSize;
clockCircle.style.height = this.config.analogSize;
2019-06-05 09:32:10 +02:00
if (this.config.analogFace !== "" && this.config.analogFace !== "simple" && this.config.analogFace !== "none") {
clockCircle.style.background = "url("+ this.data.path + "faces/" + this.config.analogFace + ".svg)";
clockCircle.style.backgroundSize = "100%";
2017-01-15 19:38:46 +01:00
// The following line solves issue: https://github.com/MichMich/MagicMirror/issues/611
2019-01-05 17:04:33 +00:00
// clockCircle.style.border = "1px solid black";
clockCircle.style.border = "rgba(0, 0, 0, 0.1)"; //Updated fix for Issue 611 where non-black backgrounds are used
2017-01-15 19:41:42 +01:00
2019-06-05 09:32:10 +02:00
} else if (this.config.analogFace !== "none") {
clockCircle.style.border = "2px solid white";
}
var clockFace = document.createElement("div");
clockFace.className = "clockFace";
var clockHour = document.createElement("div");
clockHour.id = "clockHour";
clockHour.style.transform = "rotate(" + hour + "deg)";
clockHour.className = "clockHour";
var clockMinute = document.createElement("div");
clockMinute.id = "clockMinute";
clockMinute.style.transform = "rotate(" + minute + "deg)";
clockMinute.className = "clockMinute";
// Combine analog wrappers
clockFace.appendChild(clockHour);
clockFace.appendChild(clockMinute);
if (this.config.displaySeconds) {
var clockSecond = document.createElement("div");
clockSecond.id = "clockSecond";
clockSecond.style.transform = "rotate(" + second + "deg)";
clockSecond.className = "clockSecond";
clockSecond.style.backgroundColor = this.config.secondsColor;
clockFace.appendChild(clockSecond);
}
clockCircle.appendChild(clockFace);
}
/*******************************************
* Combine wrappers, check for .displayType
*/
if (this.config.displayType === "digital") {
// Display only a digital clock
wrapper.appendChild(dateWrapper);
wrapper.appendChild(timeWrapper);
wrapper.appendChild(sunWrapper);
wrapper.appendChild(moonWrapper);
wrapper.appendChild(weekWrapper);
} else if (this.config.displayType === "analog") {
// Display only an analog clock
if (this.config.showWeek) {
weekWrapper.style.paddingBottom = "15px";
} else {
dateWrapper.style.paddingBottom = "15px";
}
if (this.config.analogShowDate === "top") {
wrapper.appendChild(dateWrapper);
wrapper.appendChild(weekWrapper);
wrapper.appendChild(clockCircle);
} else if (this.config.analogShowDate === "bottom") {
wrapper.appendChild(clockCircle);
wrapper.appendChild(dateWrapper);
wrapper.appendChild(weekWrapper);
} else {
wrapper.appendChild(clockCircle);
}
} else {
// Both clocks have been configured, check position
var placement = this.config.analogPlacement;
analogWrapper = document.createElement("div");
analogWrapper.id = "analog";
analogWrapper.style.cssFloat = "none";
analogWrapper.appendChild(clockCircle);
digitalWrapper = document.createElement("div");
digitalWrapper.id = "digital";
digitalWrapper.style.cssFloat = "none";
digitalWrapper.appendChild(dateWrapper);
digitalWrapper.appendChild(timeWrapper);
digitalWrapper.appendChild(sunWrapper);
digitalWrapper.appendChild(moonWrapper);
digitalWrapper.appendChild(weekWrapper);
2017-02-07 23:51:13 +01:00
var appendClocks = function(condition, pos1, pos2) {
2017-02-08 00:05:28 +01:00
var padding = [0,0,0,0];
padding[(placement === condition) ? pos1 : pos2] = "20px";
analogWrapper.style.padding = padding.join(" ");
if (placement === condition) {
wrapper.appendChild(analogWrapper);
wrapper.appendChild(digitalWrapper);
} else {
wrapper.appendChild(digitalWrapper);
wrapper.appendChild(analogWrapper);
}
2017-02-08 00:10:44 +01:00
};
2017-02-07 23:51:13 +01:00
if (placement === "left" || placement === "right") {
digitalWrapper.style.display = "inline-block";
digitalWrapper.style.verticalAlign = "top";
analogWrapper.style.display = "inline-block";
2017-02-07 23:51:13 +01:00
appendClocks("left", 1, 3);
} else {
digitalWrapper.style.textAlign = "center";
2017-02-07 23:51:13 +01:00
2017-02-08 00:05:28 +01:00
appendClocks("top", 2, 0);
}
}
2016-03-24 17:19:32 +01:00
// Return the wrapper to the dom.
return wrapper;
}
});