Roon API and Eventghost

So here is my grand plan;

I have a PC that runs the automation program Eventghost. Eventghost can receive IR signals from a remote, and then do any number of cool things that you tell it to do when a specific IR is received. Eventghost, via plugins, can do URLs, Telnet, SSH, and more.

I would like to write up some basics using the Roon API to Play/Pause, Skip, and Volume, and then call those API functions via an IR remote:

IR Command --> Windows PC --> Eventghost --> Roon API/Extension --> Zone.

I realize this is a very open ended question, and there are probably many questions inside, but I guess what I am looking for is if someone has some basic thoughts about how I would accomplish this. When a plugin is running via Node.js, can it receive commands via HTTP? Telnet. I have a lot of learning to do! Meanwhile, any pointers, tips, etc would be appreciated.

BTW, I do have the example Extension from GitHub running.


By using the Express.js module for Node.js you can create a webserver with only a few lines of code:

Thanks, just what I was looking for. Time to roll up my sleeves…

Maybe you could use my example below as starting point.

http get request:

cmd (mandatory): play, pause, next, previous
zone (optional): name of zone (if zone is omitted, the default zone is used)

var express = require("express");
var app = express();
var RoonApi       = require("node-roon-api");
var RoonApiStatus = require("node-roon-api-status");
var RoonApiTransport = require("node-roon-api-transport");

var zones = [];
var defaultZone="Wohnzimmer";
var transport;

var roon = new RoonApi({
    extension_id:        'com.restserver.test',
    display_name:        "Roon REST Server",
    display_version:     "0.0.1",
    publisher:           'Walter Wego',
    email:               'tbd',
    website:             'tbd',
    core_paired: function(core) {
        transport =;
        // get available zones
        transport.subscribe_zones(function(cmd, data) {
            if (cmd == "Subscribed") {
                console.log("Subscribed:", data);
                zones = data.zones;
            } else if (cmd == "Changed") {
                if ("zones_added" in data) {
                    console.log("zones_added:", data.zones_added);
                    for (var item in data.zones_added) {
                        if (!getZoneByName(data.zones_added[item].display_name)) {              
            } else {
                console.log("unhandled transport cmd",cmd,":",data);

    core_unpaired: function(core) {
        console.log("unpaired core", core.display_name);

var svc_status = new RoonApiStatus(roon);

    provided_services: [ svc_status ],
    required_services: [ RoonApiTransport ],

svc_status.set_status("Running", false);


// helper function to get zone by their name
function getZoneByName(name) {
    for (item in zones) {
        if (name == zones[item].display_name) {  
            return zones[item];
    return null;

// setup http get interface
// example:
app.get("/api/commands", function(req, res) {
    if ((zones.length < 1) || !transport) {
        console.log("initialisation error");
        res.status(404).send("initialisation error");
    if (req.query.cmd) {
    	console.log("handling command:", req.query.cmd);
    } else {
	   console.log("cmd parameter is missing");

function handleCommand(req) {
    // get zone
    var zoneName; 
    var zone;
    if ( {
        zoneName =; 
    } else {
        console.log("zone parameter is missing, using default zone", defaultZone);
        zoneName = defaultZone;
    zone = getZoneByName(zoneName); 
    if (!zone) {
   	    console.log("failed to get zone",zoneName);
    // handle command
    switch(req.query.cmd) {
        case "play":
            console.log("play in zone", zoneName);
            transport.control(zone, "play");
        case "pause":
            console.log("pause in zone", zoneName);
            transport.control(zone, "pause");
        case "next":
            console.log("next track in zone", zoneName);
            transport.control(zone, "next");
        case "previous":
            console.log("previous track in zone", zoneName);
            transport.control(zone, "previous");
            console.log("ERROR: command [" + req.query.cmd + "] not supported");

app.listen(3000, function () {
  console.log("Roon REST Server listening on port 3000!");

Awesome, this will probably save me hours of tinkering. I really appreciate it!

Code for play and pause on specific zone works perfectly. Thanks for sharing. Is there an option to select a playlist as well as transfer audio to a different zone?