El Tiempo

Introducción

Es muy habitual en la actualidad, y lo continuará siendo cada día más, que los sistemas de información trabajen con datos que se almacenan en servidores remotos a los que se accede a través de Internet, lo que se conoce como trabajar con datos en la nube. Estos datos son accesibles a través de peticiones web dirigidas a cierta URL de gestión, lo que permite obtenerlos al instante desde cualquier parte del mundo. El conjunto de peticiones para la gestión de los datos, su sintaxis y su semántica se conoce como una API (Application Programming Interface). Por supuesto, el uso de esta API no debe comprometer la seguridad de los datos ni la de su alojamiento, por lo que será necesario habitualmente un proceso de autenticación y seguridad riguroso para su uso.

En este proyecto se va a realizar una aplicación que trabaje con los datos climáticos que la Agencia Estatal de Meteorología (AEMET) pone a disposición pública, como se explica en la sección OpenData de su sitio web.

En particular, la aplicación accederá a los datos de la AEMET para obtener la lista de estaciones meteorológicas disponibles y entonces permitirá al usuario seleccionar una de ellas, de la que obtendrá los datos climáticos más recientes. Si el usuario lo desea, podrá visualizar una gráfica con la evolución de estos parámetros en las últimas 24 horas. Dado que es necesario autenticarse para utilizar la API del OpenData de la AEMET, la aplicación también permitirá introducir y gestionar la clave de autenticación.

Durante la realización de este proyecto, que conlleva cierta complejidad, se van a trabajar una serie de aspectos de gran importancia para el desarrollo de aplicaciones con MIT App Inventor y en general de cualquier sistema de información actual. A continuación se enumeran los aspectos a estudiar y se comentá acerca de su utilidad:

Aunque no se va a ir desarrollando esta aplicación de forma incremental, sí se van a explicar paso a paso sus aspectos más significativos y los de su contexto. Al final de este documento se proporciona un enlace al código completo de la aplicación.

Paso 1: información previa. La API de OpenData de AEMET, el formato JSON y la extensión para MIT App Inventor

Como se ha descrito en la introducción, la aplicación que se va a desarrollar obtendrá de la AEMET la lista de estaciones meteorológicas disponibles y permitirá al usuario seleccionar una para obtener de ella los datos climáticos más recientes. Antes de comenzar a realizar la aplicación resulta pues necesario estudiar qué recursos ofrece la API de OpenData para lograr estos objetivos. Así pues, lo primero es visitar la descripción de la API siguiendo la documentación de OpenData y el enlace de acceso a desarrolladores, o visitar directamente su descripción. La imagen inferior muestra esta página, donde en cada línea se tiene una categoría de peticiones de la API y al presionar una de ellas, «observacion-convencional» en la imagen, se despliegan todas las peticiones correspondientes.

Descripción de la API OpenData de AEMET

Las peticiones a la API se realizan mediante peticiones HTML tipo GET como aparece indicado. Junto al recuadro que lo sugiere, se muestra la URL a la que hay que enviar la petición; si en ella aparece un elemento entre corchetes {...} quiere decir que en su lugar se envía un parámetro, como veremos posteriormente. Al presionar sobre una de estas peticiones se despliega un cuadro con la explicación detallada de la petición que nos permite, además, probar su funcionamiento.

Para ello, y para poder utilizar la API en nuestra aplicación —y en general, desde cualquier aplicación— es necesario obtener una clave que se solicita desde la propia web, de nuevo en la página de OpenData y siguiendo el enlace de obtención de API Key. Una vez se haya recibido el correo con la clave, esta se podrá copiar y pegar para poder utilizar la prueba en línea de las funciones de la API. Para ello habrá que pulsar sobre el pequeño signo de admiración en un círculo de color rojo y aparecerá el desplegable que se ve en la imagen. Una vez introducida y validada la clave, el círculo pasará a color verde y se podrán realizar las pruebas de las funciones de la API.

