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 reconnus
begin 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 lircrc
Par 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( "'", '&#39;') 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