Commits

Nekmo committed 50f3a2f

Terminado hasta el cuarto cap. del tutorial

Comments (0)

Files changed (13)

_build/doctrees/environment.pickle

Binary file modified.

_build/doctrees/index.doctree

Binary file modified.

_build/doctrees/intro.doctree

Binary file modified.

_build/doctrees/tutorial.doctree

Binary file modified.

_build/html/_sources/index.txt

    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Welcome to PyNabaztag's documentation!
-======================================
+Bienvenido a la documentación de PyNabaztag!
+============================================
 
 Contents:
 
    intro
    tutorial
 
-Indices and tables
+Índices y tablas
 ==================
 
 * :ref:`genindex`

_build/html/_sources/tutorial.txt

-===============================
 Tutorial: Creación de un plugin
 ===============================
 Uno de los objetivos de PyNabaztag, es la modularidad. Toda capacidad añadida al servidor, debería de ser fácilmente activable y desactivable sin que ello requiera modificación alguna de código. Ello permite mayor personalización y encontrar más fácilmente errores.
 En el siguiente tutorial se expondrá cómo crear un plugin que cumpla las siguientes características:
 
 #. El plugin sólo se activará y funcionará si el usuario lo desea.
-#. Nabaztag hablará al recibir un evento.
+#. Nabaztag hablará al recibir un evento (pulsación de botón, objeto RFID...).
 #. Dicho evento deberá poder ser configurado por el usuario.
 #. El evento podrá ser reestablecido.
 #. Habrá un texto por defecto que será dicho, el cual será "Hola Mundo". Dicho texto podrá ser cambiado por el usuario, sin afectar a los demás usuarios del servidor.
 #. Las funcionalidades del plugin deberán poder ser desactivadas por el usuario para sí mismo, borrando datos de su presencia.
 #. El plugin se llamará "saythis".
 
-------------------------
 1. Crear un plugin nuevo
 ------------------------