Introducción de la clave

En nuestra aplicación vamos a utilizar dos peticiones. Una servirá para solicitar los datos climáticos de la estación seleccionada, y se muestra en la categoría observacion-convencional de la primera imagen. Previamente habrá que obtener la lista de estaciones, lo que se consigue con una petición de la categoría valores-climatologicos cuya prueba da los resultados que aparecen en la imagen inferior. Como se ve, la respuesta a las peticiones de la API, que aparece en el recuadro Response Body consiste en un conjunto de pares etiqueta-valor separados por comas y encerrados entre llaves. Esto responde al formato de datos comúnmente usado en transacciones HTML conocido como JSON. Al interpretar la respuesta según este formato se obtiene un objeto con cuatro componentes. A continuación se indican, para la respuesta de la imagen, estos componentes y sus valores y se describe la función de cada uno de ellos:

prueba

Se ha visto que la API OpenData de AEMET utiliza JSON como formato para los datos devueltos. Como el soporte de MIT App Inventor para este formato es muy reducido, la aplicación va a utilizar la extensión JSONTools.aix que se describe en la web JSONTOOLS EXTENSION. Al final de la página está el enlace de descarga; una vez se haya descargado, se añadirá a MIT App Inventor desde la sección Extension, pulsando el enlace Import extension. Tras importarla, aparecerá en esa sección un nuevo elemento JSONTools que se podrá arrastrar a los diseños para trabajar con datos en formato JSON.

Paso 2: usando el componente Web. Peticiones y respuestas asíncronas

Ya se ha visto cómo solicitar datos mediante la API OpenData y el formato en que devuelve los valores. Las peticiones a esta API y en general todas las peticiones web se realizan en MIT App Inventor con el componente Web de la sección Connectivity. Al arrastralo a un diseño se le dota de la posibilidad de acceso web. Todos los accesos se realizan, de forma natural, en dos fases: una vez se envía una petición a cierta URL su respuesta está fuera del control de la aplicación. El servidor podría no estar en línea o estar saturado; la conexión podría ser lenta o incluso perderse temporalmente... Muchas circunstancias indican que no es correcto detener la ejecución de un programa desde que se envía una petición web hasta que se recibe su respuesta. Por esto, MIT App Inventor asocia al componente Web un conjunto de peticiones (tipo GET, POST, PUT, DELETE) que devuelven el control a la aplicación al enviarse, y dos eventos (GotText y GotFile) que se activan cuando se recibe la respuesta.

Captura del móvil

Con esta metodología, el proceso en una aplicación será componer la URL correspondiente y realizar la petición y, dentro del evento correspondiente, recibir los datos, tratarlos, y desencadenar las acciones de respuesta correspondientes. Según esto, en la aplicación que nos ocupa el flujo del programa será el siguiente:

  1. Inicialmente se compondrá la URL, incluyendo la clave y la petición de la API, para solicitar la lista de estaciones meteorológicas.
  2. Cuando el usuario pulse sobre el botón de comienzo, se enviará la petición GET desde el evento de gestión del botón.
  3. Cuando se reciba la respuesta, dentro del evento GotText, se realizará una nueva petición GET a la URL indicada en el campo datos de la respuesta.
  4. Al recibir esta segunda respuesta, también dentro del evento GotText, se generará la lista de estaciones, separadas por provincias.
  5. Esta lista se presentará al usuario para que elija la estación de la que desea obtener los datos. En esta aplicación se presenta primero una lista de provincias y luego una lista con las estaciones de esa provincia.
  6. Cuando el usuario haya seleccionado la estación, dentro del evento AfterPicking de la lista se compondrá la URL para solicitar los datos y se enviará una nueva petición GET.
  7. Cuando se reciba la respuesta, de nuevo dentro del evento GotText, se realizará la última petición GET a la URL indicada en el campo datos de la respuesta para obtener los datos climáticos.
  8. Al recibir la respuesta con los valores, siempre dentro del evento GotText, se procesarán, se mostrarán por pantalla y se ofrecerá al usuario la posibilidad de representarlos en una gráfica o de realizar otra consulta. En el primer caso, se generará una nueva pantalla con la gráfica, de la que el usuario podrá volver a realizar una nueva consulta. Todas estas decisiones se tratarán en los eventos de los botones correspondientes. En cualquier caso, si el usuario desea realizar una nueva consulta, el flujo de programa volverá al punto 5 pues ya se dispone de la lista de estaciones.
