Commits

Nekmo committed 045317c

Terminado tutorial de creación de un plugin

Comments (0)

Files changed (8)

_build/doctrees/environment.pickle

Binary file modified.

_build/doctrees/index.doctree

Binary file modified.

_build/doctrees/tutorial.doctree

Binary file modified.

_build/html/_sources/tutorial.txt

         form.events(self.say_text)
         if form.no_data(): return form
         # ... A partir de aquí, se poseen los valores devueltos por el usuario. ...
-        self.cfg.write()
+        self.cfg.write()
+
+Esto generará un formulario con 2 campos: Un campo de texto para introducir el texto, y un cuadro de selección múltiple para elegir los eventos.
+
+5. Comunicación Servidor -> Cliente
+-----------------------------------
+Recapitulemos lo que llevamos hasta el momento: Hemos creado un plugin llamado "saythis", el cual puede activar el usuario a través de la interfaz web, y parametrar un texto que dirá Nabazag al recibir un evento (o varios), lo cual también es parametrable a través de la página web. Ahora, nos queda que al recibir el susodicho evento, se ejecute algo que haga que Nabaztag hable. Dicha función la hemos llamado en el apartado anterior como "say_text". Crearemos un nuevo método en el plugin con dicho nombre::
+    
+    def say_text(self, rabbid, data):
+        """\
+        Hablar a través del Nabaztag facilitado.
+        """
+        pass
+
+El sistema de eventos ejecutará este método entregando 2 argumentos: un primero con un objeto Rabbid, el cual será como el ya conocido *"hconn.rabbid"*. Lo siguiente que nos queda por saber, es cómo haremos las comunicaciones entre nuestro servidor, y el Nabaztag en cuestión. Aquí es donde entran los denominados **Paquetes" y "Bloques**. Para la comunicación Servidor -> Cliente, se usan bloques de instrucciones, y el paquete es un conjunto de uno o más bloques que se enviarán al Nabaztag. Existen 2 tipos diferentes de bloques: aquellos que tendrán consecuencias a largo plazo en el Nabaztag (como el bloque Reboot, que reiniciará a Nabaztag, o los "bloques ambiente", que graban datos en la memoria de Nabaztag), y los "inmediatos" y sin consecuencias a largo plazo. Estos últimos serán los más utilizados, y controlarán cosas como la reproducción de sonidos, mover las orejas o los leds. En realidad, hay un único bloque que hace esto, llamado **MessageBlock**, pero que a su vez tiene en su interior una serie de "sub-bloques" con las instrucciones.
+
+Para usar el bloque MessageBlock y Packet, descomentaremos::
+    
+    from .. blocks import Packet, MessageBlock # Comunicación S->C.
+
+Aunque Nabaztag no permita de por sí la sintetización de voz, MessageBlock tiene un sub-bloque que creará un archivo de audio con una voz sintetizada con el texto que le digamos, y pondrá la orden de reproducir dicho archivo en el MessageBlock. Este *"sub-bloque"*, se creará en el MessageBlock con el método **say**::
+    
+    # Se instancia MessageBlock
+    msg = MessageBlock()
+    msg.say("Texto a decir")
+    # Si quisiéramos que se dijese otro texto justo después:
+    # msg.say("Segundo texto que se dirá a continuación.")
+
+MessageBlock tiene además una solución para reducir esto en una sola línea (para cuando queremos poner sólo una instrucción en el MessageBlock. Aplicado a lo que necesitamos, sería::
+    
+    # Texto a decir obtenido de las configuraciones
+    to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+    # MessageBlock sólo con la instrucción de decir el texto
+    msg = MessageBlock('say', to_say)
+
+Ahora, sólo nos queda añadir el bloque MessageBlock al Packet::
+
+    def say_text(self, rabbid, data):
+        """\
+        Hablar a través del Nabaztag facilitado.
+        """
+        # Texto a decir obtenido de las configuraciones
+        to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+        # Paquete con el conejo al que se enviará (rabbid) y el bloque
+        # MessageBlock que se enviará al conejo con el texto que se dirá
+        Packet(MessageBlock('say', to_say)).send(rabbid, self.main)
+    
+En 2 sencillas líneas, hemos hecho que nuestro Nabaztag hable. ¿Sencillo, no? Es necesario decir, que si queremos que el Packet tenga más de 1 Block, el primer argumento deberá ser una tupla o un listado con los bloques. Los bloques, serán reproducidos a continuación. Hay más bloques y opciones, pero esto es todo lo que necesitamos saber en este tutorial.
+
+6. Borrar datos al deshabilitar el plugin
+-----------------------------------------
+¡Genial! Ya tenemos nuestro plugin, y nuestro Nabaztag ya habla al recibir un evento. Pero no cantemos victoria tan rápido: aún tenemos que pensar en la deshabilitación de nuestro plugin. Aunque el plugin se desactive ahora mismo, Nabaztag seguirá hablando cuando reciba el evento, y no es lo que deseamos. Tenemos que:
+
+# Eliminar los eventos que activan la función
+# Borrar los datos de configuración del usuario.
+
+Para lo primero usaremos un método que nos trae el plugin Events::
+    
+    def disable_rabbid(self, hconn):
+        # El Nabaztag fue desactivado en la lista de plugins.
+        # Eliminar todos los eventos que apuntan a la función para el conejo
+        self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+
+Y después borraremos todos los datos de configuración:
+    
+    del self.cfg['rabbids'][str(hconn.rabbid)]
+    
+El resultado final sería como el siguiente:
+
+    def disable_rabbid(self, hconn):
+        # El Nabaztag fue desactivado en la lista de plugins.
+        # Eliminar todos los eventos que apuntan a la función para el conejo
+        self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+        # Se borran todas las configuraciones para el conejo
+        del self.cfg['rabbids'][str(hconn.rabbid)]
+        # Se escribe en la configuración los cambios en realizados
+        self.cfg.write()
+
+Ya tenemos terminado nuestro plugin, y así quedaría el resultado::
+
+    # -*- coding: utf-8 -*-
+    from threading import Timer # Tareas que se ejecutarán en X tiempo.
+    import gettext # Locales
+    from .. blocks import Packet, MessageBlock # Comunicación S->C.
+    from . import base
+
+    # Diccionario con configuraciones por defecto
+    DEFAULT_CFG = {
+        'rabbids': {}, # configuraciones en cada conejo.
+    }
+    
+    # Descomentar la siguiente línea para las locales
+    _ = gettext.gettext ## Traducir texto mediante _("Texto")
+    
+    class Pluginname(base.Base):
+        def plugin_init(self):
+            # Se ejecutará justo en el momento de importarse el plugin. Úsese
+            # solo para aquellos casos en que este plugin sirva de biblioteca
+            # para otros plugins.
+            pass
+        def post_init(self):
+            # Se ejecutará tras haberse ejecutado todos los demás plugins. Si
+            # su plugin va a usar otro plugin de biblioteca (por ejemplo, el
+            # plugin events), ponga aquí aquellas cosas que le hagan uso. De
+            # lo contrario puede provocarse una excepción, ya que si se pone
+            # en plugin_init, el plugin events podría no haberse importado
+            # todavía.
+            pass
+        def web_user_options(self, hconn):
+            # Página de configuración del plugin
+            # Diccionario con la configuración del conejo
+            rabbid_cfg = self.cfg['rabbids'][str(hconn.rabbid)]
+            # Se crea el objeto formulario. El segundo argumento será un
+            # diccionario sobre el que se guarden los valores devueltos por
+            # el usuario en los campos. El atributo title será el título que
+            # encabece el formulario.
+            form = base.Form(hconn, rabbid_cfg, title='Texto a decir')
+            # ... Se establecen los campos del formulario aquí ...
+            # Campo del formulario to_say. El primer argumento es la clave en el 
+            # diccionario rabbid_cfg. La segunda el texto de ayuda del campo.
+            form.text('to_say', 'Texto a decir')
+            # Campo del formulario para seleccionar los eventos. El primer
+            # argumento Es la función que se ejecutará al recibirse el evento.
+            form.events(self.say_text)
+            if form.no_data(): return form
+            # A partir de aquí, se poseen los valores devueltos por el usuario.
+            self.cfg.write()
+        def say_text(self, rabbid, data):
+            """\
+            Hablar a través del Nabaztag facilitado.
+            """
+            # Texto a decir obtenido de las configuraciones
+            to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+            # Paquete con el conejo al que se enviará (rabbid) y el bloque
+            # MessageBlock que se enviará al conejo con el texto que se dirá
+            Packet(MessageBlock('say', to_say)).send(rabbid, self.main)
+        def enable_rabbid(self, hconn):
+            """ \
+            Crear la configuración inicial para el Nabaztag al activar el
+            plugin.
+            """
+            # Creamos un diccionario como valor a la clave del conejo,
+            # que almacene las configuraciones para el conejo
+            self.cfg[str(hconn.rabbid)] = {}
+            # Texto a decir por defecto en el nombre de clave "to_say".
+            # Será traducido al idioma del usuario con el _(...)
+            self.cfg[str(hconn.rabbid)]['to_say'] = _('Hola Mundo')
+            # Lo siguiente guardará la configuración en el archivo
+            # de configuración, para que no se pierda. Sería algo
+            # así como un commit
+            self.cfg.write()
+        def disable_rabbid(self, hconn):
+            # El Nabaztag fue desactivado en la lista de plugins.
+            # El Nabaztag fue desactivado en la lista de plugins.
+            # Eliminar todos los eventos que apuntan a la función para el conejo
+            self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+            # Se borran todas las configuraciones para el conejo
+            del self.cfg['rabbids'][str(hconn.rabbid)]
+            # Se escribe en la configuración los cambios en realizados
+            self.cfg.write()
+
+7. ¿Qué queda ahora?
+-----------------------------------------
+Esta guía sólo ha servido como un ligero aperitivo a lo que viene de verdad, pero esperamos que haya servido para que te hagas una ligera idea del funcionamiento del sistema de plugins de PyNabaztag. Aún queda mucho que aprender, porque aquí no se acaba la cosa: PyNabaztag te ofrece más campos de formulario para que eches a volar tu imaginación, te permite hacer cosas como que un campo de texto exija que el valor sea un número, o que tenga una longitud máxima o mínima. Puedes rediseñar la página del formulario con Javascript y CSS, crear nuevas páginas para la web, establecer eventos crond para que una acción se ejecute en la fecha que tú digas, crear y usar coreografías para que Nabaztag baile, entre otras muchas otras cosas... ¡Te esperamos!

_build/html/index.html

 <li class="toctree-l2"><a class="reference internal" href="tutorial.html#secciones-de-nuestro-nuevo-plugin">2. Secciones de nuestro nuevo plugin</a></li>
 <li class="toctree-l2"><a class="reference internal" href="tutorial.html#habilitar-el-plugin-configuracion-inicial-y-traduccion">3. Habilitar el plugin, configuración inicial y traducción</a></li>
 <li class="toctree-l2"><a class="reference internal" href="tutorial.html#formulario-de-configuracion-y-uso">4. Formulario de configuración y uso</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial.html#comunicacion-servidor-cliente">5. Comunicación Servidor -&gt; Cliente</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial.html#borrar-datos-al-deshabilitar-el-plugin">6. Borrar datos al deshabilitar el plugin</a></li>
+<li class="toctree-l2"><a class="reference internal" href="tutorial.html#que-queda-ahora">7. ¿Qué queda ahora?</a></li>
 </ul>
 </li>
 </ul>

_build/html/searchindex.js

-Search.setIndex({objects:{},terms:{code:2,ejecutado:2,"posibilitar\u00e1n":2,ampliando:2,"pulsaci\u00f3n":[1,2],borrando:2,desactivar:2,hconn:2,primer:2,rellenado:2,viceversa:1,otro:2,habla:1,atributo:2,taichi:2,vez:2,otra:2,init:2,decir:[1,2],hola:2,tener:2,"soluci\u00f3n":2,debemo:2,ponga:2,"c\u00f3mo":2,hemo:2,fals:2,fuent:[1,2],cumpla:2,evento:[1,2],ejecut:1,tra:[1,2],to_sai:2,comunicars:1,"petici\u00f3n":1,correctament:1,"habr\u00e1":2,pasa:2,cosa:2,bienvenido:0,cfg:2,paso:2,ejemplo:[1,2],"canci\u00f3n":1,"bot\u00f3n":[1,2],entr:1,definido:2,pass:2,"s\u00f3lo":2,index:[],habilitado:2,porqu:2,comunicacion:1,version:2,destinada:2,tercera:2,"m\u00e9todo":2,estableceremo:2,method:[],reproduc:1,importado:2,gener:2,here:[],"podr\u00e1":2,desactivado:2,aquello:2,web:2,path:1,aquella:2,desactivada:2,search:[],soporta:1,"entregar\u00e1n":1,"expandir\u00e1n":1,relacionada:2,facilitar:2,permit:[1,2],internacio:[],"comunicar\u00e1":1,habilitars:2,varia:2,modul:[],texto:2,"configuraci\u00f3n":[0,1,2],sirv:1,"administraci\u00f3n":[1,2],visibl:2,"m\u00f3dulo":[0,1],creada:2,recordemo:2,from:2,modificaremo:2,usa:1,creamo:2,duda:2,reestablecido:2,pero:2,habilitar:[0,2],desea:2,encabec:2,rabbid_cfg:2,oreja:1,saber:2,post_init:2,avisar:2,rabbit:2,descargar:1,"acr\u00f3nimo":1,none:[],"ejecuci\u00f3n":2,local:2,del:[1,2],den:2,rellenar:2,def:2,"creaci\u00f3n":[0,2],stream:1,funcionalidad:2,share:1,indic:[],tal:1,want:[],pone:2,deberemo:2,almacenar:2,objeto:2,"ser\u00e1n":2,write:2,"__init__":2,cual:[1,2],config:2,sin:2,"cuesti\u00f3n":2,algo:2,tien:1,modularidad:2,recibirs:2,"a\u00f1ada":2,mac:2,sistema:1,cambiado:2,"ser\u00eda":2,borrar:2,sonido:1,orden:1,deshabilitado:2,aportado:2,no_data:2,truco:2,primera:[1,2],vaya:2,talk:[],primero:2,bidireccion:1,plantilla:2,importars:2,xmpp:1,"har\u00e1":2,clave:2,nombr:2,mayor:2,forma:2,tendremo:2,establec:2,requiera:2,"deb\u00eda":2,"mostrar\u00e1":2,puerto:1,main:1,"return":2,python:1,mientra:2,nada:2,consigu:2,comprend:2,"realizar\u00e1":1,framework:2,"enviar\u00e1":1,"qu\u00e9":[0,1,2],desactiv:2,"as\u00ed":[1,2],trae:2,document:[],rellena:2,sido:2,individu:2,realli:[],recibir:2,toda:2,funcionamiento:1,ello:2,todo:2,event:2,"documentaci\u00f3n":0,siempr:1,req:2,"sac\u00e1ndolo":2,content:0,"deber\u00eda":2,poca:2,tienen:1,say_text:2,"ejecutar\u00e1":[1,2],comunicar:1,inici:[0,2],base:2,que:[1,2],verifiqu:2,fichero:2,thread:2,dato:2,timer:2,defecto:[1,2],"excepci\u00f3n":2,"est\u00e9":1,"pr\u00f3xima":2,cada:2,dond:2,"\u00fanico":2,"est\u00e1n":2,activado:2,"\u00fanica":2,gestionar:2,messag:1,directorio:[1,2],"comunicaci\u00f3n":[1,2],"cu\u00e1l":2,"final":2,"recib\u00eda":2,para:[1,2],haga:[1,2],segunda:2,programar:2,habers:2,requisito:2,part:[1,2],lista:2,"crear\u00e1n":2,"n\u00famero":2,posibl:2,aunqu:[1,2],my_fn:[],"guardar\u00e1":2,str:2,futura:2,plugin_init:2,planteado:2,esta:2,esto:[1,2],siendo:2,ant:2,titl:2,tabl:[],httpmod:1,crearemo:2,presencia:2,poner:2,segundo:2,"despu\u00e9":1,self:2,haciendo:2,"may\u00fascula":2,servidor:[1,2],descomentar:2,nota:2,instanci:2,funcion:1,"trav\u00e9":[1,2],clase:2,surj:2,mediant:[1,2],"\u00fasese":2,"class":2,encargado:[],audio:1,devuelto:2,usuario:2,url:1,alguna:2,"direcci\u00f3n":[1,2],cambio:[1,2],preocuparno:2,"ser\u00e1":2,"ejecutar\u00e1n":2,pynabaztag:[0,1,2],text:2,indispens:1,poder:2,diccionario:2,manera:2,"ofrecer\u00e1":[1,2],"funci\u00f3n":[1,2],activ:2,peticion:1,"f\u00e1cilment":2,relacionar:1,"introducci\u00f3n":[0,1],guarden:2,identificado:1,destinado:2,acces:2,remarcar:2,"import":2,"aqu\u00ed":2,conejo:[1,2],bar:[],amba:[1,2],partir:2,sonar:1,idioma:2,pegaremo:2,hagan:2,ser:2,binario:1,packet:[1,2],ofrecerl:1,sean:2,sea:2,saythi:2,recoja:2,"\u00edndice":0,"__all__":2,contenido:2,someth:[],juego:2,poseen:2,llamada:2,biblioteca:2,ambo:2,aviso:2,mui:[1,2],debe:2,llamado:2,solo:2,meustra:[],kei:2,"llamar\u00e1":2,estaremo:2,"sintetizaci\u00f3n":1,extens:1,"guiar\u00e1":2,pregunta:2,como:[1,2],futuro:2,punto:2,necesario:2,plugin:[0,1,2],quier:[1,2],ahora:2,solicitar:1,empezar:2,tutori:[0,2],disable_rabbid:2,uno:2,"programaci\u00f3n":2,"dem\u00e1":2,cuando:[1,2],llegado:2,cierta:2,tarea:2,consider:2,tabla:0,contrario:2,"caracter\u00edstica":2,json:2,"usar\u00e1":1,capacidad:[1,2],protocolo:1,recomend:2,pueden:2,valor:2,"c\u00f3digo":[1,2],crea:2,guardar:2,descomentaremo:2,"identificar\u00e1":1,caso:[1,2],"encargar\u00e1":2,iniciado:2,"t\u00edtulo":2,impedir:2,traducir:2,encontrar:2,fue:2,"establec\u00edamo":2,sobr:2,error:2,activar:2,sevidor:1,encuentran:2,"obligar\u00e1":2,"funcionar\u00e1":2,"activar\u00e1n":2,voz:1,usars:2,messageblock:2,welcom:[],surjir:2,argumento:2,ruta:1,formulario:[0,2],grand:1,importando:2,relacion:1,"instant\u00e1nea":1,tiempo:2,http:1,"a\u00f1adido":2,"a\u00f1adida":2,usen:2,rfid:2,muestra:2,vuelva:2,configuracion:2,hacer:[1,2],entra:2,dado:2,"servir\u00e1n":1,"a\u00fan":[1,2],"conexi\u00f3n":1,client:[1,2],relacionado:1,advertencia:2,con:[1,2],haremo:2,trabajando:2,letra:2,rabbid:2,nuestra:2,"gesti\u00f3n":2,momento:2,cuenta:2,realiza:1,nuestro:[0,2],hai:2,han:2,"deber\u00e1":2,identif:2,valid:2,"hablar\u00e1":2,"tendr\u00e1":2,campo:2,por:[1,2],lado:2,finalizado:2,hayan:2,"expondr\u00e1":2,"espec\u00edfica":2,establecen:2,"espec\u00edfico":2,una:[1,2],"localizaci\u00f3n":2,server:1,xmppmod:1,configurado:2,posibilidad:2,"uni\u00f3n":2,page:[],guardaremo:2,"encontrar\u00e1":2,"pondr\u00e1":[1,2],"secci\u00f3n":2,tenemo:2,permitir:2,servir:1,"coloc\u00f3":2,est:[1,2],activo:2,protocol:1,esa:2,inicio:2,foo:[],notabl:2,pued:2,web_user_opt:2,"b\u00fasqueda":0,necesitaremo:2,"mensajer\u00eda":1,ofrecen:2,nuevo:[0,2],"deber\u00e1n":2,usar:2,"saltar\u00e1":2,nueva:2,about:[],default_cfg:2,justo:2,uso:[0,1,2],"est\u00e1tico":1,important:2,determinada:1,mismo:2,commit:2,determinado:1,"env\u00edo":1,block:[1,2],"modificaci\u00f3n":2,anterior:2,obten:1,encuentra:1,almacen:2,objetivo:2,son:2,guard:2,ayuda:2,ver:2,trabajar:2,pierda:2,incluy:1,direccion:1,consultar:2,"todav\u00eda":2,enable_rabbid:2,"function":[],"ir\u00e1n":2,form:2,"solicitar\u00e1":1,"a\u00f1adiendo":2,completo:1,teners:2,sirva:2,"\u00faltima":2,"true":[],utf:2,seleccionar:2,consist:2,"default":2,afectar:2,resultado:2,fundament:2,"l\u00ednea":2,historia:2,crear:[0,2],sustituido:2,definen:2,acabamo:2,preocupars:2,"personalizaci\u00f3n":2,mundo:2,"a\u00f1adir":2,propio:2,archivo:[1,2],provocars:2,"activar\u00e1":2,"peque\u00f1a":2,leer:2,pluginnam:2,"est\u00e1bamo":2,movimiento:1,nabaztag:[1,2],traducido:2,gettext:2,componen:2,"podr\u00eda":2,presenc:1,"traducir\u00e1n":2,usado:2,"identificaci\u00f3n":2,siguient:[1,2],interesant:2,seccion:[0,2],parametrar:2,problema:2,dicha:2,modificado:2,"traducci\u00f3n":[0,2],"organizaci\u00f3n":[0,1],dicho:2,"p\u00e1gina":[0,1,2],graben:2,push:1,trabajemo:2,estrictament:1},objtypes:{},titles:["Bienvenido a la documentaci\u00f3n de PyNabaztag!","Introducci\u00f3n: Qu\u00e9 es PyNabaztag","Tutorial: Creaci\u00f3n de un plugin"],objnames:{},filenames:["index","intro","tutorial"]})
+Search.setIndex({objects:{},terms:{code:2,ejecutado:2,"posibilitar\u00e1n":2,ampliando:2,"pulsaci\u00f3n":[1,2],borrando:2,desactivar:2,uno:2,hai:2,hconn:2,primer:2,rellenado:2,viceversa:1,otro:2,habla:[1,2],atributo:2,listado:2,taichi:2,vez:2,send:2,init:2,decir:[1,2],nabazag:2,text:2,"soluci\u00f3n":2,debemo:2,ponga:2,"c\u00f3mo":2,aprend:2,hemo:2,fals:2,fuent:[1,2],cumpla:2,usars:2,ejecut:[1,2],"gu\u00eda":2,tra:[1,2],to_sai:2,"identificar\u00e1":1,comunicars:1,elegir:2,"petici\u00f3n":1,largo:2,correctament:1,"habr\u00e1":2,pasa:2,cosa:2,bienvenido:0,cfg:2,paso:2,"personalizaci\u00f3n":2,ejemplo:[1,2],"canci\u00f3n":1,"bot\u00f3n":[1,2],entr:[1,2],definido:2,pass:2,"s\u00f3lo":2,necesitaremo:2,index:[],habilitado:2,porqu:2,sub:2,tipo:2,comunicacion:[1,2],realizado:2,"generar\u00e1":2,punto:2,version:2,destinada:2,say_text:2,"m\u00e9todo":2,estableceremo:2,method:[],"tendr\u00e1n":2,led:2,den:2,gener:2,pregunta:2,"podr\u00e1":2,desactivado:2,aquello:2,reproducido:2,path:1,aquella:2,inici:[0,2],desactivada:2,search:[],activar:2,"entregar\u00e1n":1,instruccion:2,"deber\u00e1n":2,relacionada:2,"acci\u00f3n":2,habl:2,facilitar:2,terminado:2,permit:[1,2],internacio:[],"comunicar\u00e1":1,habilitars:2,activ:2,modul:[],texto:2,creado:2,"configuraci\u00f3n":[0,1,2],sirv:1,otra:2,visibl:2,"m\u00f3dulo":[0,1],creada:2,recordemo:2,sintetizada:2,from:2,modificaremo:2,usa:1,creamo:2,uso:[0,1,2],reestablecido:2,pero:2,crond:2,peticion:1,encabec:2,msg:2,rabbid_cfg:2,oreja:[1,2],saber:2,post_init:2,avisar:2,rabbit:2,"selecci\u00f3n":2,descargar:1,"acr\u00f3nimo":1,cantemo:2,none:[],"ejecuci\u00f3n":2,servido:2,del:[1,2],permita:2,importado:2,rellenar:2,def:2,"creaci\u00f3n":[0,2],stream:1,funcionalidad:2,share:1,"m\u00faltipl":2,indic:[],tal:1,rm_recv_by_funct_ev:2,want:[],pone:2,deberemo:2,tan:2,almacenar:2,"controlar\u00e1n":2,objeto:2,"est\u00e1tico":1,write:2,entregando:2,voz:[1,2],cual:[1,2],config:2,sin:2,css:2,determinada:1,tien:[1,2],modularidad:2,borran:2,"a\u00f1ada":2,mac:2,sistema:[1,2],cambiado:2,"ser\u00eda":2,borrar:[0,2],data:2,sonido:[1,2],orden:[1,2],determinado:1,surjir:2,deshabilitado:2,aportado:2,no_data:2,hasta:2,primera:[1,2],vaya:2,talk:[],primero:2,bidireccion:1,plantilla:2,importars:2,xmpp:1,"har\u00e1":2,clave:2,nombr:2,mayor:2,forma:2,"tambi\u00e9n":2,"pr\u00f3xima":2,tendremo:2,"f\u00e1cilment":2,establec:2,requiera:2,"deb\u00eda":2,instancia:2,puerto:1,main:[1,2],deshabilitar:[0,2],consecuencia:2,"return":2,formulario:[0,2],python:1,mientra:2,nada:2,consigu:2,comprend:2,"realizar\u00e1":1,framework:2,"dir\u00e1":2,"enviar\u00e1":[1,2],"qu\u00e9":[0,1,2],desactiv:2,"as\u00ed":[1,2],trae:2,importando:2,rellena:2,sido:2,notabl:2,obtenido:2,individu:2,idea:2,recapitulemo:2,realli:[],recibir:2,toda:2,"enviar\u00e1n":2,funcionamiento:[1,2],http:1,todo:2,event:2,"documentaci\u00f3n":0,ayuda:2,"a\u00f1adido":2,"imaginaci\u00f3n":2,req:2,reboot:2,"sac\u00e1ndolo":2,content:0,usen:2,poca:2,entran:2,tienen:1,tercera:2,"ejecutar\u00e1":[1,2],comunicar:1,ech:2,base:2,que:[1,2],verifiqu:2,fichero:2,thread:2,dijes:2,dato:[0,2],timer:2,apuntan:2,"deshabilitaci\u00f3n":2,mucha:2,defecto:[1,2],desact:2,"excepci\u00f3n":2,"est\u00e9":1,esperamo:2,mucho:2,cada:2,dond:2,cuadro:2,"\u00fanico":2,"est\u00e1n":2,"\u00fanica":2,gestionar:2,messag:1,directorio:[1,2],"comunicaci\u00f3n":[0,1,2],"cu\u00e1l":2,"final":2,"recib\u00eda":2,tupla:2,para:[1,2],entra:2,descomentar:2,programar:2,habers:2,requisito:2,part:[1,2],necesitamo:2,"redise\u00f1ar":2,lista:2,diga:2,"crear\u00e1n":2,"n\u00famero":2,llevamo:2,usar:2,posibl:2,aunqu:[1,2],my_fn:[],"guardar\u00e1":2,str:2,futura:2,plugin_init:2,seri:2,sai:2,planteado:2,esta:2,esto:[1,2],siendo:2,ant:2,tabl:[],httpmod:1,borraremo:2,crearemo:2,escrib:2,presencia:2,poner:2,segundo:2,paquet:2,"despu\u00e9":[1,2],self:2,"solicitar\u00e1":1,"may\u00fascula":2,servidor:[0,1,2],segunda:2,nota:2,interior:2,instanci:2,funcion:1,"trav\u00e9":[1,2],clase:2,surj:2,mediant:[1,2],manera:2,"\u00fasese":2,diferent:2,"class":2,encargado:[],"__all__":2,usuario:2,url:1,alguna:2,"direcci\u00f3n":[1,2],exija:2,cambio:[1,2],preocuparno:2,"ser\u00e1":2,"ejecutar\u00e1n":2,pynabaztag:[0,1,2],tener:2,ofrecen:2,"\u00faltima":2,indispens:1,diccionario:2,hace:2,bail:2,"ofrecer\u00e1":[1,2],vario:2,"funci\u00f3n":[1,2],denominado:2,fecha:2,varia:2,"instrucci\u00f3n":2,here:[],parametr:2,desea:2,pensar:2,local:2,hablando:2,relacionar:1,"introducci\u00f3n":[0,1],guarden:2,identificado:1,destinado:2,acces:2,remarcar:2,aviso:2,"aqu\u00ed":2,conejo:[1,2],bar:[],vien:2,partir:2,sonar:1,momento:2,idioma:2,aplicado:2,pegaremo:2,hagan:2,ser:2,binario:1,packet:[1,2],ofrecerl:1,sean:2,sea:2,saythi:2,recoja:2,"\u00edndice":0,devuelto:2,contenido:2,someth:[],juego:2,poseen:2,llamada:2,biblioteca:2,ambo:2,"import":2,mui:[1,2],amba:[1,2],pueden:2,llamado:2,solo:2,meustra:[],deseamo:2,kei:2,"llamar\u00e1":2,debe:2,javascript:2,sola:2,extens:1,"guiar\u00e1":2,"m\u00ednima":2,como:[1,2],futuro:2,opcion:2,"sintetizaci\u00f3n":[1,2],necesario:2,plugin:[0,1,2],quier:[1,2],ahora:[0,2],solicitar:1,empezar:2,tutori:[0,2],disable_rabbid:2,sencillo:2,con:[1,2],"coreograf\u00eda":2,"programaci\u00f3n":2,"dem\u00e1":2,cuando:[1,2],"cuesti\u00f3n":2,cierta:2,hola:2,tarea:2,consider:2,tabla:0,contrario:2,"caracter\u00edstica":2,conjunto:2,json:2,"usar\u00e1":1,capacidad:[1,2],reciba:2,recomend:2,presenc:1,valor:2,"c\u00f3digo":[1,2],volar:2,crea:2,guardar:2,descomentaremo:2,componen:2,reducir:2,caso:[1,2],"encargar\u00e1":2,iniciado:2,"t\u00edtulo":2,impedir:2,traducir:2,encontrar:2,fue:2,"establec\u00edamo":2,sobr:2,error:2,soporta:1,"crear\u00e1":2,encuentran:2,conocido:2,"obligar\u00e1":2,"funcionar\u00e1":2,movimiento:1,ofrec:2,rabbid:2,ambient:2,"activar\u00e1n":2,"__init__":2,evento:[1,2],"saltar\u00e1":2,digamo:2,messageblock:2,welcom:[],acaba:2,argumento:2,ruta:1,sencilla:2,hecho:2,grand:1,document:[],relacion:1,"instant\u00e1nea":1,tiempo:2,ello:2,memoria:2,siempr:1,"a\u00f1adida":2,"deber\u00eda":2,tenga:2,rfid:2,vuelva:2,configuracion:2,"seguir\u00e1":2,"continuaci\u00f3n":2,hacer:[1,2],haga:[1,2],dado:2,"servir\u00e1n":1,"a\u00fan":[1,2],"conexi\u00f3n":1,reproducir:2,client:[0,1,2],relacionado:1,advertencia:2,audio:[1,2],haremo:2,trabajando:2,definen:2,letra:2,protocol:1,facilitado:2,usaremo:2,nuestra:2,"gesti\u00f3n":2,verdad:2,gettext:2,realiza:1,nuestro:[0,2],web:2,han:2,utilizado:2,"deber\u00e1":2,identif:2,susodicho:2,"hablar\u00e1":2,"tendr\u00e1":2,campo:2,por:[1,2],lado:2,finalizado:2,hayan:2,llegado:2,"expondr\u00e1":2,"espec\u00edfica":2,establecen:2,"espec\u00edfico":2,ligera:2,una:[1,2],"mostrar\u00e1":2,"localizaci\u00f3n":2,server:1,xmppmod:1,truco:2,configurado:2,"quedar\u00eda":2,posibilidad:2,"uni\u00f3n":2,page:[],"administraci\u00f3n":[1,2],"encontrar\u00e1":2,guardaremo:2,"peque\u00f1a":2,"pondr\u00e1":[1,2],"secci\u00f3n":2,tenemo:2,hablar:2,permitir:2,bloqu:2,servir:1,"coloc\u00f3":2,est:[1,2],activo:2,ligero:2,esa:2,inicio:2,foo:[],reproduc:1,eliminar:2,pued:2,web_user_opt:2,"b\u00fasqueda":0,usan:2,queda:[0,2],inmediato:2,"mensajer\u00eda":1,nuevo:[0,2],"expandir\u00e1n":1,"adem\u00e1":2,activado:2,victoria:2,habilitar:[0,2],nueva:2,about:[],plazo:2,default_cfg:2,justo:2,queremo:2,duda:2,"ser\u00e1n":2,seccion:[0,2],algo:2,mismo:2,graban:2,"m\u00e1xima":2,"env\u00edo":1,block:[1,2],parametrar:2,anterior:2,obten:1,encuentra:1,almacen:2,realidad:2,objetivo:2,son:2,"quisi\u00e9ramo":2,guard:2,"r\u00e1pido":2,ver:2,trabajar:2,pierda:2,enable_rabbid:2,incluy:1,existen:2,interfaz:2,direccion:1,consultar:2,problema:2,poder:2,"function":[],"reiniciar\u00e1":2,"ir\u00e1n":2,form:2,haciendo:2,provocars:2,"a\u00f1adiendo":2,completo:1,teners:2,sirva:2,recibirs:2,"\u00faltimo":2,"reproducci\u00f3n":2,"true":[],utf:2,seleccionar:2,consist:2,"default":2,estaremo:2,commit:2,afectar:2,resultado:2,fundament:2,graben:2,"l\u00ednea":2,historia:2,crear:[0,2],sustituido:2,genial:2,activan:2,acabamo:2,preocupars:2,protocolo:1,mundo:2,"a\u00f1adir":2,propio:2,archivo:[1,2],mover:2,apartado:2,"activar\u00e1":2,muestra:2,leer:2,pluginnam:2,"est\u00e1bamo":2,titl:2,nabaztag:[1,2],traducido:2,cuenta:2,valid:2,"podr\u00eda":2,sevidor:1,aperitivo:2,"traducir\u00e1n":2,usado:2,"identificaci\u00f3n":2,siguient:[1,2],interesant:2,haya:2,important:2,"modificaci\u00f3n":2,introducir:2,longitud:2,dicha:2,modificado:2,"traducci\u00f3n":[0,2],"organizaci\u00f3n":[0,1],dicho:2,"p\u00e1gina":[0,1,2],"todav\u00eda":2,push:1,trabajemo:2,estrictament:1},objtypes:{},titles:["Bienvenido a la documentaci\u00f3n de PyNabaztag!","Introducci\u00f3n: Qu\u00e9 es PyNabaztag","Tutorial: Creaci\u00f3n de un plugin"],objnames:{},filenames:["index","intro","tutorial"]})

_build/html/tutorial.html

     <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="o">.</span><span class="n">write</span><span class="p">()</span>
 </pre></div>
 </div>
+<p>Esto generará un formulario con 2 campos: Un campo de texto para introducir el texto, y un cuadro de selección múltiple para elegir los eventos.</p>
+</div>
+<div class="section" id="comunicacion-servidor-cliente">
+<h2>5. Comunicación Servidor -&gt; Cliente<a class="headerlink" href="#comunicacion-servidor-cliente" title="Enlazar permanentemente con este título">¶</a></h2>
+<p>Recapitulemos lo que llevamos hasta el momento: Hemos creado un plugin llamado &#8220;saythis&#8221;, el cual puede activar el usuario a través de la interfaz web, y parametrar un texto que dirá Nabazag al recibir un evento (o varios), lo cual también es parametrable a través de la página web. Ahora, nos queda que al recibir el susodicho evento, se ejecute algo que haga que Nabaztag hable. Dicha función la hemos llamado en el apartado anterior como &#8220;say_text&#8221;. Crearemos un nuevo método en el plugin con dicho nombre:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">say_text</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">rabbid</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
+    <span class="sd">&quot;&quot;&quot;\</span>
+<span class="sd">    Hablar a través del Nabaztag facilitado.</span>
+<span class="sd">    &quot;&quot;&quot;</span>
+    <span class="k">pass</span>
+</pre></div>
+</div>
+<p>El sistema de eventos ejecutará este método entregando 2 argumentos: un primero con un objeto Rabbid, el cual será como el ya conocido <em>&#8220;hconn.rabbid&#8221;</em>. Lo siguiente que nos queda por saber, es cómo haremos las comunicaciones entre nuestro servidor, y el Nabaztag en cuestión. Aquí es donde entran los denominados <strong>Paquetes&#8221; y &#8220;Bloques</strong>. Para la comunicación Servidor -&gt; Cliente, se usan bloques de instrucciones, y el paquete es un conjunto de uno o más bloques que se enviarán al Nabaztag. Existen 2 tipos diferentes de bloques: aquellos que tendrán consecuencias a largo plazo en el Nabaztag (como el bloque Reboot, que reiniciará a Nabaztag, o los &#8220;bloques ambiente&#8221;, que graban datos en la memoria de Nabaztag), y los &#8220;inmediatos&#8221; y sin consecuencias a largo plazo. Estos últimos serán los más utilizados, y controlarán cosas como la reproducción de sonidos, mover las orejas o los leds. En realidad, hay un único bloque que hace esto, llamado <strong>MessageBlock</strong>, pero que a su vez tiene en su interior una serie de &#8220;sub-bloques&#8221; con las instrucciones.</p>
+<p>Para usar el bloque MessageBlock y Packet, descomentaremos:</p>
+<div class="highlight-python"><pre>from .. blocks import Packet, MessageBlock # Comunicación S-&gt;C.</pre>
+</div>
+<p>Aunque Nabaztag no permita de por sí la sintetización de voz, MessageBlock tiene un sub-bloque que creará un archivo de audio con una voz sintetizada con el texto que le digamos, y pondrá la orden de reproducir dicho archivo en el MessageBlock. Este <em>&#8220;sub-bloque&#8221;</em>, se creará en el MessageBlock con el método <strong>say</strong>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># Se instancia MessageBlock</span>
+<span class="n">msg</span> <span class="o">=</span> <span class="n">MessageBlock</span><span class="p">()</span>
+<span class="n">msg</span><span class="o">.</span><span class="n">say</span><span class="p">(</span><span class="s">&quot;Texto a decir&quot;</span><span class="p">)</span>
+<span class="c"># Si quisiéramos que se dijese otro texto justo después:</span>
+<span class="c"># msg.say(&quot;Segundo texto que se dirá a continuación.&quot;)</span>
+</pre></div>
+</div>
+<p>MessageBlock tiene además una solución para reducir esto en una sola línea (para cuando queremos poner sólo una instrucción en el MessageBlock. Aplicado a lo que necesitamos, sería:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># Texto a decir obtenido de las configuraciones</span>
+<span class="n">to_say</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="s">&#39;rabbids&#39;</span><span class="p">][</span><span class="nb">str</span><span class="p">(</span><span class="n">rabbid</span><span class="p">)][</span><span class="s">&#39;to_say&#39;</span><span class="p">]</span>
+<span class="c"># MessageBlock sólo con la instrucción de decir el texto</span>
+<span class="n">msg</span> <span class="o">=</span> <span class="n">MessageBlock</span><span class="p">(</span><span class="s">&#39;say&#39;</span><span class="p">,</span> <span class="n">to_say</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Ahora, sólo nos queda añadir el bloque MessageBlock al Packet:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">say_text</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">rabbid</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
+    <span class="sd">&quot;&quot;&quot;\</span>
+<span class="sd">    Hablar a través del Nabaztag facilitado.</span>
+<span class="sd">    &quot;&quot;&quot;</span>
+    <span class="c"># Texto a decir obtenido de las configuraciones</span>
+    <span class="n">to_say</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="s">&#39;rabbids&#39;</span><span class="p">][</span><span class="nb">str</span><span class="p">(</span><span class="n">rabbid</span><span class="p">)][</span><span class="s">&#39;to_say&#39;</span><span class="p">]</span>
+    <span class="c"># Paquete con el conejo al que se enviará (rabbid) y el bloque</span>
+    <span class="c"># MessageBlock que se enviará al conejo con el texto que se dirá</span>
+    <span class="n">Packet</span><span class="p">(</span><span class="n">MessageBlock</span><span class="p">(</span><span class="s">&#39;say&#39;</span><span class="p">,</span> <span class="n">to_say</span><span class="p">))</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">rabbid</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">main</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>En 2 sencillas líneas, hemos hecho que nuestro Nabaztag hable. ¿Sencillo, no? Es necesario decir, que si queremos que el Packet tenga más de 1 Block, el primer argumento deberá ser una tupla o un listado con los bloques. Los bloques, serán reproducidos a continuación. Hay más bloques y opciones, pero esto es todo lo que necesitamos saber en este tutorial.</p>
+</div>
+<div class="section" id="borrar-datos-al-deshabilitar-el-plugin">
+<h2>6. Borrar datos al deshabilitar el plugin<a class="headerlink" href="#borrar-datos-al-deshabilitar-el-plugin" title="Enlazar permanentemente con este título">¶</a></h2>
+<p>¡Genial! Ya tenemos nuestro plugin, y nuestro Nabaztag ya habla al recibir un evento. Pero no cantemos victoria tan rápido: aún tenemos que pensar en la deshabilitación de nuestro plugin. Aunque el plugin se desactive ahora mismo, Nabaztag seguirá hablando cuando reciba el evento, y no es lo que deseamos. Tenemos que:</p>
+<p># Eliminar los eventos que activan la función
+# Borrar los datos de configuración del usuario.</p>
+<p>Para lo primero usaremos un método que nos trae el plugin Events:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">disable_rabbid</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">hconn</span><span class="p">):</span>
+    <span class="c"># El Nabaztag fue desactivado en la lista de plugins.</span>
+    <span class="c"># Eliminar todos los eventos que apuntan a la función para el conejo</span>
+    <span class="bp">self</span><span class="o">.</span><span class="n">rm_recv_by_funct_event</span><span class="p">(</span><span class="n">hconn</span><span class="o">.</span><span class="n">rabbid</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">say_text</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Y después borraremos todos los datos de configuración:</p>
+<blockquote>
+<div>del self.cfg[&#8216;rabbids&#8217;][str(hconn.rabbid)]</div></blockquote>
+<p>El resultado final sería como el siguiente:</p>
+<blockquote>
+<div><dl class="docutils">
+<dt>def disable_rabbid(self, hconn):</dt>
+<dd># El Nabaztag fue desactivado en la lista de plugins.
+# Eliminar todos los eventos que apuntan a la función para el conejo
+self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+# Se borran todas las configuraciones para el conejo
+del self.cfg[&#8216;rabbids&#8217;][str(hconn.rabbid)]
+# Se escribe en la configuración los cambios en realizados
+self.cfg.write()</dd>
+</dl>
+</div></blockquote>
+<p>Ya tenemos terminado nuestro plugin, y así quedaría el resultado:</p>
+<div class="highlight-python"><pre># -*- coding: utf-8 -*-
+from threading import Timer # Tareas que se ejecutarán en X tiempo.
+import gettext # Locales
+from .. blocks import Packet, MessageBlock # Comunicación S-&gt;C.
+from . import base
+
+# Diccionario con configuraciones por defecto
+DEFAULT_CFG = {
+    'rabbids': {}, # configuraciones en cada conejo.
+}
+
+# Descomentar la siguiente línea para las locales
+_ = gettext.gettext ## Traducir texto mediante _("Texto")
+
+class Pluginname(base.Base):
+    def plugin_init(self):
+        # Se ejecutará justo en el momento de importarse el plugin. Úsese
+        # solo para aquellos casos en que este plugin sirva de biblioteca
+        # para otros plugins.
+        pass
+    def post_init(self):
+        # Se ejecutará tras haberse ejecutado todos los demás plugins. Si
+        # su plugin va a usar otro plugin de biblioteca (por ejemplo, el
+        # plugin events), ponga aquí aquellas cosas que le hagan uso. De
+        # lo contrario puede provocarse una excepción, ya que si se pone
+        # en plugin_init, el plugin events podría no haberse importado
+        # todavía.
+        pass
+    def web_user_options(self, hconn):
+        # Página de configuración del plugin
+        # Diccionario con la configuración del conejo
+        rabbid_cfg = self.cfg['rabbids'][str(hconn.rabbid)]
+        # Se crea el objeto formulario. El segundo argumento será un
+        # diccionario sobre el que se guarden los valores devueltos por
+        # el usuario en los campos. El atributo title será el título que
+        # encabece el formulario.
+        form = base.Form(hconn, rabbid_cfg, title='Texto a decir')
+        # ... Se establecen los campos del formulario aquí ...
+        # Campo del formulario to_say. El primer argumento es la clave en el
+        # diccionario rabbid_cfg. La segunda el texto de ayuda del campo.
+        form.text('to_say', 'Texto a decir')
+        # Campo del formulario para seleccionar los eventos. El primer
+        # argumento Es la función que se ejecutará al recibirse el evento.
+        form.events(self.say_text)
+        if form.no_data(): return form
+        # A partir de aquí, se poseen los valores devueltos por el usuario.
+        self.cfg.write()
+    def say_text(self, rabbid, data):
+        """\
+        Hablar a través del Nabaztag facilitado.
+        """
+        # Texto a decir obtenido de las configuraciones
+        to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+        # Paquete con el conejo al que se enviará (rabbid) y el bloque
+        # MessageBlock que se enviará al conejo con el texto que se dirá
+        Packet(MessageBlock('say', to_say)).send(rabbid, self.main)
+    def enable_rabbid(self, hconn):
+        """ \
+        Crear la configuración inicial para el Nabaztag al activar el
+        plugin.
+        """
+        # Creamos un diccionario como valor a la clave del conejo,
+        # que almacene las configuraciones para el conejo
+        self.cfg[str(hconn.rabbid)] = {}
+        # Texto a decir por defecto en el nombre de clave "to_say".
+        # Será traducido al idioma del usuario con el _(...)
+        self.cfg[str(hconn.rabbid)]['to_say'] = _('Hola Mundo')
+        # Lo siguiente guardará la configuración en el archivo
+        # de configuración, para que no se pierda. Sería algo
+        # así como un commit
+        self.cfg.write()
+    def disable_rabbid(self, hconn):
+        # El Nabaztag fue desactivado en la lista de plugins.
+        # El Nabaztag fue desactivado en la lista de plugins.
+        # Eliminar todos los eventos que apuntan a la función para el conejo
+        self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+        # Se borran todas las configuraciones para el conejo
+        del self.cfg['rabbids'][str(hconn.rabbid)]
+        # Se escribe en la configuración los cambios en realizados
+        self.cfg.write()</pre>
+</div>
+</div>
+<div class="section" id="que-queda-ahora">
+<h2>7. ¿Qué queda ahora?<a class="headerlink" href="#que-queda-ahora" title="Enlazar permanentemente con este título">¶</a></h2>
+<p>Esta guía sólo ha servido como un ligero aperitivo a lo que viene de verdad, pero esperamos que haya servido para que te hagas una ligera idea del funcionamiento del sistema de plugins de PyNabaztag. Aún queda mucho que aprender, porque aquí no se acaba la cosa: PyNabaztag te ofrece más campos de formulario para que eches a volar tu imaginación, te permite hacer cosas como que un campo de texto exija que el valor sea un número, o que tenga una longitud máxima o mínima. Puedes rediseñar la página del formulario con Javascript y CSS, crear nuevas páginas para la web, establecer eventos crond para que una acción se ejecute en la fecha que tú digas, crear y usar coreografías para que Nabaztag baile, entre otras muchas otras cosas... ¡Te esperamos!</p>
 </div>
 </div>
 
 <li><a class="reference internal" href="#secciones-de-nuestro-nuevo-plugin">2. Secciones de nuestro nuevo plugin</a></li>
 <li><a class="reference internal" href="#habilitar-el-plugin-configuracion-inicial-y-traduccion">3. Habilitar el plugin, configuración inicial y traducción</a></li>
 <li><a class="reference internal" href="#formulario-de-configuracion-y-uso">4. Formulario de configuración y uso</a></li>
+<li><a class="reference internal" href="#comunicacion-servidor-cliente">5. Comunicación Servidor -&gt; Cliente</a></li>
+<li><a class="reference internal" href="#borrar-datos-al-deshabilitar-el-plugin">6. Borrar datos al deshabilitar el plugin</a></li>
+<li><a class="reference internal" href="#que-queda-ahora">7. ¿Qué queda ahora?</a></li>
 </ul>
 </li>
 </ul>
 
 Esto generará un formulario con 2 campos: Un campo de texto para introducir el texto, y un cuadro de selección múltiple para elegir los eventos.
 
-4. Comunicación Servidor -> Cliente
------------------------------------
+5. Comunicación Servidor -> Cliente
+-----------------------------------
+Recapitulemos lo que llevamos hasta el momento: Hemos creado un plugin llamado "saythis", el cual puede activar el usuario a través de la interfaz web, y parametrar un texto que dirá Nabazag al recibir un evento (o varios), lo cual también es parametrable a través de la página web. Ahora, nos queda que al recibir el susodicho evento, se ejecute algo que haga que Nabaztag hable. Dicha función la hemos llamado en el apartado anterior como "say_text". Crearemos un nuevo método en el plugin con dicho nombre::
+    
+    def say_text(self, rabbid, data):
+        """\
+        Hablar a través del Nabaztag facilitado.
+        """
+        pass
+
+El sistema de eventos ejecutará este método entregando 2 argumentos: un primero con un objeto Rabbid, el cual será como el ya conocido *"hconn.rabbid"*. Lo siguiente que nos queda por saber, es cómo haremos las comunicaciones entre nuestro servidor, y el Nabaztag en cuestión. Aquí es donde entran los denominados **Paquetes" y "Bloques**. Para la comunicación Servidor -> Cliente, se usan bloques de instrucciones, y el paquete es un conjunto de uno o más bloques que se enviarán al Nabaztag. Existen 2 tipos diferentes de bloques: aquellos que tendrán consecuencias a largo plazo en el Nabaztag (como el bloque Reboot, que reiniciará a Nabaztag, o los "bloques ambiente", que graban datos en la memoria de Nabaztag), y los "inmediatos" y sin consecuencias a largo plazo. Estos últimos serán los más utilizados, y controlarán cosas como la reproducción de sonidos, mover las orejas o los leds. En realidad, hay un único bloque que hace esto, llamado **MessageBlock**, pero que a su vez tiene en su interior una serie de "sub-bloques" con las instrucciones.
+
+Para usar el bloque MessageBlock y Packet, descomentaremos::
+    
+    from .. blocks import Packet, MessageBlock # Comunicación S->C.
+
+Aunque Nabaztag no permita de por sí la sintetización de voz, MessageBlock tiene un sub-bloque que creará un archivo de audio con una voz sintetizada con el texto que le digamos, y pondrá la orden de reproducir dicho archivo en el MessageBlock. Este *"sub-bloque"*, se creará en el MessageBlock con el método **say**::
+    
+    # Se instancia MessageBlock
+    msg = MessageBlock()
+    msg.say("Texto a decir")
+    # Si quisiéramos que se dijese otro texto justo después:
+    # msg.say("Segundo texto que se dirá a continuación.")
+
+MessageBlock tiene además una solución para reducir esto en una sola línea (para cuando queremos poner sólo una instrucción en el MessageBlock. Aplicado a lo que necesitamos, sería::
+    
+    # Texto a decir obtenido de las configuraciones
+    to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+    # MessageBlock sólo con la instrucción de decir el texto
+    msg = MessageBlock('say', to_say)
+
+Ahora, sólo nos queda añadir el bloque MessageBlock al Packet::
+
+    def say_text(self, rabbid, data):
+        """\
+        Hablar a través del Nabaztag facilitado.
+        """
+        # Texto a decir obtenido de las configuraciones
+        to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+        # Paquete con el conejo al que se enviará (rabbid) y el bloque
+        # MessageBlock que se enviará al conejo con el texto que se dirá
+        Packet(MessageBlock('say', to_say)).send(rabbid, self.main)
+    
+En 2 sencillas líneas, hemos hecho que nuestro Nabaztag hable. ¿Sencillo, no? Es necesario decir, que si queremos que el Packet tenga más de 1 Block, el primer argumento deberá ser una tupla o un listado con los bloques. Los bloques, serán reproducidos a continuación. Hay más bloques y opciones, pero esto es todo lo que necesitamos saber en este tutorial.
+
+6. Borrar datos al deshabilitar el plugin
+-----------------------------------------
+¡Genial! Ya tenemos nuestro plugin, y nuestro Nabaztag ya habla al recibir un evento. Pero no cantemos victoria tan rápido: aún tenemos que pensar en la deshabilitación de nuestro plugin. Aunque el plugin se desactive ahora mismo, Nabaztag seguirá hablando cuando reciba el evento, y no es lo que deseamos. Tenemos que:
+
+# Eliminar los eventos que activan la función
+# Borrar los datos de configuración del usuario.
+
+Para lo primero usaremos un método que nos trae el plugin Events::
+    
+    def disable_rabbid(self, hconn):
+        # El Nabaztag fue desactivado en la lista de plugins.
+        # Eliminar todos los eventos que apuntan a la función para el conejo
+        self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+
+Y después borraremos todos los datos de configuración:
+    
+    del self.cfg['rabbids'][str(hconn.rabbid)]
+    
+El resultado final sería como el siguiente:
+
+    def disable_rabbid(self, hconn):
+        # El Nabaztag fue desactivado en la lista de plugins.
+        # Eliminar todos los eventos que apuntan a la función para el conejo
+        self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+        # Se borran todas las configuraciones para el conejo
+        del self.cfg['rabbids'][str(hconn.rabbid)]
+        # Se escribe en la configuración los cambios en realizados
+        self.cfg.write()
+
+Ya tenemos terminado nuestro plugin, y así quedaría el resultado::
+
+    # -*- coding: utf-8 -*-
+    from threading import Timer # Tareas que se ejecutarán en X tiempo.
+    import gettext # Locales
+    from .. blocks import Packet, MessageBlock # Comunicación S->C.
+    from . import base
+
+    # Diccionario con configuraciones por defecto
+    DEFAULT_CFG = {
+        'rabbids': {}, # configuraciones en cada conejo.
+    }
+    
+    # Descomentar la siguiente línea para las locales
+    _ = gettext.gettext ## Traducir texto mediante _("Texto")
+    
+    class Pluginname(base.Base):
+        def plugin_init(self):
+            # Se ejecutará justo en el momento de importarse el plugin. Úsese
+            # solo para aquellos casos en que este plugin sirva de biblioteca
+            # para otros plugins.
+            pass
+        def post_init(self):
+            # Se ejecutará tras haberse ejecutado todos los demás plugins. Si
+            # su plugin va a usar otro plugin de biblioteca (por ejemplo, el
+            # plugin events), ponga aquí aquellas cosas que le hagan uso. De
+            # lo contrario puede provocarse una excepción, ya que si se pone
+            # en plugin_init, el plugin events podría no haberse importado
+            # todavía.
+            pass
+        def web_user_options(self, hconn):
+            # Página de configuración del plugin
+            # Diccionario con la configuración del conejo
+            rabbid_cfg = self.cfg['rabbids'][str(hconn.rabbid)]
+            # Se crea el objeto formulario. El segundo argumento será un
+            # diccionario sobre el que se guarden los valores devueltos por
+            # el usuario en los campos. El atributo title será el título que
+            # encabece el formulario.
+            form = base.Form(hconn, rabbid_cfg, title='Texto a decir')
+            # ... Se establecen los campos del formulario aquí ...
+            # Campo del formulario to_say. El primer argumento es la clave en el 
+            # diccionario rabbid_cfg. La segunda el texto de ayuda del campo.
+            form.text('to_say', 'Texto a decir')
+            # Campo del formulario para seleccionar los eventos. El primer
+            # argumento Es la función que se ejecutará al recibirse el evento.
+            form.events(self.say_text)
+            if form.no_data(): return form
+            # A partir de aquí, se poseen los valores devueltos por el usuario.
+            self.cfg.write()
+        def say_text(self, rabbid, data):
+            """\
+            Hablar a través del Nabaztag facilitado.
+            """
+            # Texto a decir obtenido de las configuraciones
+            to_say = self.cfg['rabbids'][str(rabbid)]['to_say']
+            # Paquete con el conejo al que se enviará (rabbid) y el bloque
+            # MessageBlock que se enviará al conejo con el texto que se dirá
+            Packet(MessageBlock('say', to_say)).send(rabbid, self.main)
+        def enable_rabbid(self, hconn):
+            """ \
+            Crear la configuración inicial para el Nabaztag al activar el
+            plugin.
+            """
+            # Creamos un diccionario como valor a la clave del conejo,
+            # que almacene las configuraciones para el conejo
+            self.cfg[str(hconn.rabbid)] = {}
+            # Texto a decir por defecto en el nombre de clave "to_say".
+            # Será traducido al idioma del usuario con el _(...)
+            self.cfg[str(hconn.rabbid)]['to_say'] = _('Hola Mundo')
+            # Lo siguiente guardará la configuración en el archivo
+            # de configuración, para que no se pierda. Sería algo
+            # así como un commit
+            self.cfg.write()
+        def disable_rabbid(self, hconn):
+            # El Nabaztag fue desactivado en la lista de plugins.
+            # El Nabaztag fue desactivado en la lista de plugins.
+            # Eliminar todos los eventos que apuntan a la función para el conejo
+            self.rm_recv_by_funct_event(hconn.rabbid, self.say_text)
+            # Se borran todas las configuraciones para el conejo
+            del self.cfg['rabbids'][str(hconn.rabbid)]
+            # Se escribe en la configuración los cambios en realizados
+            self.cfg.write()
+
+7. ¿Qué queda ahora?
+-----------------------------------------
+Esta guía sólo ha servido como un ligero aperitivo a lo que viene de verdad, pero esperamos que haya servido para que te hagas una ligera idea del funcionamiento del sistema de plugins de PyNabaztag. Aún queda mucho que aprender, porque aquí no se acaba la cosa: PyNabaztag te ofrece más campos de formulario para que eches a volar tu imaginación, te permite hacer cosas como que un campo de texto exija que el valor sea un número, o que tenga una longitud máxima o mínima. Puedes rediseñar la página del formulario con Javascript y CSS, crear nuevas páginas para la web, establecer eventos crond para que una acción se ejecute en la fecha que tú digas, crear y usar coreografías para que Nabaztag baile, entre otras muchas otras cosas... ¡Te esperamos!