Forked Daapd Conrol from frontpage

In this subforum you can show projects you have made, or you are busy with. Please create your own topic.

Moderator: leecollings

Post Reply
peerkersezuuker
Posts: 70
Joined: Monday 14 December 2015 22:16
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Mierlo
Contact:

Forked Daapd Conrol from frontpage

Post by peerkersezuuker »

Hello All,
After a while playing with Domoticz for a while, i am verry pleased with it.
So i wanted to control my forked-daapd server (https://github.com/ejurgensen/forked-daapd) from the frontapge with Domoticz.
I am using Forked-daapd with a airport express to stream music / radio statios to my receiver.
It is managable with e.g. teh app remote on my iPhone, but it would be fun to port a little control to the frontpage with Domoticz.
After a lot of searching and try-ing i got it working, here is a little layout for the steps to be taken, and the scripts and plugins i used.
Any comment and recommendations are welcome.

Used:
Raspberri Pi with Raspbian.
Domoticz (of course)
Apache 2 https://www.raspberrypi.org/documentati ... /apache.md
python support for Raspbian and Apache ( in a terminal window :

Code: Select all

sudo apt-get install python
and

Code: Select all

sudo apt-get install libapache2-mod-python
)
Tools:
Winscp (great for transfering files from and to linux servers, uses ssh deamon)
Putty (for terminal acces to linux, uses ssh deamon)
Netbeans (IDE for editing e.g. python and java scripts)

copy your www folder (mine was in home/pi/domoticz.www) to /var/www (i put mine in /var/www/domoticz)
Configure the apache config (mine is /etc/apache2/sites-available/domoticz.conf)

Code: Select all

<VirtualHost 192.168.0.30:80>
	DocumentRoot /var/www/domoticz
	ServerName domoticz.sonnehoek.local
	<Directory "/var/www/domoticz">
		allow from all
		Options None
		Require all granted
	</Directory>
	<Directory "/var/www/domoticz/scripts">
        	Options ExecCGI
        	SetHandler cgi-script
    	</Directory>
</VirtualHost>
My python scripts are in /var/www/domoticx/scripts, and to use them you have to tell apache that they are CGI enabeled, see the second directory declaration (<Directory "/var/www/domoticz/scripts">) in the config.
Save the file and create a link to this file in /etc/apache2/sites-enabled,

Code: Select all

ln -s /etc/apache2/sites-available/domoticz.conf /etc/apache2/sites-enabled/domoticz.conf
.
Restart apache, and connect with a browser to the website.

Install python MPD module.
Terminal :

Code: Select all

pip install python-mpd2
if pip ain't working :

Code: Select all

sudo apt-get install python-pip
peerkersezuuker
Posts: 70
Joined: Monday 14 December 2015 22:16
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Mierlo
Contact:

Re: Forked Daapd Conrol from frontpage

Post by peerkersezuuker »

No2:

create the following scripts in the scrips folder assigned in the apache config, and make them executable.

PlayMPDSong.py

Code: Select all

#!/usr/bin/python
import mpd
import cgi
fs = cgi.FieldStorage()
songid = fs.getvalue("input")


## Connect to mpd client ###
client = mpd.MPDClient()
client.connect("localhost", 6600)
error = 0

print songid
try:
    client.clear()
    client.addid(songid)
except Exception:
    error = 1

if error == 0:
    print "Content-type:text/html\r\n\r\n"
    print "<html>"
    print "<head>"
    print "<title>Status PlayMPDSong</title>"
    print "</head>"
    print "<body>"
    print "<h2>Status = OK</h2>"
    print "</body>"
    print "</html>"
else:
    print "Content-type:text/html\r\n\r\n"
    print "<html>"
    print "<head>"
    print "<title>Status PlayMPDSong</title>"
    print "</head>"
    print "<body>"
    print "<h2>Status = Fout</h2>"
    print "</body>"
    print "</html>"
ReadMPDdata.py

Code: Select all

#!/usr/bin/python
import mpd

## Connect to mpd client ###
client = mpd.MPDClient()
client.connect("localhost", 6600)
error = 0
try:
    curstatus = client.status()
except Exception:
    error = 1
try:
    currentsong = client.currentsong()
except Exception:
    error = 1
    
if error == 0:
    try:
        output = currentsong['title'] +',' + curstatus['volume']
    except Exception:
        error = 1
    if error == 0:
        print "Content-Type: text/html\n"
        print output
    else:
        print "Content-Type: text/html\n"
        print "Fout bij uitlezen MPD data"
    
client.close()
client.disconnect()
SetVolumeMPD.py

Code: Select all

#!/usr/bin/python
import mpd
import cgi
fs = cgi.FieldStorage()
volume = fs.getvalue("input")


## Connect to mpd client ###
client = mpd.MPDClient()
client.connect("localhost", 6600)
error = 0


volume = int(volume)

try:
    client.setvol(volume)
except Exception:
    error = 1

if error == 0:
    print "Content-type:text/html\r\n\r\n"
    print "<html>"
    print "<head>"
    print "<title>Status SetVolumeMPD</title>"
    print "</head>"
    print "<body>"
    print "<h2>Status = OK</h2>"
    print "</body>"
    print "</html>"
else:
    print "Content-type:text/html\r\n\r\n"
    print "<html>"
    print "<head>"
    print "<title>Status SetVolumeMPD</title>"
    print "</head>"
    print "<body>"
    print "<h2>Status = Fout</h2>"
    print "</body>"
    print "</html>"

These are the three scripts i am using at the moment, maybe not the most beautiful scripting, but they work ;-)
For more commands usable with MPD see http://pythonhosted.org/python-mpd2/top ... mands.html.
peerkersezuuker
Posts: 70
Joined: Monday 14 December 2015 22:16
Target OS: Raspberry Pi / ODroid
Domoticz version:
Location: Mierlo
Contact:

Re: Forked Daapd Conrol from frontpage

Post by peerkersezuuker »

No3:

In my frontpage html i placed a selection box within a cel with the following code:

Code: Select all

<div id="frame_cell3">
	<div id="cell3a" class="cell3a">
					<form
						id="Radio Stations"
  						name="Radio Stations"
 						onSubmit="return false;"
					>
					<div class="selector">
					<select name="selectName3" onchange="onChange(this, this.form)">
					<optgroup>
						<option id="00">Geen Station</option>
						<option id="01" fileid="http:/Radio 1">Radio 1</option>
    					<option id="02" fileid="http:/Radio 2">Radio 2</option>
    					<option id="03" fileid="http:/3FM Serious Radio">Radio 3</option>
    					<option id="04" fileid="http:/Arrow Classic Rock">Arrow Classic Rock</option>
    					<option id="05" fileid="http:/VERONICA_ROCKAAC_SC">Baars Classic Rock</option>
    					<option id="06" fileid="http:/VERONICAAAC_SC">Radio Veronica</option>
    					<option id="07" fileid="http:/Veronica Rock Radio">Veronica Rock Radio</option>
    					<option id="08" fileid="http:/veronica-80's-hits">Veronica 80s hits</option>
    					<option id="09" fileid="http:/veronica-90's-hits">Veronica 90s hits</option>
    					<option id="10" fileid="http:/SKYRADIOAAC_SC">Sky Radio</option>
    					<option id="11" fileid="http:/Omroep Brabant radio">Omroep Brabant</option>
    					<option id="12" fileid="http:/RADIO538'">Radio 538</option>
    					<option id="13" fileid="http:/Top 2000">Top 2000</option>
    					<script language="JavaScript">
							song = getcurrentsong();
							console.log(song);
							document.getElementById(song).defaultSelected = true;
							var n = document.getElementById(song).innerText;
    						function function1() {
        						alert('Default selected Option label:\n'+'"'+n+'"');
        						document.getElementById("Radio Stations").reset(); 
    						} 
						</script>
    				</optgroup>
					</select>
					</div>
					<br><br>
					</form>
</div>
	<div id="cell3b" class="cell3a">--</div><div id="desc_cell3b" class="descgroup3">cell3</div>
</div>
I split my cell3 in two cells (3a and 3b) but this is not neccesarry, i found it much more attractive.
So the selection box is in cell3a. The little script you see is to determine wich song (radio station) is playing at page load, so the selection box states the correct name.
id= is used to calculate the current radio station.
fileid= is used in the javascript to deliver the correct stream to forked-daapd.

In my javascript for the frontpage (frontpage.js) i included the following functions :

Code: Select all

function getcurrentsong(){
    currentsong = ReadMPDdata();
    arrcurrentsong = currentsong.split(",");
    console.log(currentsong);
    currsong = arrcurrentsong[0];
    switch(currsong) {
        case 'Radio 1':
            currsong = '01';
            console.log(currsong);
            return currsong;
            console.log(currsong);
            break;
        case 'Radio 2':
            currsong = '02';
            return currsong;
            console.log(currsong);
            break;
        case '3FM Serious Radio':
            currsong = '03';
            return currsong;
            console.log(currsong);
            break;
        case 'Arrow Classic Rock':
            currsong = '04';
            return currsong;
            console.log(currsong);
            break;
        case 'VERONICA_ROCKAAC_SC':
            currsong = '05';
            return currsong;
            console.log(currsong);
            break;
        case 'VERONICAAAC_SC':
            currsong = '06';
            return currsong;
            console.log(currsong);
            break;
        case 'Veronica Rock Radio':
            currsong = '07';
            return currsong;
            console.log(currsong);
            break;
        case 'Veronica 80\'s Hits':
            currsong = '08';
            return currsong;
            console.log(currsong);
            break; 
        case 'Veronica 90\'s Hits':
            currsong = '09';
            return currsong;
            console.log(currsong);
            break;
        case 'SKYRADIOAAC_SC':
            currsong = '10';
            return currsong;
            console.log(currsong);
            break; 
        case 'Omroep Brabant radio':
            currsong = '11';
            return currsong;
            console.log(currsong);
            break;
        case 'RADIO538':
            currsong = '12';
            return currsong;
            console.log(currsong);
            break; 
        case 'Top 2000':
            currsong = '13';
            return currsong;
            console.log(currsong);
            break;
         default:
            currsong = '00';
            return currsong;
    }
}
This function is also usefull for json call's :