Captura del móvil

Siguiendo la descripción anterior se puede observar que todas las respuestas a las peticiones GET se tratan dentro del mismo evento GotText. ¿Como es posible saber a qué petición responde cada una de las respuestas, si se gestionan con el mismo código? La respuesta se puede inferir de la misma enumeración. Dado que el código sigue una serie de fases o estados —de hecho, esta tipo de implementación se llama máquina de estados— basta con tener una variable global que indique la fase que hay que ejecutar en cada momento. Cada evento completado cambia el valor de esta variable y el código de gestión del evento GotText está estructurado en una serie de if-else if-else if... que permiten tratar las distintas respuestas adecuadamente.

En la discusión anterior no se ha tenido en cuenta la posibilidad de recibir indicadores de error en las respuestas. Al final se comentará la gestión de errores, no exhaustiva, que se ha incluido en la aplicación. Por el momento, vamos a considerar el caso perfecto y a progresar explicando cómo tratar los datos para realizar la representación gráfica.

Paso 3: datos y representación gráfica

En esta aplicación se ha decidido utilizar una petición de la API OpenData de AEMET que devuelve los datos registrados en la estación seleccionada durante las últimas 24 horas, con una entrada por hora. Los datos que se registran son muy diversos e incluso pueden variar de unas a otras estaciones —más bien, no todas las estaciones registran todos los datos posibles— por lo que se ha decidido trabajar solo con aquéllos que se consideran más significativos: temperatura actual, humedad relativa del aire, presión atmosférica y velocidad del viento. El elemento JSONTools que se ha descrito anteriormente permite extraerlos fácilmente de los objetos recibidos en la respuesta.

Captura del móvil

Para mostrar los resultados en la pantalla principal, como se ve más arriba, basta con obtener el último valor para cada uno de los datos —recordemos que hay 24 valores para cada uno de ellos— o indicar de alguna forma que el dato no está disponible. Cuando se quiere realizar una representación gráfica, hay que realizar un proceso algo más complicado para que los trazos correspondientes a cada valor se vean adecuadamente en el espacio de representación.

En la aplicación, la pantalla principal se encarga de calcular los máximos y mínimos y de generar la lista de valores para la representación. También detecta si algún tipo de dato no está disponible y lo indica generando un valor máximo menor que el mínimo. La pantalla de dibujo gráfico realiza todos los cálculos de escala y desplazamientos para representar las gráficas, incluye las marcas temporales de inicio y final de los datos y genera una leyenda con los valores máximos y mínimos para cada magnitud y los colores usados. Cabe indicar que en la gráfica no se representa la velocidad del viento, solo la temperatura actual, la humedad relativa y la presión.

Paso 4: gestión de la clave y comunicación entre pantallas

Como se ha indicado al principio del documento, para acceder a la API de OpenData de AEMET es necesario disponer de una clave que proporciona al público la propia AEMET. Esta clave se ha de añadir como un parámetro a todas las peticiones, por lo que debe estar disponible en el programa para componer las URL de peticiones a la API. En un primer momento se puede pensar en copiar el texto de la clave al inicializar una variable global que se usaría para generar las URL. Con esta solución sencilla —e incorrecta— se tienen dos problemas:

Captura del móvil

