Read the statement by Michael Teeuw here.
Head first developing MM module for extreme beginners
-
Head first developing MM module for extreme beginners
Today morning, I’ve got two messages from two members of this forum at the same time. Both of two asked me the same thing - “How can I show my contents on MM with my module?”
So, here is a simple guide for developing MM module. This is not a royal road or perfect answer, but I hope this could be a helpThis article is not about using
git
or any other deeper knowledge. I assume you are familiar with javascript(nodeJS) at least.Make a workspace
cd ~/MagicMirror/modules mkdir MMM-Test cd MMM-Test touch MMM-Test.js
MMM-Test.js
is your main module script. It will be called by MM core at running.Let’s make something.
configuration
First, add your module into MM framework. open
~/MagicMirror/config/config.js
and add your module.modules: [ { module: "MMM-Test", position: "top_left", config: {} }, ]
For simple things, remove all other modules except your
MMM-Test
.MMM-Test.js
open
MMM-Test.js
and edit.nano MMM-Test.js
In that, copy this and paste into
MMM-Test.js
.Module.register("MMM-Timetable", { defaults: {}, start: function () {}, getDom: function() {}, notificationReceived: function() {}, socketNotificationReceived: function() {}, })
There are many other things to study but currently, we will focus on these 5 things.
getDom()
This function will render your content on MM screen.
Let’s do something.getDom: function() { var element = document.createElement("div") element.className = "myContent" element.innerHTML = "Hello, World!" return element },
<div>
element is created and returned to MM for rendering.
When MM needs your module’s output being refreshed, this function will be called by MM core.
See how it works.cd ~/MagicMirror npm start dev
defaults:{}
You can use default configuration values. Let’s do it
defaults: { foo: "I'm alive!" }, getDom: function() { var element = document.createElement("div") element.className = "myContent" element.innerHTML = "Hello, World! " + this.config.foo return element },
your configuration value
foo
could be used likethis.config.foo
.
this
in this module, it directs module itself. you can access various module values with it.
See how it works.And you can customize configuration values with
config.js
set yourconfig.js
like this;{ module: "MMM-Test", position: "top_left", config: { foo: "I'm the King of the world!" } },
The result will be like this;
start() & updateDom()
This function will be executed when your module is loaded successfully.
Let’s change something.start: function (){ this.count = 0 var timer = setInterval(()=>{ this.updateDom() this.count++ }, 1000) }, getDom: function() { var element = document.createElement("div") element.className = "myContent" element.innerHTML = "Hello, World! " + this.config.foo var subElement = document.createElement("p") subElement.innerHTML = "Count:" + this.count subElement.id = "COUNT" element.appendChild(subElement) return element },
After your module is loaded, your module’s output will be updated per every 1 second.
Whenever you want to update your output, you can call.updateDom()
to let MM also call your.getDom()
to redraw the output. (Don’t try to callgetDom()
directly by yourself. It will not work as your expectation. Rendering is delegated to the MM core.)By the way,
start()
is not a good place to control drawing, becausestart()
is called before your DOM objects are first rendered. Above code is somewhat bad behavior.notificationReceived()
MM and modules are communicating each other with
notification
. Your module can receivenotification
and send it also.start: function (){ this.count = 0 }, getDom: function() { var element = document.createElement("div") element.className = "myContent" element.innerHTML = "Hello, World! " + this.config.foo var subElement = document.createElement("p") subElement.innerHTML = "Count:" + this.count subElement.id = "COUNT" element.appendChild(subElement) return element }, notificationReceived: function(notification, payload, sender) { switch(notification) { case "DOM_OBJECTS_CREATED": var timer = setInterval(()=>{ this.updateDom() this.count++ }, 1000) break } },
The result looks so same with previous codes. But your timer will be started after when “DOM_OBJECTS_CREATED” notification is received but not when the module is loaded.
DOM_OBJECTS_CREATED
notification is emitted when all of modules are loaded and rendered(withgetDom()
) first time. At the moment, you can manipulate your output in your module.Another way to control output
Once your DOM is ready(after first calling
getDom()
andDOM_OBJECTS_CREATED
notification is emitted), you can access your DOM with script.notificationReceived: function(notification, payload, sender) { switch(notification) { case "DOM_OBJECTS_CREATED": var timer = setInterval(()=>{ //this.updateDom() var countElm = document.getElementById("COUNT") countElm.innerHTML = "Count:" + this.count this.count++ }, 1000) break } },
Instead
updateDom()
, direct access to DOM is used.node_helper.js
But your
MMM-Test.js
has some limitation. Because it is run on MM framework and Browser(Electron/Chromium or Midori)
When you need more than what browser can provide, you should usenode_helper.js
By example, yourMMM-Test.js
cannot read or write local files by usual way (Of course, there is a hack, but that is not the point at this moment.).
But yournode_helper.js
can. Or when you need to use other variousnode modules
,node_helper.js
exists for that purpose.Let’s make
node_helper.js
nano node_helper.js
Copy this code and paste.
var NodeHelper = require("node_helper") module.exports = NodeHelper.create({ start: function() {}, socketNotificationReceived: function() {}, })
This is a default template of
node_helper.js
. Now add some codes.var NodeHelper = require("node_helper") module.exports = NodeHelper.create({ start: function() { this.countDown = 10000000 }, socketNotificationReceived: function(notification, payload) { switch(notification) { case "DO_YOUR_JOB": this.sendSocketNotification("I_DID", (this.countDown - payload)) break } }, })
Like
MMM-Test.js
,start()
will be executed whennode_helper.js
is loaded and connected to your module.
BetweenMMM-Test.js
andnode_helper.js
,socketNotification
is used for communication. (similar withnotification
)
You can getsocketNotification
withsocketNotificationReceived()
and can send it bysendSocketNotification()
.Then, you should modify
MMM-Test.js
getDom: function() { var element = document.createElement("div") element.className = "myContent" element.innerHTML = "Hello, World! " + this.config.foo var subElement = document.createElement("p") subElement.id = "COUNT" element.appendChild(subElement) return element }, notificationReceived: function(notification, payload, sender) { switch(notification) { case "DOM_OBJECTS_CREATED": var timer = setInterval(()=>{ this.sendSocketNotification("DO_YOUR_JOB", this.count) this.count++ }, 1000) break } }, socketNotificationReceived: function(notification, payload) { switch(notification) { case "I_DID": var elem = document.getElementById("COUNT") elem.innerHTML = "Count:" + payload break } },
Result will be like this;
Now you made your
MMM-Test.js
to work withnode_helper.js
Of course, this is not so good example to show whatnode_helper.js
can do.
But I believe you can understand hownode_helper.js
andMMM-Test.js
work with each other.Well. You’ve learned 90% of how to develop MM module. the remains are described in https://github.com/MichMich/MagicMirror/blob/master/modules/README.md
Good luck to you. -
I cant get the text to appear when I run it, I made the Test.js file in the MagicMirror modules folder and copied in what was posted and put the config in the config.js file but when I run it the screens all black, here’s what happens when I try to run it
pi@raspberrypi:~/MagicMirror/modules $ cd ~/MagicMirror
pi@raspberrypi:~/MagicMirror $ npm start devmagicmirror@2.8.0 start /home/pi/MagicMirror
sh run-start.sh “dev”Starting MagicMirror: v2.8.0
Loading config …
Loading module helpers …
No helper found for module: MMM-Test.
All module helpers loaded.
Starting server on port 8080 …
Server started …
Sockets connected & modules started …
Launching application.
Shutting down server…
pi@raspberrypi:~/MagicMirror $any help would be greatly appreciated I dont see what I’m missing
-
It is because there is an error (at least I think so - I am new too) and no instructions on where to put the function.
-
change the Module.register(“MMM-Timetable”) to Module.register(“MMM-Test”)
-
The article does not tell you where to put the getDom function when it is first introduced. Replace the getDom: function() {}. with the code shown. In other words, don’t just add it to the file. This article would have benefited from a final section that gave the entire code.
I think the original author at least expected us to be very familiar with Node.js (fair enough), but alas, I am not so it took a bit of work to figure it out. :-)
Here it is, for the first part:
Module.register(“MMM-Test”, {
defaults: {},
start: function () {},getDom: function() {
var element = document.createElement(“div”)
element.className = “myContent”
element.innerHTML = “Hello, World!”
return element
},
notificationReceived: function() {},
socketNotificationReceived: function() {},
})
_ -
-
Shame, code is really messed up. Not helpful to use it as a guide to start developing modules.
-
@nrayever see my sample module,
https://github.com/sdetweil/SampleModuleand follow the module developers doc
-
@sdetweil will try it. I don’t have JS experience that is why I have found it difficult to start.
-
@nrayever sorry cant help develop module if u dont know programming and dont want to learn
-
@StuGrunt said in Head first developing MM module for extreme beginners:
It is because there is an error (at least I think so - I am new too) and no instructions on where to put the function.
-
change the Module.register(“MMM-Timetable”) to Module.register(“MMM-Test”)
-
The article does not tell you where to put the getDom function when it is first introduced. Replace the getDom: function() {}. with the code shown. In other words, don’t just add it to the file. This article would have benefited from a final section that gave the entire code.
I think the original author at least expected us to be very familiar with Node.js (fair enough), but alas, I am not so it took a bit of work to figure it out. :-)
Here it is, for the first part:
Module.register(“MMM-Test”, {
defaults: {},
start: function () {},getDom: function() {
var element = document.createElement(“div”)
element.className = “myContent”
element.innerHTML = “Hello, World!”
return element
},
notificationReceived: function() {},
socketNotificationReceived: function() {},
})
_Both of those functions go outside the getDom function pretty much anywhere you want to put them.
The socketNotificationReceived function [example below, can be used in either node_helper OR your main.js file. The example below is in my node_helper] :
socketNotificationReceived: function(notification, payload) { if (notification === 'CONFIG') { this.config = payload; } if (notification === 'GET_CURRENT') { this.getCurrent(payload); } if (notification === 'GET_FORECAST') { if (this.forecast.timestamp === this.getDate() && this.forecast.data !== null) { this.sendSocketNotification('FORECAST_RESULT', this.forecast.data); console.log("Using data we already have"); } else { this.getForecast(); console.log("Getting new data"); } } }
Here is the corresponding one in the main.js
socketNotificationReceived: function(notification, payload) { if (notification === "CURRENT_RESULT") { this.processCurrent(payload); } if (notification === "FORECAST_RESULT") { this.processForecasts(payload); } this.updateDom(this.config.initialLoadDelay); },
You can use notificationReceived to receive info from other modules like this:
Here are examples of that:
notificationReceived: function (notification, payload){ if (notification === 'CURRENT_RESULT') { this.processCurrent(payload); } },
this.sendNotification('CURRENT_RESULT', payload);
This is how I send and receive my ‘payload’ from one module to another.
The first example is getting my ‘CURRENT_RESULT’ [getting info for a Forecast module from the Current weather module] from the bottom example that is
sending out ‘CURRENT_RESULT’…The bottom example will ‘broadcast’ and in reality ANY module can get the data it’s sending out.
Hope this helps. It’s a really simple process.
-
-
How can I test the getDom() html output?
I don’t want to restart MM every time when I change something just to look if it worked. -
@Schmaniel ctrl-r to reload the screen