diff --git a/.jscsrc b/.jscsrc
index bc1fcea6..4638b44f 100644
--- a/.jscsrc
+++ b/.jscsrc
@@ -4,5 +4,6 @@
"validateQuoteMarks": "\"",
"maximumLineLength": 250,
"requireCurlyBraces": [],
- "requireCamelCaseOrUpperCaseIdentifiers": false
+ "requireCamelCaseOrUpperCaseIdentifiers": false,
+ "excludeFiles": [".jscsrc"],
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 1d79c219..4c935d31 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ MagicMirror² focuses on a modular plugin system and uses [Electron](http://elec
## Usage
#### Raspberry Pi Support
-Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself. Note the only Jessie is currently supported. If you want to use Wheezy, check out [this issue](https://github.com/MichMich/MagicMirror/issues/188).
+Electron, the app wrapper around MagicMirror², only supports the Raspberry Pi 2 & 3. The Raspberry Pi 1 is currently **not** supported. If you want to run this on a Raspberry Pi 1, use the [server only](#server-only) feature and setup a fullscreen browser yourself.
#### Automatic Installer (Raspberry Pi Only!)
@@ -39,7 +39,7 @@ curl -sL https://raw.githubusercontent.com/MichMich/MagicMirror/v2-beta/installe
1. Download and install the latest Node.js version.
2. Clone the repository and check out the beta branch: `git clone -b v2-beta https://github.com/MichMich/MagicMirror`
3. Enter the repository: `cd ~/MagicMirror`
-4. Install and run the app: `npm install && npm start` (You may have to restart your terminal before this works!)
+4. Install and run the app: `npm install && npm start`
**Important:** `npm start` does **not** work via SSH, use `DISPLAY=:0 nohup npm start &` instead. This starts the mirror on the remote display.
@@ -77,6 +77,7 @@ The following properties can be configured:
| `port` | The port on which the MagicMirror² server will run on. The default value is `8080`. |
| `language` | The language of the interface. (Note: Not all elements will be localized.) Possible values are `en`, `nl`, `ru`, `fr`, etc., but the default value is `en`. |
| `timeFormat` | The form of time notation that will be used. Possible values are `12` or `24`. The default is `24`. |
+| `units` | The units that will be used in the default weather modules. Possible values are `metric` or `imperial`. The default is `metric`. |
| `modules` | An array of active modules. **The array must contain objects. See the next table below for more information.** |
Module configuration:
diff --git a/config/config.js.sample b/config/config.js.sample
index 99ab2ead..0b5f45d4 100644
--- a/config/config.js.sample
+++ b/config/config.js.sample
@@ -9,6 +9,7 @@ var config = {
language: 'en',
timeFormat: 24,
+ units: 'metric',
modules: [
{
diff --git a/css/custom.css b/css/custom.css
index 5871da5c..7d0d0681 100644
--- a/css/custom.css
+++ b/css/custom.css
@@ -6,9 +6,9 @@
* MIT Licensed. *
* *
* Add any custom CSS below. *
- * Changes to this files will not be ignored by GIT. *
+ * Changes to this files will be ignored by GIT. *
*****************************************************/
body {
- }
\ No newline at end of file
+ }
diff --git a/installers/raspberry.sh b/installers/raspberry.sh
index 4f1d2e2c..3c1687c0 100644
--- a/installers/raspberry.sh
+++ b/installers/raspberry.sh
@@ -18,7 +18,7 @@
echo "Installing helper tools ..."
sudo apt-get install curl wget build-essential unzip || exit
ARM=$(uname -m) # Determine which Pi is running.
-NODE_LATEST="v5.11.0" # Set the latest version here.
+NODE_LATEST="v6.0.0" # Set the latest version here.
DOWNLOAD_URL="https://nodejs.org/dist/latest/node-$NODE_LATEST-linux-$ARM.tar.gz" # Construct the download URL.
echo "Installing Latest Node.js ..."
diff --git a/js/defaults.js b/js/defaults.js
index e3dc3d2b..28cc2613 100644
--- a/js/defaults.js
+++ b/js/defaults.js
@@ -12,7 +12,8 @@ var defaults = {
language: "en",
timeFormat: 24,
-
+ units: "metric",
+
modules: [
{
module: "helloworld",
diff --git a/js/logger.js b/js/logger.js
index 994fe534..5b2df990 100644
--- a/js/logger.js
+++ b/js/logger.js
@@ -21,6 +21,27 @@ var Log = (function() {
},
error: function(message) {
console.error(message);
+ },
+ warn: function(message) {
+ console.warn(message);
+ },
+ group: function(message) {
+ console.group(message);
+ },
+ groupCollapsed: function(message) {
+ console.groupCollapsed(message);
+ },
+ groupEnd: function() {
+ console.groupEnd();
+ },
+ time: function(message) {
+ console.time(message);
+ },
+ timeEnd: function(message) {
+ console.timeEnd(message);
+ },
+ timeStamp: function(message) {
+ console.timeStamp(message);
}
};
})();
diff --git a/modules/README.md b/modules/README.md
index 965051b0..b2caa2af 100644
--- a/modules/README.md
+++ b/modules/README.md
@@ -97,7 +97,7 @@ start: function() {
####`getScripts()`
**Should return: Array**
-The getScripts method is called to request any additional scripts that need to be loaded. This method should therefor return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
+The getScripts method is called to request any additional scripts that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.js')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:**
````javascript
@@ -117,7 +117,7 @@ getScripts: function() {
####`getStyles()`
**Should return: Array**
-The getStyles method is called to request any additional scripts that need to be loaded. This method should therefor return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
+The getStyles method is called to request any additional stylesheets that need to be loaded. This method should therefore return an array with strings. If you want to return a full path to a file in the module folder, use the `this.file('filename.css')` method. In all cases the loader will only load a file once. It even checks if the file is available in the default vendor folder.
**Example:**
````javascript
@@ -133,6 +133,22 @@ getStyles: function() {
````
**Note:** If a file can not be loaded, the boot up of the mirror will stall. Therefore it's advised not to use any external urls.
+####`getTranslations()`
+**Should return: Dictionary**
+
+The getTranslations method is called to request translation files that need to be loaded. This method should therefore return a dictionary with the files to load, identified by the country's short name.
+
+**Example:**
+````javascript
+getTranslations: function() {
+ return {
+ en: "translations/en.json",
+ de: "translations/de.json"
+ }
+}
+
+````
+
####`getDom()`
**Should return:** Dom Object
@@ -261,6 +277,25 @@ To show a module, you can call the `show(speed, callback)` method. You can call
**Note 2:** If the show animation is hijacked (an other method calls show on the same module), the callback will not be called.
**Note 3:** If the dom is not yet created, the show method won't work. Wait for the `DOM_OBJECTS_CREATED` [notification](#notificationreceivednotification-payload-sender).
+####`this.translate(identifier)`
+***identifier* String** - Identifier of the string that should be translated.
+
+The Magic Mirror contains a convenience wrapper for `l18n`. You can use this to automatically serve different translations for your modules based on the user's `language` configuration.
+
+**Example:**
+````javascript
+this.translate("INFO") //Will return a translated string for the identifier INFO
+````
+
+**Example json file:**
+````javascript
+{
+ "INFO": "Really important information!"
+}
+````
+
+**Note:** Currently there is no fallback if a translation identifier does not exist in one language. Right now you always have to add all identifier to all your translations even if they are not translated yet (see [#191](https://github.com/MichMich/MagicMirror/issues/191)).
+
## The Node Helper: node_helper.js
@@ -458,4 +493,4 @@ The Magic Mirror contains a convenience wrapper for logging. Currently, this log
Log.info('error');
Log.log('log');
Log.error('info');
-```
\ No newline at end of file
+````
diff --git a/modules/default/calendar/README.md b/modules/default/calendar/README.md
index df0bb0d2..cfe4e2b8 100644
--- a/modules/default/calendar/README.md
+++ b/modules/default/calendar/README.md
@@ -112,36 +112,6 @@ The following properties can be configured:
-
- loadingText |
- Text to display while loading item.
- Default value: 'Loading events …'
- |
-
-
- emptyCalendarText |
- Text to display when there are no upcoming events.
- Default value: 'No upcoming events.'
- |
-
-
- todayText |
- Text to display when a fullday event is planned for today.
- Default value: 'Today'
- |
-
-
- tomorrowText |
- Text to display when a fullday event is planned for tomorrow.
- Default value: 'Tomorrow'
- |
-
-
- runningText |
- Text to display when an event is still running.
- Default value: 'Ends in'
- |
-
diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js
index c1ffc492..5e315586 100644
--- a/modules/default/calendar/calendar.js
+++ b/modules/default/calendar/calendar.js
@@ -124,7 +124,7 @@ Module.register("calendar",{
if (event.fullDayEvent) {
if (event.today) {
timeWrapper.innerHTML = this.translate("TODAY");
- } else if (event.startDate - now < 24 * 60 * 60 * 1000) {
+ } else if (event.startDate - now < 24 * 60 * 60 * 1000 && event.startDate - now > 0) {
timeWrapper.innerHTML = this.translate("TOMORROW");
} else {
timeWrapper.innerHTML = moment(event.startDate,"x").fromNow();
@@ -141,7 +141,8 @@ Module.register("calendar",{
timeWrapper.innerHTML = this.translate("RUNNING") + ' ' + moment(event.endDate,"x").fromNow(true);
}
}
- // timeWrapper.innerHTML = moment(event.startDate,'x').format('lll');
+ //timeWrapper.innerHTML += ' - '+ moment(event.startDate,'x').format('lll');
+ //console.log(event);
timeWrapper.className = "time light";
eventWrapper.appendChild(timeWrapper);
diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js
index 771ee5b3..ac558e2f 100644
--- a/modules/default/calendar/calendarfetcher.js
+++ b/modules/default/calendar/calendarfetcher.js
@@ -99,7 +99,7 @@ var CalendarFetcher = function(url, reloadInterval, maximumEntries, maximumNumbe
continue;
}
- if (fullDayEvent && endDate < today) {
+ if (fullDayEvent && endDate <= today) {
//console.log("It's a fullday event, and it is before today. So skip: " + title);
continue;
}
diff --git a/modules/default/calendar/translations/de.json b/modules/default/calendar/translations/de.json
index 53530085..cea5588d 100644
--- a/modules/default/calendar/translations/de.json
+++ b/modules/default/calendar/translations/de.json
@@ -1,7 +1,7 @@
{
"TODAY": "Heute"
, "TOMORROW": "Morgen"
- , "RUNNING": "Endet in"
+ , "RUNNING": "noch"
, "LOADING": "Lade Termine …"
, "EMPTY": "Keine Termine."
}
diff --git a/modules/default/calendar/vendor/ical.js/node-ical.js b/modules/default/calendar/vendor/ical.js/node-ical.js
index 6ce04892..294908ea 100644
--- a/modules/default/calendar/vendor/ical.js/node-ical.js
+++ b/modules/default/calendar/vendor/ical.js/node-ical.js
@@ -28,6 +28,14 @@ ical.objectHandlers['END'] = function(val, params, curr, stack){
if (curr.rrule) {
var rule = curr.rrule.replace('RRULE:', '');
if (rule.indexOf('DTSTART') === -1) {
+
+ if (curr.start.length === 8) {
+ var comps = /^(\d{4})(\d{2})(\d{2})$/.exec(curr.start);
+ if (comps) {
+ curr.start = new Date (comps[1], comps[2], comps[3]);
+ }
+ }
+
rule += ';DTSTART=' + curr.start.toISOString().replace(/[-:]/g, '');
rule = rule.replace(/\.[0-9]{3}/, '');
}
diff --git a/modules/default/clock/README.md b/modules/default/clock/README.md
index 522b1ad2..8808825c 100644
--- a/modules/default/clock/README.md
+++ b/modules/default/clock/README.md
@@ -47,14 +47,21 @@ The following properties can be configured:
showPeriod |
- Show the period (am/pm) with 12 hour format
+ | Show the period (am/pm) with 12 hour format.
Possible values: true or false
Default value: true
|
showPeriodUpper |
- Show the period (AM/PM) with 12 hour format as uppercase
+ | Show the period (AM/PM) with 12 hour format as uppercase.
+ Possible values: true or false
+ Default value: false
+ |
+
+
+ clockBold |
+ Remove the colon and bold the minutes to make a more modern look.
Possible values: true or false
Default value: false
|
diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js
index 611170f4..7f76324f 100644
--- a/modules/default/clock/clock.js
+++ b/modules/default/clock/clock.js
@@ -1,41 +1,34 @@
/* global Log, Module, moment, config */
-
/* Magic Mirror
* Module: Clock
*
* By Michael Teeuw http://michaelteeuw.nl
* MIT Licensed.
*/
-
Module.register("clock",{
-
// Module config defaults.
defaults: {
timeFormat: config.timeFormat,
displaySeconds: true,
showPeriod: true,
showPeriodUpper: false,
+ clockBold: false
},
-
// Define required scripts.
getScripts: function() {
return ["moment.js"];
},
-
// Define start sequence.
start: function() {
Log.info("Starting module: " + this.name);
-
// Schedule update interval.
var self = this;
setInterval(function() {
self.updateDom();
}, 1000);
-
// Set locale.
moment.locale(config.language);
},
-
// Override dom generator.
getDom: function() {
// Create wrappers.
@@ -44,35 +37,36 @@ Module.register("clock",{
var timeWrapper = document.createElement("div");
var secondsWrapper = document.createElement("sup");
var periodWrapper = document.createElement("span");
-
// Style Wrappers
dateWrapper.className = "date normal medium";
timeWrapper.className = "time bright large light";
secondsWrapper.className = "dimmed";
-
// Set content of wrappers.
- // The moment().format('h') method has a bug on the Raspberry Pi.
+ // The moment().format("h") method has a bug on the Raspberry Pi.
// So we need to generate the timestring manually.
// See issue: https://github.com/MichMich/MagicMirror/issues/181
- var timeString = moment().format('HH:mm');
+ if (this.config.clockBold === true) {
+ var timeString = moment().format("HH[]mm[]");
+ } else {
+ var timeString = moment().format("HH:mm");
+ }
if (this.config.timeFormat !== 24) {
var now = new Date();
var hours = now.getHours() % 12 || 12;
- timeString = hours + moment().format(':mm');
+ if (this.config.clockBold === true) {
+ timeString = hours + moment().format("[]mm[]");
+ } else {
+ timeString = hours + moment().format(":mm");
+ }
}
-
dateWrapper.innerHTML = moment().format("dddd, LL");
timeWrapper.innerHTML = timeString;
secondsWrapper.innerHTML = moment().format("ss");
-
-
if (this.config.showPeriodUpper) {
- periodWrapper.innerHTML = moment().format('A');
+ periodWrapper.innerHTML = moment().format("A");
} else {
- periodWrapper.innerHTML = moment().format('a');
+ periodWrapper.innerHTML = moment().format("a");
}
-
-
// Combine wrappers.
wrapper.appendChild(dateWrapper);
wrapper.appendChild(timeWrapper);
@@ -82,7 +76,6 @@ Module.register("clock",{
if (this.config.showPeriod && this.config.timeFormat !== 24) {
timeWrapper.appendChild(periodWrapper);
}
-
// Return the wrapper to the dom.
return wrapper;
}
diff --git a/modules/default/currentweather/README.md b/modules/default/currentweather/README.md
index 3c818eee..2ce54aac 100644
--- a/modules/default/currentweather/README.md
+++ b/modules/default/currentweather/README.md
@@ -50,9 +50,9 @@ The following properties can be configured:
units |
- What units to use?
- Possible values: default = Kelvin, metric = Celsius, imperial =Fahrenheit
- Default value: metric
+ | What units to use. Specified by config.js
+ Possible values: config.units = Specified by config.js, default = Kelvin, metric = Celsius, imperial =Fahrenheit
+ Default value: config.units
|
@@ -90,6 +90,13 @@ The following properties can be configured:
Default value: false
+
+ showWindDirection |
+ Show the wind direction next to the wind speed.
+ Possible values: true or false
+ Default value: false
+ |
+
lang |
The language of the days.
diff --git a/modules/default/currentweather/currentweather.js b/modules/default/currentweather/currentweather.js
index 3a7520ce..4ab04c4f 100644
--- a/modules/default/currentweather/currentweather.js
+++ b/modules/default/currentweather/currentweather.js
@@ -13,12 +13,13 @@ Module.register("currentweather",{
defaults: {
location: "",
appid: "",
- units: "metric",
+ units: config.units,
updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000,
timeFormat: config.timeFormat,
showPeriod: true,
showPeriodUpper: false,
+ showWindDirection: false,
lang: config.language,
initialLoadDelay: 0, // 0 seconds delay
@@ -68,6 +69,7 @@ Module.register("currentweather",{
moment.locale(config.language);
this.windSpeed = null;
+ this.windDirection = null;
this.sunriseSunsetTime = null;
this.sunriseSunsetIcon = null;
this.temperature = null;
@@ -108,11 +110,16 @@ Module.register("currentweather",{
var windIcon = document.createElement("span");
windIcon.className = "wi wi-strong-wind dimmed";
small.appendChild(windIcon);
-
+
var windSpeed = document.createElement("span");
windSpeed.innerHTML = " " + this.windSpeed;
small.appendChild(windSpeed);
-
+
+ if (this.config.showWindDirection) {
+ var windDirection = document.createElement("span");
+ windDirection.innerHTML = " " + this.windDirection;
+ small.appendChild(windDirection);
+ }
var spacer = document.createElement("span");
spacer.innerHTML = " ";
small.appendChild(spacer);
@@ -198,6 +205,7 @@ Module.register("currentweather",{
processWeather: function(data) {
this.temperature = this.roundValue(data.main.temp);
this.windSpeed = this.ms2Beaufort(this.roundValue(data.wind.speed));
+ this.windDirection = this.deg2Cardinal(data.wind.deg);
this.weatherType = this.config.iconTable[data.weather[0].icon];
var now = new Date();
@@ -274,6 +282,44 @@ Module.register("currentweather",{
*
* 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 "ENE";
+ }else if (deg>56.25 && deg<78.75){
+ return "E";
+ }else if (deg>78.75 && deg<101.25){
+ return "ESE";
+ }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);
}
diff --git a/modules/default/weatherforecast/README.md b/modules/default/weatherforecast/README.md
index bea80514..9e93bd14 100644
--- a/modules/default/weatherforecast/README.md
+++ b/modules/default/weatherforecast/README.md
@@ -50,9 +50,17 @@ The following properties can be configured:
|
units |
- What units to use?
- Possible values: default = Kelvin, metric = Celsius, imperial =Fahrenheit
- Default value: metric
+ | What units to use. Specified by config.js
+ Possible values: config.units = Specified by config.js, default = Kelvin, metric = Celsius, imperial =Fahrenheit
+ Default value: config.units
+ |
+
+
+ maxNumberOfDays |
+ How many days of forecast to return. Specified by config.js
+ Possible values: 1 - 16
+ Default value: 7 (7 days)
+ This value is optional. By default the weatherforecast module will return 7 days.
|
diff --git a/modules/default/weatherforecast/weatherforecast.js b/modules/default/weatherforecast/weatherforecast.js
index ff7c4190..c5b8762b 100644
--- a/modules/default/weatherforecast/weatherforecast.js
+++ b/modules/default/weatherforecast/weatherforecast.js
@@ -13,7 +13,8 @@ Module.register("weatherforecast",{
defaults: {
location: "",
appid: "",
- units: "metric",
+ units: config.units,
+ maxNumberOfDays: 7,
updateInterval: 10 * 60 * 1000, // every 10 minutes
animationSpeed: 1000,
timeFormat: config.timeFormat,
@@ -189,6 +190,12 @@ Module.register("weatherforecast",{
params += "q=" + this.config.location;
params += "&units=" + this.config.units;
params += "&lang=" + this.config.lang;
+ /*
+ * Submit a specific number of days to forecast, between 1 to 16 days.
+ * The OpenWeatherMap API properly handles values outside of the 1 - 16 range and returns 7 days by default.
+ * This is simply being pedantic and doing it ourselves.
+ */
+ params += "&cnt=" + (((this.config.maxNumberOfDays < 1) || (this.config.maxNumberOfDays > 16)) ? 7 : this.config.maxNumberOfDays);
params += "&APPID=" + this.config.appid;
return params;