Tampoco es correcto que el usuario deba introducir la clave cada vez que quiere usar la aplicación. Si ya la puso en su momento, ¿por qué obligarle a tener que introducirla de nuevo? Esto es más ilógico aún cuando no hay datos sensibles comprometidos por el hecho de tener la clave almacenada, y cuando se trata de una clave tan larga y tan incómoda de manejar. Así pues la decisión tomada en la aplicación consiste en tener una pantalla dedicada para la gestión de la clave. Si al iniciar la aplicación no hay clave almacenada, esta nos dirige automáticamente a la pantalla de introducción de clave. Si ya se tiene una, debe de ser posible gestionarla. Así pues, en el estado inicial de la aplicación, como se puede observar en la primera captura, aparece un botón con un icono de configuración que permite acceder a esta pantalla de gestión de clave, por si se desea cambiar.

Se ha visto cómo gestionar la clave pero no se ha dicho nada acerca de cómo almacenarla. MIT App Inventor proporciona el elemento TinyDB en la sección Storage que permite almacenar valores de la aplicación y también el intercambio de información entre pantallas o almacenar valores generados en una pantalla para su uso posterior, después de haber pasado a otra temporalmente —por ejemplo, la lista de estaciones tras volver de la pantalla de representación gráfica—. Así, cuando la aplicación se inicia consulta su elemento TinyDB y, si no encuentra la clave, salta a la pantalla de gestión de esta. A su vez esta pantalla lee la clave al iniciarse y la escribe al volver a la pantalla principal, con lo que se consigue mantener la clave almacenada y comunicarla entre pantallas. De forma similar, cada vez que la aplicación lee la lista de estaciones meteorológicas guarda los datos en su elemento TinyDB y ya no los vuelve a pedir mediante la API. Cuando se regresa a la pantalla principal desde la gráfica, los datos de estaciones se recuperan.

La forma de trabajar con TinyDB es sencilla. Los datos se agrupan en un Namespace y se identifican por una etiqueta única para cada elemento y que elije libremente el programador. Los datos almacenados con cierta etiqueta en una pantalla, se recuperan usando la misma etiqueta. Se puede almacenar cualquier tipo de datos con los que trabaja MIT App Inventor y estos persisten entre ejecuciones de la aplicación, por lo que es factible almacenar la clave de esta manera

Se ha comentado en un paso anterior que se comunican los datos climáticos, procesados en cierta medida, entre la pantalla principal y la de representación gráfica. No se usa para ello TinyDB sino el otro mecanismo que ofrece MIT App Inventor para la comunicación entre pantallas: el paso de un valor inicial al abrir una nueva pantalla. En este caso, todos los datos a comunicar se incluyen en una lista que es pasada como valor inicial desde la pantalla principal y recuperada en una variable en la de representación gráfica.

Paso 5: detalles finales. Gestión de errores.

Hasta ahora se ha visto el comportamiento de la aplicación en caso de que todo funcione bien. En una aplicación real, sin embargo, pueden darse problemas que generarían comportamientos inesperados en la aplicación en caso de no estar previstos. En este ejemplo se han tenido en cuenta algunos de ellos, y se han dejado otros para ser tratados como posible ampliación. Los casos que están considerados son:

Existen sin embargo un conjunto de circunstancias de error que se conocen y no se han tenido en cuenta. Por ejemplo que una respuesta no se reciba por algún problema en el servidor o en la conexión o que alguno de los datos no esté presente en todas las entradas horarias. Estos errores y otros que no se pueden anticipar comprometerían el buen funcionamiento de la aplicación con comportamientos imprevistos. Esto siempre es un riesgo de todos los programas y más aún de los que utilizan accesos web con respuestas ajenas al control del programa. Una buena aplicación debe intentar poner medios para superar todos los errores, incluso los no anticipados. El proyecto que se ha presentado no es perfecto, pero ofrece un buen conjunto de técnicas para tratar los errores que puede servir de ejemplo para extender en futuras aplicaciones.

Descargar solución.