Read the statement by Michael Teeuw here.
How to send a notification
-
So I’ve been playing around with this or a while now but cant seem to get it. I essentially want to send a notification to a modules using another. Sounds simple but I cant find any examples on how to do this. If anyone can help that would be great I’m sure its something simple in the end but please have a look at the below files and let me know what I’m missing and where, many thanks in advance!
Node_Helper.js for MMM-ALEXACONTROL
/** * Magic Mirror * Node Helper: MMM-AlexaControl * * By JoChef2 * MIT Licensed. */ var NodeHelper = require("node_helper"); var FauxMo = require('node-fauxmo'); var pm2 = require('pm2'); const exec = require("child_process").exec; module.exports = NodeHelper.create({ start: function () { console.log('MMM-AlexaControl helper, started...'); this.config = null; this.setDevicesCounter = 0; // Counter for only one node_helper start look line 225 }, formattedName: function(devname,actionString){ var result=actionString if(devname != undefined){ var s = actionString.split(' ') if(s.length>1) s.splice(1,0,devname) else s.unshift(devname) result = s.join(' ') } return result; }, setDevices: function(){ _this = this; cD = this.customDevices(this.config.devices); nD = this.notificationDevices(cD, this.config.notifications); coD = this.commandDevices(nD, this.config.commands) pD = this.pageDevices(coD); mD = this.menuDevices(pD); //console.log(pD); //console.log(mD); fauxMoPages = new FauxMo(mD); // creates fauxmo devices }, customDevices : function(customD){ // creates your custom devices from config _this = this for(i = 0; i < Object.keys(customD.devices).length; i++){ if(customD.devices[i].port === undefined){ customD.devices[i].port = _this.config.startPort + 30 + i; } customD.devices[i].handler = new Function('action', customD.devices[i].handler) } return customD; }, notificationDevices: function(notificationD, notifications){ // creates your notification devices _this = this counter = 0 + Object.keys(notificationD.devices).length _this.n = [] for(i = 0; i < Object.keys(notifications).length; i++){ device = {} device.name = _this.formattedName(_this.translations["deviceName"],notifications[i].name) if(notifications[i].port === undefined){ device.port = _this.config.startPort + 50 + i }else{ device.port = notifications[i].port } if(notifications[i].OnOff){ _this.n[i] = notifications[i].notification device.handler = new Function('action', 'if(action === 1){_this.sendSocketNotification("CUSTOM",' + JSON.stringify(_this.n[i][0]) + ')}else{_this.sendSocketNotification("CUSTOM",' + JSON.stringify(_this.n[i][1]) + ')}') }else{ _this.n[i] = notifications[i].notification device.handler = new Function('action', '_this.sendSocketNotification("CUSTOM",' + JSON.stringify(_this.n[i]) + ')') } notificationD.devices[i + counter] = device } return notificationD; }, commandDevices: function(commandD, commands){ _this = this counter = 0 + Object.keys(commandD.devices).length _this.n = [] _this.opts = { timeout: 8000 }; _this.exec = exec for(i = 0; i < Object.keys(commands).length; i++){ device = {} device.name = _this.formattedName(_this.translations["deviceName"],commands[i].name) if(commands[i].port === undefined){ device.port = _this.config.startPort + 75 + i }else{ device.port = commands[i].port } _this.n[i] = commands[i].command if(commands[i].OnOff){ device.handler = new Function('action', 'if(action === 1){_this.exec(' + JSON.stringify(_this.n[i][0]) + '), _this.opts, (error, stdout, stderr) => {_this.checkForExecError(error, stdout, stderr); }}else{_this.exec(' + JSON.stringify(_this.n[i][1]) + '), _this.opts, (error, stdout, stderr) => {_this.checkForExecError(error, stdout, stderr); }}') } else{ device.handler = new Function('action', 'if(action === 1){_this.exec(' + JSON.stringify(_this.n[i]) + '), _this.opts, (error, stdout, stderr) => {_this.checkForExecError(error, stdout, stderr); }}') } commandD.devices[i + counter] = device } return commandD; }, pageDevices: function(pageD){ // creates your page devices _this = this; counter = 0 + Object.keys(pageD.devices).length this.pPort = _this.config.startPort if(_this.config.pages > 0){ for(i = 0; i < _this.config.pages; i++){ device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["page"] + (i + 1)) device.port = _this.pPort - 100 device.handler = new Function('action', `_this.sendSocketNotification("PAGE_CHANGED", ` + i +`)`) pageD.devices[i + counter] = device this.pPort++ } } return pageD }, menuDevices: function(menuD){ // create your devices to control the Mirror and pi _this = this; var opts = { timeout: 8000 }; //console.log("menu device ") counter = 0 + Object.keys(menuD.devices).length if(this.config.refresh){ device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["refresh"]) device.port = _this.config.startPort device.handler = function(action) {_this.sendSocketNotification("ACTION", "refresh")} menuD.devices[counter] = device; counter++; } _this.config.startPort++ if(this.config.restart){ // only with PM2 device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["restart"]) device.port = _this.config.startPort device.handler = function(action) { pm2.connect((err) => { if (err) { console.error(err); } console.log("Restarting PM2 process: " + _this.config.pm2ProcessName); pm2.restart(_this.config.pm2ProcessName, function(err, apps) { pm2.disconnect(); if (err) { console.log(err); } }); }); } menuD.devices[counter] = device; counter++; } _this.config.startPort++ if(this.config.stop){ // only with PM2 device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["stop"]) device.port = _this.config.startPort device.handler = function(action) { pm2.connect((err) => { if (err) { console.error(err); } console.log("Stopping PM2 process: " + _this.config.pm2ProcessName); pm2.stop(_this.config.pm2ProcessName, function(err, apps) { pm2.disconnect(); if (err) { console.log(err); } }); }); } menuD.devices[counter] = device; counter++; } _this.config.startPort++ if(this.config.reboot){ //reboot the pi device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["reboot"]) device.port = _this.config.startPort device.handler = function(action) { exec("sudo shutdown -r now", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } menuD.devices[counter] = device; counter++; } _this.config.startPort++ if(this.config.shutdown){ // shutdwon the pi device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["shutdown"]) device.port = _this.config.startPort device.handler = function(action) { exec("sudo shutdown -h now", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } menuD.devices[counter] = device; counter++; } _this.config.startPort++ /** * for me worked only vcgencmd display_power 0 and vcgencmd display_power 1 * probably for you work tvservice --off and tvservice --preferred * test it in terminal if you aren't sure */ if(this.config.monitorToggle){ console.log("monitorToggle requested") device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["monitor"]) device.port = _this.config.startPort if(this.config.vcgencmd =='vcgencmd'){ device.handler = function(action) { if(action === 1){ exec("vcgencmd display_power 1", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); }if(action === 0){ exec("vcgencmd display_power 0", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } } } else if(this.config.vcgencmd =='tvservice'){ device.handler = function(action) { if(action === 1){ exec("tvservice --preferred", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); }if(action === 0){ exec("tvservice --off", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } } } else if(this.config.vcgencmd =='cec-client'){ device.handler = function(action) { if(action === 1){ exec("echo \'on "+ _this.config.cecAddress +"\' | cec-client -s -d 1", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); }if(action === 0){ exec("echo \'standby "+ _this.config.cecAddress +"\' | cec-client -s -d 1", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } } } else if(this.config.vcgencmd =='hide'){ console.log("configuring toggle with hide") device.handler = function(action) { console.log("received monitor toggle with hide action="+action) _this.sendSocketNotification('MONITOR_ACTION', action?"SLEEP_WAKE":"SLEEP_HIDE") } } else{ device.handler = function(action){ console.log("Please configure the option vcgencmd") } } menuD.devices[counter] = device; counter++; } _this.config.startPort++ if(this.config.usb){ // toggle usb power of your pi device = {} device.name = _this.formattedName(_this.translations["deviceName"],_this.translations["usb"]) device.port = _this.config.startPort device.handler = function(action) { if(action === 0){ exec("echo '1-1' |sudo tee /sys/bus/usb/drivers/usb/unbind", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } else{ exec("echo '1-1' |sudo tee /sys/bus/usb/drivers/usb/bind", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } } menuD.devices[counter] = device; counter++; } _this.config.startPort++ return menuD; }, /* monitorOff: function(){ var opts = { timeout: 8000 }; if(this.config.vcgencmd){ exec("vcgencmd display_power 0", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); }else{ exec("tvservice --off", opts, (error, stdout, stderr) => { _this.checkForExecError(error, stdout, stderr); }); } }, */ checkForExecError: function(error, stdout, stderr) { if (stderr) { console.log('stderr: "' + stderr + '"'); return 1; } if (error !== null) { console.log('exec error: ' + error); return 1; } return 0; }, socketNotificationReceived: function(notification, payload) { if(notification === "SET_DEVICE"){ // set all your devices this.config = payload; if(this.setDevicesCounter === 0){ // set your devices only the first time a this.setDevices(); // notification is received this.setDevicesCounter++; } } if(notification === "TRANSLATIONS"){ // get translations and put them in an array this.translations = []; this.translations = payload; } } });
MMM- AlexaControl config.js
/** * Magic Mirror * Module: MMM-AlexaControl * * By JoChef2 * MIT Licensed. */ Module.register("MMM-AlexaControl",{ defaults:{ image: true, // shows the image height: 265, // heigth of the image width: 265, // width of the image pages: 0, // number off pages in MMM-Pages module. 0 means you doesn't use it devices: { // empty object for your custom devices devices: [] }, notifications: [], // empty array for your notification devices commands: [], // empty array for your command devices startPort: 11000, // the lowest used port refresh: true, // refresh your Mirror restart: true, // restart your Mirror with pm2 stop: true, // stops your Mirror with pm2 reboot: false, // reboot your pi shutdown: false, // shutdown your pi pm2ProcessName: "mm", // name of your pm2 process monitorToggle: true, // switch your monitor on and off vcgencmd: 'vcgencmd', // command you use for monitor toggle cecAddress: "0.0.0.0", // Address for the cec-client usb: false // toggle usb power of your pi }, getTranslations: function(){ // add more translations return { en: "translations/en.json", de: "translations/de.json" } }, start: function(){ Log.log('Starting module: ' + this.name); // send all translations to node_helper this.sendSocketNotification('TRANSLATIONS', {"monitor": this.translate("MONITOR"), "shutdown": this.translate("SHUTDOWN"), "reboot": this.translate("REBOOT"), "page": this.translate("PAGE"), "refresh": this.translate("REFRESH"), "restart": this.translate("RESTART"), "stop": this.translate("STOP"), "usb": this.translate("USB"), "deviceName":this.translate(this.config.deviceName)}); this.sendSocketNotification('SET_DEVICE', this.config); // send the config to node_helper }, getDom: function(){ // returns only an image or an empty div (necessary for MMM-Pages) if(this.config.image){ let img = document.createElement("img"); img.classList = "img"; img.src = "MMM-AlexaControl/AlexaLogoGrey.png"; img.height = this.config.height; img.width = this.config.width; return img; }else{ return document.createElement("div"); } }, socketNotificationReceived: function(notification, payload) { if(notification === "PAGE_CHANGED"){ // change pages of MMM-Pages this.sendNotification("PAGE_CHANGED", payload); } if(notification === "ACTION"){ // refresh the Mirror if(payload === "refresh"){ window.location.reload(true); } } if(notification === "SELFIE_SHOOT"){ this.sendNotification("SELFIE_SHOOT"); // send any notification to any module } if(notification =='MONITOR_ACTION'){ Log.log('RECEIVE monitor NOTIFICATION='+payload) this.sendNotification(notification,payload) } } });
Config.js
/* Magic Mirror Config Sample * * By Michael Teeuw https://michaelteeuw.nl * MIT Licensed. * * For more information on how you can configure this file * see https://docs.magicmirror.builders/getting-started/configuration.html#general * and https://docs.magicmirror.builders/modules/configuration.html */ let config = { address: "localhost", // Address to listen on, can be: // - "localhost", "127.0.0.1", "::1" to listen on loopback interface // - another specific IPv4/6 to listen on a specific interface // - "0.0.0.0", "::" to listen on any interface // Default, when address config is left out or empty, is "localhost" port: 8080, basePath: "/", // The URL path where MagicMirror is hosted. If you are using a Reverse proxy // you must set the sub path here. basePath must end with a / ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses // or add a specific IPv4 of 192.168.1.5 : // ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"], // or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format : // ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"], useHttps: false, // Support HTTPS or not, default "false" will use HTTP httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true language: "en", locale: "en-UK", logLevel: ["INFO", "LOG", "WARN", "ERROR"], // Add "DEBUG" for even more logging timeFormat: 12, units: "metric", serverOnly: true, // local for armv6l processors, default // starts serveronly and then starts chrome browser // false, default for all NON-armv6l devices // true, force serveronly mode, because you want to.. no UI on this device modules: [ { disabled: false, module:"MMM-Config", position:"top_right", // the QR code (if requested) will appear here config:{ showQR: false, //restart: "pm2", //force_update: true, //debug: true } }, { module: 'MMM-AlexaControl', position: 'middle_center', notifications:[ { name: 'Next page', port: 11102, OnOff: false, notification: ["PAGE_INCREMENT", 'payload'] }, { name: 'Previous page', port: 11103, OnOff: false, notification: ["PAGE_DECREMENT", 'payload'] }, { name: 'Selfie', port: 11104, OnOff: false, notification: ["SELFIE_SHOOT"] } ], config:{ image: true, deviceName: "mirror", pm2ProcessName: "mm", vcgencmd: "hide", startPort: 11001, refresh: true, restart: true, //stop: true, reboot: true, shutdown: true, monitorToggle: true, //cecAddress: "0.0.0.0", //usb: false pages: 2, } }, { module: 'MMM-SleepWake', config:{ //delay: 15, // default mode: 'pi' } }, { module: 'MMM-pages', config: { pages: 2, modules: [[ "MMM-Astro" ], [ "MMM-OpenWeatherMapForecast", "compliments" ]], fixed: [ "clock", "MMM-page-indicator" ], hiddenPages: { "screenSaver": [ "clock", "MMM-SomeBackgroundImageModule" ], "admin": [ "MMM-ShowMeSystemStatsModule", "MMM-AnOnScreenMenuModule" ], }, } }, { module: 'MMM-page-indicator', position: 'bottom_bar', config: { pages: 2, } }, { disabled: false, module: "MMM-Selfieshot", position: "bottom_center", config: { displayButton: "portrait", storePath: "/media/smartmirror/Storage/Photos", width:1280, height:720, quality: 100, device: null, shootMessage: "Smile!", shootCountdown: 3, displayCountdown: true, displayResult: true, playShutter: true, shutterSound: "shutter.mp3", } }, { disabled: false, module: 'MMM-Astro', position: 'top_left', config: { sign: "leo", iconset: "4", extend: false, } }, { module: "MMM-OpenWeatherMapForecast", header: "Weather", position: "top_right", classes: "default everyone", disabled: true, config: { iconset: "4c", units: "metric", useAnimatedIcons: true, concise: false, forecastLayout: "table", showWind: true } }, { module: "alert", }, { module: "updatenotification", position: "top_bar" }, { module: "clock", position: "top_center" }, { module: "compliments", position: "lower_third" }, ] }; /*************** DO NOT EDIT THE LINE BELOW ***************/ if (typeof module !== "undefined") {module.exports = config;}
-
@alyx said in How to send a notification:
this.sendNotification(“PAGE_CHANGED”, payload);
you can only send notifications from the modulename.js
this notification goes to all running modules
it’s a broadcastthe page_changed is the notification string.
can be anything
payload is the data that goes w that notification Id. and can be anythingthose receiving the notice MUST know what the format of payload is for and message they chose to process.all others would be ignored.
there are hundreds of notifications each minute.
-
This post is deleted! -
Hi Sam,
My replies are getting flagged as spam apologies for the delay. But I think I found where the Selfie script sends out the notification. Do you think the other scripts I sent earlier are configured correctly?
-
@hango Hi Hango!
Ok so I’ll add that first line to my MMM-SelfieShoot.js (the module that needs to be actioned by another)
And the second will be inside the MMM-AlexaControl.js? I’ll test it now :)
-
Did it there but doesn’t seem to appear in Alexa, The other functions such as restart and monitor off/on works but I’m having trouble having this appear/ work.
-
@alyx so in alexacontrol, you have to create a notification device
notifications: [ { name: 'Notificaition 1', port: 11100, OnOff: false, notification: ["NOTIFICATION", "PAYLOAD"] } ]
name is the thing Alexa will know
the tcp/ip port number to use for Alexa to contact this machine
and the notification info should look familiarthen u restart MagicMirror
( in the MagicMirror startup messages you should see a line listening on ??? where ??? is the port number you used in the notification entry)
and then Alexa discover devicesthen whatever name is, should be discovered.
then you invoke it with
Alexa, turn on xxxx
where xxxx is the name of your device
(because alexacontrol creates fake wemo electric outlets)note that notifications shown above is a list [], so each {} is a notification device
and this stuff goes in the alexacontrol module
config:{}
section -
@sdetweil I have that set up al;ready but my alexa cant discover it for some reason. If you need you can see it in my config.js file above
-
@alyx nice, but you missed one key point
and this stuff goes in the alexacontrol module
config:{}
sectionu have in the general module section not in the module config
-
@sdetweil Ah! that worked thanks :) Next issue it doesnt call the module when activated lol