Tux Droid
Cette page regroupe ce que j'ai pu trouver utile ou développer moi même sur le Tux Droid de kysoh.
Pour ce qui suit, vous devez connaitre un minimum linux et la programmation.
Quant à moi, je débute en python, soyez indulgents.
Ma configuration:
Ubuntu Karmic Koala
Tuxbox: 3.1.2
tuxhttpserver en usermode (port 54321)
Configuration
Interférences WIFI
Les premiers temps j'ai eu de très nombreux problèmes de coexistence entre tux et mon réseau wifi (Bande
passante wifi divisée par dix, déconnexions fréquentes). J'aurais aimé que cette page soit mieux signalée:
http://127.0.0.1:54321/menu/resources?resource_name=rf
Donner l'ordre d'éviter mon canal wifi (le 13):http://127.0.0.1:54321/0/rf/avoid?channel=13
L'information ne semble pas survivre à un reboot. À chaque fois je relance la commande
en même temps que tuxhttpserver pour plus de sureté. Je n'ai plus de problème wifi depuis.
Plantages du serveur web
tuxhttpserver a tendance à planter assez régulièrement. Certains messages sur les forums laissent penser que le serveur web
réagit mal en présence d'ubuntu+alsa+pulseaudio, ce qui est mon cas.
En attendant un correctif, dans le fichier /etc/crontab pour relancer le serveur en cas de plantage:* * * * * <USER> /bin/bash -c "if (( $(ps aux | grep tuxhttpserver | grep -v grep | wc -l) == 0 )); then tuxhttpserver& fi"
Respect de la vie privée
En affichant le source de la page d'administration de tux http://127.0.0.1:54321/ on voit que
kysoh utilise google analytics pour monitorer mes action.
Pour "corriger" le problème, kysoh a très aimablement indiqué que dans
/usr/share/tuxdroid/tuxhttpserver/data/web_interface/user_01/xsl/index.xsl, il faut retirer le code de la ligne 160 Ã 171 <script language="javascript">
<![CDATA[
try
{
var pageTracker = _gat._getTracker("UA-10113316-1");
pageTracker._setDomainName("none");
pageTracker._udn="none";
pageTracker._trackPageview();
}
catch(err) {}
]]>
</script>
Utilisation au jour le jour
tux en ligne de commande
Le script say est utilisé par les scripts qui suivent. Il permet, grace à ttm, de faire parler tux même
si l'interface d'administration est installée sur une autre machine et sans ouvrir cet accès à l'administration.
/usr/bin/say#!/opt/bin/bash
TEXT="$@";
if $(wget -q "http://tux.serviceinformatique06.com/ttm.php?tts=${TEXT// /+}" -O /dev/null); then echo "Success"; else echo "Failed"; fi
/usr/bin/tux#!/bin/bash
function send {
wget -q "http://127.0.0.1:54321/0/$1" -O /dev/null
}
if [ ! $1 ]; then
echo " Usage:
tux say tux say
tux eyes open
tux eyes close
tux flippers open
tux flippers close
tux mouth open
tux mouth close
tux leds on [1.0]
tux leds off
tux sound 17 [100.0]
"
else
if [ $1 = 'say' ]; then
shift
say $@
fi
if [ $1 = 'eyes' ] || [ $1 = 'flippers' ] || [ $1 = 'mouth' ]; then
if [ $2 = 'open' ]; then
send "$1/open?"
else
send "$1/close?"
fi
fi
if [ $1 = 'leds' ] ; then
if [ $2 = 'on' ]; then
if [ $3 ]; then
send "leds/on?leds=LED_BOTH&intensity=$3"
else
send "leds/on?leds=LED_BOTH&intensity=1.0"
fi
else
send "leds/off?leds=LED_BOTH"
fi
fi
if [ $1 = 'sound' ] ; then
if [ $2 ]; then
if [ $3 ]; then
send "sound_flash/play?track=$2&volume=$3"
else
send "sound_flash/play?track=$2&volume=100.0"
fi
fi
fi
fi
Pour faciliter les prochains script bash, on peut faire par exemple: tux eyes close
tux mouth open
tux leds on
...
Nouveaux mails
J'utilise procmail pour trier et filtrer les mails reçus sur mon serveur.
Dans ~/.procmailrc, après le filtre antispam et avant le déplacement du message::0bw
RESULT=|say Vous avez un nouveau message dans votre boite aux lettres
:0fhw
|formail -a "X-SentToTux: $RESULT"
Téléchargement terminé
J'utilise le client emule mldonkey. Pour me prévenir de la fin d'un téléchargement, je fais pointer la variable file_completed_cmd vers ce script:#!/bin/bash
say Téléchargement terminé: $FILENAME
VLC media player
Avec LIRC, je peux controler VLC directement depuis la télécommande de tux.
~/.lircrc.vlc
Exemple de configuration pour VLC
Le bouton standby permet alternativement de lancer VLC ou de l'éteindre
Les boutons Ch+, CH-, volume, playpause et stop sont reconnusbegin
prog = vlc
button = playpause
config = key-play-pause
repeat=32
end
begin
prog = vlc
button = stop
config = key-stop
end
begin
prog = vlc
button = power
config = key-quit
repeat=1
end
begin
prog = vlc
button = chan-up
config = key-next
end
begin
prog = vlc
button = chan-down
config = key-prev
end
begin
prog = vlc
button = back
config = key-fullscreen
end
begin
prog = vlc
button = rewind
config = key-jump-short
end
begin
prog = vlc
button = forward
config = key-jump+short
end
begin
prog = vlc
button = vol-down
config = key-vol-down
repeat=0
end
begin
prog = vlc
button = vol-up
config = key-vol-up
repeat=0
end
begin
prog = vlc
button = mute
config = key-vol-mute
end
begin
prog = vlc
button = 1
config = key-play-bookmark1
end
begin
prog = vlc
button = 2
config = key-play-bookmark2
end
begin
prog = vlc
button = 3
config = key-play-bookmark3
end
begin
prog = vlc
button = 4
config = key-set-bookmark1
end
begin
prog = vlc
button = 5
config = key-set-bookmark2
end
begin
prog = vlc
button = 6
config = key-set-bookmark3
end
Les applications sont nombreuses
Radio internet
vlc -I foo --control lirc --lirc-file ~/.lircrc.vlc radios.m3u
Réveil matin
vlc_reveil:
#!/bin/bash
killall irexec
irexec ~/.lircrc.reveil&
while [ 1 ]
do
vlc -I foo --control lirc --lirc-file ~/.lircrc.vlc radios.m3u
sleep 600
done
~/.lircrc.reveil:
begin
prog = irexec
button = ok
config = killall vlc_reveil; killall vlc
end
begin
prog = irexec
button = left
config = killall vlc
end
begin
prog = irexec
button = right
config = killall vlc
end
Il suffit d'utiliser
/etc/crontab pour exécuter
vlc_reveil à l'heure voulue.
Les touches gauche ou droite arretent le réveil pendant 10 minutes, la touche OK arrete définitivement le réveil.
Vidéo, télévision sur un autre PC
On peut controler VLC sur un pc distant en propageant les signaux lirc avec le paramètre -l pour l'emetteur et -c pour le récepteur.
vlc_lirc sur le récepteur (le media player):
#!/bin/bash
if (( $(ps aux | grep -v grep | grep -c lircd) == 0 ))
then
gksudo -- lircd -c <IP_TUX> -a
fi
irexec &
vlc --control lirc --file-caching 1000 --fullscreen playlist.m3u
Développements
Tux donne des informations très utiles ici:
http://127.0.0.1:54321/debug/
Resources Tuxware
Liste des resources:
http://127.0.0.1:54321/menu/resources?resource_name=index
Par exemple:
Bouger les ailes:
http://127.0.0.1:54321/0/flippers/up?
Parler:
http://127.0.0.1:54321/0/tts/speak?text=Je%20parle&locutor=Julie&pitch=100
Script: /usr/bin/tts
#!/bin/bash
PORT=54321;
TEXT="$@";
wget -q "http://127.0.0.1:$PORT/0/tts/speak?text=${TEXT// /%20}&locutor=Julie&pitch=100" -O /dev/null
Utilisation en ligne de commande: tts "C'est l'histoire d'un fou qui repeint son plafond..."
Voir aussi ce script pour les machines distantes: say
LIRC
Le but: utiliser la télécommande de tux pour controler ce que l'on veut sur le pc.
Comme tux ne gère pas (encore) lirc, l'idée est de faire un programme
qui réagit à la télécommande et simule un code lirc.
Installer lirc: (pendant la configuration, choisir "ATI/NVidia X10 RF (userspace)")
Pour pouvoir utiliser la fonction SIMULATE, il faut lancer lirc avec le paramètre --allow-simulate.
On supprime donc l'exécution automatique du service.sudo apt-get install lirc
sudo /etc/init.d/lirc stop
sudo chmod a-x /etc/init.d/lirc
Script: ~/tux_lirc_proxy.py, fait la liaison entre tux et lirc#!/usr/bin/python
# -*- coding: UTF8 -*-
#
# tux_lirc_proxy.py
#
# auteur: tux@serviceinformatique06.com
# version: 0.4
# Licence: GPL
#
# simule l'envoi de signaux provenant d'une
# telecommande ATI REMOTE WONDER II
#
# note: lircd a la configuration "ATI/NVidia X10 RF (userspace)"
# et doit avoir "--allow-simulate" en parametre
#
# mettre en parametre les fichiers lircrc a gerer
# la touche ESC de la telecommande permet de changer de fichier
#
from tuxisalive.api.TuxAPI import *
global tux
import os, sys, random, threading
codes = dict(
K_0 = [ "0000000000000200 0", "0" ],
K_1 = [ "0000000000000201 0", "1" ],
K_2 = [ "0000000000000202 0", "2" ],
K_3 = [ "0000000000000203 0", "3" ],
K_4 = [ "0000000000000204 0", "4" ],
K_5 = [ "0000000000000205 0", "5" ],
K_6 = [ "0000000000000206 0", "6" ],
K_7 = [ "0000000000000207 0", "7" ],
K_8 = [ "0000000000000208 0", "8" ],
K_9 = [ "0000000000000209 0", "9" ],
K_ALT = [ "00000000000002f9 0", "alt" ],
K_BACKSPACE = [ "0000000000000296 0", "back" ],
K_BLUE = [ "000000000000027a 0", "bleu" ],
K_CHANNELMINUS =[ "0000000000000221 0", "chan-down" ],
K_CHANNELPLUS = [ "0000000000000220 0", "chan-up" ],
K_DOWN = [ "0000000000000259 0", "down" ],
K_ESCAPE = [ "0000000000000282 0", "esc" ],
K_FASTFORWARD = [ "0000000000000228 0", "forward" ],
K_FASTREWIND = [ "0000000000000229 0", "rewind" ],
K_GREEN = [ "0000000000000279 0", "vert" ],
K_HANGUP = [ "0000000000000239 0", "hangup" ],
K_LEFT = [ "000000000000025a 0", "left" ],
K_MENU = [ "0000000000000254 0", "menu" ],
K_MOUSE = [ "0000000000000238 0", "mouse" ],
K_MUTE = [ "000000000000020d 0", "mute" ],
K_NEXT = [ "00000000000002be 0", "next" ],
K_NO = [ "00000000000002d5 0", "no" ],
K_OK = [ "000000000000025c 0", "ok" ],
K_PLAYPAUSE = [ "00000000000002d0 0", "playpause" ],
K_PREVIOUS = [ "00000000000002a9 0", "prev" ],
K_RECEIVECALL = [ "00000000000002aa 0", "receivecall" ],
K_RECORDING = [ "0000000000000237 0", "rec" ],
K_RED = [ "0000000000000278 0", "rouge" ],
K_RELEASED = [ "00000000000002aa 0", "released" ],
K_RIGHT = [ "000000000000025b 0", "right" ],
K_SHARP = [ "00000000000022ff 0", "sharp" ],
K_STANDBY = [ "00000000000021ff 0", "standby" ],
K_STAR = [ "00000000000012ff 0", "star" ],
K_STARTVOIP = [ "00000000000011ff 0", "startvoip" ],
K_STOP = [ "0000000000000231 0", "stop" ],
K_UP = [ "0000000000000258 0", "up" ],
K_VOLUMEMINUS = [ "0000000000000211 0", "vol-down" ],
K_VOLUMEPLUS = [ "0000000000000210 0", "vol-up" ],
K_YELLOW = [ "000000000000027b 0", "jaune" ],
K_YES = [ "00000000000010ff 0", "yes" ]
)
argv = os.sys.argv
argc = len(argv)
if argc < 2:
print "usage: tux_lirc_proxy.py lircrc_file [lircrc_file ...]\n"
print "Codes lirc utilisés:"
for k in codes.keys():
print " " + codes[k][1]
sys.exit(0)
class say(threading.Thread):
def __init__(self,tts,locutor='Julie',pitch=100):
threading.Thread.__init__(self)
self.tts=tts
self.locutor=locutor
self.pitch=pitch
self.start()
def run(self):
if tux.access.waitAcquire(10.0, ACCESS_PRIORITY_NORMAL):
tux.mouth.open()
tux.tts.setEncoding( "utf-8" )
tux.tts.speak(self.tts,self.locutor,self.pitch)
tux.mouth.close()
tux.access.release()
tux = TuxAPI('127.0.0.1', 54321)
tux.server.autoConnect(CLIENT_LEVEL_RESTRICTED, 'Tux proxy lirc ' , 'NONE')
tux.server.waitConnected(10.0)
tux.dongle.waitConnected(10.0)
tux.radio.waitConnected(10.0)
try:
def generic_keypressed(*args):
print 'keypressed: ' + args[0] + ' / ' + codes[args[0]][1]
os.system('irsend SIMULATE "' + codes[args[0]][0] + ' ' + codes[args[0]][1] + ' RemoteWonder2"')
lircrc_current = -1
lircrc = dict()
def switch_lircrc(*args):
global lircrc, lircrc_current
lircrc_current = (lircrc_current + 1) % len(lircrc)
print 'swithing to ' + lircrc[lircrc_current][0] + ' - ' + lircrc[lircrc_current][1]
say(lircrc[lircrc_current][1])
#os.system('for PID in $(ps -C irexec -o pid=); do kill -9 $PID; done; irexec "'+lircrc[lircrc_current][0]+'" &');
os.system('killall irexec; irexec "'+lircrc[lircrc_current][0]+'" &');
tux.button.remote.registerEventOnPressed(switch_lircrc, K_ESCAPE)
for k in codes.keys():
tux.button.remote.registerEventOnPressed(generic_keypressed, k)
for i in range(1,argc):
try:
l=len(lircrc)
lircrc[l]=[argv[i],"Pas de nom"]
for line in open(argv[i]):
if "TUXNAME:" in line:
lircrc[l]=[argv[i],line[line.index("TUXNAME:")+8:-1]]
print "Found lircrc file: " + lircrc[l][1] + " - " + lircrc[l][0]
except IOError:
del lircrc[l]
print('File Not Found: %s' % argv[i])
say("Proxy L I R C OK")
switch_lircrc()
# forever loop (use ctrl C to exit)
while((not tux.button.remote.waitPressed(1000.0, K_STANDBY)) or True):
pass
except:
pass
tux.server.disconnect()
tux.destroy()
sys.exit(0)
Le script prends en paramètre un ou plusieurs fichiers lircrcPar exemple:
~/.lircrc.tux
Pour faire danser tux
## TUXNAME: Robot Tuxe
begin
prog = irexec
button = 1
config = wget -q "http://127.0.0.1:54321/0/eyes/close?" -O /dev/null
end
begin
prog = irexec
button = 2
config = wget -q "http://127.0.0.1:54321/0/eyes/open?" -O /dev/null
end
begin
prog = irexec
button = 4
config = wget -q "http://127.0.0.1:54321/0/mouth/open?" -O /dev/null
end
begin
prog = irexec
button = 5
config = wget -q "http://127.0.0.1:54321/0/mouth/close?" -O /dev/null
end
begin
prog = irexec
button = 7
config = wget -q "http://127.0.0.1:54321/0/flippers/down?" -O /dev/null
end
begin
prog = irexec
button = 8
config = wget -q "http://127.0.0.1:54321/0/flippers/up?" -O /dev/null
end
~/.lircrc.touches
Pour tester les touches de la télécommande
## TUXNAME: Touches de telecommande
begin
prog = irexec
button = 0
config = say zero
end
begin
prog = irexec
button = 1
config = say un
end
begin
prog = irexec
button = 2
config = say deux
end
begin
prog = irexec
button = 3
config = say trois
end
begin
prog = irexec
button = 4
config = say quatre
end
begin
prog = irexec
button = 5
config = say cinq
end
begin
prog = irexec
button = 6
config = say six
end
begin
prog = irexec
button = 7
config = say sept
end
begin
prog = irexec
button = 8
config = say huit
end
begin
prog = irexec
button = 9
config = say neuf
end
Une ligne commençant par "## TUXNAME:" sera reconnue et le texte sera prononcé par le script à la sélection du profil
Tester le tout:sudo lircd --allow-simulate&
tuxhttpserver --restart
~/tux_lirc_proxy.py ~/.lircrc.vlc ~/.lircrc.tux ~/.lircrc.touches
La touche ESC de la télécommande permet de passer d'un profil lirc à l'autre
Talk To Me
Faites parler le Tux Droid qui se trouve dans ma chambre (Ã gauche en entrant)
Script: ~/ttm_server.py
Pas besoin d'installer un serveur web, le programme écoute sur un port donné, affiche un formulaire et fait parler tux.#!/usr/bin/python
# -*- coding: UTF8 -*-
#
# ttm_server.py - Serveur Talk To Me
#
# auteur: tux@serviceinformatique06.com
# version: 0.1
# Licence: GPL
#
# Serveur web minimaliste (une seule page)
# Affiche un formulaire et envoie le texte vers le tts de tux
#
import os, sys, re, urlparse, urllib, base64, threading
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from tuxisalive.api.TuxAPI import *
argv = os.sys.argv
argc = len(argv)
FROM_IP = ['192.168.1.1','192.168.1.2']
if argc < 2:
print "usage: ttm_server.py PORT"
sys.exit(0)
PORT = int(argv[1])
class say(threading.Thread):
def __init__(self,tts,locutor='Julie',pitch=100):
threading.Thread.__init__(self)
self.tts=tts
self.locutor=locutor
self.pitch=pitch
self.start()
def run(self):
if tux.access.waitAcquire(10.0, ACCESS_PRIORITY_NORMAL):
tux.mouth.open()
tux.tts.setEncoding( "utf-8" )
tux.tts.speak(self.tts,self.locutor,self.pitch)
tux.mouth.close()
tux.access.release()
class customHTTPServer(BaseHTTPRequestHandler):
def get_html(self,text=""):
global tux, FROM_IP
ok = False
IP = self.client_address[0]
for item in FROM_IP:
if item == IP:
ok = True
if not ok:
print 'unauthorized :' + IP
self.send_response(404)
return
params = dict({'locutor':'Bruno','pitch':100,'tts':'','histo':'','headers':'yes'})
for duo in urlparse.urlparse(self.path).query.split('&'):
v=duo.split('=')
if len(v) > 1:
params[v[0]]=urllib.unquote_plus(v[1])
params[v[0]]=params[v[0]].replace( "'", ''')
locutors = dict({'Bruno':'','Julie':''})
if params['locutor']=='Julie':
locutors['Julie']='selected'
else:
locutors['Bruno']='selected'
histo = ''
try:
histo = base64.decodestring(params['histo'])
except : pass
if params['tts']!='':
say('Message web: ' + params['tts'],params['locutor'],params['pitch'])
histo = re.sub(r'<[^>]*?>', '', histo + params['locutor'] + ': '+params['tts'] + "\n")
ifheaders = ''
if params['headers'] != 'yes':
ifheaders = '<input type=hidden id=headers name=headers value=no />'
html = """
<div id=ttm_form>
<form method=get">
<input type="text" id="tts" name="tts" value='"""+params['tts']+"""' />
<select id="locutor" name="locutor">
<option """+ locutors['Julie'] + """ value=Julie>Voix: Julie</option>
<option """+ locutors['Bruno'] + """ value=Bruno>Voix: Bruno</option>
</select>""" + ifheaders + """
<input type=hidden id=histo name=histo value='""" + base64.encodestring(histo) + """' />
<input type="submit" id=submit name=submit value="Parler !" />
</form></div>
<div id=ttm_histo><pre>""" + histo + '</pre></div>'
if params['headers'] == 'yes':
html = """<html><head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Talk To Me</title>
</head>
<body>""" + html + '</body></html>'
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(html)
def do_GET(self):
self.get_html()
def do_POST(self):
self.get_html()
def main():
global tux
tux = TuxAPI('127.0.0.1', 54321)
tux.server.autoConnect(CLIENT_LEVEL_RESTRICTED, 'Serveur Talk To Me' , 'NONE')
tux.server.waitConnected(10.0)
tux.dongle.waitConnected(10.0)
tux.radio.waitConnected(10.0)
try:
server = HTTPServer(('',PORT),customHTTPServer)
print 'server started at port ' + str(PORT)
server.serve_forever()
except KeyboardInterrupt:
server.socket.close()
tux.server.disconnect()
tux.destroy()
if __name__=='__main__':
main()
Script: ~/ttm.php
Si vous avez un serveur web sur une autre machine, vous pouvez encapsuler la page pour la formater comme vous voulez.<?
@file_put_contents ( 'ttm.log', mktime()."\t".$_SERVER['REMOTE_ADDR']."\t".$_GET['locutor'].': '.$_GET['tts']."\n", FILE_APPEND);
?><html><head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Talk To Me</title>
<style>
a { color:#000000; text-decoration:underline; }
a:hover { color:#9999ff; text-decoration:underline; }
#tts { position: absolute; top: 165px; left: 60px; width: 300px; }
#locutor { position: absolute; top: 190px; left: 100px; width: 100px; }
#submit { position: absolute; top: 190px; left: 200px; width: 100px; }
#ttm_histo { position: absolute; top: 390px; left: 20px; width: 100px; z-index: -1; }
#bulle { position: absolute; top: 80px; left: 0px; }
#url { position: absolute; top: 740px; left: 310px; z-index: 1; }
#msg { position: absolute; top: 0px; left: 20px; border: 1px solid #D0DAE2; background: #ECF0F3; margin-top: 0.2em; padding: 0.5em; }}
</style>
<body>
<div id=url><pre><a href=http://tux.serviceinformatique06.com>tux.serviceinformatique06.com</a></pre></div>
<div id=msg><pre>Faites parler le <a target=_blank href="http://www.zebulon.fr/dossiers/93-tux-droid-test-pingouin-intelligent.html">Tux Droid</a> qui se trouve dans ma chambre (Ã gauche en entrant)</pre></div>
<img id=bulle src=bulle.png>
<?
$content = @file_get_contents('http://VOTRETUX:PORT/?headers=no&'.$_SERVER['QUERY_STRING']);
echo ( $content ? $content : '<div align=center id=tts>Le pingouin dort...<br> À plus tard :)</div>');
?></body>
</html>
Le formulaire utilise des paramètres GET. Cela permet d'utiliser la fonction vocale de tux en automatique depuis
une autre machine du réseau local ou d'internet sans avoir accès à la totalité des pages d'administration de tux.
Dernières modifications: 16/11/2009