Running python script from MagicMirror module - javascript

I am currently developing a custom module for my magic mirror.
I want this module to execute a python script.
This python script fetches data from a web server and creates a .json file in the module folder with the data.
I then want the module to import this datafile inside javascript and display it on screen.
However i cant get the magic mirror module to run the python script.
I have very little javascript knowledge so any help is appreciated.
This is the code i have so far
defaults: {
},
start: function () {
var timer = setInterval(()=>{
const spawn = require('child_process').spawn;
const childPython = spawn('python3', ['./modules/MMM-Test/bussavganger.py']);
this.updateDom()
}, 5000)
},
getDom: function() {
var element = document.createElement("div")
element.className = "myContent"
element.innerHTML = "Hello, everybody!"
return element
}
})
Currently i am just trying to run the module to see if the .json file is created. it is not.
If i run the python script separately the file is created, so i know the .py file is not the problem.

You tried calling the python script via your module's js file, but instead you should use to call the python script via the node_helper.js file.
So use the socketNotification function of the MagicMirror to call the noder_helper and in return the node_helper then calls your python script, you can do something with it and at the end send back a socketNotification to your module's js file, e. g. the result of your python program or the exit code, etc.
In your start: function() you could call the node_helper via this command, so that your python program is being started by the module helper later directly after booting up your module (doing that from now on every interval):
var self = this;
setInterval(function() {
self.sendSocketNotification('DO_PYTHON', <transmit data for your node_helper here>);
self.updateDom();
}, 5000);
Create a node_helper.js file in your module folder with the following:
var NodeHelper = require("node_helper");
const spawn = require("child_process").spawn;
module.exports = NodeHelper.create({
init() {
},
start() {
},
stop() {
},
// If notification of the main.js file is received, the node_helper will do this here:
socketNotificationReceived(notification, payload) {
if (notification === "DO_PYTHON") {
// this.config = payload;
this.yourOwnMethod();
} else {
// ...
}
},
yourOwnMethod() {
var self = this;
var process = spawn("python3", ["/absolute/path/to/modules/MMM-Test/bussavganger.py"]);
// do something else here
this.sendSocketNotification("PYTHON_DONE", <e. g. exit state as your payload>)
},
You can read in your json file with fs in the node_helper.js as well and parse it there and send it back via the sendSocketNotification.
Be sure to have included the two beginning lines in the node_helper.js and (!) important use always absolute paths.

Related

Show a variable from one JS file to another JS file

I have a node.js controller that create a csv file and it is stored in a specific folder, there is also a value from a var called endpoint.
The thing is that in the end of the lines of the controller i have this start function to start another process in another javascript file called bot.js
const scrapeWebsite = require('../bot')
const start = new scrapeWebsite();
var endpoint ="192.186.1.1"
try {
start.init();
} catch (e) {
res.send(JSON.stringify(e, null, 5))
}
In the bot.js file i want to access the value of the endpoint variable of my controller.js to use it in the bot function process and i dont know how to do this.
like declare a new var in the bot.js with the same value as the endpoint in the controller.js
class scrapeWebsite {
init() {
this.startScrape()
}
async startScrape() {...}
Export that function from bot.js
module.exports.function_name = function_name
And when you want to run it in node.js controller, import it using
const {function_name} = require("../bot")
And when you want to execute that function, call that function
function_name(<with, arguments, from, bot.js, file>)

How to use multiple js files with Duktape?

I'm using Duktape in embedded MCU. For test case i have:
main.js file:
(function(){
test();
})();
test.js file:
(function test(){
print("func");
})
Both compiled as global default code and main.js is executed with duk_call(ctx, 0);
The problem is it throws error when calling a test() function.
I've also tried using just
function test() {
print("test");
}
in test.js code, but it does not work either.
What I understand is that both files have separate execution context. That is why function is inaccessible.
But what is the right way to split code into multiple files for Duktape?
P.S. I am aiming to avoid using global context, because in documentation it is said that accessing variables is slow this way, that's why main.js looks that way.
P.P.S. I'm sure that test() function is unreachable, but I don't know how to write js code so that everything works.
P.P.P.S print() is a C function that outputs to serial port of esp32 and it works. even main.js works without a test() function call.
Basically, what you want is file import functionality. You can implement that in two ways:
Provide a function in your backend and export that to JS, to allow loading a file dynamically at runtime.
Implement module handling like in Node.js (which essentially also boils down to an import function).
The second idea is what is used most and implements a well defined approach to include other files in your JS application. Duktape comes with an extra file that implements the require command, just like in Node.js. You only have to provide your own functions for resolving a module and to load it from disk (as duktape has not file I/O support).
I implemented this approach in the MGA tool in MySQL Workbench. The duktape file for implementing node module handling is here. The function to resolve modules (which includes handling of nested node_modules folders etc.) is implemented in the ScriptingContext class. The relevant part of it is this:
/**
* Part of the module loading machinery. JS interfacing is done by the duk_module_node code.
* But we have to do the file work here. On the stack we get the value passed to `require()` as a "module ID" and
* the ID of the calling script (which is empty for the main script).
*/
duk_ret_t ScriptingContext::resolveModule(duk_context *ctx) {
// stack: [ requested_id parent_id ]
std::string requestedID = duk_get_string(ctx, 0);
std::string callingID = duk_get_string(ctx, 1);
std::string parentPath = FS::isDir(callingID) ? callingID : Path::dirname(callingID);
// Module resolution strategy in Node.js style: https://nodejs.org/api/modules.html#modules_all_together
auto modules = getInternalModules();
if (modules.find(requestedID) != modules.end()) {
duk_push_string(ctx, requestedID.c_str());
return 1;
}
ScriptingContext *context = ScriptingContext::fromDuktapeContext(ctx);
std::string resolvedID;
std::string cwd = Process::cwd();
try {
if (Path::isAbsolute(requestedID) || Utilities::hasPrefix(requestedID, ".")) {
std::string temp;
if (Path::isAbsolute(requestedID)) {
temp = Path::relative(cwd, requestedID);
} else
temp = Path::join({ parentPath, requestedID });
resolvedID = resolveFile(temp);
if (resolvedID.empty())
resolvedID = resolveFolder(context, temp);
}
} catch (std::runtime_error &e) {
// Triggered for parse errors in package.json.
context->throwScriptingError(ScriptingError::Syntax, e.what());
return 0;
}
// No files found so far. Check node modules.
if (resolvedID.empty()) {
for (auto &folder : moduleFolders(parentPath)) {
std::string path = Path::join({ folder, requestedID });
std::string temp = resolveFile(path);
if (!temp.empty()) {
resolvedID = temp;
break;
}
temp = resolveFolder(context, path);
if (!temp.empty()) {
resolvedID = temp;
break;
}
}
}
if (resolvedID.empty()) {
context->throwScriptingError(ScriptingError::Error, Utilities::format("Cannot resolve module %s", requestedID.c_str()));
return 0;
}
duk_push_string(ctx, resolvedID.c_str());
return 1; // Use result on stack.
}

Gulp: call an async function which provides its own callback from within a transform function

I want to create a function for use in a pipe() call in Gulp that'll enable conversion of xlsx files to json.
I had this working with NPM package 'excel-as-json' for gulp 3, however Gulp 4 has forced me to actually understand what it is doing ;-)
Six hours in, and my incapacity of getting this to work due to a lack of js/async/streaming knowledge is punching my curiosity.
Code is as follows:
paths = {excel_sourcefiles: "./sourcefiles/*.xls*", excel_targetdir_local_csvjson: "./targetfiles_local/*.*"}
var replaceExt = require('replace-ext');
var PluginError = require('plugin-error')
var gulpFunction = require('gulp-function').gulpFunction // default ES6 export
var through = require("through2")
var convertExcel = require('excel-as-json').processFile;
var changed = require('gulp-changed');
var assign = Object.assign || require('object.assign');
var notify = require('gulp-notify');
var gulpIgnore = require('gulp-ignore');
var rename = require('gulp-rename');
gulp.task('excel-to-jsoncsv', function() {
return gulp.src(paths.excel_sourcefiles)
// .pipe(debug())
.pipe(gulpIgnore.exclude("*\~*")) // Ignore temporary files by Excel while xlsx is open
.pipe(gulpIgnore.exclude("*\$*")) // Ignore temporary files by Excel while xlsx is open
.pipe(plumber({errorHandler: notify.onError('gulp-excel-to-jsoncsv error: <%= error.message %>')}))
.pipe(changed(paths.excel_targetdir_local_glob, { extension: '.csv' }))
.pipe(GulpConvertExcelToJson()) // This is where the magic should happen
.pipe(rename({ extname: '.csv' })) // Saving as .csv for SharePoint (does not allow .json files to be saved)
.pipe(gulp.dest(paths.excel_targetdir_local))
});
function GulpConvertExcelToJson() {
return through.obj(function(chunk, enc, callback) {
var self = this
if (chunk.isNull() || chunk.isDirectory()) {
callback() // Do not process directories
// return
}
if (chunk.isStream()) {
callback() // Do not process streams
// return
}
if (chunk.isBuffer()) {
convertExcel(chunk.path, null, null, // Converts file found at `chunk.path` and returns (err, `data`) its callback.
function(err, data) {
if (err) {
callback(new PluginError("Excel-as-json", err))
}
chunk.contents = new Buffer(JSON.stringify(data))
self.push(chunk)
callback()
// return
})
} else {
callback()
}
})
}
I (now) know there are other excel > json gulp modules that could allow me to solve this problem without writing my own module, yet I'd like to understand what I should do different here.
The error returned is 'Did you forget to signal Async completion?', which I tried to not do. However, probably my attempt to fix the error 'this.push is not a function' with a var self = this is not what I was supposed to do.
Looking at examples like gulp-zip introduced me to unfamiliar notations, making me unable to autodidact my way out of this.
In summary:
how would I go about calling an async function during a through.obj function call, where the chunk's contents is updated with the (not under my control) async function that only provides me with a callback (err, data)?
This problem is coming deep from the excel library. The end event should be finish.
Unsuccessful Try
I did the following, to install the modified excel.js:
rm -rf node_modules/excel
git clone https://github.com/bdbosman/excel.js node_modules/excel
cd node_modules/excel && npm i && cd ../..
This is not enough since the excel-as-json uses an old version of excel.
Either you have to modify the excel-as-json module, to use excel#1.0.0 (after the Pull Request is merged) or manually edit the file in node_modules. I will describe the second way here.
Temporary Solution
Edit the excel file after installing the modules.
I used:
sed -i "s/'end'/'finish'/g" node_modules/excel/excelParser.js
And then I ran:
$ gulp excel-to-jsoncsv
[19:48:21] Using gulpfile ~/so-question-gulp-async-function-call-xlsx/gulpfile.js
[19:48:21] Starting 'excel-to-jsoncsv'...
[19:48:21] Finished 'excel-to-jsoncsv' after 126 ms
And it apparently worked.

How can I run two files in javascript with node?

I am new to javascript and Node.js and having problems testing some code I wrote recently. I am trying to test code written in a file called "compareCrowe.js" with "testCrowe.js" using Node.js.
Here are the contents of testCrowe.js:
var compareCrowe = required['./compareCrowe'];
console.log('begin test');
var connection = {Type:1, Label:"label", linkTo:null};
var table1 = {name:"table1", body:"description1", out:[connection]};
var table2 = {name:"table2", body:"description2", out:null};
connection.linkTo = table2;
var crowe = [table1, table2];
var result = compareCrowe.compareCrowesFoot(crowe, crowe);
console.log(result.feedback);
where the function "compareCrowesFoot" is defined in compareCrowe.js. From the console on an Ubuntu virtual machine I ran:
node compareCrowe.js testCrowe.js
however, nothing was printed. There were no errors or warnings or explanation of any kind. It didn't even print the "begin test" line I placed at the top of testCrowe.js. If I run the command:
node testCrowe.js
it complains that compareCrowesFoot is undefined. How can I test the contents of compareCrowe.js?
Welcome to the party of JS.
I'm not sure where you're learning from, but a few of the resources that have helped me and many others are superherojs.com, nodeschool.io, the MDN developer docs, Node.js API docs, and Youtube (seriously).
The basic idea of Node.js is that it operates with modules (chunks of reusable code), which is what NPM is made up of. These can then be included in other modules and used anywhere else in your application.
So for a basic example, say you had compareCrowe.js, to make it includable/reusable in another file, you could write something like:
module.exports = function() {
var compareCrowesFoot = function(crowe1, crowe2) { /* compare crows feet and return something here */ }
return { compareCrowesFoot: compareCrowesFoot };
// return an object with a property of whatever you want to access it as , and the value as your function name
// e.g. - you could return { compare: compareCrowesFoot };
}
Then in testCrowe.js you could require compareCrowe like this:
var compareCrowe = require("./compareCrowe");
/* your code here... */
var result = compareCrowe.compareCrowesFoot(crowe1, crowe2);
// if your returned object was { compare: compareCrowesFoot };
// this would be compareCrowe.compare(crowe1, crowe1);
And to run your tests, you could then run node testCrowe.js from the command line.
In your case it seems like you've got your syntax a little messed up. It should be more like:
var compareCrowe = require('./compareCrowe.js');
That would make any methods you've exported in compareCrowe.js, such as your compareCrowe.compareCrowesFoot function, available to testCrowe.js.
Then, in your terminal, you would run the following:
node testCrowe.js
And that should do the trick provided you don't have any further errors in your code.

Nodejs execute exported function from string

I want to know if it's possible to run a function that is named a string in nodejs. All this code is running on the server side with no browser appearance at all.
Assuming I export a file test.js with the following code
module.exports.test = function(x)
{
console.log(x*5);
}
Can I do this somehow?
main.js
imp = require('test.js');
toExecute = "test";
// somehow call imp.test using toExecute`
Sure:
imp[toExecute](5);
Logs 25.

Categories

Resources