domingo 20 de mayo de 2007

Usando el API de Google Calendar: Alarmas por SMS

   Hace un tiempo descubrí Google Calendar, y aunque no lo utilizo mucho tiene una característica que me llamó la atención desde el principio. Una vez que defines una cita o evento, puedes asociarle un recordatorio para que te avise por email o con un pop-up si tienes la web abierta; hasta aquí ninguna novedad. Lo realmente novedoso es que también permite avisarte por medio de un SMS al móvil y además, en el caso de España es gratis. Sólo hay que asociar a nuestra cuenta de google nuestro número de teléfono móvil y después de una verificación de identidad podemos añadir el aviso por sms como recordatorio de las citas.

   Teniendo esto en mente pensé que sería realmente útil poder enviar alarmas al móvil con el estado de los servidores de casa. Así, si normalmente tenemos alguna tarea programada en crontab que nos avisa por email cuando termina mal, algún monitor de los servicios, espacio en los filesystems,... podríamos añadir una llamada a un programa adicional que introdujese un evento en nuestro calendario de google para que nos llegara el aviso por medio de un sms al móvil y así estar informados en todo momento del estado de nuestras máquinas :-).

   Pues dicho y hecho, me puse a buscar y lo que encontré no pudo ser mejor. Google proporciona un acceso completo a su API, con ejemplos, foros, y además disponible desde varios lenguajes de programación (Java, .Net, PHP y Python). De todos estos, el que más conozco y con el que más he trabajado (aunque ya haga tiempo que no lo toco demasiado) es Java. Además, tiene la ventaja de que al ser multiplataforma podría monitorizar también máquinas windows.

   Me instalé Eclipse y la máquina virtual de Sun 1.5 (ojo que esto es importante, no funciona con la 1.4.2) por medio de un simple apt-get. De la web de google descargué las librerías cliente para java y me puse a probar alguno de los ejemplos. La verdad es que el código es muy sencillo y está muy bien explicado. Hay ejemplos de autenticación, consulta de los eventos del calendario entre determinadas fechas, alta de eventos, borrado, actualización...

   Después de realizar un par de pruebas tenía un esqueleto que funcionaba aunque había que afinar. Examinando con más detalle las clases con Eclipse y con unas pocas pruebas posteriores terminé la versión 0.1 que permite insertar un evento en nuestro calendario y recibir un aviso por sms en 1 minuto. Aspectos a tener en cuenta del código:
  • Para conseguir que llegue el aviso dentro de un minuto lo que hago es poner la hora de comienzo del evento dentro de 6 minutos y la notificación por sms 5 minutos antes. Aunque por código es posible fijar el recordatorio dentro de 1 minuto, luego, cuando se envía la petición se nos muestra un error diciendo que no es válida.
  • Parece que la clase DateTime de Google no se lleva muy bien con la clase Date. Al principio creé un objeto con la hora actual para asignarselo al comienzo del evento, pero una vez asignado, la fecha que ponía era de dos horas antes!. Lo resolví creando un TimeZone para "Europe/Madrid" y utilizándolo en la creación de dicho objeto. Luego hablando con un compañero y comentándole esas 2 horas de diferencia (yo pensé que debería ser sólo una por eso de que estamos en zona GMT+1), me dijo que, con el cambio a horario de verano hemos pasado "automáticamente" a GMT+2. En fin, misterio aclarado. Gracias Jose!.
  • El programa recibe como parámetros el usuario, el password y el texto que queremos enviar. Aunque a priori no hay limitación en la longitud del texto, al móvil sólo nos llegarán los primeros 57 caracteres.
  • Después de construir la petición y enviarla, por la consola aparece un error. Según he investigado parece como si la versión que utilizo de las librerías de google fuera antigua, aunque realmente estoy trabajando con la última. En principio no afecta al funcionamiento, aunque queda un poco "feo".
  • Adicionalmente, es necesario descargar las librerías mail.jar del API JavaMail y activation.jar del Framework de Sun JavaBeansActivation.

  •    Y ya, sin más divagaciones, el código:
    /*
    * Programa para el envío de alarmas por SMS al móvil por medio del API Google Calendar
    *
    * Iván López Martín
    * http://lopezivan.blogspot.com
    *
    */

    import java.net.URL;
    import java.util.Calendar;
    import java.util.GregorianCalendar;
    import java.util.TimeZone;

    import com.google.gdata.client.calendar.CalendarService;
    import com.google.gdata.data.DateTime;
    import com.google.gdata.data.Person;
    import com.google.gdata.data.PlainTextConstruct;
    import com.google.gdata.data.extensions.EventEntry;
    import com.google.gdata.data.extensions.Reminder;
    import com.google.gdata.data.extensions.When;


    public class EnviaSMS {

    public static void main(String[] args) {

    // Comprobamos los argumentos necesarios: usuario, password y texto del mensaje
    if (args.length < 3) {
    System.err.println("Uso: EnviaSMS <usuario> <password> <texto>");
    return;
    }

    String userName = args[0];
    String userPassword = args[1];
    String sms = "";
    // El máximo son 57 caracteres
    for(int i=2; i<args.length; i++) {
    sms = sms + args[i] + " ";
    }
    sms = sms.trim();

    try {
    URL feedUrl = new URL("http://www.google.com/calendar/feeds/" + userName + "/private/full");

    CalendarService myService = new CalendarService("EnviaSMS");

    // Nos autenticamos en google Calendar
    myService.setUserCredentials(userName, userPassword);

    // Creamos el evento
    EventEntry myEntry = new EventEntry();
    myEntry.setTitle(new PlainTextConstruct(sms));

    Person author = new Person(userName, null, userName);
    myEntry.getAuthors().add(author);

    // Definimos la zona horaria
    TimeZone tz = TimeZone.getTimeZone("Europe/Madrid");

    Calendar cal = GregorianCalendar.getInstance();
    cal.add(Calendar.MINUTE, 6);
    DateTime startTime = new DateTime(cal.getTime(), tz); //6m

    cal.add(Calendar.MINUTE, 1);
    DateTime endTime = new DateTime(cal.getTime(), tz); //7m

    // Definimos la hora de comienzo y de fin del evento
    When eventTimes = new When();
    eventTimes.setStartTime(startTime);
    eventTimes.setEndTime(endTime);
    myEntry.addTime(eventTimes);

    // Añadimos el recordatorio sólo como SMS y que avise 5 minutos antes
    Reminder reminder = new Reminder();
    reminder.setMinutes(new Integer(5));
    reminder.setMethod(Reminder.Method.SMS);
    myEntry.getReminder().add(reminder);

    // Enviamos la petición para insertar el evento en el calendario
    EventEntry insertedEntry = (EventEntry)myService.insert(feedUrl, myEntry);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

       Para la ejecución desde la línea de comandos, he creado un pequeño script:
    ivan@doraemon:~$ cat enviaSMS.sh 
    #!/bin/sh
    #################################
    # Iván López Martín #
    # http://lopezivan.blogspot.com #
    #################################
    SMS_HOME=/home/ivan/workspace/GoogleCalendar

    CLASSPATH=$SMS_HOME/lib/activation.jar:$SMS_HOME/lib/gdata-calendar-1.0.jar:$SMS_HOME/lib/gdata-client-1.0.jar:$SMS_HOME/lib/mail.jar:$SMS_HOME/bin

    java -classpath $CLASSPATH EnviaSMS $@

    ivan@doraemon:~$ ./enviaSMS.sh usuario@gmail.com password "Visita el blog de Iván López. http://lopezivan.blogspot.com"

       En nuestro calendario de google aparece el nuevo evento con las opciones que hemos indicado:

       Y un minuto después, en el móvil el sms:


       Como ya he dicho, se trata de la versión 0.1, aunque me parece que no la voy a modificar mucho más porque lo único que necesito es el envío de los sms. No voy a borrar los eventos del calendario, ni actulizarlos, ni consultarlos desde el programa,...

       Cualquiera es libre de modificar el código, usarlo y mejorarlo siempre que por favor respete el comentario del autor y el enlace a este blog. Además, si lo mejoras por favor házmelo saber para poder actulizar este artículo.

       Que lo disfrutéis...

    36 comments:

    Super Coco dijo...

    Me ha parecido muy interesante y de verdad muy, muy útil.

    Cuando he leído la entrada he pensado : Esto debería de salir en Menéame. He ido y he visto que ya estaba enviado pero que algunos votos negativos lo habían bloqueado. Una auténtica lástima, porque es una idea buena de verdad.

    De todas formas, si se popularizara seguro que pronto lo quitaban, no sé qué será mejor ;-)

    Iván dijo...

    Gracias, me alegro de que te guste y te resulte útil. En un próximo artículo pondré el uso que le estoy dando para la monitorización del servidor.
    A parte de enviarlo a Meneame, también lo envié a Barrapunto, pero parece ser que no terminó de cuajar. Yo también pienso que es muy útil (no porque lo haya hecho yo), porque puedes saber en cualquier momento cómo están tus servidores. En fin, como dicen por ahí "Barrapunto ya no es lo que era" ;-).

    Saludos, Iván.

    José A. (Pepe) dijo...

    Genial, muy bueno, gracias...

    Iván dijo...

    Gracias Pepe, muy escueto pero muy explicativo!. Me alegro de que te guste y de que te sea útil.

    Saludos, Iván.

    Jose Antonio dijo...

    Genial Ivan!!!! simplemente genial!!!

    belay dijo...

    Fenomenal, isto é increible ;-D

    Iván dijo...

    Jose Antonio y Belay, muchas gracias a los dos!. Me alegro de que os haya gustado y de que os pueda resultar útil.

    Saludos, Iván.

    Errante dijo...

    Con tu permiso, me he tomado la libertad de pasarlo a php para poder usarlo desde mas sitios.

    http://elias.badenes.es/2007/07/04/alarmas-sms-gracias-a-google

    Saludos y gracias

    Iván dijo...

    Hola Errante,

    no hay ningún problema. Puse el código para que cualquiera pudiera usarlo y modificarlo a su gusto. Además pedí por favor que se respetaran los créditos y tu lo has hecho. Muchas gracias.

    Saludos, Iván.

    angel miguel dijo...

    me ha parecido muy interesante, me gustaria saber en que version del eclipse y que plugins instalaste para que te funcione estas librerias, si es posible me ayudes como configurar la plataforma de desarrollo java eclipse google, espero que me puedas ayudar porque soy nuevo en esto y trato de implementar un calendario en mi aplicacion pero tengo problemas con lineas de codigo como estas

    import com.google.gdata.client.*;
    import com.google.gdata.client.calendar.*;
    import com.google.gdata.data.*;
    import com.google.gdata.data.acl.*;
    import com.google.gdata.data.calendar.*;
    import com.google.gdata.data.extensions.*;
    import com.google.gdata.util.ServiceException;

    espero me ayudes para poder acabar de implementar mi aplicacion.
    Att: Angel Macas
    mail
    ammacas1@hotmail.com
    ammacas1@yahoo.es

    Iván dijo...

    Hola Angel Miguel,

    te respondo por partes:
    * Versión de Eclipse: Version: 3.2.2
    Build id: M20070212-1330 (Ubuntu version: 3.2.2-0ubuntu3). Como digo en el artículo la instalé con apt-get
    * Plugins: Ninguno adicional.
    * Para que te funcionen las librerías lo único que tienes que hacer es descargarlas y desde eclipse añadirlas al proyecto. Para hacerlo:
    Botón derecho en el proyecto -> Properties. Opción Java Build Path, solapa Libraries y ahí añades las librerías. Yo tengo añadidas: activation.jar, gdata-calendar-1.0.jar, gdata-client-1.0.jar y mail.jar. Con esas 4 librerías ya no deberías tener ningún problema ni de compilación ni en los imports.

    Espero que con esta explicación sea suficiente. Si necesitas algo más no dudes en preguntar.

    Saludos, Iván.

    pedro dijo...

    Muy buenas

    He seguido el articulo y me ha dado problemas a la hora de la compilacion en dos metodos que no se encuentran implementados en las clases.

    Las Lineas son estas:

    reminder.setMethod(Reminder.Method.SMS);
    myEntry.getReminder().add(reminder);

    No reconoce ninguna de las dos

    Gracias

    Iván dijo...

    Hola Pedro,

    el método setMethod está implementado en la clase com.google.gdata.data.extensions.Reminder y el método getReminder está implementado en la clase com.google.gdata.data.extensions.BaseEventEntry. Ambas clases se encuentran en la librería gdata-client-1.0.jar que como has visto en el post debes añadir a tu proyecto.

    Espero que con esto soluciones tu problema.

    Saludos, Iván.

    Pedro dijo...

    Hola Ivan.

    La libreria gdata-client-1.0.jar ya se encuantra incluida en el proyecto y me sigue diciendo lo mismo. No se si será que necesita una mas moderna o hay algo que hacer en l workspace o en el proyect adicional. La liberia la baje de la web de google.

    Saludos

    Iván dijo...

    Hola de nuevo Pedro,

    debes estar haciendo algo mal porque acabo de probar con la última versión de las librerías de google y compila sin problemas. Además, sin esa librería tendrías muchos más errores de compilación. Has añadido el resto de librerías: activation.jar, gdata-calendar-1.0.jar y mail.jar?

    En la configuración del proyecto y del workspace del eclipse no he hecho nada adicional que no sea incluir las librerías y utilizar la versión 1.5 del jdk.

    Saludos, Iván.

    pedro dijo...

    Hola Ivan ...

    Esas tres librerias si que estan incluidas, y de igual manera estoy usando la version 1.5 de java ...

    No se me ocurre que puede fallar ...

    Me puedes indicar el link de donde la bajaste?

    Gracias

    Iván dijo...

    Siento no poder ayudarte más, los links de los que descargué todas las librerías están en el post original. Ahí puedes encontrarlos todos, tanto las de google como las 2 adicionales de la páginas de Sun.

    Saludos, Iván.

    pedro dijo...

    Tambien tengo cargado el gdata-client-1.0.jar. Ese tb es necesario, no?

    Gracias.

    Iván dijo...

    Sí, esa librería también es necesaria.

    Saludos, Iván.

    pedro dijo...

    OKS, ya he conseguido solucionar eso ...

    Ahora tengo oro problema ... jejejejeje

    cuando intento ejecutarlo con el script, me dice esto:

    Exception in thread "main"
    java.lang.NoClassDefFoundError: java

    Alguna idea?

    Gracias

    Iván dijo...

    Hola Pedro,

    según el script que puse tienes que definir el path correcto en la variable SMS_HOME y dentro de ese path tiene que existir el directorio lib con los .jar y el bin con el .class. Comprueba que tengas todo bien definido.

    Saludos, Iván.

    bytecoders dijo...

    Lo primero: gracias por el script.
    Me he permitido el lujo de acoplar el código para un sistema de aviso por SMS de nuevo voicemail en Asterisk.

    http://bytecoders.homelinux.com/content/aviso-por-sms-de-nuevo-voicemail.html

    Buen trabajo :)
    Bytecoders

    Iván dijo...

    Hola Bytecoders,

    me alegro de que le hayas dado una utilidad al aviso por SMS. Gracias por compartirlo.

    Saludos, Iván.

    miguelico dijo...

    muy bueno lo de los sms ivan

    solo tengo una pregunta. Si usas varios moviles, por ejemplo uno poersonal y otro de empresa? es posible que la nota llegue a varios moviles?

    espero tu respuesta

    miguel

    Iván dijo...

    Hola Miguelico,

    me alegro de que te haya gustado. Respecto a tu pregunta, la única opción que se me ocurre es que asocies el teléfono de tu empresa a una cuenta que te crees de gmail y utilices mi programa para enviar el mismo aviso 2 veces, cada una con un usuario y password distinto.
    Habría que ver si añadiendo el evento a varias personas a la vez llega a los dos.

    Saludos, Iván.

    Anónimo dijo...

    Sin embargo en la página:

    http://www.google.com/support/calendar/bin/answer.py?answer=37025&query=sms&topic=&type=

    dice que NO ES GRATUITO.
    Léase lo siguiente:

    ¿Es gratuito?
    Sí, Google Calendar es un servicio gratuito. Sin embargo, si decides recibir notificaciones tales como recordatorios de eventos mediante SMS, es posible que se apliquen las tarifas estándar de tu operador móvil por la recepción de mensajes de texto. Tu operador también puede cobrarte por los mensajes SMS salientes. Si tienes alguna pregunta acerca de la tarifa, por favor, ponte en contacto con tu operador para obtener más información.

    Iván dijo...

    Hola Anónimo,

    lo que dice el enlace es que es gratuito porque Google no cobra nada. Lo que sí dice es que puede que tu operador móvil te cobre por recibir sms, pero esto último no ocurre en España (creo que en USA sí).

    Saludos, Iván.

    David Gonzalez dijo...

    Hola
    Felicidades por tu codigo, aunque yo tambien e encotrado problemas con las librerias, para solucionarlo yo tambien añadi el gdata-core-1.0.jar y se soluciono ;)

    Iván dijo...

    Hola David González,

    muchas grcias por las felicitaciones y por tu comentario.

    Saludos, Iván.

    Feliun dijo...

    Hola,

    Ante todo,muchas gracias por el código, es realmente útil. Pero tengo una duda: ¿se puede configurar tu telefono movil programaticamente de alguna forma, o hay que hacerlo "a mano"?Porque, en el caso de que quieras añadir usuarios (por XML, por ejemplo), lo deseable es que ellos simplemente te dieran su telefono movil, y tu internamente gestionaras todo. Es esto posible?
    Muchas gracias!!

    Iván dijo...

    Hola Feliun,

    no estoy seguro de haber comprendido lo que quieres hacer.
    Al principio parace que me preguntas que si puedes desarrollar algún tipo de software para tu móvil. Sin embargo, más adelante lo que parezco deducir es que quieres enviar la notificación por SMS a varios usuarios.
    En este segundo caso, creo que sí se podría hacer aunque habría que modificar el programa. A la hora de crear el evento para insertarlo en el calendario, tendrías que añadir como participantes a todas las personas a las que quieres enviar el SMS. Éstas a su vez deberían configurar su cuenta de Google Calendar para recibir SMS como aviso de eventos.

    Si no es esto lo que preguntabas, por favor aclarámelo y te intento responder.

    Saludos, Iván.

    feliun dijo...

    hola,iván

    Disculpame, me exprese mal. Mi pregunta es otra. Quiero saber si es totalmente necesario configurar "a mano" la cuenta de google calendar para indicar cual es tu movil. Quiero decir, imaginate que yo te digo mi numero de movil, y tu no sabes nada mas de mi. Podrias enviarme un sms? Podrias configurar desde una aplicacion una cuenta con mi numero de movil, o tendriamos que hacerlo a mano? Mi pregunta es si en la API existe algun metodo para poder configurar una cuenta con un movil sin necesidad de hacerlo a mano.
    gracias de nuevo

    Iván dijo...

    Hola Feliun,

    la asociación del número del móvil a la cuenta Google hay que hacerla a mano. De hecho, una vez que introduces el número de teléfono, google te envía un SMS con un código que tienes que poner en la web para verificar tu identidad.
    Así, sin hacer tú nada a mano es imposible que yo tu pudiera enviar un sms. Otra cosa sería lo que te he comentado de añadir el evento contigo como asistente.

    Saludos, Iván.

    Feliun dijo...

    Pues muchas gracias. Es una pena, porque el poder automatizarlo seria un gran avance.

    Un saludo

    juegossex dijo...

    A mi no me ha funcionado, existe algún log donde pueda ver que está ocurriendo, por que no me arroja nigún error

    Muchas gracias por tu trabajo

    Iván dijo...

    Hola juegossex,

    el único log que se muestra es lo que sale por pantalla cuando ejecutas el script. Puedes comprobar en la web de google calendar si el evento se está cargando correctamente en tu calendario. Si es así y no te llega el SMS, el problema es de Google. Si no se carga el evento en el calendario es que puede que haya cambiado algo en la API y tenga que actualizar el programa.

    Saludos, Iván.