From d00aa21c962ade3b4eed2cd0b420d0cab620e051 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:19:54 +0200 Subject: [PATCH 01/10] git ignore changes --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b512c09d..f3a8c749 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +!modules/node_modules/node_helper \ No newline at end of file From ede3e669c7e4a4211a614555970aa2a1df3f4afb Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:29:44 +0200 Subject: [PATCH 02/10] Minor visual change. --- modules/weatherforecast/weatherforecast.css | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/weatherforecast/weatherforecast.css b/modules/weatherforecast/weatherforecast.css index 323e65a6..c46c633a 100644 --- a/modules/weatherforecast/weatherforecast.css +++ b/modules/weatherforecast/weatherforecast.css @@ -5,6 +5,7 @@ .weatherforecast .weather-icon { padding-right: 30px; + text-align: center; } .weatherforecast .min-temp { From 26a6eb506e3c8b88cbe33318d271db41aba8d12d Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:44:16 +0200 Subject: [PATCH 03/10] Allow use of class.js in node. --- js/class.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/class.js b/js/class.js index b1b3059f..cd6cd2dc 100644 --- a/js/class.js +++ b/js/class.js @@ -62,4 +62,8 @@ return Class; }; -})(); \ No newline at end of file +})(); + + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== 'undefined') {module.exports = Class;} \ No newline at end of file From 1de52f7b645a8e7411ae89f80b1812af24218982 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:44:49 +0200 Subject: [PATCH 04/10] Update gitignore. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index f3a8c749..73b9e99c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ node_modules -!modules/node_modules/node_helper \ No newline at end of file +!modules/node_modules/* From c55ac659c197939272e525ab74352d0a13be33f4 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:47:57 +0200 Subject: [PATCH 05/10] Git ignore changes. --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 73b9e99c..3c3629e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ node_modules -!modules/node_modules/* From 1585b7a1422682fe3d43abf3656d2e011ae3898e Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:52:32 +0200 Subject: [PATCH 06/10] add git ignore for modules/node_modules --- modules/node_modules/.gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 modules/node_modules/.gitignore diff --git a/modules/node_modules/.gitignore b/modules/node_modules/.gitignore new file mode 100644 index 00000000..7e43a75e --- /dev/null +++ b/modules/node_modules/.gitignore @@ -0,0 +1,2 @@ +!/node_helper +!/node_helper/** \ No newline at end of file From 609e49897351acc95895c8d9ef1ad6fa42ffa7b8 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:56:53 +0200 Subject: [PATCH 07/10] git ignore fix --- .gitignore | 1 - modules/node_modules/.gitignore | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 .gitignore delete mode 100644 modules/node_modules/.gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 3c3629e6..00000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules diff --git a/modules/node_modules/.gitignore b/modules/node_modules/.gitignore deleted file mode 100644 index 7e43a75e..00000000 --- a/modules/node_modules/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -!/node_helper -!/node_helper/** \ No newline at end of file From 1be0edaf03612bfd8fe2c77b831f911fbbafc45b Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 13:57:06 +0200 Subject: [PATCH 08/10] git ignore fix --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules From 7e873880f547c1c8771713292174861ba38a8130 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 14:00:42 +0200 Subject: [PATCH 09/10] fix git ignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3c3629e6..8fee70e2 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -node_modules +/node_modules + +!/modules/node_helper +!/modules/node_helper/** \ No newline at end of file From af910aa3f79018d0826a49977bdb087a955e1802 Mon Sep 17 00:00:00 2001 From: Michael Teeuw Date: Wed, 30 Mar 2016 14:49:37 +0200 Subject: [PATCH 10/10] New node_helper loading. --- README.md | 3 +- js/electron.js | 33 ++-- modules/newsfeed/fetcher.js | 111 +++++++++++ modules/newsfeed/newsfetcher.js | 53 ++++++ modules/newsfeed/node_helper.js | 218 +++++----------------- modules/node_modules/node_helper/index.js | 72 +++++++ 6 files changed, 301 insertions(+), 189 deletions(-) create mode 100644 modules/newsfeed/fetcher.js create mode 100644 modules/newsfeed/newsfetcher.js create mode 100644 modules/node_modules/node_helper/index.js diff --git a/README.md b/README.md index 5d5ffcdf..84cc8315 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ Things that still have to be implemented or changed. ####Loader - Loading of module uses `eval()`. We might want to look into a better solution. [loader.js#L112](https://github.com/MichMich/MagicMirror/blob/v2-beta/js/loader.js#L112). - +####NodeHelper +- The node_helper superclass creates a seperate socket connection for each module. It's preferred to use the overall socket connection of the server. diff --git a/js/electron.js b/js/electron.js index 1ce98a0e..c83164a4 100755 --- a/js/electron.js +++ b/js/electron.js @@ -56,31 +56,20 @@ function loadConfig (callback) { function loadModule(moduleName) { var helperPath = __dirname + '/../modules/' + moduleName + '/node_helper.js'; - + var loadModule = true; try { fs.accessSync(helperPath, fs.R_OK); - - var child = spawn('node', [helperPath]); - - // Make sure the output is logged. - child.stdout.on('data', function(data) { - process.stdout.write('[' + moduleName + '] ' + data); - }); - - child.stderr.on('data', function(data) { - process.stdout.write('[' + moduleName + '] ' + data); - }); - - child.on('close', function(code) { - console.log(moduleName + ' closing code: ' + code); - }); - - //Log module name - console.log("Started helper script for module: " + moduleName + "."); - } catch (e) { + loadModule = false; console.log("No helper found for module: " + moduleName + "."); } + + if (loadModule) { + var Module = require(helperPath); + var m = new Module(); + m.setName(moduleName); + m.start(); + } } function loadModules(modules) { @@ -112,7 +101,9 @@ loadConfig(function(c) { // initialization and is ready to create browser windows. app.on('ready', function() { var server = new Server(config, function() { - createWindow(); + setTimeout(function() { + createWindow(); + }, 1000); }); }); diff --git a/modules/newsfeed/fetcher.js b/modules/newsfeed/fetcher.js new file mode 100644 index 00000000..4a41a41b --- /dev/null +++ b/modules/newsfeed/fetcher.js @@ -0,0 +1,111 @@ +/* Magic Mirror + * Fetcher + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + */ + +var NewsFetcher = require('./newsfetcher.js'); + +/* Fetcher + * Responsible for requesting an update on the set interval and broadcasting the data. + * + * attribute url string - URL of the news feed. + * attribute reloadInterval number - Reload interval in milliseconds. + */ + +var Fetcher = function(url, reloadInterval) { + var self = this; + var newsFetcher = new NewsFetcher(); + if (reloadInterval < 1000) { + reloadInterval = 1000; + } + + var reloadTimer = null; + var items = []; + + var fetchFailedCallback = function() {}; + var itemsReceivedCallback = function() {}; + + /* private methods */ + + /* fetchNews() + * Request the new items from the newsFetcher. + */ + + var fetchNews = function() { + //console.log('Fetch news.'); + clearTimeout(reloadTimer); + reloadTimer = null; + newsFetcher.fetchNews(url, function(fetchedItems) { + items = fetchedItems; + self.broadcastItems(); + scheduleTimer(); + }, function(error) { + fetchFailedCallback(self, error); + scheduleTimer(); + }); + }; + + /* scheduleTimer() + * Schedule the timer for the next update. + */ + + var scheduleTimer = function() { + //console.log('Schedule update timer.'); + clearTimeout(reloadTimer); + reloadTimer = setTimeout(function() { + fetchNews(); + }, reloadInterval); + }; + + /* public methods */ + + /* setReloadInterval() + * Update the reload interval, but only if we need to increase the speed. + * + * attribute interval number - Interval for the update in milliseconds. + */ + this.setReloadInterval = function(interval) { + if (interval > 1000 && interval < reloadInterval) { + reloadInterval = interval; + } + }; + + /* startFetch() + * Initiate fetchNews(); + */ + this.startFetch = function() { + fetchNews(); + }; + + /* broadcastItems() + * Broadcast the exsisting items. + */ + this.broadcastItems = function() { + if (items.length <= 0) { + //console.log('No items to broadcast yet.'); + return; + } + //console.log('Broadcasting ' + items.length + ' items.'); + itemsReceivedCallback(self); + }; + + this.onReceive = function(callback) { + itemsReceivedCallback = callback; + }; + + this.onError = function(callback) { + fetchFailedCallback = callback; + }; + + this.url = function() { + return url; + }; + + this.items = function() { + return items; + }; +}; + +module.exports = Fetcher; diff --git a/modules/newsfeed/newsfetcher.js b/modules/newsfeed/newsfetcher.js new file mode 100644 index 00000000..a93d6d09 --- /dev/null +++ b/modules/newsfeed/newsfetcher.js @@ -0,0 +1,53 @@ +/* Magic Mirror + * NewsFetcher + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + */ + +var FeedMe = require('feedme'); +var request = require('request'); + +var NewsFetcher = function() { + var self = this; + + self.successCallback = function(){}; + self.errorCallback = function(){}; + + self.items = []; + + var parser = new FeedMe(); + + parser.on('item', function(item) { + //console.log(item); + self.items.push({ + title: item.title, + pubdate: item.pubdate, + }); + }); + + parser.on('end', function(item) { + self.successCallback(self.items); + }); + + parser.on('error', function(error) { + self.errorCallback(error); + }); + + /* public methods */ + + /* fetchNews() + * Fetch the new news items. + * + * attribute url string - The url to fetch. + * attribute success function(items) - Callback on succes. + * attribute error function(error) - Callback on error. + */ + self.fetchNews = function(url, success, error) { + self.successCallback = success; + self.errorCallback = error; + request(url).pipe(parser); + }; +}; + +module.exports = NewsFetcher; \ No newline at end of file diff --git a/modules/newsfeed/node_helper.js b/modules/newsfeed/node_helper.js index f1423736..a0648750 100644 --- a/modules/newsfeed/node_helper.js +++ b/modules/newsfeed/node_helper.js @@ -1,184 +1,68 @@ - -// Load modules. -var request = require('request'); -var FeedMe = require('feedme'); +var NodeHelper = require('node_helper'); var validUrl = require('valid-url'); -var MMSocket = require('../../js/socketclient.js'); -var socket = new MMSocket('newsfeed'); +var Fetcher = require('./fetcher.js'); -var fetchers = {}; +module.exports = NodeHelper.create({ + // Subclass start method. + start: function() { + console.log('Starting module: ' + this.name); -// Register the notification callback. -socket.setNotificationCallback(function(notification, payload) { - if(notification === 'ADD_FEED') { - createFetcher(payload.url, payload.reloadInterval); - } -}); + this.fetchers = []; + }, -/* createFetcher(url, reloadInterval) - * Creates a fetcher for a new url if it doesn't exsist yet. - * Otherwise it reoses the exsisting one. - * - * attribute url string - URL of the news feed. - * attribute reloadInterval number - Reload interval in milliseconds. - */ - -var createFetcher = function(url, reloadInterval) { - if (!validUrl.isUri(url)){ - socket.sendNotification('INCORRECT_URL', url); - return; - } - - var fetcher; - if (typeof fetchers[url] === 'undefined') { - console.log('Create new news fetcher for url: ' + url + ' - Interval: ' + reloadInterval); - fetcher = new Fetcher(url, reloadInterval); - fetchers[url] = fetcher; - } else { - console.log('Use exsisting news fetcher for url: ' + url); - fetcher = fetchers[url]; - fetcher.setReloadInterval(reloadInterval); - fetcher.broadcastItems(); - } - - fetcher.startFetch(); -}; - - -/* Fetcher - * Responsible for requesting an update on the set interval and broadcasting the data. - * - * attribute url string - URL of the news feed. - * attribute reloadInterval number - Reload interval in milliseconds. - */ - -var Fetcher = function(url, reloadInterval) { - var self = this; - var newsFetcher = new NewsFetcher(); - if (reloadInterval < 1000) { - reloadInterval = 1000; - } - - var reloadTimer = null; - var items = []; - - /* private methods */ - - /* fetchNews() - * Request the new items from the newsFetcher. - */ - - var fetchNews = function() { - //console.log('Fetch news.'); - clearTimeout(reloadTimer); - reloadTimer = null; - newsFetcher.fetchNews(url, function(fetchedItems) { - //console.log(fetchedItems.length + ' items received.'); - items = fetchedItems; - self.broadcastItems(); - scheduleTimer(); - }, function(error) { - //console.log('Unable to load news: ' + error); - socket.sendNotification('UNABLE_TO_LOAD_NEWS', {url:url, error:error}); - scheduleTimer(); - }); - }; - - /* scheduleTimer() - * Schedule the timer for the next update. - */ - - var scheduleTimer = function() { - //console.log('Schedule update timer.'); - clearTimeout(reloadTimer); - reloadTimer = setTimeout(function() { - fetchNews(); - }, reloadInterval); - }; - - /* public methods */ - - /* setReloadInterval() - * Update the reload interval, but only if we need to increase the speed. - * - * attribute interval number - Interval for the update in milliseconds. - */ - this.setReloadInterval = function(interval) { - if (interval > 1000 && interval < reloadInterval) { - reloadInterval = interval; + // Subclass socketNotificationReceived received. + socketNotificationReceived: function(notification, payload) { + if(notification === 'ADD_FEED') { + this.createFetcher(payload.url, payload.reloadInterval); } - }; + }, - /* startFetch() - * Initiate fetchNews(); + /* createFetcher(url, reloadInterval) + * Creates a fetcher for a new url if it doesn't exsist yet. + * Otherwise it reoses the exsisting one. + * + * attribute url string - URL of the news feed. + * attribute reloadInterval number - Reload interval in milliseconds. */ - this.startFetch = function() { - fetchNews(); - }; - /* broadcastItems() - * Broadcast the exsisting items. - */ - this.broadcastItems = function() { - if (items.length <= 0) { - //console.log('No items to broadcast yet.'); + createFetcher: function(url, reloadInterval) { + var self = this; + + if (!validUrl.isUri(url)){ + self.sendSocketNotification('INCORRECT_URL', url); return; } - //console.log('Broadcasting ' + items.length + ' items.'); - socket.sendNotification('NEWS_ITEMS', { - url: url, - items: items - }); - }; -}; -/* NewsFetcher - * Responsible for requesting retrieving the data. - */ + var fetcher; + if (typeof self.fetchers[url] === 'undefined') { + console.log('Create new news fetcher for url: ' + url + ' - Interval: ' + reloadInterval); + fetcher = new Fetcher(url, reloadInterval); + + fetcher.onReceive(function(fetcher) { + self.sendSocketNotification('NEWS_ITEMS', { + url: fetcher.url(), + items: fetcher.items() + }); + }); -var NewsFetcher = function() { - var self = this; - - self.successCallback = function(){}; - self.errorCallback = function(){}; - - self.items = []; - - var parser = new FeedMe(); - - parser.on('item', function(item) { - //console.log(item); - self.items.push({ - title: item.title, - pubdate: item.pubdate, - }); - }); - - parser.on('end', function(item) { - self.successCallback(self.items); - }); - - parser.on('error', function(error) { - self.errorCallback(error); - }); - - /* public methods */ - - /* fetchNews() - * Fetch the new news items. - * - * attribute url string - The url to fetch. - * attribute success function(items) - Callback on succes. - * attribute error function(error) - Callback on error. - */ - self.fetchNews = function(url, success, error) { - self.successCallback = success; - self.errorCallback = error; - request(url).pipe(parser); - }; -}; + fetcher.onError(function(fetcher, error) { + self.sendSocketNotification('FETCH_ERROR', { + url: fetcher.url(), + error: error + }); + }); + self.fetchers[url] = fetcher; + } else { + console.log('Use exsisting news fetcher for url: ' + url); + fetcher = self.fetchers[url]; + fetcher.setReloadInterval(reloadInterval); + fetcher.broadcastItems(); + } + fetcher.startFetch(); + } +}); diff --git a/modules/node_modules/node_helper/index.js b/modules/node_modules/node_helper/index.js new file mode 100644 index 00000000..774f7ef3 --- /dev/null +++ b/modules/node_modules/node_helper/index.js @@ -0,0 +1,72 @@ +/* Magic Mirror + * Node Helper Superclass + * + * By Michael Teeuw http://michaelteeuw.nl + * MIT Licensed. + */ + +var Class = require('../../../js/class.js'); +var MMSocket = require('../../../js/socketclient.js'); + +NodeHelper = Class.extend({ + init: function() { + console.log('Initializing new module helper ...'); + }, + + start: function() { + console.log('Staring module helper: ' + this.name); + }, + + /* socketNotificationReceived(notification, payload) + * This method is called when a socket notification arrives. + * + * argument notification string - The identifier of the noitication. + * argument payload mixed - The payload of the notification. + */ + socketNotificationReceived: function(notification, payload) { + Log.log(this.name + ' received a socket notification: ' + notification + ' - Payload: ' + payload); + }, + + /* setName(data) + * Set the module name. + * + * argument name string - Module name. + */ + setName: function(name) { + this.name = name; + this.socket(); + }, + + /* socket() + * Returns a socket object. If it doesn't exsist, it's created. + * It also registers the notification callback. + */ + socket: function() { + if (typeof this._socket === 'undefined') { + this._socket = this._socket = new MMSocket(this.name); + } + + var self = this; + this._socket.setNotificationCallback(function(notification, payload) { + self.socketNotificationReceived(notification, payload); + }); + + return this._socket; + }, + + /* sendSocketNotification(notification, payload) + * Send a socket notification to the node helper. + * + * argument notification string - The identifier of the noitication. + * argument payload mixed - The payload of the notification. + */ + sendSocketNotification: function(notification, payload) { + this.socket().sendNotification(notification, payload); + } +}); + +NodeHelper.create = function(moduleDefinition) { + return NodeHelper.extend(moduleDefinition); +}; + +module.exports = NodeHelper; \ No newline at end of file