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.

2 Likes

By using the Express.js module for Node.js you can create a webserver with only a few lines of code:
http://expressjs.com/en/starter/hello-world.html

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:
http://ip_address:3000/api/commands?cmd=play&zone=Schlafzimmer

Parameters:
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 = core.services.RoonApiTransport;
        // 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)) {              
                            zones.push(data.zones_added[item]);
                        }
                    } 
                }
            } 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);

roon.init_services({
    provided_services: [ svc_status ],
    required_services: [ RoonApiTransport ],
});

svc_status.set_status("Running", false);

roon.start_discovery();

// 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: http://192.168.1.2:3000/api/commands?cmd=play&zone=Schlafzimmer
app.get("/api/commands", function(req, res) {
    if ((zones.length < 1) || !transport) {
        console.log("initialisation error");
        res.status(404).send("initialisation error");
        res.end();
        return;
    }
    res.end();
    if (req.query.cmd) {
    	console.log("handling command:", req.query.cmd);
        handleCommand(req);
    } else {
	   console.log("cmd parameter is missing");
    }
});

function handleCommand(req) {
    // get zone
    var zoneName; 
    var zone;
    if (req.query.zone) {
        zoneName = req.query.zone; 
    } 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);
        return;
    }
    
    // handle command
    switch(req.query.cmd) {
        case "play":
            console.log("play in zone", zoneName);
            transport.control(zone, "play");
            break;
            
        case "pause":
            console.log("pause in zone", zoneName);
            transport.control(zone, "pause");
            break;
                
        case "next":
            console.log("next track in zone", zoneName);
            transport.control(zone, "next");
            break;
                
        case "previous":
            console.log("previous track in zone", zoneName);
            transport.control(zone, "previous");
            break;
                
        default:
            console.log("ERROR: command [" + req.query.cmd + "] not supported");
            break;
    }
}

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

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?

Thanks,