Important edit. I’ve implemented another solution for the integration between the server and the client using a mirror module. I’ll be moving forward with that solution. Check out this article. For a solution using a change of the core code : please read on…
Allright, now things get a bit more complicated. In the last article I showed you how I got my debug hub talking to the mirror. That is, to the web server in the mirror. There was nothing visible on the screen. There was not actual visible interaction.
In this article I want to tackle this.
The electron website explains this very well in its documentation. In fact, there is a special framework foreseen (and it comes out of the box) to make communication from the client (also called the renderer process) to send to the server (the main process). A main process (being the web server running in the background) listens to inputs from its renderer proces(ses) (there can be multiple in theory).
See the electron documentation here :
There is one big catch here. This framework supports a message being sent from the renderer process to the main process… and that’s not what we want. We want it the other way around. Let me remind me of my ultimate goal : to be able to control the mirror with outside interaction. Whether it be an application (such as the debug hub), an external proces or even Alexa Voice Service, all through a simple HTTP request. Easy to integrate, easy to maintain, and very easy to deploy.
That’s it. That wasn’t all that hard.
Now for the tricky part. And the confusing part. There are 2 sides to the IPC integration. IPC stands for inter process communication in which there is a process that sends a message on a certain channel, and another process that listens for it. The ipcMain object is responsible for receiving messages. The ipcRenderer object is responsible for sending them. It’s also important to know that the ipcRenderer is an object that cannot be used in the webserver process. It will give you issues such as undefined objects or unknown attributes. Trust me. I learned it the hard way. You should think there’s a problem here because we want the communication to go in the other direction, but it’s time to get creative.
This is where it gets confusing. The ipcMain object can be used in the web server processes which is situated in the server.js file. The ipcRenderer can be used in the client process, being the magic mirror module which is located in the main.js file. That’s right. Main.js. What’s in a name… right? There is no actual relation between the object called ipcMain and the source file called main.js.
Well, now that we’ve got that out of the way, it’s time to start adjusting the server.js file.
First I’ll be defining the ipcMain object on top by doing this :
Somewhere in the code of the server process I can now call the “on” function of ipcMain that lets you specify a channel (called asynchronous-message below) and a callback function, which is implemented with a console log and a reply in the screenshot below.
You might notice how I just copy pasted the code from the electron website, just to test if this would work. Note that there are 2 ways to communicate : synchronous and asynchronous. The first one would be done using the sync method, but it would mean that the client process would wait for a response of the server. Which is not what we want. So i’ll be implementing the asynchronous method.
Notice how the code sends a reply. There’s the key to our solution! This is a way to send a message back to the client process.
Time to adjust the code of the client process. This is done in main.js where the MM object is defined. I started with defining the ipcRenderer object here :
In Main.js, you can locate the function called modulesStarted. It’s a callback function, executed when all mirror modules have stopped loading. According to me, an ideal place to start testing some stuff. I added the following lines of code to that function
First I send a message to the ipcMain object by using the send method. The first parameter is the channel (the same one as the ipcRenderer is listening on), and the second parameter is the message i’m sending.
In the second statement I create a listener for a reply. All it does is print the reply to the console. Let’s test that out. And what do you know. Here you can see the output: On the one hand the console log of the dev tools in the mirror, and on the other hand the console log in the terminal of the web server. Ping and Pong.
Now, we can get cracking with our own code.
Let’s start with making a method that will hide all of the mirror modules. There’s a hideModule method already in the MM object, so I’ll just create one that loops through the modules, and hides them all. Like so :
All it does is send a message, and wait for a reply.
Testing that out, it will give something like this : a lot of pings and a lot of pongs in the consoles.
Well… actually, the above screenshot is not 100% correct. That is, it will work, but essentially I’m creating a new listener every time around. That will create a memory leak, so I adjusted to code and customised it a bit like so :
The “mirror” channel is now used for communication. There’s a listener on the channel “mirror-command”.
In the server.js file, I’ve adjusted the listener as well, to listen for the “mirror” channel.
So, summing it all up. What’s happening? Every second, the mirror sends the question the server : “what do I have to do?”. In most cases it will get no response. But if there is something to do, the server will send a reply with that command, at which time it is executed by the mirror.
The only thing we have to do, is make sure that a HTTP command coming into the webserver, somehow gets into that reply. That isn’t very difficult. We only have to create a simple queueing mechanism in which a command is queued up, and every time the question comes “what do I have to do?”, this queue is read and passed on.
Editing the code of server.js, I just define an empty array and call it command queue.
Next, I adjust the app.get method of the express framework to push a command onto the queue.
The listener for the command requests by the mirror just checks if there is a command in the queue, and removes it by using the shift method. It then passes it on to the reply. Easy peasy :
See how, in the above code I sent some console logs. Let’s start this.
Press the button in the debug hub :
Here’s the log of the mirror :
… and the log of the server.
Cool, now just to hook it all up. In the main.js process I will check for the “hide-all-modules” command, and if it comes, I will call my new function hideAllModules.
I added a console log to see if it works well.
And here we go : I pressed the button, and the modules disappear in the mirror. Notice how the “hiding module” console log message appears it the bottom right corner :
I’ll add another function to show the modules again. Very similar :
And extend the event handler to allow a second command :
If we run that I noticed that the server comes with a message that we’re using a deprecated function in the express framework.
Looking at the website of express js, that’s indeed true. It has been replaced with another principal. Check it out here :
So i will adjust my code accordingly. Notice how the parameter is now specified in the URL, and it can be addressed as an attribute of the params object.
I have to adjust the debug hub as well to pass along in the correct syntax :
And that’s it. We’ve hooked it all up. I proudly present you the result below :