Help on modifying WebUI for personal setup

Hi all,

I just started using Volumio, and I love the system. I am running it on a RPi, and I am using a Control4 amplifier. We are using Control4 for our home automation, but the music playing capabilities left a little to be desired, hence the switch to Volumio. I managed to figure out how to control the Control4 amplifier using UDP commands, and currently am controlling it from a Android app that I wrote. However, I would like to add it into the “home screen” of the Volumio controller so that I can control everything from one place and not have to switch between Volumio and the amplifier control app.

Note: I don’t want to go into too much detail on how to control the Control4 system because they are extremely closed source, and I think there may have been a “no reverse engineering” clause in the purchase contract.

I have had several programming classes in C++, and I taught myself Python, but web programming is new to me. Poking around in the file system, I managed to change the color of the volume knob by modifying the indextpl.html file (using ssh and PuTTy’s FTP program). I copied all of the /var/www directory onto my local machine so that I can ply with it and by copying it back (at least I had the foresight to make a backup :unamused: ), I know I can make changes to the webUI (changing the volume knob color, switching the volume up and volume down commands, etc.).

What I would like to know is what “framework” the webUI uses and if there is a tutorial or help forum that I can run through to get a better idea of the structure of the webUI. Basically what I want to do is modify the webUI to include a couple of checkboxes on the “now playing” screen to select which zones it is playing in, and modify the volume commands so that the output of the RPi is always at 100%, and the volume changes the amplifier volume. These are done by sending UDP commands to the amplifier (local IP address).

Hi J,

While I am by no mean expert on web development and even “an amateur” status can be an overstatement, i will try to give you few tips to start up your exploration of Volumio, until someone smarter chime in…

WebUI of Volumio is written in HTML, CSS and Javascript, with PHP as a server scripting tool. Combination of this give you functionality and look and feel of the web interface of the Volumio. So:

  1. HTML is used to design web elements of the player
  2. Javascript will provide “guts” for the application where all the smarts are
  3. CSS will define look and color for the interface (some of the elements will be declared in the HTML itself, as you already discovered for the Volume knob!)
  4. PHP is scripting language managing the engine and all of the above…

To pinpoint exact way to modify volumio ui to suit your needs, have a look in the JS subfolder of the www, and start exploring script-playback.js to start with… This will lead back to the HTML code with classes…Good Luck!

On the same note, why not just use the Volumio volume to adjust output (if your DAC supports hardware “mixer” even better)? That way your Control4 amp is a poweramp, and all controls are in your hand with Volumio…Check the “Playback” section of the Volumio menu…

Hi ducca683,

That helps alot. The pieces that I was missing were what CSS did and whether I should be looking to modify the php or the js. I’ll take a look around the files and see how much trouble I can get into :wink: .

In answer to your question about the volume, I have to set the zones that play each time on the amplifier because it is an multi-zone amp (each zone is a room in the house), and we are always changing which rooms we play music in. Also, right now the “coarse” volume control is being set on the amplifier (set it and leave it), and then Volumio controls the “fine” control (“live feedback”). This creates some problems when you’ve set the amplifier up high and are playing the music low because it’s not the best system in the world and there is a little bit of background hiss. To solve that you turn the amplifier down, but then you are limited on your maximum volume :neutral_face: . Hence, you always have to switch back and forth between volumio and the amplifier control. I figured it would be best to just leave the output of the RPi at 100% (or close to it) and then adjust the amplifier volume. That way you have the highest signal to noise ratio along the line-level portion.

Well, After spending most of the day coding (or attempting to), and burning myself out on several Pandora stations, I think I’m almost there.

I’ve managed to add a new item to the menu that pulls up my own page (the amplifier control page). That page has several checkboxes (one for each zone), but here I’ve hit a snag. I’m trying use the jquery/ajax to call/execute a python script (I have it in the /var/www directory), but I think the web server is set up wrong. If I execute it using “python” from ssh, it executes and all is well. However, if I point my web browser to “IP address/”, it downloads the python file. After reading around, I think the issue is how the web server handles .py scripts (“serves” them to the client instead of running them).

Does anybody know what web server Volumio uses, and/or how to configure it to run the python script instead of “serving” it? I think that if I get that configured right, then the $.ajax(“”) call that I call when the checkbox is clicked should cause the python script to run.