Code: Select all

function httpGetAsync(theUrl, callback, theType)
{
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function() { 
        if (xmlHttp.readyState === 4 && xmlHttp.status === 200)
           if(callback) callback(xmlHttp.responseText);
            console.log(callback);
    };
    xmlHttp.open(theType, theUrl, true); // true for asynchronous 
    xmlHttp.send(null);
}   

Code: Select all

function onChange(select, form) {
    var radio01 = select.options[select.selectedIndex].getAttribute("fileid");
    var callback = "";
    url = "/scripts/PlayMPDSong.py?input=" + radio01;
    console.log(url);
    type = "POST";
    httpGetAsync(url, callback, type);
}

Code: Select all

function ReadMPDdata()
{
    var mpddata = "-";
    $.ajax({
        type: "POST",
        async: false,
        url: "scripts/ReadMPDdata.py",
        //dataType: "text",
        success: function(response){
            mpddata = response;
            return mpddata;
        }
    });
    return mpddata;
}
This function set's a virtual light switch on/of so the display on the frontpage shows the volume when on, and a off switch when off.
The pause is not working, still work on progress.

Code: Select all

function RadioToggle(idx, switchcmd)
{
    callback = "";
    if (switchcmd === 'On'){
        pause = 0;
        type = "POST";
        url = "/scripts/SetPauseMPD.py?input=" + pause;
        console.log(url);
        httpGetAsync(url, callback, type);
        
    }
    else {
        pause = 1;
        type = "POST";
        url = "/scripts/SetPauseMPD.py?input=" + pause;
        console.log(url);
        httpGetAsync(url, callback, type);
        
    }
        $.ajax({
        url: $.domoticzurl + "/json.htm?type=command&param=switchlight&idx=" + idx + "&switchcmd=" + switchcmd,
        async: false,
        dataType: 'json',
        success: function () {
            console.log('SUCCES');
        },
        error: function () {
            console.log('ERROR');
        }
        }); 
    RefreshData();
}
Function for Volume up/down

Code: Select all

function isInteger(possibleInteger) {
        return Object.prototype.toString.call(possibleInteger) !== "[object Array]" && /^[\d]+$/.test(possibleInteger);
}

// Volume Forked-daapd
function Volume(OpenDicht, level, idx) {
    var callback = "";
    console.log('level', level);
    console.log('OpenDicht', OpenDicht);
    if (isInteger(level)){ 
        if (OpenDicht === "plus"){
            var d = level + 10;
            if (d > 100){
                d = 100;
            }
            type = "POST";
            url = "/scripts/SetVolumeMPD.py?input=" + d;
            console.log(url);
            httpGetAsync(url, callback, type);
        } 
        else{
            //var d = 0;
            var d = level - 10;
            //console.log("in min",d,level);
            if (d < 0){
             d = 0;
            }
            type = "POST";
            url = "/scripts/SetVolumeMPD.py?input=" + d;
            console.log(url);
            httpGetAsync(url, callback, type);
        }
    //sleep(3000);        
        RefreshData();
    }
}
And last the code in frontpage.js to manipulate data in cell 3a and cell 3b

Code: Select all

if (item.idx === idx_radio) {
                                        data = ReadMPDdata();
                                        arrdata = data.split(",");
                                        // vplusmin = arrdata[1];
                                        console.log(vplusmin);
                                        vdimmercurrent = parseInt(arrdata[1]);
                                     if(vplusmin > 0) {
                                        if (vdata === txt_off) {
                                            var hlp =  "<span onclick=RadioToggle("+item.idx+",\'On\'); style=" +alarmcss+ ">"+ vdata +"</span>";
                                            var plus = "<img src=/icons/up_off.png align=right onclick=Volume('plus',txt_off," + item.idx + ","+ vdimmercurrent+")>";
                                            var min = "<img src=/icons/down_off.png align=left onclick=Volume('min',txt_off," + item.idx + ","+ vdimmercurrent+")>";
                                        }
                                        else {
                                            vdata = parseInt(arrdata[1]);
                                            var hlp = "<span onclick=RadioToggle("+item.idx+",\'Off\'); style=" +alarmcss+ ">" + vdata + "</span>";
                                            var plus = "<img src=/icons/up.png align=right onclick=Volume('plus'," + vdata + "," + item.idx + ","+ vdimmercurrent+")>";
                                            var min = "<img src=/icons/down.png align=left onclick=Volume('min'," + vdata + "," + item.idx + ","+ vdimmercurrent+")>";
                                        }
                                        vdata = min.concat(hlp,plus);
                                        // console.log(vdata);
                                    }
                                }
I hope it helps people with Forked-daapd control.
Have fun with it, and if you have a question, don's hesitate to ask.
Screenshots will follow.

With regards
Peer van Hoek
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest