mirror of
https://github.com/MichMich/MagicMirror.git
synced 2025-08-22 13:09:26 +00:00
Merge remote-tracking branch 'mich/develop' into hidePrivate
Resolved merge conflict
This commit is contained in:
@@ -45,9 +45,9 @@ The following properties can be configured:
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>display_time</code></td>
|
||||
<td>Time a notification is displayed in seconds.<br>
|
||||
<br><b>Possible values:</b> <code>float</code> <code>int</code>
|
||||
<br><b>Default value:</b> <code>3.5</code>
|
||||
<td>Time a notification is displayed in milliseconds.<br>
|
||||
<br><b>Possible values:</b> <code>int</code>
|
||||
<br><b>Default value:</b> <code>3500</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -62,7 +62,7 @@ The following properties can be configured:
|
||||
<td><code>welcome_message</code></td>
|
||||
<td>Message shown at startup.<br>
|
||||
<br><b>Possible values:</b> <code>string</code> <code>false</code>
|
||||
<br><b>Default value:</b> <code>Welcome, start was successfull!</code>
|
||||
<br><b>Default value:</b> <code>false</code> (no message at startup)
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -162,4 +162,4 @@ self.sendNotification("SHOW_ALERT", {});
|
||||
|
||||
## Open Source Licenses
|
||||
###[NotificationStyles](https://github.com/codrops/NotificationStyles)
|
||||
See [ympanus.net](http://tympanus.net/codrops/licensing/) for license.
|
||||
See [ympanus.net](http://tympanus.net/codrops/licensing/) for license.
|
||||
|
4
modules/default/alert/translations/da.json
Normal file
4
modules/default/alert/translations/da.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notifikation",
|
||||
"welcome": "Velkommen, modulet er succesfuldt startet!"
|
||||
}
|
4
modules/default/alert/translations/es.json
Normal file
4
modules/default/alert/translations/es.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"sysTitle": "MagicMirror Notificaciones",
|
||||
"welcome": "Bienvenido, ¡se iniciado correctamente!"
|
||||
}
|
@@ -106,10 +106,16 @@ The following properties can be configured:
|
||||
<td><code>titleReplace</code></td>
|
||||
<td>An object of textual replacements applied to the tile of the event. This allow to remove or replace certains words in the title.<br>
|
||||
<br><b>Example:</b> <br>
|
||||
|
||||
<code>
|
||||
titleReplace: {'Birthday of ' : '', 'foo':'bar'}
|
||||
</code>
|
||||
<br><b>Default value:</b>
|
||||
<code>
|
||||
{
|
||||
"De verjaardag van ": "",
|
||||
"'s birthday": ""
|
||||
}
|
||||
</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -119,6 +125,13 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>false</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>dateFormat</code></td>
|
||||
<td>Format to use for the date of events (when using absolute dates)<br>
|
||||
<br><b>Possible values:</b> See <a href="http://momentjs.com/docs/#/parsing/string-format/">Moment.js formats</a>
|
||||
<br><b>Default value:</b> <code>MMM Do</code> (e.g. Jan 18th)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>timeFormat</code></td>
|
||||
<td>Display event times as absolute dates, or relative time<br>
|
||||
@@ -126,12 +139,26 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>relative</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>getRelative</code></td>
|
||||
<td>How much time (in hours) should be left until calendar events start getting relative?<br>
|
||||
<br><b>Possible values:</b> <code>0</code> (events stay absolute) - <code>48</code> (48 hours before the event starts)
|
||||
<br><b>Default value:</b> <code>6</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>urgency</code></td>
|
||||
<td>When using a timeFormat of <code>absolute</code>, the <code>urgency</code> setting allows you to display events within a specific time frame as <code>relative</code>
|
||||
This allows events within a certain time frame to be displayed as relative (in xx days) while others are displayed as absolute dates<br>
|
||||
<br><b>Possible values:</b> a positive integer representing the number of days for which you want a relative date, for example <code>7</code> (for 7 days)<br>
|
||||
<br><b>Default value:</b> <code>0</code> (disabled)
|
||||
<br><b>Default value:</b> <code>7</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>broadcastEvents</code></td>
|
||||
<td>If this property is set to true, the calendar will broadcast all the events to all other modules with the notification message: <code>CALENDAR_EVENTS</code>. The event objects are stored in an array and contain the following fields: <code>title</code>, <code>startDate</code>, <code>endDate</code>, <code>fullDayEvent</code>, <code>location</code> and <code>geo</code>.<br>
|
||||
<br><b>Possible values:</b> <code>true</code>, <code>false</code> <br>
|
||||
<br><b>Default value:</b> <code>true</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -178,24 +205,24 @@ config: {
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code> symbol </code></td>
|
||||
<td><code>symbol</code></td>
|
||||
<td>The symbol to show in front of an event. This property is optional.<br>
|
||||
<br><b>Possible values:</b> See <a href="http://fontawesome.io/icons/" target="_blank">Font Awesome</a> website.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code> repeatingCountTitle </code></td>
|
||||
<td><code>repeatingCountTitle</code></td>
|
||||
<td>The count title for yearly repating events in this calendar. <br>
|
||||
<br><b>Example:</b> <br>
|
||||
<code>'Birthday'</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code> user </code></td>
|
||||
<td><code>user</code></td>
|
||||
<td>The username for HTTP Basic authentication.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code> pass </code></td>
|
||||
<td><code>pass</code></td>
|
||||
<td>The password for HTTP Basic authentication.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
* MIT Licensed.
|
||||
*/
|
||||
|
||||
Module.register("calendar",{
|
||||
Module.register("calendar", {
|
||||
|
||||
// Define module defaults
|
||||
defaults: {
|
||||
@@ -16,13 +16,15 @@ Module.register("calendar",{
|
||||
displaySymbol: true,
|
||||
defaultSymbol: "calendar", // Fontawesome Symbol see http://fontawesome.io/cheatsheet/
|
||||
displayRepeatingCountTitle: false,
|
||||
defaultRepeatingCountTitle: '',
|
||||
defaultRepeatingCountTitle: "",
|
||||
maxTitleLength: 25,
|
||||
fetchInterval: 5 * 60 * 1000, // Update every 5 minutes.
|
||||
animationSpeed: 2000,
|
||||
fade: true,
|
||||
urgency: 7,
|
||||
timeFormat: "relative",
|
||||
dateFormat: "MMM Do",
|
||||
getRelative: 6,
|
||||
fadePoint: 0.25, // Start on 1/4th of the list.
|
||||
hidePrivate: false,
|
||||
calendars: [
|
||||
@@ -35,20 +37,21 @@ Module.register("calendar",{
|
||||
"De verjaardag van ": "",
|
||||
"'s birthday": ""
|
||||
},
|
||||
broadcastEvents: true
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getStyles: function() {
|
||||
getStyles: function () {
|
||||
return ["calendar.css", "font-awesome.css"];
|
||||
},
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
getScripts: function () {
|
||||
return ["moment.js"];
|
||||
},
|
||||
|
||||
// Define required translations.
|
||||
getTranslations: function() {
|
||||
getTranslations: function () {
|
||||
// The translations for the defaut modules are defined in the core translation files.
|
||||
// Therefor we can just return false. Otherwise we should have returned a dictionairy.
|
||||
// If you're trying to build your own module including translations, check out the documentation.
|
||||
@@ -56,7 +59,7 @@ Module.register("calendar",{
|
||||
},
|
||||
|
||||
// Override start method.
|
||||
start: function() {
|
||||
start: function () {
|
||||
Log.log("Starting module: " + this.name);
|
||||
|
||||
// Set locale.
|
||||
@@ -73,11 +76,15 @@ Module.register("calendar",{
|
||||
},
|
||||
|
||||
// Override socket notification handler.
|
||||
socketNotificationReceived: function(notification, payload) {
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
if (this.hasCalendarURL(payload.url)) {
|
||||
this.calendarData[payload.url] = payload.events;
|
||||
this.loaded = true;
|
||||
|
||||
if (this.config.broadcastEvents) {
|
||||
this.broadcastEvents();
|
||||
}
|
||||
}
|
||||
} else if (notification === "FETCH_ERROR") {
|
||||
Log.error("Calendar Error. Could not fetch calendar: " + payload.url);
|
||||
@@ -91,7 +98,7 @@ Module.register("calendar",{
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
getDom: function () {
|
||||
|
||||
var events = this.createEventList();
|
||||
var wrapper = document.createElement("table");
|
||||
@@ -110,27 +117,27 @@ Module.register("calendar",{
|
||||
eventWrapper.className = "normal";
|
||||
|
||||
if (this.config.displaySymbol) {
|
||||
var symbolWrapper = document.createElement("td");
|
||||
var symbolWrapper = document.createElement("td");
|
||||
symbolWrapper.className = "symbol";
|
||||
var symbol = document.createElement("span");
|
||||
var symbol = document.createElement("span");
|
||||
symbol.className = "fa fa-" + this.symbolForUrl(event.url);
|
||||
symbolWrapper.appendChild(symbol);
|
||||
eventWrapper.appendChild(symbolWrapper);
|
||||
}
|
||||
|
||||
var titleWrapper = document.createElement("td"),
|
||||
repeatingCountTitle = '';
|
||||
repeatingCountTitle = "";
|
||||
|
||||
|
||||
if (this.config.displayRepeatingCountTitle) {
|
||||
|
||||
repeatingCountTitle = this.countTitleForUrl(event.url);
|
||||
|
||||
if(repeatingCountTitle !== '') {
|
||||
if (repeatingCountTitle !== "") {
|
||||
var thisYear = new Date().getFullYear(),
|
||||
yearDiff = thisYear - event.firstYear;
|
||||
|
||||
repeatingCountTitle = ', '+ yearDiff + '. ' + repeatingCountTitle;
|
||||
repeatingCountTitle = ", " + yearDiff + ". " + repeatingCountTitle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,28 +145,24 @@ Module.register("calendar",{
|
||||
titleWrapper.className = "title bright";
|
||||
eventWrapper.appendChild(titleWrapper);
|
||||
|
||||
var timeWrapper = document.createElement("td");
|
||||
var timeWrapper = document.createElement("td");
|
||||
//console.log(event.today);
|
||||
var now = new Date();
|
||||
// Define second, minute, hour, and day variables
|
||||
var one_second = 1000; // 1,000 milliseconds
|
||||
var one_minute = one_second * 60;
|
||||
var one_hour = one_minute * 60;
|
||||
var one_day = one_hour * 24;
|
||||
var oneSecond = 1000; // 1,000 milliseconds
|
||||
var oneMinute = oneSecond * 60;
|
||||
var oneHour = oneMinute * 60;
|
||||
var oneDay = oneHour * 24;
|
||||
if (event.fullDayEvent) {
|
||||
if (event.today) {
|
||||
timeWrapper.innerHTML = this.translate("TODAY");
|
||||
} else if (event.startDate - now < one_day && event.startDate - now > 0) {
|
||||
timeWrapper.innerHTML = this.translate("TOMORROW");
|
||||
} else if (event.startDate - now < 2*one_day && event.startDate - now > 0) {
|
||||
/*Provide ability to show "the day after tomorrow" instead of "in a day"
|
||||
*if "DAYAFTERTOMORROW" is configured in a language's translation .json file,
|
||||
*,which can be found in MagicMirror/translations/
|
||||
*/
|
||||
if (this.translate('DAYAFTERTOMORROW') !== 'DAYAFTERTOMORROW') {
|
||||
timeWrapper.innerHTML = this.translate("DAYAFTERTOMORROW");
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("TODAY"));
|
||||
} else if (event.startDate - now < oneDay && event.startDate - now > 0) {
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW"));
|
||||
} else if (event.startDate - now < 2 * oneDay && event.startDate - now > 0) {
|
||||
if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") {
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW"));
|
||||
} else {
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
}
|
||||
} else {
|
||||
/* Check to see if the user displays absolute or relative dates with their events
|
||||
@@ -170,26 +173,26 @@ Module.register("calendar",{
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||
// This event falls within the config.urgency period that the user has set
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").format("MMM Do");
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
||||
}
|
||||
} else {
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (event.startDate >= new Date()) {
|
||||
if (event.startDate - now < 2 * one_day) {
|
||||
if (event.startDate - now < 2 * oneDay) {
|
||||
// This event is within the next 48 hours (2 days)
|
||||
if (event.startDate - now < 6 * one_hour) {
|
||||
if (event.startDate - now < this.config.getRelative * oneHour) {
|
||||
// If event is within 6 hour, display 'in xxx' time format or moment.fromNow()
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
// Otherwise just say 'Today/Tomorrow at such-n-such time'
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").calendar();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar());
|
||||
}
|
||||
} else {
|
||||
/* Check to see if the user displays absolute or relative dates with their events
|
||||
@@ -200,18 +203,18 @@ Module.register("calendar",{
|
||||
* Note: this needs to be put in its own function, as the whole thing repeats again verbatim
|
||||
*/
|
||||
if (this.config.timeFormat === "absolute") {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * one_day))) {
|
||||
if ((this.config.urgency > 1) && (event.startDate - now < (this.config.urgency * oneDay))) {
|
||||
// This event falls within the config.urgency period that the user has set
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
} else {
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").format("MMM Do");
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat));
|
||||
}
|
||||
} else {
|
||||
timeWrapper.innerHTML = moment(event.startDate, "x").fromNow();
|
||||
timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timeWrapper.innerHTML = this.translate("RUNNING") + ' ' + moment(event.endDate,"x").fromNow(true);
|
||||
timeWrapper.innerHTML = this.capFirst(this.translate("RUNNING")) + " " + moment(event.endDate, "x").fromNow(true);
|
||||
}
|
||||
}
|
||||
//timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
|
||||
@@ -245,7 +248,7 @@ Module.register("calendar",{
|
||||
*
|
||||
* return bool - Has calendar url
|
||||
*/
|
||||
hasCalendarURL: function(url) {
|
||||
hasCalendarURL: function (url) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url) {
|
||||
@@ -261,7 +264,7 @@ Module.register("calendar",{
|
||||
*
|
||||
* return array - Array with events.
|
||||
*/
|
||||
createEventList: function() {
|
||||
createEventList: function () {
|
||||
var events = [];
|
||||
var today = moment().startOf("day");
|
||||
for (var c in this.calendarData) {
|
||||
@@ -280,7 +283,7 @@ Module.register("calendar",{
|
||||
}
|
||||
}
|
||||
|
||||
events.sort(function(a, b) {
|
||||
events.sort(function (a, b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
@@ -292,7 +295,7 @@ Module.register("calendar",{
|
||||
*
|
||||
* argument url sting - Url to add.
|
||||
*/
|
||||
addCalendar: function(url, user, pass) {
|
||||
addCalendar: function (url, user, pass) {
|
||||
this.sendSocketNotification("ADD_CALENDAR", {
|
||||
url: url,
|
||||
maximumEntries: this.config.maximumEntries,
|
||||
@@ -310,10 +313,10 @@ Module.register("calendar",{
|
||||
*
|
||||
* return string - The Symbol
|
||||
*/
|
||||
symbolForUrl: function(url) {
|
||||
symbolForUrl: function (url) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url && typeof calendar.symbol === "string") {
|
||||
if (calendar.url === url && typeof calendar.symbol === "string") {
|
||||
return calendar.symbol;
|
||||
}
|
||||
}
|
||||
@@ -327,10 +330,10 @@ Module.register("calendar",{
|
||||
*
|
||||
* return string - The Symbol
|
||||
*/
|
||||
countTitleForUrl: function(url) {
|
||||
countTitleForUrl: function (url) {
|
||||
for (var c in this.config.calendars) {
|
||||
var calendar = this.config.calendars[c];
|
||||
if (calendar.url === url && typeof calendar.repeatingCountTitle === "string") {
|
||||
if (calendar.url === url && typeof calendar.repeatingCountTitle === "string") {
|
||||
return calendar.repeatingCountTitle;
|
||||
}
|
||||
}
|
||||
@@ -347,14 +350,23 @@ Module.register("calendar",{
|
||||
*
|
||||
* return string - The shortened string.
|
||||
*/
|
||||
shorten: function(string, maxLength) {
|
||||
shorten: function (string, maxLength) {
|
||||
if (string.length > maxLength) {
|
||||
return string.slice(0,maxLength) + "…";
|
||||
return string.slice(0, maxLength) + "…";
|
||||
}
|
||||
|
||||
return string;
|
||||
},
|
||||
|
||||
/* capFirst(string)
|
||||
* Capitalize the first letter of a string
|
||||
* Eeturn capitalized string
|
||||
*/
|
||||
|
||||
capFirst: function (string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
},
|
||||
|
||||
/* titleTransform(title)
|
||||
* Transforms the title of an event for usage.
|
||||
* Replaces parts of the text as defined in config.titleReplace.
|
||||
@@ -364,7 +376,7 @@ Module.register("calendar",{
|
||||
*
|
||||
* return string - The transformed title.
|
||||
*/
|
||||
titleTransform: function(title) {
|
||||
titleTransform: function (title) {
|
||||
for (var needle in this.config.titleReplace) {
|
||||
var replacement = this.config.titleReplace[needle];
|
||||
title = title.replace(needle, replacement);
|
||||
@@ -372,5 +384,28 @@ Module.register("calendar",{
|
||||
|
||||
title = this.shorten(title, this.config.maxTitleLength);
|
||||
return title;
|
||||
},
|
||||
|
||||
/* broadcastEvents()
|
||||
* Broadcasts the events to all other modules for reuse.
|
||||
* The all events available in one array, sorted on startdate.
|
||||
*/
|
||||
broadcastEvents: function () {
|
||||
var eventList = [];
|
||||
for (url in this.calendarData) {
|
||||
var calendar = this.calendarData[url];
|
||||
for (e in calendar) {
|
||||
var event = cloneObject(calendar[e]);
|
||||
delete event.url;
|
||||
eventList.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
eventList.sort(function(a,b) {
|
||||
return a.startDate - b.startDate;
|
||||
});
|
||||
|
||||
this.sendNotification("CALENDAR_EVENTS", eventList);
|
||||
|
||||
}
|
||||
});
|
||||
|
@@ -25,9 +25,10 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
|
||||
clearTimeout(reloadTimer);
|
||||
reloadTimer = null;
|
||||
|
||||
nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
var opts = {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Node.js 6.0.0) MagicMirror/v2 (https://github.com/MichMich/MagicMirror/)'
|
||||
"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -77,10 +78,11 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
|
||||
if (!isFacebookBirthday) {
|
||||
endDate = startDate;
|
||||
} else {
|
||||
endDate = moment(startDate).add(1, 'days');
|
||||
endDate = moment(startDate).add(1, "days");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// calculate the duration f the event for use with recurring events.
|
||||
var duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x"));
|
||||
|
||||
@@ -95,13 +97,17 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
|
||||
title = event.description;
|
||||
}
|
||||
|
||||
var location = event.location || false;
|
||||
var geo = event.geo || false;
|
||||
var description = event.description || false;
|
||||
|
||||
if (typeof event.rrule != "undefined" && !isFacebookBirthday) {
|
||||
var rule = event.rrule;
|
||||
var dates = rule.between(today, future, true, limitFunction);
|
||||
|
||||
for (var d in dates) {
|
||||
startDate = moment(new Date(dates[d]));
|
||||
endDate = moment(parseInt(startDate.format("x")) + duration, 'x');
|
||||
endDate = moment(parseInt(startDate.format("x")) + duration, "x");
|
||||
if (endDate.format("x") > now) {
|
||||
newEvents.push({
|
||||
title: title,
|
||||
@@ -109,7 +115,10 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
|
||||
endDate: endDate.format("x"),
|
||||
fullDayEvent: isFullDayEvent(event),
|
||||
class: event.class,
|
||||
firstYear: event.start.getFullYear()
|
||||
firstYear: event.start.getFullYear(),
|
||||
location: location,
|
||||
geo: geo,
|
||||
description: description
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -133,15 +142,19 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
|
||||
continue;
|
||||
}
|
||||
|
||||
// Every thing is good. Add it to the list.
|
||||
// Every thing is good. Add it to the list.
|
||||
|
||||
newEvents.push({
|
||||
title: title,
|
||||
startDate: startDate.format("x"),
|
||||
endDate: endDate.format("x"),
|
||||
fullDayEvent: fullDayEvent,
|
||||
class: event.class
|
||||
location: location,
|
||||
geo: geo,
|
||||
description: description
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -188,7 +201,7 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
|
||||
|
||||
if (end - start === 24 * 60 * 60 * 1000 && startDate.getHours() === 0 && startDate.getMinutes() === 0) {
|
||||
// Is 24 hours, and starts on the middle of the night.
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@@ -8,18 +8,18 @@
|
||||
|
||||
var CalendarFetcher = require("./calendarfetcher.js");
|
||||
|
||||
var url = 'https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics';
|
||||
var url = "https://calendar.google.com/calendar/ical/pkm1t2uedjbp0uvq1o7oj1jouo%40group.calendar.google.com/private-08ba559f89eec70dd74bbd887d0a3598/basic.ics";
|
||||
var fetchInterval = 60 * 60 * 1000;
|
||||
var maximumEntries = 10;
|
||||
var maximumNumberOfDays = 365;
|
||||
|
||||
console.log('Create fetcher ...');
|
||||
console.log("Create fetcher ...");
|
||||
|
||||
fetcher = new CalendarFetcher(url, fetchInterval, maximumEntries, maximumNumberOfDays);
|
||||
|
||||
fetcher.onReceive(function(fetcher) {
|
||||
console.log(fetcher.events());
|
||||
console.log('------------------------------------------------------------');
|
||||
console.log(fetcher.events());
|
||||
console.log("------------------------------------------------------------");
|
||||
});
|
||||
|
||||
fetcher.onError(function(fetcher, error) {
|
||||
@@ -29,5 +29,5 @@ fetcher.onError(function(fetcher, error) {
|
||||
|
||||
fetcher.startFetch();
|
||||
|
||||
console.log('Create fetcher done! ');
|
||||
console.log("Create fetcher done! ");
|
||||
|
||||
|
@@ -115,5 +115,13 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>top</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>timezone</code></td>
|
||||
<td>Specific a timezone to show clock.<br>
|
||||
<br><b>Possible examples values:</b> <code>America/New_York</code>, <code>America/Santiago</code>, <code>Etc/GMT+10</code>
|
||||
<br><b>Default value:</b> <code>none</code>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
Module.register("clock",{
|
||||
// Module config defaults.
|
||||
defaults: {
|
||||
displayType: 'digital', // options: digital, analog, both
|
||||
displayType: "digital", // options: digital, analog, both
|
||||
|
||||
timeFormat: config.timeFormat,
|
||||
displaySeconds: true,
|
||||
@@ -18,15 +18,16 @@ Module.register("clock",{
|
||||
showDate: true,
|
||||
|
||||
/* 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',
|
||||
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,
|
||||
},
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
return ["moment.js"];
|
||||
return ["moment.js", "moment-timezone.js"];
|
||||
},
|
||||
// Define styles.
|
||||
getStyles: function() {
|
||||
@@ -69,10 +70,14 @@ Module.register("clock",{
|
||||
// So we need to generate the timestring manually.
|
||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
||||
var timeString;
|
||||
var now = moment();
|
||||
if (this.config.timezone) {
|
||||
now.tz(this.config.timezone);
|
||||
}
|
||||
if (this.config.clockBold === true) {
|
||||
timeString = moment().format("HH[<span class=\"bold\">]mm[</span>]");
|
||||
timeString = now.format("HH[<span class=\"bold\">]mm[</span>]");
|
||||
} else {
|
||||
timeString = moment().format("HH:mm");
|
||||
timeString = now.format("HH:mm");
|
||||
}
|
||||
|
||||
if (this.config.timeFormat !== 24) {
|
||||
@@ -80,27 +85,21 @@ Module.register("clock",{
|
||||
// var hours = now.getHours() % 12 || 12;
|
||||
if (this.config.clockBold === true) {
|
||||
//timeString = hours + moment().format("[<span class=\"bold\">]mm[</span>]");
|
||||
timeString = moment().format("h[<span class=\"bold\">]mm[</span>]");
|
||||
timeString = now.format("h[<span class=\"bold\">]mm[</span>]");
|
||||
} else {
|
||||
//timeString = hours + moment().format(":mm");
|
||||
timeString = moment().format("h:mm");
|
||||
timeString = now.format("h:mm");
|
||||
}
|
||||
}
|
||||
if(this.config.showDate){
|
||||
dateWrapper.innerHTML = moment().format("dddd, LL");
|
||||
dateWrapper.innerHTML = now.format("dddd, LL");
|
||||
}
|
||||
timeWrapper.innerHTML = timeString;
|
||||
secondsWrapper.innerHTML = moment().format("ss");
|
||||
secondsWrapper.innerHTML = now.format("ss");
|
||||
if (this.config.showPeriodUpper) {
|
||||
periodWrapper.innerHTML = moment().format("A");
|
||||
periodWrapper.innerHTML = now.format("A");
|
||||
} else {
|
||||
periodWrapper.innerHTML = moment().format("a");
|
||||
}
|
||||
if (this.config.displaySeconds) {
|
||||
timeWrapper.appendChild(secondsWrapper);
|
||||
}
|
||||
if (this.config.showPeriod && this.config.timeFormat !== 24) {
|
||||
timeWrapper.appendChild(periodWrapper);
|
||||
periodWrapper.innerHTML = now.format("a");
|
||||
}
|
||||
if (this.config.displaySeconds) {
|
||||
timeWrapper.appendChild(secondsWrapper);
|
||||
@@ -113,12 +112,15 @@ Module.register("clock",{
|
||||
* Create wrappers for ANALOG clock, only if specified in config
|
||||
*/
|
||||
|
||||
if (this.config.displayType !== 'digital') {
|
||||
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(),
|
||||
second = now.seconds() * 6,
|
||||
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;
|
||||
|
||||
@@ -129,10 +131,10 @@ Module.register("clock",{
|
||||
clockCircle.style.width = this.config.analogSize;
|
||||
clockCircle.style.height = this.config.analogSize;
|
||||
|
||||
if (this.config.analogFace != '' && this.config.analogFace != 'simple' && this.config.analogFace != 'none') {
|
||||
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%";
|
||||
} else if (this.config.analogFace != 'none') {
|
||||
} else if (this.config.analogFace != "none") {
|
||||
clockCircle.style.border = "2px solid white";
|
||||
}
|
||||
var clockFace = document.createElement("div");
|
||||
@@ -166,18 +168,18 @@ Module.register("clock",{
|
||||
* Combine wrappers, check for .displayType
|
||||
*/
|
||||
|
||||
if (this.config.displayType === 'digital') {
|
||||
if (this.config.displayType === "digital") {
|
||||
// Display only a digital clock
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(timeWrapper);
|
||||
} else if (this.config.displayType === 'analog') {
|
||||
} else if (this.config.displayType === "analog") {
|
||||
// Display only an analog clock
|
||||
dateWrapper.style.textAlign = "center";
|
||||
dateWrapper.style.paddingBottom = "15px";
|
||||
if (this.config.analogShowDate === 'top') {
|
||||
if (this.config.analogShowDate === "top") {
|
||||
wrapper.appendChild(dateWrapper);
|
||||
wrapper.appendChild(clockCircle);
|
||||
} else if (this.config.analogShowDate === 'bottom') {
|
||||
} else if (this.config.analogShowDate === "bottom") {
|
||||
wrapper.appendChild(clockCircle);
|
||||
wrapper.appendChild(dateWrapper);
|
||||
} else {
|
||||
@@ -197,11 +199,11 @@ Module.register("clock",{
|
||||
digitalWrapper.appendChild(dateWrapper);
|
||||
digitalWrapper.appendChild(timeWrapper);
|
||||
|
||||
if (placement === 'left' || placement === 'right') {
|
||||
if (placement === "left" || placement === "right") {
|
||||
digitalWrapper.style.display = "inline-block";
|
||||
digitalWrapper.style.verticalAlign = "top";
|
||||
analogWrapper.style.display = "inline-block";
|
||||
if (placement === 'left') {
|
||||
if (placement === "left") {
|
||||
analogWrapper.style.padding = "0 20px 0 0";
|
||||
wrapper.appendChild(analogWrapper);
|
||||
wrapper.appendChild(digitalWrapper);
|
||||
@@ -212,7 +214,7 @@ Module.register("clock",{
|
||||
}
|
||||
} else {
|
||||
digitalWrapper.style.textAlign = "center";
|
||||
if (placement === 'top') {
|
||||
if (placement === "top") {
|
||||
analogWrapper.style.padding = "0 0 20px 0";
|
||||
wrapper.appendChild(analogWrapper);
|
||||
wrapper.appendChild(digitalWrapper);
|
||||
@@ -227,4 +229,4 @@ Module.register("clock",{
|
||||
// Return the wrapper to the dom.
|
||||
return wrapper;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@@ -56,6 +56,15 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> See <i>compliment configuration</i> below.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>remoteFile</code></td>
|
||||
<td>External file from which to load the compliments<br>
|
||||
<br><b>Possible values:</b>Path to a JSON file containing compliments, configured
|
||||
as per the value of the <i>compliments configuration</i> (see below). An object with three arrays:
|
||||
morning, afternoon and evening. - <code>compliments.json</code>
|
||||
<br><b>Default value:</b> <code>null</code> (Do not load from file)
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -63,6 +72,44 @@ The following properties can be configured:
|
||||
|
||||
The `compliments` property contains an object with three arrays: <code>morning</code>, <code>afternoon</code> and<code>evening</code>. Based on the time of the day, the compliments will be picked out of one of these arrays. The arrays contain one or multiple compliments.
|
||||
|
||||
|
||||
If use the currentweather is possible use a actual weather for set compliments. The availables properties are:
|
||||
* <code>day_sunny</code>
|
||||
* <code>day_cloudy</code>
|
||||
* <code>cloudy</code>
|
||||
* <code>cloudy_windy</code>
|
||||
* <code>showers</code>
|
||||
* <code>rain</code>
|
||||
* <code>thunderstorm</code>
|
||||
* <code>snow</code>
|
||||
* <code>fog</code>
|
||||
* <code>night_clear</code>
|
||||
* <code>night_cloudy</code>
|
||||
* <code>night_showers</code>
|
||||
* <code>night_rain</code>
|
||||
* <code>night_thunderstorm</code>
|
||||
* <code>night_snow</code>
|
||||
* <code>night_alt_cloudy_windy</code>
|
||||
|
||||
#### Example use with currentweather module
|
||||
````javascript
|
||||
config: {
|
||||
compliments: {
|
||||
day_sunny: [
|
||||
'Today is a sunny day',
|
||||
'It\'s a beautiful day'
|
||||
],
|
||||
snow: [
|
||||
'Snowball battle!'
|
||||
],
|
||||
rain: [
|
||||
'Don\'t forget your umbrella'
|
||||
]
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
|
||||
#### Default value:
|
||||
````javascript
|
||||
config: {
|
||||
@@ -85,3 +132,32 @@ config: {
|
||||
}
|
||||
}
|
||||
````
|
||||
|
||||
### External Compliment File
|
||||
You may specify an external file that contains the three compliment arrays. This is particularly useful if you have a
|
||||
large number of compliments and do not wish to crowd your `config.js` file with a large array of compliments.
|
||||
Adding the `remoteFile` variable will override an array you specify in the configuration file.
|
||||
|
||||
This file must be straight JSON. Note that the array names need quotes
|
||||
around them ("morning", "afternoon", "evening", "snow", "rain", etc.).
|
||||
#### Example compliments.json file:
|
||||
````json
|
||||
{
|
||||
"morning" : [
|
||||
"Good morning, sunshine!",
|
||||
"Who needs coffee when you have your smile?",
|
||||
"Go get 'em, Tiger!"
|
||||
],
|
||||
"afternoon" : [
|
||||
"Hitting your stride!",
|
||||
"You are making a difference!",
|
||||
"You're more fun than bubble wrap!"
|
||||
],
|
||||
"evening" : [
|
||||
"You made someone smile today, I know it.",
|
||||
"You are making a difference.",
|
||||
"The day was better for your efforts."
|
||||
]
|
||||
}
|
||||
````
|
||||
|
||||
|
@@ -29,9 +29,13 @@ Module.register("compliments",{
|
||||
]
|
||||
},
|
||||
updateInterval: 30000,
|
||||
remoteFile: null,
|
||||
fadeSpeed: 4000
|
||||
},
|
||||
|
||||
// Set currentweather from module
|
||||
currentWeatherType: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
return ["moment.js"];
|
||||
@@ -43,6 +47,12 @@ Module.register("compliments",{
|
||||
|
||||
this.lastComplimentIndex = -1;
|
||||
|
||||
if (this.config.remoteFile != null) {
|
||||
this.complimentFile((response) => {
|
||||
this.config.compliments = JSON.parse(response);
|
||||
});
|
||||
}
|
||||
|
||||
// Schedule update timer.
|
||||
var self = this;
|
||||
setInterval(function() {
|
||||
@@ -84,14 +94,36 @@ Module.register("compliments",{
|
||||
*/
|
||||
complimentArray: function() {
|
||||
var hour = moment().hour();
|
||||
var compliments = null;
|
||||
|
||||
if (hour >= 3 && hour < 12) {
|
||||
return this.config.compliments.morning;
|
||||
compliments = this.config.compliments.morning;
|
||||
} else if (hour >= 12 && hour < 17) {
|
||||
return this.config.compliments.afternoon;
|
||||
compliments = this.config.compliments.afternoon;
|
||||
} else {
|
||||
return this.config.compliments.evening;
|
||||
compliments = this.config.compliments.evening;
|
||||
}
|
||||
|
||||
if ( this.currentWeatherType in this.config.compliments) {
|
||||
compliments.push.apply(compliments, this.config.compliments[this.currentWeatherType]);
|
||||
}
|
||||
return compliments;
|
||||
|
||||
},
|
||||
|
||||
/* complimentFile(callback)
|
||||
* Retrieve a file from the local filesystem
|
||||
*/
|
||||
complimentFile: function(callback) {
|
||||
var xobj = new XMLHttpRequest();
|
||||
xobj.overrideMimeType("application/json");
|
||||
xobj.open("GET", this.file(this.config.remoteFile), true);
|
||||
xobj.onreadystatechange = function () {
|
||||
if (xobj.readyState == 4 && xobj.status == "200") {
|
||||
callback(xobj.responseText);
|
||||
}
|
||||
};
|
||||
xobj.send(null);
|
||||
},
|
||||
|
||||
/* complimentArray()
|
||||
@@ -112,10 +144,44 @@ Module.register("compliments",{
|
||||
|
||||
var compliment = document.createTextNode(complimentText);
|
||||
var wrapper = document.createElement("div");
|
||||
wrapper.className = "thin xlarge bright";
|
||||
wrapper.className = this.config.classes ? this.config.classes : "thin xlarge bright";
|
||||
wrapper.appendChild(compliment);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// From data currentweather set weather type
|
||||
setCurrentWeatherType: function(data) {
|
||||
var weatherIconTable = {
|
||||
"01d": "day_sunny",
|
||||
"02d": "day_cloudy",
|
||||
"03d": "cloudy",
|
||||
"04d": "cloudy_windy",
|
||||
"09d": "showers",
|
||||
"10d": "rain",
|
||||
"11d": "thunderstorm",
|
||||
"13d": "snow",
|
||||
"50d": "fog",
|
||||
"01n": "night_clear",
|
||||
"02n": "night_cloudy",
|
||||
"03n": "night_cloudy",
|
||||
"04n": "night_cloudy",
|
||||
"09n": "night_showers",
|
||||
"10n": "night_rain",
|
||||
"11n": "night_thunderstorm",
|
||||
"13n": "night_snow",
|
||||
"50n": "night_alt_cloudy_windy"
|
||||
};
|
||||
this.currentWeatherType = weatherIconTable[data.weather[0].icon];
|
||||
},
|
||||
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
if (notification == "CURRENTWEATHER_DATA") {
|
||||
this.setCurrentWeatherType(payload.data);
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
@@ -14,7 +14,7 @@ modules: [
|
||||
config: {
|
||||
// See 'Configuration options' for more information.
|
||||
location: 'Amsterdam,Netherlands',
|
||||
locationID: '', //Location ID from http://bulk.openweather.org/sample/
|
||||
locationID: '', //Location ID from http://openweathermap.org/help/city_list.txt
|
||||
appid: 'abcde12345abcde12345abcde12345ab' //openweathermap.org API key.
|
||||
}
|
||||
}
|
||||
@@ -35,19 +35,20 @@ The following properties can be configured:
|
||||
</tr>
|
||||
<thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td><code>location</code></td>
|
||||
<td>The location used for weather information.<br>
|
||||
<br><b>Example:</b> <code>Amsterdam,Netherlands</code>
|
||||
<br><b>Default value:</b> <code>New York</code>
|
||||
<br><b>Example:</b> <code>'Amsterdam,Netherlands'</code>
|
||||
<br><b>Default value:</b> <code>false</code><br><br>
|
||||
<strong>Note:</strong> When the <code>location</code> and <code>locationID</code> are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>locationID</code></td>
|
||||
<td>Location ID from <a href="http://bulk.openweather.org/sample/">OpenWeather</a> <b>This will override anything you put in location.</b><br>Leave blank if you want to use location.
|
||||
<td>Location ID from <a href="http://openweathermap.org/help/city_list.txt">OpenWeatherMap</a> <b>This will override anything you put in location.</b><br>Leave blank if you want to use location.
|
||||
<br><b>Example:</b> <code>1234567</code>
|
||||
<br><b>Default value:</b> <code></code>
|
||||
<br><b>Default value:</b> <code>false</code><br><br>
|
||||
<strong>Note:</strong> When the <code>location</code> and <code>locationID</code> are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -63,18 +64,25 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>config.units</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>roundTemp</code></td>
|
||||
<td>Round temperature value to nearest integer.<br>
|
||||
<br><b>Possible values:</b> <code>true</code> (round to integer) or <code>false</code> (display exact value with decimal point)
|
||||
<br><b>Default value:</b> <code>false</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>updateInterval</code></td>
|
||||
<td>How often does the content needs to be fetched? (Milliseconds)<br>
|
||||
<br><b>Possible values:</b> <code>1000</code> - <code>86400000</code>
|
||||
<br><b>Default value:</b> <code>300000</code> (10 minutes)
|
||||
<br><b>Default value:</b> <code>600000</code> (10 minutes)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>animationSpeed</code></td>
|
||||
<td>Speed of the update animation. (Milliseconds)<br>
|
||||
<br><b>Possible values:</b><code>0</code> - <code>5000</code>
|
||||
<br><b>Default value:</b> <code>2000</code> (2 seconds)
|
||||
<br><b>Default value:</b> <code>1000</code> (1 second)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -105,6 +113,20 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>true</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>showHumidity</code></td>
|
||||
<td>Show the current humidity<br>
|
||||
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
|
||||
<br><b>Default value:</b> <code>false</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>onlyTemp</code></td>
|
||||
<td>Show only current Temperature and weather icon.<br>
|
||||
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
|
||||
<br><b>Default value:</b> <code>false</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>useBeaufort</code></td>
|
||||
<td>Pick between using the Beaufort scale for wind speed or using the default units.<br>
|
||||
@@ -151,6 +173,18 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>'weather'</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appendLocationNameToHeader</code></td>
|
||||
<td>If set to <code>true</code>, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.<br>
|
||||
<br><b>Default value:</b> <code>true</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>calendarClass</code></td>
|
||||
<td>The class for the calender module to base the event based weather information on.<br>
|
||||
<br><b>Default value:</b> <code>'calendar'</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>iconTable</code></td>
|
||||
<td>The conversion table to convert the weather conditions to weather-icons.<br>
|
||||
|
@@ -6,3 +6,11 @@
|
||||
-webkit-transform: translate(0, -3px); /* Safari */
|
||||
transform: translate(0, -3px);
|
||||
}
|
||||
|
||||
.currentweather .humidityIcon {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.currentweather .humidity-padding {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
@@ -11,8 +11,8 @@ Module.register("currentweather",{
|
||||
|
||||
// Default module config.
|
||||
defaults: {
|
||||
location: "",
|
||||
locationID: "",
|
||||
location: false,
|
||||
locationID: false,
|
||||
appid: "",
|
||||
units: config.units,
|
||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
@@ -23,6 +23,7 @@ Module.register("currentweather",{
|
||||
showWindDirection: true,
|
||||
useBeaufort: true,
|
||||
lang: config.language,
|
||||
showHumidity: false,
|
||||
|
||||
initialLoadDelay: 0, // 0 seconds delay
|
||||
retryDelay: 2500,
|
||||
@@ -31,6 +32,12 @@ Module.register("currentweather",{
|
||||
apiBase: "http://api.openweathermap.org/data/",
|
||||
weatherEndpoint: "weather",
|
||||
|
||||
appendLocationNameToHeader: true,
|
||||
calendarClass: "calendar",
|
||||
|
||||
onlyTemp: false,
|
||||
roundTemp: false,
|
||||
|
||||
iconTable: {
|
||||
"01d": "wi-day-sunny",
|
||||
"02d": "wi-day-cloudy",
|
||||
@@ -53,6 +60,12 @@ Module.register("currentweather",{
|
||||
},
|
||||
},
|
||||
|
||||
// create a variable for the first upcoming calendaar event. Used if no location is specified.
|
||||
firstEvent: false,
|
||||
|
||||
// create a variable to hold the location name based on the API result.
|
||||
fetchedLocatioName: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
return ["moment.js"];
|
||||
@@ -88,35 +101,16 @@ Module.register("currentweather",{
|
||||
this.loaded = false;
|
||||
this.scheduleUpdate(this.config.initialLoadDelay);
|
||||
|
||||
this.updateTimer = null;
|
||||
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.config.appid === "") {
|
||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
||||
wrapper.className = "dimmed light small";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (this.config.location === "") {
|
||||
wrapper.innerHTML = "Please set the openweather <i>location</i> in the config for module: " + this.name + ".";
|
||||
wrapper.className = "dimmed light small";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (!this.loaded) {
|
||||
wrapper.innerHTML = this.translate('LOADING');
|
||||
wrapper.className = "dimmed light small";
|
||||
return wrapper;
|
||||
}
|
||||
// add extra information of current weather
|
||||
// windDirection, humidity, sunrise and sunset
|
||||
addExtraInfoWeather: function(wrapper) {
|
||||
|
||||
var small = document.createElement("div");
|
||||
small.className = "normal medium";
|
||||
|
||||
|
||||
var windIcon = document.createElement("span");
|
||||
windIcon.className = "wi wi-strong-wind dimmed";
|
||||
small.appendChild(windIcon);
|
||||
@@ -134,6 +128,22 @@ Module.register("currentweather",{
|
||||
spacer.innerHTML = " ";
|
||||
small.appendChild(spacer);
|
||||
|
||||
if (this.config.showHumidity) {
|
||||
var humidity = document.createElement("span");
|
||||
humidity.innerHTML = this.humidity;
|
||||
|
||||
var spacer = document.createElement("sup");
|
||||
spacer.innerHTML = " ";
|
||||
|
||||
var humidityIcon = document.createElement("sup");
|
||||
humidityIcon.className = "wi wi-humidity humidityIcon";
|
||||
humidityIcon.innerHTML = " ";
|
||||
|
||||
small.appendChild(humidity);
|
||||
small.appendChild(spacer);
|
||||
small.appendChild(humidityIcon);
|
||||
}
|
||||
|
||||
var sunriseSunsetIcon = document.createElement("span");
|
||||
sunriseSunsetIcon.className = "wi dimmed " + this.sunriseSunsetIcon;
|
||||
small.appendChild(sunriseSunsetIcon);
|
||||
@@ -142,6 +152,29 @@ Module.register("currentweather",{
|
||||
sunriseSunsetTime.innerHTML = " " + this.sunriseSunsetTime;
|
||||
small.appendChild(sunriseSunsetTime);
|
||||
|
||||
wrapper.appendChild(small);
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function() {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.config.appid === "") {
|
||||
wrapper.innerHTML = "Please set the correct openweather <i>appid</i> in the config for module: " + this.name + ".";
|
||||
wrapper.className = "dimmed light small";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (!this.loaded) {
|
||||
wrapper.innerHTML = this.translate("LOADING");
|
||||
wrapper.className = "dimmed light small";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (this.config.onlyTemp === false) {
|
||||
this.addExtraInfoWeather(wrapper);
|
||||
}
|
||||
|
||||
var large = document.createElement("div");
|
||||
large.className = "large light";
|
||||
|
||||
@@ -154,16 +187,54 @@ Module.register("currentweather",{
|
||||
temperature.innerHTML = " " + this.temperature + "°";
|
||||
large.appendChild(temperature);
|
||||
|
||||
wrapper.appendChild(small);
|
||||
wrapper.appendChild(large);
|
||||
return wrapper;
|
||||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
return this.data.header + " " + this.fetchedLocatioName;
|
||||
}
|
||||
|
||||
return this.data.header;
|
||||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
this.hide(0, {lockString: this.identifier});
|
||||
}
|
||||
}
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
var senderClasses = sender.data.classes.toLowerCase().split(" ");
|
||||
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
|
||||
var lastEvent = this.firstEvent;
|
||||
this.firstEvent = false;
|
||||
|
||||
for (e in payload) {
|
||||
var event = payload[e];
|
||||
if (event.location || event.geo) {
|
||||
this.firstEvent = event;
|
||||
//Log.log("First upcoming event with location: ", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* updateWeather(compliments)
|
||||
* Requests new data from openweather.org.
|
||||
* Calls processWeather on succesfull response.
|
||||
*/
|
||||
updateWeather: function() {
|
||||
if (this.config.appid === "") {
|
||||
Log.error("CurrentWeather: APPID not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.weatherEndpoint + this.getParams();
|
||||
var self = this;
|
||||
var retry = true;
|
||||
@@ -175,11 +246,10 @@ Module.register("currentweather",{
|
||||
if (this.status === 200) {
|
||||
self.processWeather(JSON.parse(this.response));
|
||||
} else if (this.status === 401) {
|
||||
self.config.appid = "";
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
Log.error(self.name + ": Incorrect APPID.");
|
||||
retry = false;
|
||||
retry = true;
|
||||
} else {
|
||||
Log.error(self.name + ": Could not load weather.");
|
||||
}
|
||||
@@ -199,11 +269,19 @@ Module.register("currentweather",{
|
||||
*/
|
||||
getParams: function() {
|
||||
var params = "?";
|
||||
if(this.config.locationID !== "") {
|
||||
if(this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else {
|
||||
} else if(this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
return;
|
||||
}
|
||||
|
||||
params += "&units=" + this.config.units;
|
||||
params += "&lang=" + this.config.lang;
|
||||
params += "&APPID=" + this.config.appid;
|
||||
@@ -224,6 +302,7 @@ Module.register("currentweather",{
|
||||
return;
|
||||
}
|
||||
|
||||
this.humidity = parseFloat(data.main.humidity);
|
||||
this.temperature = this.roundValue(data.main.temp);
|
||||
|
||||
if (this.config.useBeaufort){
|
||||
@@ -244,20 +323,20 @@ Module.register("currentweather",{
|
||||
// So we need to generate the timestring manually.
|
||||
// See issue: https://github.com/MichMich/MagicMirror/issues/181
|
||||
var sunriseSunsetDateObject = (sunrise < now && sunset > now) ? sunset : sunrise;
|
||||
var timeString = moment(sunriseSunsetDateObject).format('HH:mm');
|
||||
var timeString = moment(sunriseSunsetDateObject).format("HH:mm");
|
||||
if (this.config.timeFormat !== 24) {
|
||||
//var hours = sunriseSunsetDateObject.getHours() % 12 || 12;
|
||||
if (this.config.showPeriod) {
|
||||
if (this.config.showPeriodUpper) {
|
||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm A');
|
||||
timeString = moment(sunriseSunsetDateObject).format('h:mm A');
|
||||
timeString = moment(sunriseSunsetDateObject).format("h:mm A");
|
||||
} else {
|
||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm a');
|
||||
timeString = moment(sunriseSunsetDateObject).format('h:mm a');
|
||||
timeString = moment(sunriseSunsetDateObject).format("h:mm a");
|
||||
}
|
||||
} else {
|
||||
//timeString = hours + moment(sunriseSunsetDateObject).format(':mm');
|
||||
timeString = moment(sunriseSunsetDateObject).format('h:mm');
|
||||
timeString = moment(sunriseSunsetDateObject).format("h:mm");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,9 +344,10 @@ Module.register("currentweather",{
|
||||
this.sunriseSunsetIcon = (sunrise < now && sunset > now) ? "wi-sunset" : "wi-sunrise";
|
||||
|
||||
|
||||
|
||||
this.show(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.loaded = true;
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
this.sendNotification("CURRENTWEATHER_DATA", {data: data});
|
||||
},
|
||||
|
||||
/* scheduleUpdate()
|
||||
@@ -306,52 +386,51 @@ Module.register("currentweather",{
|
||||
return 12;
|
||||
},
|
||||
|
||||
deg2Cardinal: function(deg) {
|
||||
if (deg>11.25 && deg<=33.75){
|
||||
return "NNE";
|
||||
} else if (deg > 33.75 && deg <= 56.25) {
|
||||
return "NE";
|
||||
} else if (deg > 56.25 && deg <= 78.75) {
|
||||
return "ENE";
|
||||
} else if (deg > 78.75 && deg <= 101.25) {
|
||||
return "E";
|
||||
} else if (deg > 101.25 && deg <= 123.75) {
|
||||
return "ESE";
|
||||
} else if (deg > 123.75 && deg <= 146.25) {
|
||||
return "SE";
|
||||
} else if (deg > 146.25 && deg <= 168.75) {
|
||||
return "SSE";
|
||||
} else if (deg > 168.75 && deg <= 191.25) {
|
||||
return "S";
|
||||
} else if (deg > 191.25 && deg <= 213.75) {
|
||||
return "SSW";
|
||||
} else if (deg > 213.75 && deg <= 236.25) {
|
||||
return "SW";
|
||||
} else if (deg > 236.25 && deg <= 258.75) {
|
||||
return "WSW";
|
||||
} else if (deg > 258.75 && deg <= 281.25) {
|
||||
return "W";
|
||||
} else if (deg > 281.25 && deg <= 303.75) {
|
||||
return "WNW";
|
||||
} else if (deg > 303.75 && deg <= 326.25) {
|
||||
return "NW";
|
||||
} else if (deg > 326.25 && deg <= 348.75) {
|
||||
return "NNW";
|
||||
} else {
|
||||
return "N";
|
||||
}
|
||||
},
|
||||
|
||||
/* function(temperature)
|
||||
* Rounds a temperature to 1 decimal.
|
||||
* Rounds a temperature to 1 decimal or integer (depending on config.roundTemp).
|
||||
*
|
||||
* argument temperature number - Temperature.
|
||||
*
|
||||
* return number - Rounded Temperature.
|
||||
*/
|
||||
|
||||
deg2Cardinal: function(deg) {
|
||||
if (deg>11.25 && deg<=33.75){
|
||||
return "NNE";
|
||||
} else if (deg > 33.75 && deg <= 56.25) {
|
||||
return "NE";
|
||||
} else if (deg > 56.25 && deg <= 78.75) {
|
||||
return "ENE";
|
||||
} else if (deg > 78.75 && deg <= 101.25) {
|
||||
return "E";
|
||||
} else if (deg > 101.25 && deg <= 123.75) {
|
||||
return "ESE";
|
||||
} else if (deg > 123.75 && deg <= 146.25) {
|
||||
return "SE";
|
||||
} else if (deg > 146.25 && deg <= 168.75) {
|
||||
return "SSE";
|
||||
} else if (deg > 168.75 && deg <= 191.25) {
|
||||
return "S";
|
||||
} else if (deg > 191.25 && deg <= 213.75) {
|
||||
return "SSW";
|
||||
} else if (deg > 213.75 && deg <= 236.25) {
|
||||
return "SW";
|
||||
} else if (deg > 236.25 && deg <= 258.75) {
|
||||
return "WSW";
|
||||
} else if (deg > 258.75 && deg <= 281.25) {
|
||||
return "W";
|
||||
} else if (deg > 281.25 && deg <= 303.75) {
|
||||
return "WNW";
|
||||
} else if (deg > 303.75 && deg <= 326.25) {
|
||||
return "NW";
|
||||
} else if (deg > 326.25 && deg <= 348.75) {
|
||||
return "NNW";
|
||||
} else {
|
||||
return "N";
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
roundValue: function(temperature) {
|
||||
return parseFloat(temperature).toFixed(1);
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
}
|
||||
});
|
||||
|
@@ -15,7 +15,8 @@ var defaultModules = [
|
||||
"currentweather",
|
||||
"helloworld",
|
||||
"newsfeed",
|
||||
"weatherforecast"
|
||||
"weatherforecast",
|
||||
"updatenotification"
|
||||
];
|
||||
|
||||
/*************** DO NOT EDIT THE LINE BELOW ***************/
|
||||
|
@@ -53,6 +53,7 @@ The following properties can be configured:
|
||||
{
|
||||
title: "New York Times",
|
||||
url: "http://www.nytimes.com/services/xml/rss/nyt/HomePage.xml",
|
||||
encoding: "UTF-8"
|
||||
}
|
||||
]</code>
|
||||
</td>
|
||||
@@ -98,7 +99,7 @@ The following properties can be configured:
|
||||
<td><code>animationSpeed</code></td>
|
||||
<td>Speed of the update animation. (Milliseconds)<br>
|
||||
<br><b>Possible values:</b><code>0</code> - <code>5000</code>
|
||||
<br><b>Default value:</b> <code>2000</code> (2 seconds)
|
||||
<br><b>Default value:</b> <code>2500</code> (2.5 seconds)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@@ -44,8 +44,9 @@ var Fetcher = function(url, reloadInterval, encoding) {
|
||||
parser.on("item", function(item) {
|
||||
|
||||
var title = item.title;
|
||||
var description = item.description || item.summary || item.content || '';
|
||||
var description = item.description || item.summary || item.content || "";
|
||||
var pubdate = item.pubdate || item.published || item.updated;
|
||||
var url = item.url || item.link || "";
|
||||
|
||||
if (title && pubdate) {
|
||||
|
||||
@@ -56,15 +57,16 @@ var Fetcher = function(url, reloadInterval, encoding) {
|
||||
title: title,
|
||||
description: description,
|
||||
pubdate: pubdate,
|
||||
url: url,
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
console.log("Can't parse feed item:");
|
||||
console.log(item);
|
||||
console.log('Title: ' + title);
|
||||
console.log('Description: ' + description);
|
||||
console.log('Pubdate: ' + pubdate);
|
||||
// console.log("Can't parse feed item:");
|
||||
// console.log(item);
|
||||
// console.log('Title: ' + title);
|
||||
// console.log('Description: ' + description);
|
||||
// console.log('Pubdate: ' + pubdate);
|
||||
|
||||
}
|
||||
});
|
||||
@@ -79,7 +81,9 @@ var Fetcher = function(url, reloadInterval, encoding) {
|
||||
scheduleTimer();
|
||||
});
|
||||
|
||||
var headers = {'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A'};
|
||||
nodeVersion = Number(process.version.match(/^v(\d+\.\d+)/)[1]);
|
||||
headers = {"User-Agent": "Mozilla/5.0 (Node.js "+ nodeVersion + ") MagicMirror/" + global.version + " (https://github.com/MichMich/MagicMirror/)"}
|
||||
|
||||
request({uri: url, encoding: null, headers: headers}).pipe(iconv.decodeStream(encoding)).pipe(parser);
|
||||
|
||||
};
|
||||
|
@@ -25,8 +25,8 @@ Module.register("newsfeed",{
|
||||
updateInterval: 10 * 1000,
|
||||
animationSpeed: 2.5 * 1000,
|
||||
maxNewsItems: 0, // 0 for unlimited
|
||||
removeStartTags: '',
|
||||
removeEndTags: '',
|
||||
removeStartTags: "",
|
||||
removeEndTags: "",
|
||||
startTags: [],
|
||||
endTags: []
|
||||
|
||||
@@ -93,33 +93,41 @@ Module.register("newsfeed",{
|
||||
var sourceAndTimestamp = document.createElement("div");
|
||||
sourceAndTimestamp.className = "light small dimmed";
|
||||
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== '') sourceAndTimestamp.innerHTML = this.newsItems[this.activeItem].sourceTitle;
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== '' && this.config.showPublishDate) sourceAndTimestamp.innerHTML += ', ';
|
||||
if (this.config.showPublishDate) sourceAndTimestamp.innerHTML += moment(new Date(this.newsItems[this.activeItem].pubdate)).fromNow();
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== '' || this.config.showPublishDate) sourceAndTimestamp.innerHTML += ':';
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "") {
|
||||
sourceAndTimestamp.innerHTML = this.newsItems[this.activeItem].sourceTitle;
|
||||
}
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "" && this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += ", ";
|
||||
}
|
||||
if (this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += moment(new Date(this.newsItems[this.activeItem].pubdate)).fromNow();
|
||||
}
|
||||
if (this.config.showSourceTitle && this.newsItems[this.activeItem].sourceTitle !== "" || this.config.showPublishDate) {
|
||||
sourceAndTimestamp.innerHTML += ":";
|
||||
}
|
||||
|
||||
wrapper.appendChild(sourceAndTimestamp);
|
||||
}
|
||||
|
||||
//Remove selected tags from the beginning of rss feed items (title or description)
|
||||
|
||||
if (this.config.removeStartTags == 'title' || 'both') {
|
||||
if (this.config.removeStartTags == "title" || "both") {
|
||||
|
||||
for (f=0; f<this.config.startTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(0,this.config.startTags[f].length) == this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(this.config.startTags[f].length,this.newsItems[this.activeItem].title.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (this.config.removeStartTags == 'description' || 'both') {
|
||||
if (this.config.removeStartTags == "description" || "both") {
|
||||
|
||||
if (this.config.showDescription) {
|
||||
for (f=0; f<this.config.startTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(0,this.config.startTags[f].length) == this.config.startTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].description.slice(this.config.startTags[f].length,this.newsItems[this.activeItem].description.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,14 +139,14 @@ Module.register("newsfeed",{
|
||||
for (f=0; f<this.config.endTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].title.slice(-this.config.endTags[f].length)==this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].title = this.newsItems[this.activeItem].title.slice(0,-this.config.endTags[f].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.showDescription) {
|
||||
for (f=0; f<this.config.endTags.length;f++) {
|
||||
if (this.newsItems[this.activeItem].description.slice(-this.config.endTags[f].length)==this.config.endTags[f]) {
|
||||
this.newsItems[this.activeItem].description = this.newsItems[this.activeItem].description.slice(0,-this.config.endTags[f].length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,10 +242,10 @@ Module.register("newsfeed",{
|
||||
for (var f in this.config.feeds) {
|
||||
var feed = this.config.feeds[f];
|
||||
if (feed.url === feedUrl) {
|
||||
return feed.title || '';
|
||||
return feed.title || "";
|
||||
}
|
||||
}
|
||||
return '';
|
||||
return "";
|
||||
},
|
||||
|
||||
/* scheduleUpdateInterval()
|
||||
|
@@ -35,8 +35,8 @@ module.exports = NodeHelper.create({
|
||||
createFetcher: function(feed, config) {
|
||||
var self = this;
|
||||
|
||||
var url = feed.url || '';
|
||||
var encoding = feed.encoding || 'UTF-8';
|
||||
var url = feed.url || "";
|
||||
var encoding = feed.encoding || "UTF-8";
|
||||
var reloadInterval = config.reloadInterval || 5 * 60 * 1000;
|
||||
|
||||
if (!validUrl.isUri(url)) {
|
||||
|
42
modules/default/updatenotification/README.md
Normal file
42
modules/default/updatenotification/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Module: Update Notification
|
||||
The `updatenotification` module is one of the default modules of the MagicMirror.
|
||||
This will display a message whenever a new version of the MagicMirror application is available.
|
||||
|
||||
## Using the module
|
||||
|
||||
To use this module, add it to the modules array in the `config/config.js` file:
|
||||
````javascript
|
||||
modules: [
|
||||
{
|
||||
module: 'updatenotification',
|
||||
position: 'top_center', // This can be any of the regions.
|
||||
config: {
|
||||
// The config property is optional.
|
||||
// See 'Configuration options' for more information.
|
||||
}
|
||||
}
|
||||
]
|
||||
````
|
||||
|
||||
## Configuration options
|
||||
|
||||
The following properties can be configured:
|
||||
|
||||
<table width="100%">
|
||||
<!-- why, markdown... -->
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th width="100%">Description</th>
|
||||
</tr>
|
||||
<thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>updateInterval</code></td>
|
||||
<td>How often do you want to check for a new version? This value represents the interval in milliseconds.<br>
|
||||
<br><b>Possible values:</b> Any value above <code>60000</code> (1 minute);
|
||||
<br><b>Default value:</b> <code>600000</code> (10 minutes);
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
87
modules/default/updatenotification/node_helper.js
Normal file
87
modules/default/updatenotification/node_helper.js
Normal file
@@ -0,0 +1,87 @@
|
||||
var SimpleGit = require("simple-git");
|
||||
var simpleGits = [];
|
||||
var fs = require("fs");
|
||||
var path = require("path");
|
||||
var defaultModules = require(__dirname + "/../defaultmodules.js");
|
||||
var NodeHelper = require("node_helper");
|
||||
|
||||
module.exports = NodeHelper.create({
|
||||
|
||||
config: {},
|
||||
|
||||
updateTimer: null,
|
||||
|
||||
start: function () {
|
||||
},
|
||||
|
||||
configureModules: function(modules) {
|
||||
for (moduleName in modules) {
|
||||
if (defaultModules.indexOf(moduleName) < 0) {
|
||||
// Default modules are included in the main MagicMirror repo
|
||||
var moduleFolder = path.normalize(__dirname + "/../../" + moduleName);
|
||||
|
||||
var stat;
|
||||
try {
|
||||
stat = fs.statSync(path.join(moduleFolder, ".git"));
|
||||
} catch(err) {
|
||||
// Error when directory .git doesn't exist
|
||||
// This module is not managed with git, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
var res = function(mn, mf) {
|
||||
var git = SimpleGit(mf);
|
||||
git.getRemotes(true, function(err, remotes) {
|
||||
if (remotes.length < 1 || remotes[0].name.length < 1) {
|
||||
// No valid remote for folder, skip
|
||||
return;
|
||||
}
|
||||
|
||||
// Folder has .git and has at least one git remote, watch this folder
|
||||
simpleGits.push({"module": mn, "git": git});
|
||||
});
|
||||
}(moduleName, moduleFolder);
|
||||
}
|
||||
}
|
||||
|
||||
// Push MagicMirror itself last, biggest chance it'll show up last in UI and isn't overwritten
|
||||
simpleGits.push({"module": "default", "git": SimpleGit(path.normalize(__dirname + "/../../../"))});
|
||||
},
|
||||
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "CONFIG") {
|
||||
this.config = payload;
|
||||
} else if(notification === "MODULES") {
|
||||
this.configureModules(payload);
|
||||
this.preformFetch();
|
||||
}
|
||||
},
|
||||
|
||||
preformFetch() {
|
||||
var self = this;
|
||||
|
||||
simpleGits.forEach(function(sg) {
|
||||
sg.git.fetch().status(function(err, data) {
|
||||
data.module = sg.module;
|
||||
if (!err) {
|
||||
self.sendSocketNotification("STATUS", data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.scheduleNextFetch(this.config.updateInterval);
|
||||
},
|
||||
|
||||
scheduleNextFetch: function(delay) {
|
||||
if (delay < 60 * 1000) {
|
||||
delay = 60 * 1000
|
||||
}
|
||||
|
||||
var self = this;
|
||||
clearTimeout(this.updateTimer);
|
||||
this.updateTimer = setTimeout(function() {
|
||||
self.preformFetch();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
});
|
70
modules/default/updatenotification/updatenotification.js
Normal file
70
modules/default/updatenotification/updatenotification.js
Normal file
@@ -0,0 +1,70 @@
|
||||
Module.register("updatenotification", {
|
||||
|
||||
defaults: {
|
||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
},
|
||||
|
||||
status: false,
|
||||
|
||||
start: function () {
|
||||
Log.log("Start updatenotification");
|
||||
|
||||
},
|
||||
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
this.sendSocketNotification("CONFIG", this.config);
|
||||
this.sendSocketNotification("MODULES", Module.definitions);
|
||||
this.hide(0,{lockString: self.identifier});
|
||||
}
|
||||
},
|
||||
|
||||
socketNotificationReceived: function (notification, payload) {
|
||||
if (notification === "STATUS") {
|
||||
this.status = payload;
|
||||
this.updateUI();
|
||||
}
|
||||
},
|
||||
|
||||
updateUI: function() {
|
||||
var self = this;
|
||||
if (this.status && this.status.behind > 0) {
|
||||
self.updateDom(0);
|
||||
self.show(1000, {lockString: self.identifier});
|
||||
}
|
||||
},
|
||||
|
||||
// Override dom generator.
|
||||
getDom: function () {
|
||||
var wrapper = document.createElement("div");
|
||||
|
||||
if (this.status && this.status.behind > 0) {
|
||||
var message = document.createElement("div");
|
||||
message.className = "small bright";
|
||||
|
||||
var icon = document.createElement("i");
|
||||
icon.className = "fa fa-exclamation-circle";
|
||||
icon.innerHTML = " ";
|
||||
message.appendChild(icon);
|
||||
|
||||
var text = document.createElement("span");
|
||||
if (this.status.module == "default") {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION");
|
||||
} else {
|
||||
text.innerHTML = this.translate("UPDATE_NOTIFICATION_MODULE").replace("MODULE_NAME", this.status.module);
|
||||
}
|
||||
message.appendChild(text);
|
||||
|
||||
wrapper.appendChild(message);
|
||||
|
||||
var subtext = document.createElement("div");
|
||||
subtext.innerHTML = this.translate("UPDATE_INFO")
|
||||
.replace("COMMIT_COUNT", this.status.behind + " " + ((this.status.behind == 1)? "commit" : "commits"))
|
||||
.replace("BRANCH_NAME", this.status.current);
|
||||
subtext.className = "xsmall dimmed";
|
||||
wrapper.appendChild(subtext);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
});
|
@@ -14,7 +14,7 @@ modules: [
|
||||
config: {
|
||||
// See 'Configuration options' for more information.
|
||||
location: 'Amsterdam,Netherlands',
|
||||
locationID: '', //Location ID from http://bulk.openweather.org/sample/
|
||||
locationID: '', //Location ID from http://openweathermap.org/help/city_list.txt
|
||||
appid: 'abcde12345abcde12345abcde12345ab' //openweathermap.org API key.
|
||||
}
|
||||
}
|
||||
@@ -35,19 +35,20 @@ The following properties can be configured:
|
||||
</tr>
|
||||
<thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td><code>location</code></td>
|
||||
<td>The location used for weather information.<br>
|
||||
<br><b>Example:</b> <code>Amsterdam,Netherlands</code>
|
||||
<br><b>Default value:</b> <code>New York</code>
|
||||
<br><b>Example:</b> <code>'Amsterdam,Netherlands'</code>
|
||||
<br><b>Default value:</b> <code>false</code><br><br>
|
||||
<strong>Note:</strong> When the <code>location</code> and <code>locationID</code> are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>locationID</code></td>
|
||||
<td>Location ID from <a href="http://bulk.openweather.org/sample/">OpenWeather</a> <b>This will override anything you put in location.</b><br>Leave blank if you want to use location.
|
||||
<td>Location ID from <a href="http://openweathermap.org/help/city_list.txt">OpenWeatherMap</a> <b>This will override anything you put in location.</b><br>Leave blank if you want to use location.
|
||||
<br><b>Example:</b> <code>1234567</code>
|
||||
<br><b>Default value:</b> <code></code>
|
||||
<br><b>Default value:</b> <code>false</code><br><br>
|
||||
<strong>Note:</strong> When the <code>location</code> and <code>locationID</code> are both not set, the location will be based on the information provided by the calendar module. The first upcoming event with location data will be used.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -63,6 +64,13 @@ The following properties can be configured:
|
||||
<br><b>Default value:</b> <code>config.units</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>roundTemp</code></td>
|
||||
<td>Round temperature values to nearest integer.<br>
|
||||
<br><b>Possible values:</b> <code>true</code> (round to integer) or <code>false</code> (display exact value with decimal point)
|
||||
<br><b>Default value:</b> <code>false</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>maxNumberOfDays</code></td>
|
||||
<td>How many days of forecast to return. Specified by config.js<br>
|
||||
@@ -71,21 +79,28 @@ The following properties can be configured:
|
||||
<br>This value is optional. By default the weatherforecast module will return 7 days.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>showRainAmount</code></td>
|
||||
<td>Should the predicted rain amount be displayed?<br>
|
||||
<br><b>Possible values:</b> <code>true</code> or <code>false</code>
|
||||
<br><b>Default value:</b> <code>false</code>
|
||||
<br>This value is optional. By default the weatherforecast module will not display the predicted amount of rain.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>updateInterval</code></td>
|
||||
<td>How often does the content needs to be fetched? (Milliseconds)<br>
|
||||
<br><b>Possible values:</b> <code>1000</code> - <code>86400000</code>
|
||||
<br><b>Default value:</b> <code>300000</code> (10 minutes)
|
||||
<br><b>Default value:</b> <code>600000</code> (10 minutes)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>animationSpeed</code></td>
|
||||
<td>Speed of the update animation. (Milliseconds)<br>
|
||||
<br><b>Possible values:</b><code>0</code> - <code>5000</code>
|
||||
<br><b>Default value:</b> <code>2000</code> (2 seconds)
|
||||
<br><b>Default value:</b> <code>1000</code> (1 second)
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>lang</code></td>
|
||||
<td>The language of the days.<br>
|
||||
@@ -111,7 +126,7 @@ The following properties can be configured:
|
||||
<td><code>initialLoadDelay</code></td>
|
||||
<td>The initial delay before loading. If you have multiple modules that use the same API key, you might want to delay one of the requests. (Milliseconds)<br>
|
||||
<br><b>Possible values:</b> <code>1000</code> - <code>5000</code>
|
||||
<br><b>Default value:</b> <code>0</code>
|
||||
<br><b>Default value:</b> <code>2500</code> (2.5 seconds delay. This delay is used to keep the OpenWeather API happy.)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -134,11 +149,23 @@ The following properties can be configured:
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>weatherEndpoint</code></td>
|
||||
<td><code>forecastEndpoint</code></td>
|
||||
<td>The OpenWeatherMap API endPoint.<br>
|
||||
<br><b>Default value:</b> <code>'forecast/daily'</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>appendLocationNameToHeader</code></td>
|
||||
<td>If set to <code>true</code>, the returned location name will be appended to the header of the module, if the header is enabled. This is mainly intresting when using calender based weather.<br>
|
||||
<br><b>Default value:</b> <code>true</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>calendarClass</code></td>
|
||||
<td>The class for the calender module to base the event based weather information on.<br>
|
||||
<br><b>Default value:</b> <code>'calendar'</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>iconTable</code></td>
|
||||
<td>The conversion table to convert the weather conditions to weather-icons.<br>
|
||||
@@ -164,6 +191,5 @@ The following properties can be configured:
|
||||
}</code>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
@@ -12,3 +12,8 @@
|
||||
padding-left: 20px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.weatherforecast .rain {
|
||||
padding-left: 20px;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
@@ -11,11 +11,12 @@ Module.register("weatherforecast",{
|
||||
|
||||
// Default module config.
|
||||
defaults: {
|
||||
location: "",
|
||||
locationID: "",
|
||||
location: false,
|
||||
locationID: false,
|
||||
appid: "",
|
||||
units: config.units,
|
||||
maxNumberOfDays: 7,
|
||||
showRainAmount: false,
|
||||
updateInterval: 10 * 60 * 1000, // every 10 minutes
|
||||
animationSpeed: 1000,
|
||||
timeFormat: config.timeFormat,
|
||||
@@ -30,6 +31,11 @@ Module.register("weatherforecast",{
|
||||
apiBase: "http://api.openweathermap.org/data/",
|
||||
forecastEndpoint: "forecast/daily",
|
||||
|
||||
appendLocationNameToHeader: true,
|
||||
calendarClass: "calendar",
|
||||
|
||||
roundTemp: false,
|
||||
|
||||
iconTable: {
|
||||
"01d": "wi-day-sunny",
|
||||
"02d": "wi-day-cloudy",
|
||||
@@ -52,6 +58,12 @@ Module.register("weatherforecast",{
|
||||
},
|
||||
},
|
||||
|
||||
// create a variable for the first upcoming calendaar event. Used if no location is specified.
|
||||
firstEvent: false,
|
||||
|
||||
// create a variable to hold the location name based on the API result.
|
||||
fetchedLocatioName: "",
|
||||
|
||||
// Define required scripts.
|
||||
getScripts: function() {
|
||||
return ["moment.js"];
|
||||
@@ -95,12 +107,6 @@ Module.register("weatherforecast",{
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (this.config.location === "") {
|
||||
wrapper.innerHTML = "Please set the openweather <i>location</i> in the config for module: " + this.name + ".";
|
||||
wrapper.className = "dimmed light small";
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
if (!this.loaded) {
|
||||
wrapper.innerHTML = this.translate("LOADING");
|
||||
wrapper.className = "dimmed light small";
|
||||
@@ -139,6 +145,17 @@ Module.register("weatherforecast",{
|
||||
minTempCell.className = "align-right min-temp";
|
||||
row.appendChild(minTempCell);
|
||||
|
||||
if (this.config.showRainAmount) {
|
||||
var rainCell = document.createElement("td");
|
||||
if (isNaN(forecast.rain)) {
|
||||
rainCell.innerHTML = "";
|
||||
} else {
|
||||
rainCell.innerHTML = forecast.rain + " mm";
|
||||
}
|
||||
rainCell.className = "align-right bright rain";
|
||||
row.appendChild(rainCell);
|
||||
}
|
||||
|
||||
if (this.config.fade && this.config.fadePoint < 1) {
|
||||
if (this.config.fadePoint < 0) {
|
||||
this.config.fadePoint = 0;
|
||||
@@ -156,11 +173,50 @@ Module.register("weatherforecast",{
|
||||
return table;
|
||||
},
|
||||
|
||||
// Override getHeader method.
|
||||
getHeader: function() {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
return this.data.header + " " + this.fetchedLocatioName;
|
||||
}
|
||||
|
||||
return this.data.header;
|
||||
},
|
||||
|
||||
// Override notification handler.
|
||||
notificationReceived: function(notification, payload, sender) {
|
||||
if (notification === "DOM_OBJECTS_CREATED") {
|
||||
if (this.config.appendLocationNameToHeader) {
|
||||
this.hide(0, {lockString: this.identifier});
|
||||
}
|
||||
}
|
||||
if (notification === "CALENDAR_EVENTS") {
|
||||
var senderClasses = sender.data.classes.toLowerCase().split(" ");
|
||||
if (senderClasses.indexOf(this.config.calendarClass.toLowerCase()) !== -1) {
|
||||
var lastEvent = this.firstEvent;
|
||||
this.firstEvent = false;
|
||||
|
||||
for (e in payload) {
|
||||
var event = payload[e];
|
||||
if (event.location || event.geo) {
|
||||
this.firstEvent = event;
|
||||
//Log.log("First upcoming event with location: ", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* updateWeather(compliments)
|
||||
* Requests new data from openweather.org.
|
||||
* Calls processWeather on succesfull response.
|
||||
*/
|
||||
updateWeather: function() {
|
||||
if (this.config.appid === "") {
|
||||
Log.error("WeatherForecast: APPID not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
var url = this.config.apiBase + this.config.apiVersion + "/" + this.config.forecastEndpoint + this.getParams();
|
||||
var self = this;
|
||||
var retry = true;
|
||||
@@ -172,11 +228,10 @@ Module.register("weatherforecast",{
|
||||
if (this.status === 200) {
|
||||
self.processWeather(JSON.parse(this.response));
|
||||
} else if (this.status === 401) {
|
||||
self.config.appid = "";
|
||||
self.updateDom(self.config.animationSpeed);
|
||||
|
||||
Log.error(self.name + ": Incorrect APPID.");
|
||||
retry = false;
|
||||
retry = true;
|
||||
} else {
|
||||
Log.error(self.name + ": Could not load weather.");
|
||||
}
|
||||
@@ -196,11 +251,19 @@ Module.register("weatherforecast",{
|
||||
*/
|
||||
getParams: function() {
|
||||
var params = "?";
|
||||
if(this.config.locationID !== "") {
|
||||
if(this.config.locationID) {
|
||||
params += "id=" + this.config.locationID;
|
||||
} else {
|
||||
} else if(this.config.location) {
|
||||
params += "q=" + this.config.location;
|
||||
} else if (this.firstEvent && this.firstEvent.geo) {
|
||||
params += "lat=" + this.firstEvent.geo.lat + "&lon=" + this.firstEvent.geo.lon
|
||||
} else if (this.firstEvent && this.firstEvent.location) {
|
||||
params += "q=" + this.firstEvent.location;
|
||||
} else {
|
||||
this.hide(this.config.animationSpeed, {lockString:this.identifier});
|
||||
return;
|
||||
}
|
||||
|
||||
params += "&units=" + this.config.units;
|
||||
params += "&lang=" + this.config.lang;
|
||||
/*
|
||||
@@ -220,6 +283,7 @@ Module.register("weatherforecast",{
|
||||
* argument data object - Weather information received form openweather.org.
|
||||
*/
|
||||
processWeather: function(data) {
|
||||
this.fetchedLocatioName = data.city.name + ", " + data.city.country;
|
||||
|
||||
this.forecast = [];
|
||||
for (var i = 0, count = data.list.length; i < count; i++) {
|
||||
@@ -230,13 +294,14 @@ Module.register("weatherforecast",{
|
||||
day: moment(forecast.dt, "X").format("ddd"),
|
||||
icon: this.config.iconTable[forecast.weather[0].icon],
|
||||
maxTemp: this.roundValue(forecast.temp.max),
|
||||
minTemp: this.roundValue(forecast.temp.min)
|
||||
minTemp: this.roundValue(forecast.temp.min),
|
||||
rain: this.roundValue(forecast.rain)
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//Log.log(this.forecast);
|
||||
|
||||
this.show(this.config.animationSpeed, {lockString:this.identifier});
|
||||
this.loaded = true;
|
||||
this.updateDom(this.config.animationSpeed);
|
||||
},
|
||||
@@ -279,13 +344,15 @@ Module.register("weatherforecast",{
|
||||
},
|
||||
|
||||
/* function(temperature)
|
||||
* Rounds a temperature to 1 decimal.
|
||||
* Rounds a temperature to 1 decimal or integer (depending on config.roundTemp).
|
||||
*
|
||||
* argument temperature number - Temperature.
|
||||
*
|
||||
* return number - Rounded Temperature.
|
||||
*/
|
||||
roundValue: function(temperature) {
|
||||
return parseFloat(temperature).toFixed(1);
|
||||
var decimals = this.config.roundTemp ? 0 : 1;
|
||||
return parseFloat(temperature).toFixed(decimals);
|
||||
}
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user