Jarvis is a conky / screenlets alternative.
The idea is to have something easier to expand and easier
to style.
Jarvis is a daemon serving a JSON API to one or more clients called Views
The purposes of this project are:
The idea is to have a listening application without GUI (aka Demon) that just returns information in a json
format about the system (aka json restful API).
Then some Views will be able to get those informations and draw them on screen.
The JSON protocol eases the creation of HTML/QML view and/or the integration with existing clients.
Once started, the deamon runs at localhost on 8080.
There is currently no support for multiple users (aka: dynamic port) or authentication... there will be
soon though, and it will also run on https.
The daemon will reply to any valid json messages sent on its websocket.
You can try and talk to the demon opening a browser and going to the console page
(download the entire directory locally on your hd: it will not work online)
You can also talk to it the terminal:
$ npm install wscat $ wscat -c ws://localhost:8080 connected (press CTRL+C to quit) > Yeah! < {"error":true,"error_id":"Invalid request"} > {"module":"os","action":"ping"} < {"module":"os","action":"ping","pong":true} > {"module":"os","action":"getactions"} < {"module":"os","action":"getactions","result":"ok","actions":["systeminfo","cpuinfo","ping","setperiod","getactions","listen","unlisten"]} >
You will need wscat (and not just netcat) because this nodejs implementation uses the websocket protocol and only that. There will be soon the plain socket support.
The following screenshot is taken using a Qt/webkit view and the Harmattan-like demo View. This means: what you see in the screenshot is done in HTML and Javascript.
Features and information has been grouped in modules.
There are few implemented modules in the nodejs daemon:
Other are planned:
Hopefully many more...
Q: Why another daemon just to proxy some already available information?
A: Because those information are not really available to all kind of clients (HTML)
and .
Q: Why not dbus?
A: Because json is just an API not an implementation. It would be really complex to
write a
client in html talking with a dbus. And I do not actually like dbus: I like its concept and what it
wuold do:
not how is used/done/thought.
Once connected to the daemon everything is sent and received in a JSON format.
If the daemon receives something strange and not in json, it reply with an error in json format:
$ wscat -c ws:localhost:8080 connected (press CTRL+C to quit) > asd < {"error":true,"error_id":"Invalid request"} >All the message are in this form:
{ "module": "core", "action": "ping", "messageid": "283672019", }
module: tells the name of module to call (core, process, forecast...)
action: is the actual name of the function to call in the module
Those two properties are alwais needed.
There is also an optional property:
messageid: the value will be sent back from the daemon in the answer. It is useful to couple a
message from the daemon to a request from the client.
In an async world it could always happen to receive and unexpected message while waiting to a real reply.
Currently the best way to play and understand the API is to download and go with a browser to the console page. You will see something like the image shown on the right.
If you decide to extend the node harvis module, you have to inherit the Module class.
Ex: to create the SpotifyModule you will create a new file called
jarvis_module_spotify.js. The file must be placed near the other: currently there is a better
module-plugin-sistem.
var Module = require(__dirname+'/module'); var JarvisMessage = require(__dirname+'/jarvismessage'); var util = require('util'); var EventEmitter = require('events').EventEmitter; function ModuleSpotify() { Module.call(this, "spotify"); } util.inherits(ModuleSpotify, Module); ModuleSpotify.prototype.Init = function (cb) { /* this is an optional init function. it will be called just once at the first module usage */ cb(null, this); } ModuleSpotify.prototype.onYeah = function (msg) { console.log("Yeah!"); } ModuleSpotify.prototype.onSayhello = function (message) { if ( !message.content.hasOwnProperty("please") ) { message.replyError("Invalid request: missing politeness").send(); return; } else { data = { {"greeting": "hello"} message.reply( data ).send(); } }; ModuleOs.prototype.timerFunction = function () { o = { "action" : "tictac" }; m = new JarvisMessage(this, o ); m.send(); console.log("Tictac!"); }; module.exports = ModuleSpotify;
How it works step by step:
function ModuleSpotify() { Module.call(this, "spotify"); }
This is important: that "spotify" string must match with the last part of the filename.
As soon a client sends a message with the module property set to the string "spotify", the node daemon
try to load a file called jarvis_module_spotify.js and, if present, try to run its Init()
function.
On success, the function must call cb(null, this); or cb("Big error"); in case
something went wrong (ex: see the forecast module).
ModuleSpotify.prototype.onYeah = function (msg) { console.log("Yeah!"); }
this function will be called when a client send a message with "yeah" as action:
{ "module": "spotify", "action": "yeah", "play" : "queen", "album" : "innuendo" }
Generally any OnWhatever function will be called when the action is equal to its name without
the "on" part and lowercase. Note: OnYeah() is different that Onyeah() or OnYeaH().
The msg parameter will contain the entire client message in its content property: in
this case msg.content.album will be "innuendo".
... message.replyError("Invalid request: missing politeness").send(); ... message.reply( data ).send(); ... };
This is how to reply to a message: just call the reply() function. The message wil be delivered to the caller client. The function takes an optional object that will be jsonized and attached to the message.
ModuleSpotify.prototype.timerFunction = function () { o = { "action" : "tictac" }; m = new JarvisMessage(this, o ); m.send(); console.log("Tictac!"); };
This function is optional: it will be called any x milliseconds by the daemon.
It is not active by default and the client must activate or change it with a message like this:
{ "module": "spotify", "action": "setperiod", "period": 1000 }
In this case the function will also send a broadcast message. Any message sent from a module (not sent with the reply() function) will be delivered to all the listening listening. A listening client is a client who sent a message like this:
{ "module": "spotify", "action": "listen" }
All the module will also have some common functions (actions) as:
Finally, if a message is sent to a not-existing action (function OnXxx not implemented) that particular message will be delivered to:
ModuleSpotify.prototype.receivedMessage = function (message) { console.log("received message"); };
For any suggestion or question write to mario.signorino@gnufish.net.