-Lo primero de todo, crearemos un archivo en *pynabaztag/plugins/* llamado *saythis.py*, y pegaremos la siguiente plantilla que nos guiará en la creación de nuestro plugin.
+Lo primero de todo, crearemos un archivo en el directorio **pynabaztag/plugins/** llamado **saythis.py**, y pegaremos la siguiente plantilla que nos guiará en la creación de nuestro plugin.
 
 .. code-block:: python
 
     # -*- 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
 
     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
             # 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
+            pass
+
+Finalizado esto, haremos que en la próxima ejecución de PyNabaztag el plugin que acabamos de crear se importe. Para ello modificaremos el archivo **pynabaztag/plugins/__init__.py** añadiendo el nombre del fichero del plugin:::
+
+    __all__ = ['events', 'taichi', (...), 'saythis']
+    
+.. NOTE::
+   En futuras versiones de PyNabaztag, el anterior paso puede no ser necesario, siendo sustituido por una página de administración para gestionar los plugins activos.
+
+2. Secciones de nuestro nuevo plugin
+------------------------------------
+Ya tenemos una plantilla de lo que será nuestro futuro plugin, pero antes de empezar a programar, es necesario comprender el uso que tendrá cada sección del código.::
+
+        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
+
+Ambos métodos se ejecutarán en el inicio del plugin, pero la primera se ejecutará cuando los plugins aún se están importando, y la segunda cuando todos los plugins ya se han importado. En el uso general, se debe usar la segunda. La primera solo deberá usarse en casos específicos, cuando se vaya a crear algo de lo que haga uso otros plugins. 
+
+::
+    
+        def web_user_options(self, hconn):
+            # Página de configuración del plugin
+            pass
+        def enable_rabbid(self, hconn):
+            # El Nabaztag ha sido activado a través de la página de plugins
+            pass
+        def disable_rabbid(self, hconn):
+            # El Nabaztag fue desactivado en la lista de plugins.
+            pass
+
+Las 3 se encuentran relacionadas con la página de gestión del plugin a través de la web de PyNabaztag. En el caso de la primera, se mostrará la página de configuración y uso del plugin. La segunda cuando el plugin se ha habilitado por el usuario, y la tercera cuando se ha deshabilitado. Es importante remarcar, que en las 3, estaremos trabajando destinados a 1 conejo. Esto quiere decir, que mientras que en los métodos de init estábamos haciendo cosas destinadas a todos los Nabaztag que usen el plugin, en estas 3 lo haremos con 1 conejo en específico.
+
+3. Habilitar el plugin, configuración inicial y traducción
+----------------------------------------------------------
+En nuestro plugin, establecíamos que por defecto, el texto a decir por Nabaztag cuando recibía un evento, debía ser "Hola mundo". Esto texto lo estableceremos como una configuración que estableceremos justo al habilitarse el plugin en la lista de plugins en la página de configuración. Ahora, surje el siguiente problema, ¿cómo almacenar configuraciones? PyNabaztag trae una solución para ello, que consiste en un diccionario individual por cada plugin, accesible mediante:::
+    
+    self.cfg[key]
+
+Y aquí es donde entra en juego *DEFAULT_CFG*, que será el contenido por defecto de dicho diccionario al importarse el plugin por primera vez. Guardaremos las configuraciones por cada conejo de manera que sean accesibles de la siguiente forma:::
+    
+    self.cfg['rabbids'][<id del conejo>]
+
+Para que "rabbids" sea un diccionario que almacene configuraciones de los conejos, descomentaremos "rabbids" en *DEFAULT_CFG*::
+
+    # Diccionario con configuraciones por defecto
+    DEFAULT_CFG = {
+        'rabbids': {}, # configuraciones en cada conejo.
+    }
+
+.. WARNING::
+   Tras haberse modificado el DEFAULT_CFG, debe tenerse en cuenta que si el plugin ya fue iniciado por primera vez, no se crearán las nuevas cosas que hayan añadido al DEFAULT_CFG. Deberemos borrar la configuración ya creada para que se vuelva a crear, con las nuevas cosas por defecto que se hayan añadido. El archivo de configuración se encontrará en *~/.config/pynabaztag/plugins/saythis.json*.
+
+Ya tenemos una base para lo que serán nuestras configuraciones para todos los conejos que usen el plugin. Ahora, la pregunta es, *¿cuál es esa "id del conejo" y cómo se consigue?* En el argumentos "hconn", tenemos un método llamado **hconn.rabbid**, el cual es un objeto Rabbid con varias cosas interesantes para trabajar con el Nabaztag en cuestión (aunque esto será otra historia... :) ). El problema, es que el objeto Rabbid no puede ser la clave del diccionario. Pero no debemos preocuparnos, porque el objeto Rabbid (cada objeto Rabbid es específico de un Nabaztag) nos ofrecerá un número de identificación mediante::
+    
+    str(hconn.rabbid)
+
+Dicha identificación será el número de dirección MAC del Nabaztag, el cual se puede consultar en la base. Cada MAC es única y específica para un Nabaztag.
+
+.. TIP::
+   **Rabbid** es la unión de los nombres **Rabbit + IDentification**. Es decir, "Identificación del Conejo".
+
+El siguiente código sería una solución al problema planteado::
+    
+    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".
+        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()
+
+Como se puede leer en el código fuente, tras hacer cambios ne la configuración, es necesario hacer al final lo siguiente para que se guarde en el archivo de configuración::
+    
+    self.cfg.write()
+
+Algo que no se ha planteado como un requisito, pero que sería recomendable, es permitir la localización (traducción a otros idiomas) de nuestro plugin. De esta manera, el texto por defecto se encontrará en su propio idioma. Para habilitar la localización, descomentaremos lo siguiente::
+    
+    import gettext # Locales
+    [...]
+    # Descomentar la siguiente línea para las locales
+    _ = gettext.gettext # Traducir texto mediante _("Texto")
+
+Y modificaremos la línea donde se establece el texto por defecto como se muestra aquí::
+    
+    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()
+
+4. Formulario de configuración y uso
+------------------------------------
+Uno de los puntos más notables de PyNabaztag es, su framework para creación de formularios de configuración. En sólo unas pocas líneas, es posible crear un formulario que recoja unos datos, los verifique en el lado del cliente, los valide en el lado del servidor y se guarden en la configuración. Los formularios se componen de 3 partes fundamentes: Una primera donde se crea el formulario, una segunda donde se definen los campos, y una tercera donde se hará lo necesario tras tener los datos dados por el usuario. ::
+
+    def web_user_options(self, hconn):
+        # 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í ...
+        if form.no_data(): return form
+        # ... A partir de aquí, se poseen los valores devueltos por el usuario. ...
+
+Recordemos las configuraciones que necesitaremos poder parametrar:
+# El texto a decir por el conejo
+# Los eventos que activarán la función "decir el texto". 
+
+Para ambas cosas, hay campos en el formulario que nos posibilitarán hacer esto muy fácilmente. ::
+    
+    # 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)
+        
+.. NOTE::
+   Al usar los formularios no debe preocuparse de la localización. Aquellos argumentos que se considere que son de ayuda y visibles al usuario, se traducirán.
+   
+Antes que nada, avisar de que hemos definido una función que se ejecutará al recibirse el evento llamada *self.say_text*, como se puede ver en el código.
+
+En este ejemplo, solo hemos usado una pequeña parte de las posibilidades que nos ofrecen los campos del Framework para Formularios de PyNabaztag. Hay más posibilidades que se irán ampliando, para facilitar la programación. Llegados a este punto, pueden surjir ciertas dudas: *¿qué pasa si el usuario no rellena el campo? ¿Se mostrará en el campo el valor que se colocó la última vez?*  A la primera pregunta, es necesario saber que el campo text obligará a rellenar el campo. Si no se rellena, saltará un aviso. Para desactivar esto, añada el argumento *req=False*. A la segunda pregunta, el campo pondrá el texto rellenado por última vez, sacándolo de rabbid_cfg. Esto se puede impedir con el argumento *default='texto a poner'*.
+
+PyNabaztag se encargará, como ya hemos dicho, de guardar las configuraciones en el diccionario de configuración den conejo. Lo único que tendremos que hacer será añadir al final, en la tercera sección del formulario, que se graben los datos aportados en el archivo de configuración. ::
+    
+    # ... A partir de aquí, se poseen los valores devueltos por el usuario. ...
+    self.cfg.write()
+
+El resultado final sería el siguiente::
+    
+    def web_user_options(self, hconn):
+        # 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()

_build/html/_static/translations.js

+Documentation.addTranslations({"locale": "es", "plural_expr": "(n != 1)", "messages": {"Search Results": "Resultados de la b\u00fasqueda", "Preparing search...": "Preparando la b\u00fasqueda", "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories.": "La b\u00fasqueda no dio ning\u00fan resultado.  Por favor aseg\u00farese que escribi\u00f3 todas las palabras correctamente y que ha seleccionado suficientes categor\u00edas", "Search finished, found %s page(s) matching the search query.": "B\u00fasqueda finalizada, se han encontrado %s p\u00e1gina(s) que concuerdan con su consulta", ", in ": "", "Expand sidebar": "", "Permalink to this headline": "Enlazar permanentemente con este t\u00edtulo", "Searching": "Buscando", "Collapse sidebar": "", "Permalink to this definition": "Enlazar permanentemente con esta definici\u00f3n", "Hide Search Matches": "Coincidencias de la b\u00fasqueda"}});

_build/html/index.html

   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     
-    <title>Welcome to PyNabaztag’s documentation! &mdash; PyNabaztag v2.7 documentation</title>
+    <title>Bienvenido a la documentación de PyNabaztag! &mdash; PyNabaztag v2.7 documentation</title>
     <link rel="stylesheet" href="_static/default.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
     <script type="text/javascript">
         <div class="bodywrapper">
           <div class="body">
             
-  <div class="section" id="welcome-to-pynabaztag-s-documentation">
-<h1>Welcome to PyNabaztag&#8217;s documentation!<a class="headerlink" href="#welcome-to-pynabaztag-s-documentation" title="Enlazar permanentemente con este título">¶</a></h1>
+  <div class="section" id="bienvenido-a-la-documentacion-de-pynabaztag">
+<h1>Bienvenido a la documentación de PyNabaztag!<a class="headerlink" href="#bienvenido-a-la-documentacion-de-pynabaztag" title="Enlazar permanentemente con este título">¶</a></h1>
 <p>Contents:</p>
 <div class="toctree-wrapper compound">
 <ul>
 </li>
 <li class="toctree-l1"><a class="reference internal" href="tutorial.html">Tutorial: Creación de un plugin</a><ul>
 <li class="toctree-l2"><a class="reference internal" href="tutorial.html#crear-un-plugin-nuevo">1. Crear un plugin nuevo</a></li>
+<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>
 </ul>
 </li>
 </ul>
 </div>
 </div>
-<div class="section" id="indices-and-tables">
-<h1>Indices and tables<a class="headerlink" href="#indices-and-tables" title="Enlazar permanentemente con este título">¶</a></h1>
+<div class="section" id="indices-y-tablas">
+<h1>Índices y tablas<a class="headerlink" href="#indices-y-tablas" title="Enlazar permanentemente con este título">¶</a></h1>
 <ul class="simple">
 <li><a class="reference internal" href="genindex.html"><em>Índice</em></a></li>
 <li><a class="reference internal" href="py-modindex.html"><em>Índice de Módulos</em></a></li>
         <div class="sphinxsidebarwrapper">
   <h3><a href="#">Contenidos</a></h3>
   <ul>
-<li><a class="reference internal" href="#">Welcome to PyNabaztag&#8217;s documentation!</a><ul>
+<li><a class="reference internal" href="#">Bienvenido a la documentación de PyNabaztag!</a><ul>
 </ul>
 </li>
-<li><a class="reference internal" href="#indices-and-tables">Indices and tables</a></li>
+<li><a class="reference internal" href="#indices-y-tablas">Índices y tablas</a></li>
 </ul>
 
   <h4>Próximo tema</h4>

_build/html/intro.html

     <script type="text/javascript" src="_static/translations.js"></script>
     <link rel="top" title="PyNabaztag v2.7 documentation" href="index.html" />
     <link rel="next" title="Tutorial: Creación de un plugin" href="tutorial.html" />
-    <link rel="prev" title="Welcome to PyNabaztag’s documentation!" href="index.html" /> 
+    <link rel="prev" title="Bienvenido a la documentación de PyNabaztag!" href="index.html" /> 
   </head>
   <body>
     <div class="related">
           <a href="tutorial.html" title="Tutorial: Creación de un plugin"
              accesskey="N">siguiente</a> |</li>
         <li class="right" >
-          <a href="index.html" title="Welcome to PyNabaztag’s documentation!"
+          <a href="index.html" title="Bienvenido a la documentación de PyNabaztag!"
              accesskey="P">anterior</a> |</li>
         <li><a href="index.html">PyNabaztag v2.7 documentation</a> &raquo;</li> 
       </ul>
 
   <h4>Tema anterior</h4>
   <p class="topless"><a href="index.html"
-                        title="Capítulo anterior">Welcome to PyNabaztag&#8217;s documentation!</a></p>
+                        title="Capítulo anterior">Bienvenido a la documentación de PyNabaztag!</a></p>
   <h4>Próximo tema</h4>
   <p class="topless"><a href="tutorial.html"
                         title="Próximo capítulo">Tutorial: Creación de un plugin</a></p>
           <a href="tutorial.html" title="Tutorial: Creación de un plugin"
              >siguiente</a> |</li>
         <li class="right" >
-          <a href="index.html" title="Welcome to PyNabaztag’s documentation!"
+          <a href="index.html" title="Bienvenido a la documentación de PyNabaztag!"
              >anterior</a> |</li>
         <li><a href="index.html">PyNabaztag v2.7 documentation</a> &raquo;</li> 
       </ul>

_build/html/searchindex.js

-Search.setIndex({objects:{},terms:{bidireccion:1,pynabaztag:[0,1,2],code:2,protocol:1,ejecutado:2,letra:2,importars:2,xmpp:1,borrando:2,indispens:1,"caracter\u00edstica":2,momento:2,diccionario:2,hconn:2,plantilla:2,nombr:2,mayor:2,"ofrecer\u00e1":1,otro:2,habla:1,"funci\u00f3n":1,lista:2,activ:2,movimiento:1,sonido:1,"deber\u00e1":2,peticion:1,decir:1,"acr\u00f3nimo":1,"pulsaci\u00f3n":1,puerto:1,main:1,"f\u00e1cilment":2,ponga:2,relacion:1,relacionar:1,"return":[],"introducci\u00f3n":[0,1],trabajemo:2,python:1,por:[1,2],identificado:1,"est\u00e9":1,cumpla:2,"expondr\u00e1":2,ejecut:1,"enviar\u00e1":1,"qu\u00e9":[0,1],realiza:1,"as\u00ed":1,"aqu\u00ed":2,conejo:[1,2],foo:[],bar:[],tra:[1,2],mui:1,comunicars:1,server:1,xmppmod:1,"todav\u00eda":2,"petici\u00f3n":1,sonar:1,pone:2,correctament:1,page:[],"habr\u00e1":2,pegaremo:2,hagan:2,ser:2,cosa:2,"pondr\u00e1":1,"personalizaci\u00f3n":2,ejemplo:[1,2],"canci\u00f3n":1,realli:[],"bot\u00f3n":1,ofrecerl:1,entr:1,funcionamiento:1,saythi:2,http:1,todo:2,"s\u00f3lo":2,"\u00edndice":0,index:[],est:[1,2],comunicacion:1,someth:[],tienen:1,crearemo:2,content:0,biblioteca:2,orden:1,toda:2,"trav\u00e9":[1,2],"import":2,viceversa:1,reproduc:1,method:[],pued:2,llamado:2,solo:2,web_user_opt:2,"b\u00fasqueda":0,pluginnam:2,"mensajer\u00eda":1,primero:2,sido:2,"ejecutar\u00e1":[1,2],comunicar:1,"sintetizaci\u00f3n":1,nuevo:[0,2],extens:1,desactivado:2,base:2,que:[1,2],here:[],como:1,plugin_init:2,path:1,aquella:2,"podr\u00e1":2,desactivada:2,search:[],servir:1,"entregar\u00e1n":1,thread:2,plugin:[0,1,2],quier:1,justo:2,"deber\u00e1n":2,timer:2,incluy:1,"est\u00e1tico":1,tutori:[0,2],archivo:[1,2],determinada:1,permit:[1,2],"realizar\u00e1":1,aquello:2,determinado:1,"env\u00edo":1,"comunicar\u00e1":1,uno:2,obten:1,encuentra:1,"ejecutar\u00e1n":2,"dem\u00e1":2,"expandir\u00e1n":1,defecto:[1,2],modul:[],"excepci\u00f3n":2,cuando:1,texto:2,cada:2,"configuraci\u00f3n":[1,2],sirv:1,"administraci\u00f3n":1,usar:2,"m\u00f3dulo":[0,1],tarea:2,contrario:2,from:2,directorio:1,usa:1,uso:[1,2],activado:2,reestablecido:2,pero:2,messag:1,direccion:1,"usar\u00e1":1,"comunicaci\u00f3n":[1,2],requiera:2,encargado:[],enable_rabbid:2,"function":[],capacidad:[1,2],oreja:1,cual:[1,2],desactiv:2,"solicitar\u00e1":1,presenc:1,post_init:2,habers:2,nuestro:2,completo:1,part:1,sirva:2,packet:[1,2],"true":[],descargar:1,hola:2,"instant\u00e1nea":1,desea:2,none:[],utf:2,"identificar\u00e1":1,"hablar\u00e1":2,caso:[1,2],default_cfg:2,afectar:2,aunqu:1,my_fn:[],encontrar:2,fue:2,del:[1,2],importado:2,error:2,about:[],soporta:1,alguna:2,def:2,crear:[0,2],block:[1,2],"creaci\u00f3n":[0,2],objetivo:2,"conexi\u00f3n":1,stream:1,funcionalidad:2,esto:1,share:1,"c\u00f3mo":2,mundo:2,solicitar:1,"funcionar\u00e1":2,amba:1,rabbid:2,want:[],tabl:0,tal:1,httpmod:1,provocars:2,"activar\u00e1":2,voz:1,presencia:2,binario:1,recibir:2,configurado:2,para:[1,2],una:[1,2],"despu\u00e9":1,titl:[],nabaztag:[1,2],grand:1,"may\u00fascula":2,event:2,servidor:[1,2],pass:2,ruta:1,cambio:1,instanci:2,"podr\u00eda":2,indic:0,sevidor:1,dond:2,document:0,"llamar\u00e1":2,sin:2,disable_rabbid:2,tiempo:2,fuent:1,ello:2,modularidad:2,siempr:1,evento:[1,2],"a\u00f1adida":2,"deber\u00eda":2,clase:2,tien:1,mediant:1,"\u00fasese":2,siguient:[1,2],mismo:2,dato:2,"modificaci\u00f3n":2,configuracion:2,sistema:1,cambiado:2,messageblock:2,"c\u00f3digo":[1,2],"class":2,welcom:0,hacer:1,haga:1,audio:1,"servir\u00e1n":1,poder:2,"a\u00fan":1,usuario:2,url:1,funcion:1,"organizaci\u00f3n":[0,1],"direcci\u00f3n":1,dicho:2,primera:[1,2],client:1,"p\u00e1gina":[0,1,2],relacionado:1,"ser\u00e1":2,push:1,"guiar\u00e1":2,protocolo:1,con:[1,2],self:2,talk:[],estrictament:1},objtypes:{},titles:["Welcome to PyNabaztag&#8217;s documentation!","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,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"]})

_build/html/tutorial.html

 <p>En el siguiente tutorial se expondrá cómo crear un plugin que cumpla las siguientes características:</p>
 <ol class="arabic simple">
 <li>El plugin sólo se activará y funcionará si el usuario lo desea.</li>
-<li>Nabaztag hablará al recibir un evento.</li>
+<li>Nabaztag hablará al recibir un evento (pulsación de botón, objeto RFID...).</li>
 <li>Dicho evento deberá poder ser configurado por el usuario.</li>
 <li>El evento podrá ser reestablecido.</li>
 <li>Habrá un texto por defecto que será dicho, el cual será &#8220;Hola Mundo&#8221;. Dicho texto podrá ser cambiado por el usuario, sin afectar a los demás usuarios del servidor.</li>
 </ol>
 <div class="section" id="crear-un-plugin-nuevo">
 <h2>1. Crear un plugin nuevo<a class="headerlink" href="#crear-un-plugin-nuevo" title="Enlazar permanentemente con este título">¶</a></h2>
-<p>Lo primero de todo, crearemos un archivo en <em>pynabaztag/plugins/</em> llamado <em>saythis.py</em>, y pegaremos la siguiente plantilla que nos guiará en la creación de nuestro plugin.</p>
+<p>Lo primero de todo, crearemos un archivo en el directorio <strong>pynabaztag/plugins/</strong> llamado <strong>saythis.py</strong>, y pegaremos la siguiente plantilla que nos guiará en la creación de nuestro plugin.</p>
 <div class="highlight-python"><div class="highlight"><pre><span class="c"># -*- coding: utf-8 -*-</span>
 <span class="c"># from threading import Timer ## Tareas que se ejecutarán en X tiempo.</span>
+<span class="c"># import gettext ## Locales</span>
 <span class="c"># from .. blocks import Packet, MessageBlock ## Comunicación S-&gt;C.</span>
 <span class="kn">from</span> <span class="nn">.</span> <span class="kn">import</span> <span class="n">base</span>
 
     <span class="c"># &#39;rabbids&#39;: {}, ## configuraciones en cada conejo.</span>
 <span class="p">}</span>
 
+<span class="c"># Descomentar la siguiente línea para las locales</span>
+<span class="c"># _ = gettext.gettext ## Traducir texto mediante _(&quot;Texto&quot;)</span>
+
 <span class="k">class</span> <span class="nc">Pluginname</span><span class="p">(</span><span class="n">base</span><span class="o">.</span><span class="n">Base</span><span class="p">):</span>
     <span class="k">def</span> <span class="nf">plugin_init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
         <span class="c"># Se ejecutará justo en el momento de importarse el plugin. Úsese</span>
         <span class="k">pass</span>
 </pre></div>
 </div>
+<p>Finalizado esto, haremos que en la próxima ejecución de PyNabaztag el plugin que acabamos de crear se importe. Para ello modificaremos el archivo <strong>pynabaztag/plugins/__init__.py</strong> añadiendo el nombre del fichero del plugin::</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="n">__all__</span> <span class="o">=</span> <span class="p">[</span><span class="s">&#39;events&#39;</span><span class="p">,</span> <span class="s">&#39;taichi&#39;</span><span class="p">,</span> <span class="p">(</span><span class="o">...</span><span class="p">),</span> <span class="s">&#39;saythis&#39;</span><span class="p">]</span>
+</pre></div>
+</div>
+<div class="admonition note">
+<p class="first admonition-title">Nota</p>
+<p class="last">En futuras versiones de PyNabaztag, el anterior paso puede no ser necesario, siendo sustituido por una página de administración para gestionar los plugins activos.</p>
+</div>
+</div>
+<div class="section" id="secciones-de-nuestro-nuevo-plugin">
+<h2>2. Secciones de nuestro nuevo plugin<a class="headerlink" href="#secciones-de-nuestro-nuevo-plugin" title="Enlazar permanentemente con este título">¶</a></h2>
+<p>Ya tenemos una plantilla de lo que será nuestro futuro plugin, pero antes de empezar a programar, es necesario comprender el uso que tendrá cada sección del código.:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">plugin_init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+    <span class="c"># Se ejecutará justo en el momento de importarse el plugin. Úsese</span>
+    <span class="c"># solo para aquellos casos en que este plugin sirva de biblioteca</span>
+    <span class="c"># para otros plugins.</span>
+    <span class="k">pass</span>
+<span class="k">def</span> <span class="nf">post_init</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
+    <span class="c"># Se ejecutará tras haberse ejecutado todos los demás plugins. Si</span>
+    <span class="c"># su plugin va a usar otro plugin de biblioteca (por ejemplo, el</span>
+    <span class="c"># plugin events), ponga aquí aquellas cosas que le hagan uso. De</span>
+    <span class="c"># lo contrario puede provocarse una excepción, ya que si se pone</span>
+    <span class="c"># en plugin_init, el plugin events podría no haberse importado</span>
+    <span class="c"># todavía.</span>
+    <span class="k">pass</span>
+</pre></div>
+</div>
+<p>Ambos métodos se ejecutarán en el inicio del plugin, pero la primera se ejecutará cuando los plugins aún se están importando, y la segunda cuando todos los plugins ya se han importado. En el uso general, se debe usar la segunda. La primera solo deberá usarse en casos específicos, cuando se vaya a crear algo de lo que haga uso otros plugins.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">web_user_options</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"># Página de configuración del plugin</span>
+    <span class="k">pass</span>
+<span class="k">def</span> <span class="nf">enable_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 ha sido activado a través de la página de plugins</span>
+    <span class="k">pass</span>
+<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="k">pass</span>
+</pre></div>
+</div>
+<p>Las 3 se encuentran relacionadas con la página de gestión del plugin a través de la web de PyNabaztag. En el caso de la primera, se mostrará la página de configuración y uso del plugin. La segunda cuando el plugin se ha habilitado por el usuario, y la tercera cuando se ha deshabilitado. Es importante remarcar, que en las 3, estaremos trabajando destinados a 1 conejo. Esto quiere decir, que mientras que en los métodos de init estábamos haciendo cosas destinadas a todos los Nabaztag que usen el plugin, en estas 3 lo haremos con 1 conejo en específico.</p>
+</div>
+<div class="section" id="habilitar-el-plugin-configuracion-inicial-y-traduccion">
+<h2>3. Habilitar el plugin, configuración inicial y traducción<a class="headerlink" href="#habilitar-el-plugin-configuracion-inicial-y-traduccion" title="Enlazar permanentemente con este título">¶</a></h2>
+<p>En nuestro plugin, establecíamos que por defecto, el texto a decir por Nabaztag cuando recibía un evento, debía ser &#8220;Hola mundo&#8221;. Esto texto lo estableceremos como una configuración que estableceremos justo al habilitarse el plugin en la lista de plugins en la página de configuración. Ahora, surje el siguiente problema, ¿cómo almacenar configuraciones? PyNabaztag trae una solución para ello, que consiste en un diccionario individual por cada plugin, accesible mediante::</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
+</pre></div>
+</div>
+<p>Y aquí es donde entra en juego <em>DEFAULT_CFG</em>, que será el contenido por defecto de dicho diccionario al importarse el plugin por primera vez. Guardaremos las configuraciones por cada conejo de manera que sean accesibles de la siguiente forma::</p>
+<div class="highlight-python"><pre>self.cfg['rabbids'][&lt;id del conejo&gt;]</pre>
+</div>
+<p>Para que &#8220;rabbids&#8221; sea un diccionario que almacene configuraciones de los conejos, descomentaremos &#8220;rabbids&#8221; en <em>DEFAULT_CFG</em>:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># Diccionario con configuraciones por defecto</span>
+<span class="n">DEFAULT_CFG</span> <span class="o">=</span> <span class="p">{</span>
+    <span class="s">&#39;rabbids&#39;</span><span class="p">:</span> <span class="p">{},</span> <span class="c"># configuraciones en cada conejo.</span>
+<span class="p">}</span>
+</pre></div>
+</div>
+<div class="admonition warning">
+<p class="first admonition-title">Advertencia</p>
+<p class="last">Tras haberse modificado el DEFAULT_CFG, debe tenerse en cuenta que si el plugin ya fue iniciado por primera vez, no se crearán las nuevas cosas que hayan añadido al DEFAULT_CFG. Deberemos borrar la configuración ya creada para que se vuelva a crear, con las nuevas cosas por defecto que se hayan añadido. El archivo de configuración se encontrará en <em>~/.config/pynabaztag/plugins/saythis.json</em>.</p>
+</div>
+<p>Ya tenemos una base para lo que serán nuestras configuraciones para todos los conejos que usen el plugin. Ahora, la pregunta es, <em>¿cuál es esa &#8220;id del conejo&#8221; y cómo se consigue?</em> En el argumentos &#8220;hconn&#8221;, tenemos un método llamado <strong>hconn.rabbid</strong>, el cual es un objeto Rabbid con varias cosas interesantes para trabajar con el Nabaztag en cuestión (aunque esto será otra historia... :) ). El problema, es que el objeto Rabbid no puede ser la clave del diccionario. Pero no debemos preocuparnos, porque el objeto Rabbid (cada objeto Rabbid es específico de un Nabaztag) nos ofrecerá un número de identificación mediante:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="nb">str</span><span class="p">(</span><span class="n">hconn</span><span class="o">.</span><span class="n">rabbid</span><span class="p">)</span>
+</pre></div>
+</div>
+<p>Dicha identificación será el número de dirección MAC del Nabaztag, el cual se puede consultar en la base. Cada MAC es única y específica para un Nabaztag.</p>
+<div class="admonition tip">
+<p class="first admonition-title">Truco</p>
+<p class="last"><strong>Rabbid</strong> es la unión de los nombres <strong>Rabbit + IDentification</strong>. Es decir, &#8220;Identificación del Conejo&#8221;.</p>
+</div>
+<p>El siguiente código sería una solución al problema planteado:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">enable_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="sd">&quot;&quot;&quot; \</span>
+<span class="sd">    Crear la configuración inicial para el Nabaztag al activar el plugin.</span>
+<span class="sd">    &quot;&quot;&quot;</span>
+    <span class="c"># Creamos un diccionario como valor a la clave del conejo,</span>
+    <span class="c"># que almacene las configuraciones para el conejo</span>
+    <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="p">{}</span>
+    <span class="c"># Texto a decir por defecto en el nombre de clave &quot;to_say&quot;.</span>
+    <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="nb">str</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="s">&#39;to_say&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s">&#39;Hola Mundo&#39;</span>
+    <span class="c"># Lo siguiente guardará la configuración en el archivo</span>
+    <span class="c"># de configuración, para que no se pierda. Sería algo</span>
+    <span class="c"># así como un commit</span>
+    <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>Como se puede leer en el código fuente, tras hacer cambios ne la configuración, es necesario hacer al final lo siguiente para que se guarde en el archivo de configuración:</p>
+<div class="highlight-python"><div class="highlight"><pre><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>Algo que no se ha planteado como un requisito, pero que sería recomendable, es permitir la localización (traducción a otros idiomas) de nuestro plugin. De esta manera, el texto por defecto se encontrará en su propio idioma. Para habilitar la localización, descomentaremos lo siguiente:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">gettext</span> <span class="c"># Locales</span>
+<span class="p">[</span><span class="o">...</span><span class="p">]</span>
+<span class="c"># Descomentar la siguiente línea para las locales</span>
+<span class="n">_</span> <span class="o">=</span> <span class="n">gettext</span><span class="o">.</span><span class="n">gettext</span> <span class="c"># Traducir texto mediante _(&quot;Texto&quot;)</span>
+</pre></div>
+</div>
+<p>Y modificaremos la línea donde se establece el texto por defecto como se muestra aquí:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">enable_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="sd">&quot;&quot;&quot; \</span>
+<span class="sd">    Crear la configuración inicial para el Nabaztag al activar el plugin.</span>
+<span class="sd">    &quot;&quot;&quot;</span>
+    <span class="c"># Creamos un diccionario como valor a la clave del conejo,</span>
+    <span class="c"># que almacene las configuraciones para el conejo</span>
+    <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="nb">str</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="o">=</span> <span class="p">{}</span>
+    <span class="c"># Texto a decir por defecto en el nombre de clave &quot;to_say&quot;.</span>
+    <span class="c"># Será traducido al idioma del usuario con el _(...)</span>
+    <span class="bp">self</span><span class="o">.</span><span class="n">cfg</span><span class="p">[</span><span class="nb">str</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="s">&#39;to_say&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="n">_</span><span class="p">(</span><span class="s">&#39;Hola Mundo&#39;</span><span class="p">)</span>
+    <span class="c"># Lo siguiente guardará la configuración en el archivo</span>
+    <span class="c"># de configuración, para que no se pierda. Sería algo</span>
+    <span class="c"># así como un commit</span>
+    <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>
+</div>
+<div class="section" id="formulario-de-configuracion-y-uso">
+<h2>4. Formulario de configuración y uso<a class="headerlink" href="#formulario-de-configuracion-y-uso" title="Enlazar permanentemente con este título">¶</a></h2>
+<p>Uno de los puntos más notables de PyNabaztag es, su framework para creación de formularios de configuración. En sólo unas pocas líneas, es posible crear un formulario que recoja unos datos, los verifique en el lado del cliente, los valide en el lado del servidor y se guarden en la configuración. Los formularios se componen de 3 partes fundamentes: Una primera donde se crea el formulario, una segunda donde se definen los campos, y una tercera donde se hará lo necesario tras tener los datos dados por el usuario.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">web_user_options</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"># Diccionario con la configuración del conejo</span>
+    <span class="n">rabbid_cfg</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">hconn</span><span class="o">.</span><span class="n">rabbid</span><span class="p">)]</span>
+    <span class="c"># Se crea el objeto formulario. El segundo argumento será un</span>
+    <span class="c"># diccionario sobre el que se guarden los valores devueltos por</span>
+    <span class="c"># el usuario en los campos. El atributo title será el título que</span>
+    <span class="c"># encabece el formulario.</span>
+    <span class="n">form</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">Form</span><span class="p">(</span><span class="n">hconn</span><span class="p">,</span> <span class="n">rabbid_cfg</span><span class="p">,</span> <span class="n">title</span><span class="o">=</span><span class="s">&#39;Texto a decir&#39;</span><span class="p">)</span>
+    <span class="c"># ... Se establecen los campos del formulario aquí ...</span>
+    <span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">no_data</span><span class="p">():</span> <span class="k">return</span> <span class="n">form</span>
+    <span class="c"># ... A partir de aquí, se poseen los valores devueltos por el usuario. ...</span>
+</pre></div>
+</div>
+<p>Recordemos las configuraciones que necesitaremos poder parametrar:
+# El texto a decir por el conejo
+# Los eventos que activarán la función &#8220;decir el texto&#8221;.</p>
+<p>Para ambas cosas, hay campos en el formulario que nos posibilitarán hacer esto muy fácilmente.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># Campo del formulario to_say. El primer argumento es la clave en el</span>
+<span class="c"># diccionario rabbid_cfg. La segunda el texto de ayuda del campo.</span>
+<span class="n">form</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s">&#39;to_say&#39;</span><span class="p">,</span> <span class="s">&#39;Texto a decir&#39;</span><span class="p">)</span>
+<span class="c"># Campo del formulario para seleccionar los eventos. El primer argumento</span>
+<span class="c"># Es la función que se ejecutará al recibirse el evento.</span>
+<span class="n">form</span><span class="o">.</span><span class="n">events</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>
+<div class="admonition note">
+<p class="first admonition-title">Nota</p>
+<p class="last">Al usar los formularios no debe preocuparse de la localización. Aquellos argumentos que se considere que son de ayuda y visibles al usuario, se traducirán.</p>
+</div>
+<p>Antes que nada, avisar de que hemos definido una función que se ejecutará al recibirse el evento llamada <em>self.say_text</em>, como se puede ver en el código.</p>
+<p>En este ejemplo, solo hemos usado una pequeña parte de las posibilidades que nos ofrecen los campos del Framework para Formularios de PyNabaztag. Hay más posibilidades que se irán ampliando, para facilitar la programación. Llegados a este punto, pueden surjir ciertas dudas: <em>¿qué pasa si el usuario no rellena el campo? ¿Se mostrará en el campo el valor que se colocó la última vez?</em>  A la primera pregunta, es necesario saber que el campo text obligará a rellenar el campo. Si no se rellena, saltará un aviso. Para desactivar esto, añada el argumento <em>req=False</em>. A la segunda pregunta, el campo pondrá el texto rellenado por última vez, sacándolo de rabbid_cfg. Esto se puede impedir con el argumento <em>default=&#8217;texto a poner&#8217;</em>.</p>
+<p>PyNabaztag se encargará, como ya hemos dicho, de guardar las configuraciones en el diccionario de configuración den conejo. Lo único que tendremos que hacer será añadir al final, en la tercera sección del formulario, que se graben los datos aportados en el archivo de configuración.</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="c"># ... A partir de aquí, se poseen los valores devueltos por el usuario. ...</span>
+<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>El resultado final sería el siguiente:</p>
+<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">web_user_options</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"># Diccionario con la configuración del conejo</span>
+    <span class="n">rabbid_cfg</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">hconn</span><span class="o">.</span><span class="n">rabbid</span><span class="p">)]</span>
+    <span class="c"># Se crea el objeto formulario. El segundo argumento será un</span>
+    <span class="c"># diccionario sobre el que se guarden los valores devueltos por</span>
+    <span class="c"># el usuario en los campos. El atributo title será el título que</span>
+    <span class="c"># encabece el formulario.</span>
+    <span class="n">form</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">Form</span><span class="p">(</span><span class="n">hconn</span><span class="p">,</span> <span class="n">rabbid_cfg</span><span class="p">,</span> <span class="n">title</span><span class="o">=</span><span class="s">&#39;Texto a decir&#39;</span><span class="p">)</span>
+    <span class="c"># ... Se establecen los campos del formulario aquí ...</span>
+    <span class="c"># Campo del formulario to_say. El primer argumento es la clave en el</span>
+    <span class="c"># diccionario rabbid_cfg. La segunda el texto de ayuda del campo.</span>
+    <span class="n">form</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s">&#39;to_say&#39;</span><span class="p">,</span> <span class="s">&#39;Texto a decir&#39;</span><span class="p">)</span>
+    <span class="c"># Campo del formulario para seleccionar los eventos. El primer argumento</span>
+    <span class="c"># Es la función que se ejecutará al recibirse el evento.</span>
+    <span class="n">form</span><span class="o">.</span><span class="n">events</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">say_text</span><span class="p">)</span>
+    <span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">no_data</span><span class="p">():</span> <span class="k">return</span> <span class="n">form</span>
+    <span class="c"># ... A partir de aquí, se poseen los valores devueltos por el usuario. ...</span>
+    <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>
 </div>
 </div>
 
   <ul>
 <li><a class="reference internal" href="#">Tutorial: Creación de un plugin</a><ul>
 <li><a class="reference internal" href="#crear-un-plugin-nuevo">1. Crear un plugin nuevo</a></li>
+<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>
 </ul>
 </li>
 </ul>
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Welcome to PyNabaztag's documentation!
-======================================
+Bienvenido a la documentación de PyNabaztag!
+============================================
 
 Contents:
 
    intro
    tutorial
 
-Indices and tables
+Índices y tablas
 ==================
 
 * :ref:`genindex`
-===============================
 Tutorial: Creación de un plugin
 ===============================
 Uno de los objetivos de PyNabaztag, es la modularidad. Toda capacidad añadida al servidor, debería de ser fácilmente activable y desactivable sin que ello requiera modificación alguna de código. Ello permite mayor personalización y encontrar más fácilmente errores.
 En el siguiente tutorial se expondrá cómo crear un plugin que cumpla las siguientes características:
 
 #. El plugin sólo se activará y funcionará si el usuario lo desea.
-#. Nabaztag hablará al recibir un evento.
+#. Nabaztag hablará al recibir un evento (pulsación de botón, objeto RFID...).
 #. Dicho evento deberá poder ser configurado por el usuario.
 #. El evento podrá ser reestablecido.
 #. Habrá un texto por defecto que será dicho, el cual será "Hola Mundo". Dicho texto podrá ser cambiado por el usuario, sin afectar a los demás usuarios del servidor.
 #. Las funcionalidades del plugin deberán poder ser desactivadas por el usuario para sí mismo, borrando datos de su presencia.
 #. El plugin se llamará "saythis".
 
-------------------------
 1. Crear un plugin nuevo
 ------------------------
-Lo primero de todo, crearemos un archivo en *pynabaztag/plugins/* llamado *saythis.py*, y pegaremos la siguiente plantilla que nos guiará en la creación de nuestro plugin.
+Lo primero de todo, crearemos un archivo en el directorio **pynabaztag/plugins/** llamado **saythis.py**, y pegaremos la siguiente plantilla que nos guiará en la creación de nuestro plugin.
 
 .. code-block:: python
 
     # -*- 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
 
     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
             # 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
+            pass
+
+Finalizado esto, haremos que en la próxima ejecución de PyNabaztag el plugin que acabamos de crear se importe. Para ello modificaremos el archivo **pynabaztag/plugins/__init__.py** añadiendo el nombre del fichero del plugin:::
+
+    __all__ = ['events', 'taichi', (...), 'saythis']
+    
+.. NOTE::
+   En futuras versiones de PyNabaztag, el anterior paso puede no ser necesario, siendo sustituido por una página de administración para gestionar los plugins activos.
+
+2. Secciones de nuestro nuevo plugin
+------------------------------------
+Ya tenemos una plantilla de lo que será nuestro futuro plugin, pero antes de empezar a programar, es necesario comprender el uso que tendrá cada sección del código.::
+
+        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
+
+Ambos métodos se ejecutarán en el inicio del plugin, pero la primera se ejecutará cuando los plugins aún se están importando, y la segunda cuando todos los plugins ya se han importado. En el uso general, se debe usar la segunda. La primera solo deberá usarse en casos específicos, cuando se vaya a crear algo de lo que haga uso otros plugins. 
+
+::
+    
+        def web_user_options(self, hconn):
+            # Página de configuración del plugin
+            pass
+        def enable_rabbid(self, hconn):
+            # El Nabaztag ha sido activado a través de la página de plugins
+            pass
+        def disable_rabbid(self, hconn):
+            # El Nabaztag fue desactivado en la lista de plugins.
+            pass
+
+Las 3 se encuentran relacionadas con la página de gestión del plugin a través de la web de PyNabaztag. En el caso de la primera, se mostrará la página de configuración y uso del plugin. La segunda cuando el plugin se ha habilitado por el usuario, y la tercera cuando se ha deshabilitado. Es importante remarcar, que en las 3, estaremos trabajando destinados a 1 conejo. Esto quiere decir, que mientras que en los métodos de init estábamos haciendo cosas destinadas a todos los Nabaztag que usen el plugin, en estas 3 lo haremos con 1 conejo en específico.
+
+3. Habilitar el plugin, configuración inicial y traducción
+----------------------------------------------------------
+En nuestro plugin, establecíamos que por defecto, el texto a decir por Nabaztag cuando recibía un evento, debía ser "Hola mundo". Esto texto lo estableceremos como una configuración que estableceremos justo al habilitarse el plugin en la lista de plugins en la página de configuración. Ahora, surje el siguiente problema, ¿cómo almacenar configuraciones? PyNabaztag trae una solución para ello, que consiste en un diccionario individual por cada plugin, accesible mediante:::
+    
+    self.cfg[key]
+
+Y aquí es donde entra en juego *DEFAULT_CFG*, que será el contenido por defecto de dicho diccionario al importarse el plugin por primera vez. Guardaremos las configuraciones por cada conejo de manera que sean accesibles de la siguiente forma:::
+    
+    self.cfg['rabbids'][<id del conejo>]
+
+Para que "rabbids" sea un diccionario que almacene configuraciones de los conejos, descomentaremos "rabbids" en *DEFAULT_CFG*::
+
+    # Diccionario con configuraciones por defecto
+    DEFAULT_CFG = {
+        'rabbids': {}, # configuraciones en cada conejo.
+    }
+
+.. WARNING::
+   Tras haberse modificado el DEFAULT_CFG, debe tenerse en cuenta que si el plugin ya fue iniciado por primera vez, no se crearán las nuevas cosas que hayan añadido al DEFAULT_CFG. Deberemos borrar la configuración ya creada para que se vuelva a crear, con las nuevas cosas por defecto que se hayan añadido. El archivo de configuración se encontrará en *~/.config/pynabaztag/plugins/saythis.json*.
+
+Ya tenemos una base para lo que serán nuestras configuraciones para todos los conejos que usen el plugin. Ahora, la pregunta es, *¿cuál es esa "id del conejo" y cómo se consigue?* En el argumentos "hconn", tenemos un método llamado **hconn.rabbid**, el cual es un objeto Rabbid con varias cosas interesantes para trabajar con el Nabaztag en cuestión (aunque esto será otra historia... :) ). El problema, es que el objeto Rabbid no puede ser la clave del diccionario. Pero no debemos preocuparnos, porque el objeto Rabbid (cada objeto Rabbid es específico de un Nabaztag) nos ofrecerá un número de identificación mediante::
+    
+    str(hconn.rabbid)
+
+Dicha identificación será el número de dirección MAC del Nabaztag, el cual se puede consultar en la base. Cada MAC es única y específica para un Nabaztag.
+
+.. TIP::
+   **Rabbid** es la unión de los nombres **Rabbit + IDentification**. Es decir, "Identificación del Conejo".
+
+El siguiente código sería una solución al problema planteado::
+    
+    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".
+        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()
+
+Como se puede leer en el código fuente, tras hacer cambios ne la configuración, es necesario hacer al final lo siguiente para que se guarde en el archivo de configuración::
+    
+    self.cfg.write()
+
+Algo que no se ha planteado como un requisito, pero que sería recomendable, es permitir la localización (traducción a otros idiomas) de nuestro plugin. De esta manera, el texto por defecto se encontrará en su propio idioma. Para habilitar la localización, descomentaremos lo siguiente::
+    
+    import gettext # Locales
+    [...]
+    # Descomentar la siguiente línea para las locales
+    _ = gettext.gettext # Traducir texto mediante _("Texto")
+
+Y modificaremos la línea donde se establece el texto por defecto como se muestra aquí::
+    
+    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()
+
+4. Formulario de configuración y uso
+------------------------------------
+Uno de los puntos más notables de PyNabaztag es, su framework para creación de formularios de configuración. En sólo unas pocas líneas, es posible crear un formulario que recoja unos datos, los verifique en el lado del cliente, los valide en el lado del servidor y se guarden en la configuración. Los formularios se componen de 3 partes fundamentes: Una primera donde se crea el formulario, una segunda donde se definen los campos, y una tercera donde se hará lo necesario tras tener los datos dados por el usuario. ::
+
+    def web_user_options(self, hconn):
+        # 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í ...
+        if form.no_data(): return form
+        # ... A partir de aquí, se poseen los valores devueltos por el usuario. ...
+
+Recordemos las configuraciones que necesitaremos poder parametrar:
+# El texto a decir por el conejo
+# Los eventos que activarán la función "decir el texto". 
+
+Para ambas cosas, hay campos en el formulario que nos posibilitarán hacer esto muy fácilmente. ::
+    
+    # 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)
+        
+.. NOTE::
+   Al usar los formularios no debe preocuparse de la localización. Aquellos argumentos que se considere que son de ayuda y visibles al usuario, se traducirán.
+   
+Antes que nada, avisar de que hemos definido una función que se ejecutará al recibirse el evento llamada *self.say_text*, como se puede ver en el código.
+
+En este ejemplo, solo hemos usado una pequeña parte de las posibilidades que nos ofrecen los campos del Framework para Formularios de PyNabaztag. Hay más posibilidades que se irán ampliando, para facilitar la programación. Llegados a este punto, pueden surjir ciertas dudas: *¿qué pasa si el usuario no rellena el campo? ¿Se mostrará en el campo el valor que se colocó la última vez?*  A la primera pregunta, es necesario saber que el campo text obligará a rellenar el campo. Si no se rellena, saltará un aviso. Para desactivar esto, añada el argumento *req=False*. A la segunda pregunta, el campo pondrá el texto rellenado por última vez, sacándolo de rabbid_cfg. Esto se puede impedir con el argumento *default='texto a poner'*.
+
+PyNabaztag se encargará, como ya hemos dicho, de guardar las configuraciones en el diccionario de configuración den conejo. Lo único que tendremos que hacer será añadir al final, en la tercera sección del formulario, que se graben los datos aportados en el archivo de configuración. ::
+    
+    # ... A partir de aquí, se poseen los valores devueltos por el usuario. ...
+    self.cfg.write()
+
+El resultado final sería el siguiente::
+    
+    def web_user_options(self, hconn):
+        # 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()
+
+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
+-----------------------------------