Once I get that figured out (running the python script when I click the checkbox), I think I can figure out how to pass it the arguments (which checkbox) and I should be home free!!! :sunglasses:

Sound like you have progressed well… Unfortunately, my snake charming skills are nonexistent. All I can do now is to wish you best of luck and hope that some smart board members will join. If that fails, maybe some reading will help ( … e-examples)…At least you might get an idea.

Keep us posted.

Hi and a happy new year!
I’d love to hear about your progress on modifications - I’ve a similar to-do for my setup at home and would like to integrat it to the existing web UI. Could you post some information about the steps and files you changed please?
Thanks and kind regards

Hi All, it’s been crazy and school just started back up, but I’ve finally had some time to work on it again.

Hfecht: See the end of this post for what I’ve done so far.

I’ve changed control schemes slightly. Now, I am using one RPi running a python script to control the amplifier and maintain a “running tally” of who has what room so that it knows what zones to modify when a particular player calls for a change. So, That part I’ve got down pretty well. The player RPi’s (All running volumio) will send UDP commands to the control RPi with information like change volume, select these zones to this player, mute, etc. I know how to send UDP commands from PHP, so I’m set there, and I know how to run AJAX using JQuery (built in which is nice :sunglasses: ), but I’m having a little bit of trouble navigating Volumio’s massive maze of includes and files.

Here is my question: does anybody know how Volumio handles the volume from the “now playing” page? I.e., when I click the volume up or down or mute buttons, what happens; or, when I move the volume knob (either by click/drag, or by mousing over it and scrolling), what happens? They aren’t using a “standard” (standard to me, I’m really new at this) JS method (i.e., the onClick() functions) that I can tell, so I’m having trouble finding what happens.

I’ve think I’ve traced the volume knob’s actions to the sendCmd() function in the player_lib.js file, (about line 50, see below) but from there I lose it.

function sendCmd(inputcmd) { $.ajax({ type: 'GET', url: 'command/?cmd=' + inputcmd, async: true, cache: false, success: function(data){ GUI.halt = 1; // console.log('GUI.halt (sendCmd)= ', GUI.halt); }, }); }

The URL there is what’s losing me. I can’t tell where that leads, and it’s freaking me out :unamused:. I also remember finding one time what happens when the volume up/down/mute buttons were clicked, but I can’t find it now.

Basically, what I want to do is “hijack” each of those AJAX calls to redirect to my own PHP page(s). These will then contain my code that sends the UDP strings to my control RPi and viola, I am done :smiley:. So, it would be nice if anybody knows what data those AJAX calls send along or receive (i.e., the current volume level), but, if you point me to the calls and files, I should be able to figure that out.

I also want the RPi’s volume to always be at 100% to improve the signal to noise ratio going into the amps, so If anybody knows how to do that, that would be helpful too.

Hfecht, Here is kind of a list of what I’ve done so far, please feel free to ask me for explanation about anything, but my knowledge will probably be pretty limited.

  • Replaced the “Main” item in the menu dropdown to point to my own “Amplifier” page that allows me to select which zones I want to be playing. That is in the “_header.php” file, somewhere near the bottom of it. I wrote the Amplifier page mostly from scratch (I actually copied the “index.php” file and replaced all the content so that I could still have the header and footer and the color schemes) and it just has a bunch of radio buttons that send UDP commands to the control RPi when clicked.

  • Changed the color of the top bar in all the pages (really part of the header from _header.php) to match the color of the RPi case for that specific player (that is how I distinguish the players, eg, I use the blue player, mom uses the red player, etc.) That is done by changing the “background” variable in the “panels.css” file (line 163)

That’s basically it, but once I know how to, I will grab the volume controls just like I mentioned above.

I’ve done a little bit more work, and I think I’ve also found out how the Volume knob works. I looked at the source for the jquery volume knob, and I think I understand a little bit more about how it works. I haven’t gone back into Volumio’s files and looked at the knob in there, but I’m pretty sure I can figure it out. Still, if anybody has some insight, I would greatly appreciate it, I’m basically feeling my way around the dark and hoping I don’t bark my shin on the coffee tables (although I do have a save if I do :laughing: ).

Right now I’ve managed to set up the amplifier controls and get them working with an auxiliary input (control volume, select rooms etc.) using files to store which zones are selected because I still haven’t figured out how to share variables between php pages that exit when finished. It’s a little clunky, but it seems to work okay, so I just need to port it over to work with the music players as well.

If anybody knows how to set the analog output volume of the RPi through Volumio (in its code that is), I would greatly appreciate some pointers, because if I can’t, I will probably end up with two volume sliders, one for the RPi (the same one that is on the now playing page now), and a separate one for the amplifier volume. I would really like to be able to set the RPi output at 100% and then just use the volume knob to control the amplifier though (for sound quality). I’m thinking I might be able to get away with hijacking the jquery calls from the volume knob to send 100% volume each time and then just send the volume command to my amplifier page instead. The problem is, from the way the UI behaves when changing the volume (a little bit of a stutter if I try to scroll too fast), I’m thinking there may be a “feedback” command set that I’m missing somewhere, but I guess it could just be the way the knob works.

Also, I’ve changed my whole scheme slightly (again) where each music player keeps track of which zones it controls separately (through a file system, see above). This does allow multiple players to try to grab the same zone, but the way it’s set up in the house each player will usually stick to its own set of zones, and overlaps are easily figured out by the wetware (read: humans). So far, it seems to be working okay, but it’s been crazy with school lately. I will try to work on it this week, and once I’ve “finished” it to a working point, I will try to post my entire scheme if anybody is interested.

hi Jcoman

function sendCmd(inputcmd) basically sends commands to MPD.
the details are here

if you call sendCmd(‘setvol 75’); it will set the MPD volume to 75%
if your MPD is using ALSA, this will be the analogue output volume of RPI

this is called when the volume up/down buttons are pressed

        // step volume control
        else if ($(this).hasClass('btn-volume')) {
            if (GUI.volume == null ) {
                GUI.volume = $('#volume').val();
            if ($(this).attr('id') == 'volumedn') {
                var vol = parseInt(GUI.volume) - 1;
                GUI.volume = vol;
            } else if ($(this).attr('id') == 'volumeup') {
                var vol = parseInt(GUI.volume) + 1;
                GUI.volume = vol;
            } else if ($(this).attr('id') == 'volumemute') {
                if ($('#volume').val() != 0 ) {
                    GUI.volume = $('#volume').val();
                    var vol = 0;
                } else {
                    var vol = GUI.volume;
            //console.log('volume = ', GUI.volume);
            sendCmd('setvol ' + vol);

this is called when the knob is used

    // volume knob
        change : function (value) {
        release : function (value) {


// set volume with knob
function setvol(val) {
    GUI.volume = val;
    GUI.halt = 1;
    // console.log('GUI.halt (setvol)= ', GUI.halt);
    sendCmd('setvol ' + val);

Hi Jcoman, Apologies if this is not the right forum or adding to an old thread but I was wondering if you could share how you managed to discover those UDP command for the C4 amp as I want to do something similar and am stuck on that. Many Thanks.

Hi Smurph,

In a word: WireShark.

I used a passive network tap (similar to this) and wireshark to sniff the packets going to (and coming from) the C4 amp. I did this while I had the entire C4 system set up the way it should be, i.e., the controller (media center? the main “thing” that does all the C4 stuff), amp, remotes and all the other devices were set up, I was just sniffing what was sent to the amp. Also, I was using a fairly old system, so I don’t know if the new ones are at all similar.

As far as I could tell, and my skills were very rudimentary at that point, the controller sends UDP packets to the amp for things like mute/un-mute channels, change volume or inputs, etc, and the amp sends a UDP packet back as acknowledgement. The Ack packet sent back was sent to the address of the device that sent the packet to begin with, so when I started sending the packets from my computer, the amp sent the Ack packets back to me instead of the controller.

The packages themselves are fairly straight-forward, the only odd part I can remember is there was a string of 4 (i think) digits at the front of the packet that had no apparent rhyme or reason other than they just increment with each command (regardless of the device it went to). I believe that is just sort of a packet “ID number” because it was included in the Ack packet sent back. If I just used the same number (i.e., 0001) on all the packets, the amp would only respond to the first one, then ignore the rest. So, I simply had a counter that counted up each time it sent a command and put that number in the packet (note, it has to be 4 (i think) digits long, so I had to pad with leading zeros. Actually, all the numbers used had to be a set length long, that’s why the “format” function is used in the python code below, it will pad with leading zeros when neccesary).

IIRC, there was a lot of other traffic as well, but because I used the home LAN/WIFI network, I suspect not all of it was directed at the amp. However, I would expect there to be a host of other data going to the amp for things like set-up, general health, etc. However, I would guess that would be standard TCP packets instead of the UDP ones because they would want the reliability there, not speed.

If you were starting from scratch, and wanted to just use the amp without the rest of the C4 system, it might be better to use a dedicated network (i.e., only the devices you’re interested in) with the router set up to mirror packets to your analyzer (or something like that, I really have very little knowledge in this area :mrgreen: ).

Since then, I have left the C4 behind in favor of something a little bit more user friendly: these amplifiers or others in the same series. They are really cheap, and I can take care of the volume control on my sending end, so they work really well for me. And, if you’re really handy, you might be able to re-purpose the C4 amp’s case and power supply and connectors, and… :wink: .

Anyway, I hope this helps, and good luck!

Ps, here is the python code I had to generate the UDP commands, my copy of the command list has been lost to the ravages of time and computer upgrades :smiley: . It should give you an idea of what to look for on your particular set-up.

This is a snippet from a python program, so the different “defs” are different functions. Also, there are two “channels” for each set of speakers (right and left), so technically the amp was a 16-channel amp (8 stereo channels).

import socket
import binascii
import time

count = 0
UDP_IP = ""
UDP_PORT = 8750

def sendChannelSelect(output, input):
	global count                 #'global' here makes sure it uses the same variable for all the functions, instead of one for each
	cmdString = '0s' + format(count, '04x') + ' c4.amp.out '\
				+ getChannelFromNumber(output, 0) + ' '\
				+ getChannelFromNumber(input, 0) + '\r\n'
	sock.sendto(bytes(cmdString,'utf-8'), (UDP_IP, UDP_PORT))
	count = count + 1
	cmdString = '0s' + format(count, '04x') + ' c4.amp.out '\
				+ getChannelFromNumber(output, 1) + ' '\
				+ getChannelFromNumber(input, 1) + '\r\n'
	sock.sendto(bytes(cmdString,'utf-8'), (UDP_IP, UDP_PORT))
	count = count + 1
def sendChannelVol(output, volume):
	global count
	cmdString = '0s' + format(count, '04x') + ' c4.amp.chvol '\
				+ getChannelFromNumber(output, 0) + ' '\
				+ format(int(volume)+155, '02x') + '\r\n'
	sock.sendto(bytes(cmdString,'utf-8'), (UDP_IP, UDP_PORT))
	count = count + 1
	cmdString = '0s' + format(count, '04x') + ' c4.amp.chvol '\
				+ getChannelFromNumber(output, 1) + ' '\
				+ format(int(volume)+155, '02x') + '\r\n'
	sock.sendto(bytes(cmdString,'utf-8'), (UDP_IP, UDP_PORT))
	count = count + 1

def sendOutputMute(output, mute):
	global count
	cmdString = '0s' + format(count, '04x') + ' c4.amp.mute '\
				+ getChannelFromNumber(output, 0) + ' '\
				+ format(mute, '02x') + '\r\n'
	sock.sendto(bytes(cmdString,'utf-8'), (UDP_IP, UDP_PORT))
	count = count + 1
	cmdString = '0s' + format(count, '04x') + ' c4.amp.mute '\
				+ getChannelFromNumber(output, 1) + ' '\
				+ format(mute, '02x') + '\r\n'
	sock.sendto(bytes(cmdString,'utf-8'), (UDP_IP, UDP_PORT))
	count = count + 1
def getChannelFromNumber(number, channel):
	if number>0:
		return format(((number-1)*2 + channel + 1), '02x')
		return '00'

Hi Jcoman

Sorry for the late reply but looks like I’m not getting notifications. This is really useful stuff and a great help. I was originally using Wireshark but only later realised that my switch was not routing the packets everywhere. So now I can capture the traffic I can see the commands. I too find the c4 stuff just too limiting so was investigating other options. This is just one piece of the Jigsaw which may go no where but having the Amp under my control which is already wired around the house is a big step forward.

Thanks again for this information