Pantallas informativas (anexo 2): No reproducir un archivo

Más cosicas que van saliendo: En este caso se trata de que una pantalla no reproduzca un determinado archivo.

Habíamos preparado el script proyecta.sh para que una determinada pantalla pudiera reproducir un determinado archivo, pero no al revés. De eso se trata ahora, de que todas pantallas reproduzcan un archivo, menos una. Esto, por ejemplo, puede permitir que una pantalla reproduzca un vídeo con sonido mientras que el resto lo hagan en silencio ¿cómo? Pues creando dos archivos: al de audio le diríamos que sólo lo reprodujera la pantalla en la que queremos sonido y subiríamos otro archivo idéntico, sin la opción AAA y con esta opción que vamos a implementar.

La forma de hacerlo no puede ser más simple, casi alcanza la categoría de “chorrada”. Vamos a modificar el script proyecta.sh añadiendo las siguientes líneas, así:

Vamos, que lo que hemos hecho ha sido coger las líneas que ya teníamos para que sólo reprodujera una pantalla y justo debajo colocar estas, cambiando las X por Z y la comparación pues de distinto (!=) a igual (=). No hay más misterio, no obstante va una captura un poco más amplia:

Vemos aquí las dos opciones: Con XXX más el nombre de la pantalla el archivo el archivo se reproduce (recordamos que el if lo que hace es lo contrario: “con XXX si no está el nombre de la pantalla no hagas nada”; y con ZZZ más el nombre de la pantalla no se reproduce (aquí el if hace justamente eso: con ZZZ si está el nombre de la pantalla, no hagas nada)

A partir de ahora tenemos que:

Si un archivo comienza por XXX seguido del nombre de la pantalla, ese archivo sólo se reproduce en esa pantalla.

Si un archivo comienza por ZZZ seguido del nombre de la pantalla, ese archivo es ignorado por dicha pantalla y no se reproduce.

¡Molto facile e divertente!

Pues hala, hasta la próxima ocurrencia 🙂

Pantallas informativas (anexo 1): Caducidad de ficheros

Hay detalles en los que uno no repara en la fase de diseño pero que luego echa en falta cuando tiene la aplicación, o lo que sea, en marcha. Como comentaba al final de la última entrada relativa al proyecto de las pantallitas, aquí echábamos de menos un control sobre la fecha de caducidad de los ficheros. Afortunadamente tenemos múltiples formas de solucionar esto, desde las elegantes: con un formulario, haciendo uso de Apache, PHP, MariaDB, etc; hasta la fácil y churrutera que he elegido: modificando el nombre de archivo.

Modificando el nombre de archivo podemos seguir trabajando como hasta ahora, subimos un archivo con un determinado nombre y el sistema hace el resto. Ahora bien, se trataba de no perder el resto de funcionalidades: archivos que van a una sola pantalla, sonido, etc.

También esto se puede hacer de varias formas, pero por la que me he decantado es añadiendo un punto y la fecha antes de la extensión, quedando más o menos así:

[opción]nombre_archivo[opción].ext >> [opción]nombre_archivo[opción].fecha.ext

Y siempre justo antes de la extensión pongamos o no opciones de especificar pantalla o audio.

La fecha que habremos de poner será la del evento o del último día del mismo, en formato YYYYMMDD (año con cuatro dígitos, mes con dos dígitos y día del mes con dos dígitos), y al día siguiente el fichero será borrado.

Si no se pone fecha no pasa nada, simplemente ese fichero no caducará.

El script es muy sencillo, de hecho no lleva ningún sistema de control ni nada, por lo que si ponemos una fecha mal el fichero no se borrará, pero vamos, esto ya cada cual que lo complique cuanto quiera pero creo que no hace falta más, vamos a tener informes con los nombres de ficheros, los vamos a estar viendo al conectarnos a la carpeta… en fin, que un mínimo de atención por nuestra parte tampoco viene mal.

Este es el código:

Fácil ¿no?

Vamos por partes: Este script lo alojaremos en el servidor y le daremos permisos de ejecución. Programaremos una tarea con crontab para que se ejecute de madrugada, a partir de las 00 La hora en realidad da un poco lo mismo, porque desde que apagamos la pantalla por la noche hasta que se enciende por la mañana, en cualquier momento una vez hayamos cambiado de fecha puede ejecutarse.

En la línea 2 calculamos la fecha que era “ayer” y si coincide con la que tiene el fichero lo borramos. ¿Por qué ayer? Pues porque si el evento se celebró “ayer” o “ayer” era el último día, es obvio que “hoy” no hace falta anunciar nada. La comparación se hace en la línea 8, de forma que si las variables “de texto” coinciden el fichero se borra. Compararlas como si fueran de texto tiene la ventaja de que nos evitamos errores si el fichero no lleva fecha e intentamos comparar un número con una cadena alfanumérica. Por contra, para nuestros fines, nos obliga a usar sólo el “igual que”, ya que de otro modo se podrían borrar ficheros sin caducidad, por eso hay que tomar la fecha de “ayer”.

Para sacar la fecha de caducidad del nombre del fichero recurrimos a algo que ya tenemos hecho en el script proyecta.sh Primero almacenamos el nombre con la extensión, eliminando la ruta ($filename en línea 5), luego eliminamos del mismo la extensión ($nombrefichero en línea 6) y luego extraemos la fecha del mismo modo que extraemos la extensión en proyecta.sh (variable $caduca en línea 7)

Ahora ya sólo resta programar el script para que se ejecute con crontab a una hora indecente y listo.

Modificar proyecta.sh

Pero el intercalar esto en el nombre de archivo nos obliga necesariamente a modificar ligeramente, muy ligeramente, el fichero proyecta.sh Recordamos que allí recogemos en la variable $fname el nombre del fichero sin la extensión, vamos a hacer memoria:

Estas son las líneas que tenemos ahora, que están pensadas para un fichero del tipo nombre_fichero.ext de forma que $extension sería “ext” y $fname sería “nombre_fichero”.

Sin embargo si ahora tenemos nombre_fichero.fecha.ext la variable $fname será “nombre_fichero.fecha”, con lo que la opción de audio no nos funcionaría, así que necesitamos quitar la fecha de esa variable y el procedimiento no es nada complicado, de hecho es una minúscula y sutil modificación, esta:

Exacto, cambiar “%” por “%%” en la línea que asigna el valor a la variable $fname (en este caso de ejemplo, la línea 49). Esto hace que se elimine, desde la derecha, todo lo que haya hasta el último punto. Si hay uno se eliminará sólo la extensión, si hay dos, porque el fichero lleva fecha de caducidad, se eliminará la extensión y la fecha. Y ojo, ya imagino que os habréis dado cuenta, pero acabamos de añadir una restricción en los nombres de ficheros que es importante: el nombre del fichero no puede contener más puntos.

Y ya está, eso es todo, ya podemos subir ficheros sin preocuparnos de tener que borrarlos.

Dejo el script “caducados.sh” en formato texto para copiar y pegar. La modificación del “proyecta.sh” es tan simple que no veo que haga falta copiar todo el texto ni subir un fichero nuevo, que hay que ahorrar ancho de banda que está la cosa mu achuchá.

#!/bin/bash
ayer=$(date --date "yesterday" +%Y%m%d)
for file in /home/pi/datos/pantallas/*
do
	filename=$(basename $file)
	nombrefichero="${filename%.*}"
	caduca="${nombrefichero##*.}"
	if [ $caduca = $ayer ]
		then
			rm $file
	fi
done

Ah! Y sí, me encantan los mejillones, son nutritivos, muy beneficiosos para la salud y están de rechupete en cualquiera de sus formas y presentaciones 🙂


Ficheros tratados en esta entrada

    • caducados.sh: Borra los ficheros cuando caduquen.
    • proyecta.sh: Hay que modificar el script principal como se indica
    • crontab: Hay que programar caducados.sh para que se ejecute con crontab

Pantallas informativas: Instrucciones

Sí, bueno, en el apartado VIII decíamos que había que ponérselo fácil al usuario, pero claro, si después de todo esto no le decimos qué tiene que hacer y cómo, por muy fácil que sea no lo va a adivinar, así que al tajo. Ah! y al final de la entrada están en pdf, por si alguien quiere llevárselas a la playa y ahorrarse la novela.

Instrucciones para el usuario

Lo que debemos saber

Las pantallas pueden:

    • Reproducir un vídeo con o sin sonido
    • Que un determinado archivo sólo lo reproduzca una pantalla
    • Que un archivo lo reproduzcan todas pantallas menos una
    • Poner una fecha de caducidad al archivo
    • Funcionar en los días y horas que les programemos

Y además nos informarán por correo electrónico:

    • Cuando se actualicen
    • Cuando encuentren algún problema para actualizarse
    • Cuando cambien la IP
    • Cuando les requiramos un informe

Extensiones de archivo

El sistema de pantallas reproduce imágenes fijas en formato jpg y vídeos en distintos formatos, avi o mp4, por ejemplo (con codificación mpeg, H264, etc.). Nos ceñiremos exclusivamente a estas extensiones.

Los jpg los podemos preparar con un montón de aplicaciones, quizás la más potente y gratuita sea GIMP, que tiene versiones para Linux y Windows (y creo que también para Mac).

Para el uso de este tipo de pantallas vienen también muy bien las presentaciones en PowerPoint, son fáciles de hacer y vistosas. Desde, al menos, la versión 2016, PowerPoint permite guardar las presentaciones en formato de vídeo mp4, totalmente compatible con este sistema de pantallas.

Resolución

Una pantalla HD normalmente tiene una resolución de 1920×1080, lo ideal es dejar vídeos e imágenes a esa resolución, pero hay ocasiones en que no se puede, bien porque es un vídeo en formato antiguo 4:3, una imagen de un cartel en vertical, etc. En estos casos el sistema ajustará la imagen para que ocupe toda la pantalla, pero sin cortarla, ampliando o reduciendo y dejando espacios en negro verticales u horizontales, según sea el caso.

No obstante, atentos a la resolución, porque aunque la pantalla ajuste la imagen no hace milagros. Si por ejemplo nos pasan un jpg de muy poca resolución, al pasarlo a la pantalla la imagen se ajustará, pero lo veremos con unos pixels del tamaño de un perrico pequeño, y el resultado será lo que saca ese perrico por ese orificio que tiene junto al rabo.

Y del mismo modo a la inversa: si nos pasan para subir el vídeo muy chulo de una presentación en 4K y que aunque se proyecte en total silencio va con audio en tres idiomas en 5.1… pues a ver, que aunque quepa en la tarjeta lo que tiene que reproducirlo es una modesta Pi Zero que lo mismo sale corriendo.

Resumiendo: Antes de subir un archivo a las pantallas hay que fijarse en la resolución y características. Habrá archivos que simplemente no valgan, sin más. En estos casos se avisa al remitente y que nos mande un archivo conforme a lo que le especifiquemos.

Reproducción de archivos

Nombre de los archivos

En los nombres de archivos vamos a evitar, por si acaso: espacios, símbolos (excepto guion y guion bajo), acentos y eñes. Lo más fácil: usar sólo letras, números y guión bajo. Sólo con esto podemos hacer diabluras, no es complicado y es una buena práctica.

Un archivo del tipo nombre.ext se reproducirá de forma normal, sin sonido, en todas las pantallas. Para conseguir que un archivo tenga un comportamiento especial hay que modificar su nombre de archivo, bien al principio o al final:

    • XXX + Nombre de la pantalla + nombre del archivo + extensión: Muestra el archivo sólo en esa pantalla
    • ZZZ + Nombre de la pantalla + nombre del archivo + extensión: Muestra el archivo en todas pantallas menos en esa
    • nombre del archivo + AAA + extensión: Reproduce el vídeo con audio.
    • nombre del archivo + .fecha de caducidad + extensión: Indica cuando caduca el archivo.

Por ejemplo, si tenemos tres pantallas llamadas: pantalla1, pantalla2 y pantalla3; si lo que queremos es que la 2, y sólo la 2, muestre un archivo llamado, por ejemplo “jornadas.avi”, lo deberemos nombrar así:

XXXpantalla2jornadas.avi

El nombre del archivo deberá comenzar por XXX en mayúscula y a continuación el nombre de la pantalla, para luego seguir con el nombre del archivo, etc.

Y si quisiéramos justo lo contrario, que la pantalla2 no reprodujera ese archivo, pondríamos lo mismo pero comenzando por ZZZ:

ZZZpantalla2jornadas.avi

Por defecto los vídeos tienen el sonido desactivado, pero si por lo que sea queremos que alguno se muestre con sonido basta colocar AAA, en mayúscula, justo después del nombre del archivo y antes del punto:

jornadasAAA.avi

Y para establecer una fecha de caducidad a un archivo deberemos poner justo antes de la extensión y separado por un punto la fecha de caducidad en formato AAAAMMDD (año con cuatro dígitos, mes con dos dígitos y día del mes con dos dígitos). Esa fecha será la del último día que el fichero estará en pantalla, es decir, se reproducirá desde que lo subamos hasta esa fecha inclusive.

Si por ejemplo, siguiendo con el archivo anterior, queremos que este se reproduzca hasta el 22 de abril de 2019 inclusive pondríamos:

jornadas.20190422.avi

Por supuesto todas las opciones se pueden combinar. Si por ejemplo quisiéramos que el vídeo del ejemplo se reprodujera en la pantalla2, con audio, y hasta esa fecha, pues deberíamos nombrarlo:

XXXpantalla2jornadasAAA.20190422.avi

De hecho aún podemos dar una vuelta de tuerca, si queremos que ese vídeo se muestre con sonido en una pantalla, la pantalla2 por ejemplo, y sin sonido en el resto. Tendríamos que subir el mismo archivo con dos nombres distintos, uno el que justo acabamos de ver y otro este:

ZZZpantalla2jornadas.20190422.avi

Este último nombre hace que el vídeo se reproduzca en todas pantallas excepto en la pantalla2 y sin audio (por defecto).

No obstante, aunque todas las opciones que se pongan estén bien y nunca estén de más, lo que más se va a usar y con diferencia es el nombre de archivo liso y laso junto con la fecha de caducidad:

Jornadas.20190422.avi

Subir y actualizar archivos

En el servidor tenemos configurado un usuario y un password. Lo normal es que en el ordenador donde estemos trabajando tengamos una carpeta de red conectada con nuestro usuario a la del servidor que contiene los archivos, así que la forma de subirlos o actualizarlos será tan fácil como arrastrar un archivo de la carpeta que sea de nuestro PC a esa carpeta o unidad de red.

Las pantallas también tienen programado el horario de encendido y apagado en función del tipo de día: horarios de verano, reducidos, etc. Estos horarios están definidos en los ficheros que contiene la carpeta “días” del servidor, son ficheros de texto que podemos modificar quitando o poniendo las fechas que sean necesarias. Las fechas van en el mismo formato comentado antes, AAAAMMDD, y separadas por un espacio.

Para acceder a estos archivos del servidor hay que conectarse por FTP con nuestro usuario y password.

También, por supuesto, por FTP podemos actualizar los archivos que ha de reproducir la pantalla, ya que por este sistema tendremos accesible la carpeta “días” y la carpeta “pantallas” que alberga dichos archivos.

Si queremos que un determinado archivo deje de reproducirse tan sólo tenemos que borrarlo del servidor. La Raspberry se conecta con el servidor cada media hora, por lo que las actualizaciones que llevemos a cabo se harán efectivas en las pantallas al punto y a la media, salvo errores de conexión, claro.

Opciones especiales

Podemos subir ficheros especiales que provoquen lo siguiente:

    • Envío de un informe de todas las Raspberry
    • Apagar una determinada Raspberry
    • Reiniciar una determinada Raspberry
    • Parar la Raspberry y dejar encendida la pantalla
    • Parar la Raspberry y apagar la pantalla

Respectivamente esto lo podemos hacer subiendo los siguientes ficheros como si fueran ficheros a reproducir y que pueden estar completamente vacíos:

    • test.txt
    • nombrepantalla.001
    • nombrepantalla.002
    • nombrepantalla.003
    • nombrepantalla.222

Obviamente, nombrepantalla deberá ser cambiado por el nombre de la pantalla que queremos que haga lo que sea. Y también, para ser exactos, la última extensión, ahora mismo, podría ser cualquier número mayor que 003 (basta mirar el código de proyecta.sh para comprenderlo), que son las acciones que en estos momentos hay definidas, pero por si acaso se me ocurre meter alguna cosa más y como no tengo ganas de andar revisando instrucciones, pues ponemos 222, que tiene número de galleta y dudo que meta más de doscientas opciones.

Las Raspberry nos enviarán correos electrónicos a la dirección que hayamos definido, indicando entre otras cosas actualizaciones e IP.

También podemos, con otro usuario y pass, acceder a la Raspberry por SSH y realizar las acciones que sean precisas, pero para esto deberemos usar un ordenador que esté conectado a la misma red que la Raspberry.

Instrucciones en PDF

Pantallas informativas: Anexos e Instrucciones

Llega el momento de los parches y los zurcidos, y es que es poniendo a funcionar el montaje cuando vamos viendo algunas funcionalidades que nos serían útiles. En las siguientes entradas se detallarán aquellos ajustes que se han llevado a cabo una vez puesto en marcha el sistema.

De momento ninguno de los códigos de los anexos está recogido en los ficheros para descargar de la entrada IX, ya que de momento son pequeñas modificaciones y tampoco estamos haciendo una aplicación, sino comentando un sistema y dando algunas ideas. No obstante, si en algún momento se hace algún cambio importante se modificará el fichero de la mencionada entrada y se indicará aquí y en los anexos correspondientes.

Pantallas informativas (IX): Resumen, ajustes para añadir pantallas, cómo adaptar para otros usuarios y ficheros para descargar

En las anteriores entradas hemos visto cómo llevar a cabo todo el sistema, desde cero. Llegados a este punto ya tenemos todos los códigos de los scripts, los archivos de texto que usaremos para controlar nuestra aplicación, etc. Es el momento de recapitular y enumerar los pasos necesarios para hacer una instalación desde cero y cómo añadir a ella nuevas pantallas o distintos usuarios.

Instalación desde cero

Raspberry

    • Instalación de Raspbian Desktop
      • Configurar País, lenguaje, Timezone
      • Poner un pass decente a Pi
      • Activar SSH, VNC, desactivar overscan y poner memoria GPU a 128
      • Configurar WiFi y actualizar sistema
      • Poner nombre a host que lo diferencie del resto de pantallas
    • Generar el par de claves para conectar al servidor
      • Configurar ficheros y carpetas
    • Instalar FIM
      • Crear fichero de configuración FIM
    • Instalar cec-utils
    • Crear las carpetas y copiar los ficheros
      • Carpeta dias
      • Ficheros de días
      • Ficheros de texto de control
      • Scripts
    • Configurar
      • Configurar conexión
      • Configurar correo de destino de los mensajes
      • Configurar Mutt
      • Configurar Crontab

Servidor

    • Instalar Ubuntu
    • Crear usuario “Pi” para conexión con Pi de Raspberry
      • Crear fichero con clave pública
      • Crear estructura de carpetas
      • Almacenar ficheros de días (calendario)
    • Crear usuario userpantallas para actualizaciones
      • Crear enlace simbólico ftp en su home hacia carpeta de Pi
    • Configurar Samba
    • Instalar VSFTPD
      • Configurar VSFTPD

Partiendo desde cero, es largo, pero tampoco excesivamente complicado cuanto ya tenemos los scripts preparados. Llegados a este punto sería bueno “clonar” la tarjeta de la Raspberry y así, además de servirnos como copia de seguridad ante un eventual desastre, podemos añadir más pantallas muy fácilmente.

Añadir más pantallas (para un mismo usuario)

Para añadir más pantallas lo único que habría que hacer sería grabar esta imagen en una nueva tarjeta y:

En la Raspberry

    • Cambiar el nombre del host
    • Generar un nuevo par de claves
    • Machacar la anterior clave privada con la nueva (dar mismo nombre)

En el servidor

    • Mandar la clave pública al servidor y añadirla al final del archivo.

Y ya está, ahora sí que es tremendamente fácil y podemos añadir todas las que queramos.

Añadir otros usuarios (y sus pantallas)

Y si lo que queremos es añadir otros usuarios que se conecten a nuestro servidor, pero que usen sus conexiones y proyecten sus propios archivos, lo que haríamos sería lo siguiente:

En la Raspberry

Tras grabar nuestra imagen:

    • Cambiar el nombre del host
    • Generar un nuevo par de claves
    • Machacar la anterior clave privada con la nueva (dar mismo nombre)
    • Modificar el archivo correo.txt con el correo al que dirigir los mensajes
    • Modificar wpa_supplicant con login y pass de conexión
    • Modificar los scripts actualiza y actualizadia con el login Pi2 (o el que creemos nuevo en el servidor)

En el servidor

    • Crear otro usuario “Pi2”
      • Crear su fichero con clave pública
      • Crear estructura de carpetas
      • Almacenar ficheros de días (calendario)
    • Crear otro usuario userpantallas2 en sistema y Samba
      • Crear enlace simbólico ftp en su home hacia carpeta de Pi2
    • Configurar Samba para este usuario
    • Añadir este usuario a la lista de usuarios de VSPFTP

Es importante que el enlace simbólico también se llame ftp y así sólo habrá que añadirlo a la lista de usuarios VSFTPD

Y que no se nos olvide clonar su tarjeta por seguridad.

En fin, que lo más laborioso ha sido toda la creación de scripts y ficheros, pero una vez que los tenemos, añadir usuarios o trasladar el sistema a otros nuevos no requiere de mucha complicación.

Además, he modificado ligeramente los scripts para que el correo de destino lo tomen del archivo correo.txt, que aparece en estas líneas, para que haya que tocar menos cosas.

Descarga de ficheros

En las entradas previas se ha ido publicando el código de los distintos scripts paso a paso. El siguiente enlace contiene, comprimido en 7z, todos los scripts que se han ido mencionando y los ficheros de texto para el manejo de la aplicación.

Ficheros Proyecto Pantallas v.01

Clonar la tarjeta

Llegados a este punto resultaría bastante aconsejable que una vez que tengamos perfectamente configurada la tarjeta le sacáramos una copia por si las moscas. Además con esta copia sería fácil adaptar el sistema para nuevas pantallas.

Para el proceso de clonado de la tarjeta hay una entrada específica aquí:

Clonar la tarjeta

Cosas que faltan

Seguridad

Como ya he dicho por algún rincón de este blog, todo esto son cosas que van saliendo conforme las voy aprendiendo, así que estoy a eones de ser ya no un “experto” sino un mero “entendido”, pero me relaja escribir y me sirve de bloc de notas, y quién sabe si además le puede servir esto a alguien. Pero esta inexperiencia es la que me lleva a pensar que lo primero que habría que revisar es la seguridad en la Raspberry y el Servidor, cerrar las puertas a que alguien pueda hacer “la gracia”. La seguridad hay que revisarla.

Apagado de emergencia

Un botón de apagado de emergencia en la Raspberry estaría bien. Alguna prueba me ha llevado a tener que apagarla a las bravas, y aunque la tarjeta de memoria ha aguantado sé que estas prácticas le sientan como una patada entre las patillas (del micro). Se me ocurre que con Python se podría hacer un programeja para controlar una interrupción en un puerto GPIO, de modo que al accionar un pulsador se activara dicha interrupción y se produjera un apagado ordenado, incluso le podríamos poner un led para saber cuando concluye el apagado y no tener que abrir la caja, que además los bombillicos siempre hacen gracia.

SAI

Un SAI de bajo coste también aparece como tarea pendiente: Un power bank con capacidad de carga y descarga simultánea (pass through charging), un relé, un par de adaptadores y lo de antes, mediante una interrupción controlar cuando hay un corte de suministro eléctrico, controlar el tiempo que estamos sin electricidad y llegado el momento hacer un apagado ordenado.

Fecha de caducidad de los ficheros

También, en el tiempo que llevamos de pruebas con una de las pantallas, se va echando en falta un sistema de caducidad de los ficheros a reproducir. Y para evitar que las tripas de la Raspberry se vayan llenando de ficheros caducados, se me ocurre que eso habría que hacerlo en el servidor. Quizás con un simple script leyendo un índice supiera qué fichero tiene que borrar y cuando.

Se podría hacer lo mismo pero a la inversa, programar un día de activación del fichero, en la web (WordPress, Drupal, etc.) es una opción que encontramos disponible de forma bastante común, pero mi experiencia precisamente con la publicación de contenidos web en trabajos similares al que nos ocupa me dice que es una característica perfectamente prescindible, creo que no la he usado nunca. La caducidad sí, no hay publicación de esta índole que se libre porque se trata de charlas, cursos, exposiciones, etc. perfectamente definidos en el tiempo, pero la activación no, porque interesa que el evento tenga días de publicidad y además lo habitual es que te den las cosas “para ayer”.

Pantallas informativas (VIII): Gestión de ficheros fácil para el usuario

Introducción

Conforme estaba escribiendo esta entrada me estaba dando cuenta de que para muchos casos esto puede ser matar moscas a cañonazos, por eso he creído conveniente poner esta introducción inicial.

Si en algún momento del espacio-tiempo alguien, aparte de mí mismo, lee estas entradas y decide poner en marcha un sistema como este, es muy posible que lo que a continuación se detalla no le sea necesario. Si alguien pretende ponerse una tele en el escaparate de su tienda y manejarla él mismo, puede tranquilamente saltarse este apartado, pues podrá poner los ficheros a reproducir subiéndolos por ftp como se ha estado haciendo hasta ahora para las pruebas.

En nuestro caso concreto, y hablo del montaje que se está realizando, esto no va a ser así. Al cargo de la actualización de los archivos podrá haber personas con unos conocimientos de informática a nivel ofimático y de usuario, que a lo mejor lo de combinar correspondencia en Word lo manejan con la habilidad de un prestidigitador, pero que lo del manejo del FTP y demás les sobrepasa un poco, por tanto hay que diseñar un sistema fácil y seguro.

Desarrollo

Al usuario hay que ponérselo fácil, lo más fácil posible, como un juego de niños. Siempre he dicho que hay que considerar al usuario como un completo ignorante en temas informáticos, pero no en modo despectivo en absoluto, sino porque tiene, tenemos, todo el derecho del mundo a serlo, y porque cuando yo mismo uso alguna nueva aplicación agradezco si su creador ha seguido esta misma filosofía y no me obliga a invocar a los arcanos mayores para hacer cosas de lo más simples.

Así pues, para copiar un archivo ¿Qué hay más simple para un usuario que arrastrar un icono de una carpeta a otra? Eso lo haremos con Samba.

Pero también habrá que configurar esos otros archivos del calendario, una tarea que haremos de ciento a viento. Eso lo haremos por FTP “enjaulando” a los usuarios. Se podría hacer tranquilamente por Samba, con otro usuario y otra configuración, de forma muy fácil, pero para qué engañarnos, también me apetecía probar esta configuración a ver qué pasaba, así hay donde elegir.

Y a la vez, todo esto lo haremos evitando conectarnos al servidor como “pi”, eso lo dejaremos para el maestro de ceremonias, es decir, nosotros mismos, preservando todos los ficheros de configuración de cualquier mal uso.

Comenzamos.

Samba

Con Samba vamos a crear un usuario en el servidor que tenga acceso “sólo” a la carpeta de los archivos que han de reproducir la pantallas, luego conectaremos esa carpeta en un ordenador con Windows para tenerla siempre disponible y después sólo será necesario tratarla como una carpeta más de nuestro ordenador: abrimos con doble clic, arrastramos un fichero, borramos el que ya no necesitemos, etc. Más fácil imposible.

Bien, en el servidor tenemos al usuario pi y en su home tenemos el directorio datos y sus subdirectorios dias y pantallas. Va a ser el subdirectorio pantallas el que compartamos con Samba.

Como root, vamos a crear en primer lugar un usuario con home pero sin acceso a la shell, le asignaremos una contraseña y lo meteremos en el grupo de pi, lo vamos a llamar “userpantallas“:

useradd -g pi -m -s /usr/sbin/nologin userpantallas

Le configuramos un pass

passwd userpantallas

Y aquí le colocamos el que queramos, pero lo apuntamos para que no se nos olvide porque lo usaremos más adelante con FTP, ahora confirmamos y seguimos.

Nos metemos en el home de userpantallas y vamos a crear aquí un enlace simbólico a la carpeta datos de pi, así:

ln -s /home/pi/datos ftp

Como estamos como root, vamos a asignarle a ese enlace el propietario userpantallas y el grupo pi. Ojo, que la instrucción chown con los enlaces no se puede usar tal cual, o cambiará el propietario de las carpetas de destino, y eso no es lo que nos interesa, hay que poner la opción -h

chown -h userpantallas:pi ftp

Bien, si ahora entramos en ftp veremos que dentro encontramos los subdirectorios dias y pantallas ¡¡¡Magia!!! No, no es magia, es… un enlace simbólico.

Comprobamos que Samba está instalado en el ordenador con Ubuntu, nuestro servidor, y si no procedemos a ello como con cualquier aplicación, no tiene misterio.

Ahora tenemos que seguir con el usuario userpantallas y crearlo en Samba, para ello:

smbpasswd -a userpantallas

Nos pedirá un pass, así que le asignamos uno que no tiene que ser el mismo que hemos puesto en Ubuntu, y seguimos.

Ahora toca configurar Samba, para ello editamos el fichero smb.conf que se encuentra en la ruta /etc/samba

El fichero es bastante extenso porque vienen comentadas las opciones de configuración. Sobre esto, como soy bastante profano en la materia, se recomienda tirar de manual para hacer una configuración correcta y segura. No obstante, la que he llevado a cabo y funciona es la siguiente, que pego sin comentarios:

[global]
   workgroup = NUESTRO_GRUPO_DE_TRABAJO
   server string = %h server (Samba, Ubuntu)
   dns proxy = no
   log file = /var/log/samba/log.%m
   max log size = 1000
   syslog = 0
   panic action = /usr/share/samba/panic-action %d
   server role = standalone server
   passdb backend = tdbsam
   obey pam restrictions = yes
   unix password sync = yes
   passwd program = /usr/bin/passwd %u
   passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\n *password\supdated\ssuccessfully* .
   pam password change = yes
   map to guest = bad user
# Desactivamos impresion en samba
   load printers = no
   printing = bsd
   printcap name = /dev/null
   disable spoolss = yes
[printers]
   comment = All Printers
   browseable = no
   path = /var/spool/samba
   printable = yes
   guest ok = no
   read only = yes
   create mask = 0700
[print$]
   comment = Printer Drivers
   path = /var/lib/samba/printers
   browseable = yes
   read only = yes
   guest ok = no
[pantallas]
   path = /home/userpantallas/ftp/pantallas
   browseable = no
   writeable = yes
   valid users = userpantallas
   directory mask = 0775
   guest ok = no

Al principio del fichero de configuración hay que poner el grupo de trabajo que tengamos configurado en nuestra red, y al final, como valid users, está el usuario que hemos creado para este fin y el path un tanto extraño que nos enlaza con la carpeta que contiene los ficheros a reproducir, ya que recordamos que la carpeta ftp de userpantallas es un enlace simbólico a la carpeta datos de pi, con todo su contenido.

El resto de opciones de global son las que vienen por defecto, aunque se han borrado algunas que contenía el fichero original. Las opciones de impresión no las vamos a usar, así que no sé si están correctamente configuradas ni para usarlas ni para dejar de hacerlo.

Y llegados a este punto guardamos el fichero con esta configuración y reiniciamos Samba.

samba restart

Vale, pues vamos a probar la conexión desde un equipo con Windows. En este caso va a ser un equipo con Windows 10 pero el proceso es similar en otras versiones de Windows.

Pinchamos con el botón derecho del ratón sobre el icono de Red y seleccionamos “conectar a unidad de red”.

Nos saldrá una ventana como esta:

Ponemos la IP del equipo y el nombre de la carpeta que hemos compartido en Samba. Ojo que ya se puede ver que no es la trayectoria completa, sólo \\IP\nombre_carpeta_samba. Marcamos también la casilla “Conectar con otras credenciales” y si quisiéramos tenerla siempre disponible marcaríamos también “Conectar de nuevo al iniciar sesión”, como es una prueba, de momento esa la dejamos en blanco. Pinchamos en “Finalizar” y pasamos a la siguiente ventana:

Aquí ponemos el nombre de usuario de Samba, según el ejemplo que estamos siguiendo en esta entrada será userpantallas y el pass que le hemos puesto en Samba. Y lo mismo, si quisiéramos tener siempre accesible esta carpeta marcaríamos “Recordar mis credenciales“, en este caso lo dejamos en blanco y pulsamos “Aceptar“.

Y listo. Si todo ha ido bien y no hemos metido la pata en la configuración deberíamos estar viendo el contenido de la carpeta pantallas y podríamos trabajar con ella como si fuera una carpeta más de nuestro propio equipo.

Ahora, viendo que todo funciona, sería cuestión de realizar esta conexión en el equipo, o equipos, que se encarguen de actualizar la información que muestren las pantallas, y en estos casos sí que marcaríamos las casillas que antes hemos dejado en blanco, tanto la de “conectar de nuevo al iniciar sesión” como la de “recordar mis credenciales“, con ello esta carpeta aparecería siempre como si fuera una unidad más de disco.

Llegados a este punto sería muy fácil crear otro usuario en Samba y que se conectara a la carpeta /datos/dias para actualizarla cuando fuera preciso, pero la ventaja que tiene Samba es que permite tener la carpeta siempre accesible y eso es precisamente lo que no queremos para la carpeta dias, y como además nos gusta probar cosas, pues vamos a hacer que esa actualización sea por FTP.

VSFTPD

 Como ya hemos dicho, queda una parte que también ha de actualizar de vez en cuando el usuario y que no nos interesa que sea “tan” fácil, y es la actualización de los archivos con los listados de fechas. Estas actualizaciones se harán una vez al año o poco más si sale alguna incidencia, y es por ello que no interesa que esa carpeta esté ahí todo el tiempo siendo candidata a un “¡Ay! Es que la he borrado sin darme cuenta”. Subimos un poco el nivel y para actualizar ese directorio habrá que usar el FTP. ¡Un drama!

Pues no, nada de drama. Vamos a instalar VSFTPD en nuestro particular servidor y vamos a “enjaular” a los usuarios, de tal modo que cuando el usuario se conecte por FTP sólo tenga acceso a la carpeta que nos interese pero sin que pueda navegar por otros directorios del sistema o de otros usuarios, ni siquiera por el resto de subdirectorios de su home, preservando así, por ejemplo, la clave pública para la conexión de la Raspberry y otros archivos de configuración.

Como puede verse, dificultad mínima, tan sólo se trata de instalar un programa de FTP y conectarse a una IP con un pass. Punto pelota.

Que nadie sufra por estos dos “usuarios”, fueron “desenjaulados” al cabo de un rato. A los nuestros sin embargo les espera la perpetua ¡Nunca saldrán de su home!

Vamos al tajo. Comenzamos instalando vsftpd en el servidor con Ubuntu:

sudo apt-get install vsftpd

Y lo activamos

sudo systemctl start vsftpd

sudo systemctl enable vsftpd

Hacemos una copia del fichero de configuración

sudo cp /etc/vsftpd.conf /etc/vsftpd_conf.bak

Y ahora editamos el fichero de configuración vsftpd.conf para adaptarlo a nuestros propósitos. El fichero viene bastante comentado y hay páginas por Internet que nos pueden ayudar a su comprensión.

La instrucción clave es allow_writeable_chroot=YES esto es lo que permite enjaular a los usuarios en el local root que les definamos, que será ftp con esta instrucción: local_root=/home/$USER/ftp

Además crearemos una lista de usuarios capaces de usar este servicio, no valdrá sólo con crear un usuario y agregarle un directorio ftp, deberá estar también en la lista: vsftpd.userlist

Pero pese a todo he de reconocer que un par de veces que lo he configurado en distintos equipos, un par de veces que he tenido que pelear con él. Así pues, a continuación pongo el fichero de configuración que está probado y funcionando, aunque posiblemente se pueda afinar algo más.

En la línea 10 tenemos el “enjaulamiento”, en la 20 el directorio, que en este caso es un enlace simbólico pero funciona exactamente igual que si fuera un directorio y en la 22 la ubicación y nombre del archivo de los usuarios autorizados. Deberemos pues crear este fichero y colocar allí a userpantallas

sudo nano /etc/vsftpd.userlist

Guardamos, salimos y reiniciamos el servicio

systemctl restart vsftpd

Y para saber si todo está funcionando bien podemos probar:

service vsftpd status

Deberíamos ver “algo verde”, si vemos “algo rojo” es que “algo” no va bien. Muy posiblemente un triste error sintáctico en el fichero de configuración, cuestión de repasarlo (nada que no consigamos tras toda una tarde tirándonos de los pelos, lo normal…)

Por último, si recordamos, hemos creado al usuario userpantallas sin shell, indicando que usara /usr/sbin/nologin Bien, pues ahora toca añadir esto en el archivo /etc/shells porque si no no podremos conectarnos por ftp, así que nos situamos en etc y:

sudo nano shells

Y añadimos al final la línea indicada. El fichero quedará más o menos así:

Y llegados a este punto toca probar. Vamos a probar con WinSCP:

En protocolo seleccionamos FTP sin cifrado, colocamos la IP del ordenador al que queremos conectarnos, el que hace de nuestro servidor y colocamos el nombre de usuario del usuario que tenemos creado userpantallas y el pass que le hemos dado a la hora de crearlo en Ubuntu, no el de Samba, VSFTPD funcionará con el pass que tenga el usuario en el sistema.

Y ahí están las carpetas dias y pantallas accesibles.

Ha sido un trabajo un poquito laborioso, pero hemos conseguido dejarlo fácil para el usuario, que es de lo que se trataba.

El código de fsftpd.conf para copy&paste, aquí:

listen=NO
listen_ipv6=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
chroot_local_user=YES
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
ssl_enable=NO
pasv_enable=YES
pasv_min_port=10000
pasv_max_port=11000
user_sub_token=$USER
local_root=/home/$USER/ftp
userlist_enable=YES
userlist_file=/etc/vsftpd.userlist
userlist_deny=NO
allow_writeable_chroot=YES
check_shell=NO

 

 

Pantallas informativas (VII): Programado de encendido y actualizaciones con crontab

En las anteriores entradas hemos estado hablando de automatizar y apagar, ahora toca encender la pantalla y ejecutar las tareas a su hora, y eso lo vamos a hacer muy fácil con crontab.

Vamos a definir:

    • Ejecución de comandos y scripts al iniciar.
    • Actualización del calendario a las 6 am.
    • Encendido de la pantalla a las 9 am.
    • Comprobar actualizaciones de ficheros cada 30 minutos.
    • Comprobar IP cada 15 minutos.

Como paso previo, damos a todos los scripts que hemos hecho permisos de ejecución con el comando:

chmod +x nombre_del_script.sh

Y ahora programamos su ejecución con crontab.

Sin extendernos mucho, porque no lo voy a hacer mejor que el peor manual de Linux, para programar con crontab la forma es:

* * * * * nombre_del_script.sh

Y los asteriscos, comenzando por la izquierda, son:

      • minuto
      • hora
      • día de la semana
      • mes
      • día del mes

Así, si a las seis de la mañana queremos que se actualicen los ficheros de fechas, pondremos lo siguiente:

* 6 * * * /home/pi/actualizadia.sh

Fácil, ¿no?

Si queremos que una tarea se ejecute cada 15 minutos en el primer asterisco pondremos */15, que vendría a decir “cada 15”, ya que si ponemos sólo 15 la tarea se ejecutará en el minuto 15 de cada hora, no cada 15 minutos. Así:

*/15 * * * * /home/pi/queip.sh

Y si queremos que algo se ejecute al iniciar la Raspberry pues ponemos @reboot loquesea.

Bien, pues ahora que sabemos qué queremos hacer y cómo, nos ponemos manos a la obra.

Editamos crontab, como pi, con el siguiente comando:

crontab -e

Y la primera vez nos preguntará por nuestro editor de textos favorito, yo me apaño bien con nano, pero para gustos colores, podéis seleccionar el que queráis.

Una vez dentro del fichero veremos un montón de líneas comentadas, pues al final colocamos las nuestras:

@reboot sleep 10 && /home/pi/iniciando.sh > /dev/tty1; /home/pi/actualizadia.sh; /home/pi/actualiza.sh; /home/pi/fecha.sh &
#
* 6 * * * /home/pi/actualizadia.sh &
#
* 9 * * * /home/pi/fecha.sh &
#
*/15 * * * * /home/pi/queip.sh &
#
*/30 * * * * /home/pi/actualiza.sh &

Al iniciar le decimos que espere 10 segundos, para dar un poco de tiempo a que acabe de cargar todo antes de comenzar a ejecutar los scripts.

Primero ejecutará iniciando.sh que es el que se encarga de comprobar la wifi. Podríamos haber hecho eso mismo con el sistema, pero prefiero hacerlo así y ver que tengo conexión y una mínima indicación en pantalla por si en algún momento algo va mal.

Y aquí vemos que la salida se ha enviado a /dev/tty1, la pantalla, porque este script lo está ejecutando crontab, no nosotros, y él no necesita pantalla. Por ello, los scripts que hemos hecho hasta ahora habrá que modificarlos ligeramente teniendo en cuenta esto, que habrá que mandar la salida a /dev/tty1 y que si hay que borrar habrá que cambiar el clear por clear_screen. Luego lo vemos porque en realidad son cuatro cambios.

Siguiendo con la línea del inicio, ejecutamos también el fichero de actualización de fechas y luego el de archivos y por último el fecha.sh que será el que compruebe qué tipo de día es y según eso lance el script principal proyecta.sh. Este último será el que tengamos que modificar más cuidadosamente.

Y el resto de líneas es lo comentado anteriormente según lo definido al inicio.

Por legibilidad hemos dejado una línea en blanco, comentada, entre medio de cada línea de tareas programada, pero es completamente optativo esto.

Modificación del fichero proyecta.sh

Bien, como este script es lanzado por otro que a su vez lo ejecuta crontab, cuando decimos en él “borrar pantalla” tenemos que decir “qué pantalla”, así que dirigiremos el comando a /dev/tty1 en estas líneas:

Fácil. Modificamos las líneas necesarias y hemos dejado comentadas las viejas para mejor comprensión.

En esta entrada no pego el código completo del script porque en la siguiente estarán todos los ficheros empaquetaditos y los cambios como se puede ver son mínimos.

Y esto ya casi está. En la siguiente entrada hacemos un repaso, vemos cómo preparar el sistema para más pantallas, para otros usuarios, clonar tarjetas y listo.


Ficheros tratados en esta entrada

    • proyecta.sh (script principal)
    • crontab

 

Pantallas informativas (VI): Distintos horarios de funcionamiento según la fecha

Para hacer el sistema lo más autónomo posible, lo suyo sería que se encendiera y apagara a la hora programada, e incluso, que el encendido y apagado pudiera variar según determinadas fechas.

Este proyecto está diseñado para varios edificios con una apertura al público en diversos horarios según la época del año, es un tanto particular, así que vamos a buscar un ejemplo que lo haga más comprensible.

Vamos a imaginarnos un comercio que abre la persiana a las 9 y la baja a las 20 horas, que abre determinados sábados de 9 a 13, algunos sueltos, y que para el verano tiene días de apertura sólo de mañanas, de 9 a 15, por último este comercio cierra los días de vacaciones como es lógico, y los domingos y festivos.

Vamos a ver cómo programar todo esto, que parece un lío pero no es para tanto. De momento lo planteamos

Tenemos cuatro posibilidades:

    1. Horario normal, N: encendido a las 9 y apagado a las 20
    2. Horario de mañanas, M: encendido a las 9 y apagado a las 15
    3. Horario de sábados, S: encendido a las 9 y apagado a las 13
    4. Festivos, F: apagado todo el día

Estamos en Zaragoza (España), es 2019 y el calendario que nos pasan podría ser como el siguiente:

En rojo están los festivos, sábados y domingos; en azul las vacaciones, en amarillo los días que abre sólo de mañana y en verde los sábados que tiene apertura.

Vamos a preparar un script que haga lo siguiente:

    1. Partiremos suponiendo que el día es normal y asignaremos a una variable, $hoy, el valor “N”

A partir de aquí haremos una serie de comparaciones if-then, no if-elif-else, pues el valor puede cambiar varias veces, que parece una estupidez pero es por comodidad, luego lo veremos más claro.

    1. El segundo paso será comprobar si es horario de mañanas, y si lo es, cambiar $hoy a “M”.
    2. El tercer paso será comprobar si es vacaciones o festivo, en cuyo caso asignaremos $hoy a “F”
    3. A continuación comprobamos si es sábado o domingo y de serlo asignamos a $hoy el valor “F”
    4. Y por último comprobamos si estamos ante un sábado de apertura, en cuyo caso asignamos a $hoy el valor “S”

Bien, hemos hablado de comprobar, comprobar, comprobar… ¿pero cómo comprobamos? Pues con un un listado de fechas para cada tipo de día. Necesitaremos en este caso: Un fichero con las fechas de los festivos y días de vacaciones, otro con los días que sólo se abre de mañanas y un tercero con los sábados que se trabaja. Una vez que tengamos estos ficheros el sistema es cargar el contenido en un array y comparar cada registro del array con la fecha en la que nos encontremos.

Ficheros con los listados de fechas

Una forma fácil de hacer la comparación que mencionábamos es usar la nomenclatura Año-mes-día sin separación. Si escribimos en el shell

date -%Y%m%d

veremos que obtenemos la fecha así, saldrá algo como 20190304. Así pues, los ficheros que generemos tendrán que tener la fecha en este formato, uno detrás de otro y separado por un espacio, por ejemplo el de los sábados de apertura tendrá el contenido: 20190105 20190302 20190907 20191005 20191019 y lo guardaremos como sabados.txt

Para el resto de tipos de días haremos lo mismo. Un truquillo es hacerlo con Excel o Calc. Por ejemplo, para los días de apertura sólo de mañanas podemos coger el Excel y en la primera celda (A1) escribir 20190708 y en la de la derecha (B1) poner una fórmula que sume 1 a esta celda (=A1+1)

Luego hacer un llenado hacia la derecha (Ctrl-D) y rellenar las celdas hasta el valor 20190726, es decir, 19 celdas.

Salvamos como texto separado por comas (o punto y coma) y con nuestro editor de texto favorito reemplazamos el carácter separador por un espacio. Luego añadiríamos los días que faltan, del 19 al 23 de agosto, y listo. Ya tenemos un bloque.

Con el de vacaciones haríamos lo mismo, y los días festivos los añadiríamos a mano al fichero. Parece muy lioso pero en realidad es más largo de explicar que de hacer.

Y llegados a este punto puede deducirse por qué antes hemos hablado de usar if-then e ir cambiando el valor. Vemos que no nos hemos preocupado de separar fechas en el mes de julio, considerando del 8 al 26 como que se abre de mañanas, pero entre medio hay sábados y domingos. Haciéndolo así nos despreocupamos de tener que ir seleccionando los días a la hora de crear los ficheros, simplemente al comparar la fecha, por ejemplo, del 20 de julio, primero tomará, por defecto, el valor “N”, luego comparará el fichero de “sólo mañanas” y tomará el valor “M” pero después verá que cae en fin de semana y cambiará de nuevo, esta vez finalmente a “F”.

Bien, pues damos por hecho que ya tenemos tres ficheros, que van a llamarse: “mananas.txt”, “festivos.txt” y “sabados.txt” y los vamos a meter en una carpeta que se llame “dias” en el home de pi en la Raspberry y a preparar el script que se habrá de ejecutar a las 9 de la mañana:

Script fecha.sh

Comenzamos asignando el valor a tres variables:

La variable $dia va a contener el tipo de día que es, comenzando como ya hemos dicho por “N” (normal)

$esfinde la usaremos para saber qué día de la semana es +%u devolverá 1 si es lunes, 2 si es martes, etc. La usaremos para saber si estamos ante un fin de semana.

$hoy tomará el valor de la fecha actual en el formato antes mencionado.

Hacemos ahora la primera comparación para ver si estamos en horario de mañanas:

Y aquí está todo el meollo: Creamos un array en el que metemos el contenido del fichero de texto “mananas.txt”, luego vamos recorriendo con un for-in cada uno de sus elementos y comparándolos con el contenido de la variable $hoy, si alguno coincide asignamos a $dia el valor “M” y si no no hacemos nada.

Y el resto de comparaciones van igual, sólo que con otros nombres:

Se puede ver en la imagen que los bloques de comparación son casi un calco unos de otros a excepción del que comprueba si es sábado o domingo, que lo que hace es comprobar que el valor numérico de la variable $esfinde es mayor que 5.

Vale, ya tenemos el valor de $dia, así que ahora tan sólo tenemos que arrancar nuestro script de visualización de los archivos pasando este valor como argumento, así:

Vamos a llamar a este script fecha.sh y su código completo es el siguiente:

#!/bin/bash
dia="N"
esfinde=$(date +%u)
hoy=$(date +%Y%m%d)
# Comprobamos si se abre solo de mañanas
mananas=$(cat /home/pi/dias/mananas.txt)
for m in $mananas
do
	if [ $m = $hoy ]
	then
		dia="M"
	fi
done
# Comprobamos si es festivo o vacaciones
festivos=$(cat /home/pi/dias/festivos.txt)
for f in $festivos
do
	if [ $f = $hoy ]
	then
		dia="F"
	fi
done
# Comprobamos si es sabado o domingo
if [ $esfinde -gt 5 ]
then
	dia="F"
fi
# Comprobamos si es sábado de apertura
sabados=$(cat /home/pi/dias/sabados.txt)
for s in $sabados
do
	if [ $s = $hoy ]
	then
		dia="S"
	fi
done
# Si no es festivo, arrancamos proyecta.sh
# y pasamos en el argumento el tipo de día
if [ $dia != "F" ]
then
	bash proyecta.sh $dia
fi

Modificando proyecta.sh

Ahora toca hacer los ajustes en el script proyecta.sh que venimos arrastrando desde las anteriores entradas. Al principio del mismo colocamos lo siguiente:

 

La variable $1 contiene el argumento que acabamos de pasar, así que hacemos un case y a la variable $apaga le asignamos el valor numérico que corresponderá a la hora-minutos de apagado.

Una aclaración: aquí bien podríamos haber pasado el valor de la hora de apagado directamente como argumento, haciendo el case en el script que compara los ficheros, e incluso no haciéndolo y asignando 1300 en lugar de “S” a la variable cuando detecte un sábado laborable, por ejemplo, y así con el resto. Pero es todo cuestión de orden y a mí me resulta más cómodo así, por tener todas las variables que pueda definir lo más agrupadas posibles, por si en algún momento tengo que modificar algo.

Bien, pues ya tenemos la variable $apaga con la hora de apagado de la pantalla, ahora nos metemos en el while y comprobamos:

Al inicio del bucle cargamos en la variable $quehora la hora actual del sistema en formato HHMM, acto seguido comparamos si el valor de dicha variable es superior al de la variable $apaga, en caso de serlo cambiamos el valor de la variable que hace que el bucle se ejecute, ponemos $ejecutacon valor “fin” y salimos del bucle.

Obviamente no es un apagado “exacto”, pero el sistema tampoco nos requiere una precisión de cirujano y nos sirve perfectamente.

Y ahora toca apagar la pantalla. Hay varias formas de hacer esto, yendo de lo más fácil a lo más complicado serían:

    • Quitar la señal al HDMI
    • Usar comandos CEC
    • Quitar la corriente a la pantalla con un relé usando un puerto GPIO

Vamos a comenzar probando a, simplemente, desactivar el HDMI. Hay unos cuantos monitores y pantallas que si dejan de recibir señal por HDMI se ponen automáticamente en standby transcurridos unos segundos, pues bien, probaremos a ver si tenemos suerte.

Escribimos esto en el shell. Esto desactivará el HDMI en el que tenemos conectada la pantalla:

vcgencmd display_power 0

Si al cabo de unos segundos la pantalla pasa a standby, fenomenal, yo no me complicaría más y lo dejaría así. Iríamos al final del archivo, donde están los if para realizar las tareas oportunas una vez que hemos salido del bucle, y colocaríamos nuestra condición para apagar:

Y para encender sería la misma instrucción pero con un “1”, que colocaríamos al principio del script:

vcgencmd display_power 1

Sin embargo en nuestro caso eso no ha funcionado, se desactivaba el HDMI pero se quedaba el cartelito de “no signal” ad eternum. Tocaba exprimirse las meninges.

Lo primero fue instalar las cec-utils

sudo apt-get install cec-utils

Y a continuación probar los comandos para apagar y encender:

Apagado:

echo standby 0 | cec-client -s -d 1

Para encender:

echo on 0 | cec-client -s -d 1

Y sí, eso en nuestra pantalla de pruebas, una Panasonic modelo viejo, gordo y pesado, funcionó. O casi, porque al encender se iba al canal de TV. Tras unas cuantas pruebas el comando que funcionó para cambiar al HDMI 1 que era donde estaba conectada la Raspberry fue el siguiente:

echo 'tx 4f:82:11:00' | cec-client -s -d 1

Sinceramente no puedo explicar bien estos comandos. Los de encendido y apagado lo he visto en varias páginas de Internet, los he probado y funcionan, el de cambio a HDMI ya es un poco “refrito y prueba”. Ayudado por la página: http://www.cec-o-matic.com/ “4” sería “fuente: playback 1”; “f”: “destino: broadcast”; “82”: “Used by a new source to indicate that it has started to transmit a stream OR used in response to a “Request Active Source” (Brodcast)”; y “11:00”: “physical address” que por lo que tengo entendido, con esos valores corresponde a HDMI 1 que es donde está pinchada la Raspberry.

Así que, con un poco de deducción y otro poco de la técnica que usó el burro del cuento para hacer sonar la flauta, el caso es que la tele se enciende, se apaga y conseguimos pasarla al HDMI que nos interesa. Así que nuestro if para apagar tiene que quedar así:

De paso añadimos también el apagado de pantalla si salimos con una opción que no sea la 003:

Y por supuesto añadimos el encendido de pantalla al principio del fichero con las dos instrucciones mencionadas: la de encendido y la de conmutar a HDMI 1

Y si esto tampoco funciona, entonces el asunto ya se nos complicaría un poco. Tendríamos que programar una salida del GPIO para que activara un relé que cerrara el paso de corriente de la pantalla, y que esta se encendiera cuando se activara el relé y se apagara cuando el relé le cortara la corriente. Pero ojo, además necesitaríamos que al encender así, a lo bruto, la pantalla se activara completamente y no quedara en standby que es como muchas suelen quedar si las desenchufamos a las bravas y las volvemos a enchufar, de ser así, de quedarse en standby, ya nos podemos ir olvidando porque no tendremos más remedio que usar el mando a distancia al no poder controlarla por HDMI de ninguna manera.

Y habréis visto que en el script hemos configurado el horario de apagado de las pantallas, pero no el de encendido. Eso lo haremos con crontab y lo trataremos en la siguiente entrada, pero antes de pasar a ella nos queda todavía algo pendiente ¿cómo actualizamos los archivos de fechas en la Raspberry? Necesitamos otro script.

Actualización de fechas en la Raspberry

Este va a ser fácil, será una modificación del script actualiza.sh La Raspberry comprobará si existe alguna diferencia entre los ficheros almacenados en las carpetas dias del servidor y la Raspberry, tal como se hace con los archivos a proyectar. La única diferencia es que aquí prescindiremos de la opción -delete y no borraremos nada .

Bueno, hay más diferencias, el script sólo nos mandará un correo si realiza alguna actualización en los archivos, en caso contrario no nos molestará. Lo que sí que hará será guardar la fecha y hora de cualquier actualización o comprobación exitosa, en un fichero que llamaremos calendario.txt, así, ante cualquier duda, podremos pedir un test y sabremos cuando se han actualizado o verificado estos archivos. En proyecta.sh lo integraríamos así:

Fragmento de “proyecta.sh”. Hemos insertado en las líneas 66 y 67 la información relativa a la fecha y hora de la actualización o verificación de los ficheros de fechas

Vamos ya con el script para actualizar el calendario, le llamaremos actualizadia.sh

Script muy sencillo para la actualización de los ficheros de fechas en la Raspberry. La verdad es que podría estar más currado, con algún control para verificar que todos los archivos necesarios existen en el servidor, las fechas están actualizadas, etc. pero en fin, eso lo dejaremos para una posterior actualización.

Vemos que la conexión y la actualización se realiza como ya hemos visto antes, aunque con las carpetas de los calendarios. En la línea 11 se graba la fecha si la actualización o verificación ha sido correcta y luego en la línea 18 al comparar los contenidos de los ficheros que contienen el antes y el después, sólo se mandará un mail si existe alguna diferencia.

Este script haremos que se ejecute diariamente a eso de las 6 am, a fin de que no interfiera con ninguna tarea.

Aquí está el código para copy&paste:

#!/bin/bash
nombrepantalla=$(hostname)
correo="[email protected]"
#Volcamos el contenido de los ficheros de "dias" en dias.old
cat /home/pi/dias/* > /home/pi/dias.old
rsync -rtvu --timeout=60 -e "ssh -i /home/pi/.ssh/miclave" [email protected]:/home/pi/datos/dias/ /home/pi/dias/
if [ $? -eq 0 ]
#Si no hay errores apuntamos en calendario.txt la fecha de comprobación
then
	resultado="Actualización calendario correcta"
	date > calendario.txt
fi
#Volvemos a volcar el contenido de los ficheros de "dias" esta vez en dias.new
#y comparamos los ficheros, si hay diferencias mandamos un correo
#así sólo se enviará un correo cuando haya una actualización exitosa
#y que modifique alguno de los archivos
cat /home/pi/dias/* > /home/pi/dias.new
diff -q /home/pi/dias.old /home/pi/dias.new > /dev/null
if [ $? -ne 0 ]
then
	hostname -I > /home/pi/ip.new
	cp /home/pi/ip.new /home/pi/ip.old
	printf "$resultado en $nombrepantalla\n\n" > /home/pi/report.txt
	printf "\nIp: " >> /home/pi/report.txt
	cat /home/pi/ip.new >> /home/pi/report.txt
	printf "\nActualizado a: " >> /home/pi/report.txt
	cat /home/pi/calendario.txt >> /home/pi/report.txt
	mutt -s "$resultado en $nombrepantalla" $correo < /home/pi/report.txt
fi

Para terminar, aquí está el código actualizado del script proyecta.sh con los últimos cambios que hemos visto en esta entrada:

#!/bin/bash
#Establecemos en estas variables:
#El nombre de la pantalla a gestionar
#El correo donde mandar los informes
#La variable $ejecuta: bucle, salir, apagar...
nombrepantalla=$(hostname)
longitud=${#nombrepantalla}
correo="[email protected]"
ejecuta="bucle"
#Almacenamos en la variable $apaga la hora de apagado según el tipo de día
case $1 in
	'N') apaga=2000 ;;
	'M') apaga=1500 ;;
	'S') apaga=1300 ;;
esac
#Encendemos la pantalla y seleccionamos HDMI 1
echo on 0 | cec-client -s -d 1
echo 'tx 4f:82:11:00' | cec-client -s -d 1
#Ocultamos el cursor
echo -e "\e[?1;0;0c"
clear
#Ejecutamos el bucle mientras $ejecuta sea "bucle"
while [ $ejecuta = "bucle" ]
do
#Comprobamos la hora para apagar la pantalla
#Si la sobrepasa cambiamos el valor de la variable $ejecuta y salimos del bucle
#Fuera del bucle compararemos el valor de la variable y apagaremos
quehora=$(date +%H%M)
if [ $quehora -gt $apaga ]
then
	ejecuta="fin"
	break
fi
#Refrescamos el directorio
cd .
#Recorremos el directorio
for file in /home/pi/Videos/*
do
#Metemos en variable el nombre y la extensión
extension="${file##*.}"
filename=$(basename $file)
fname="${filename%.*}"
#A continuación usaremos nombres de archivo que reservaremos para ejecutar diversas acciones
#Si el nombre coincide con el nombre de la pantalla pararemos el bucle
#borraremos el fichero y luego ejecutaremos la acción relacionada
if [ $fname = $nombrepantalla ]
	then
		ejecuta=$extension
		rm $file
		break
#Si el fichero comienza por XXX pero a continuación no se encuentra el nombre
#de la pantalla, lo ignoraremos.
elif [ ${fname:0:3} = "XXX" ] && [ ${fname:3:$longitud} != $nombrepantalla ]
	then
		:
#Si el nombre del fichero es test preparamos un informe que nos diga
#los ficheros que contiene el directorio, IP y la hora de actualización
elif [ $fname = 'test' ]
	then
		printf "Test de $nombrepantalla\n\nArchivos en directorio:\n\n" > /home/pi/report.txt
		ls /home/pi/Videos >> /home/pi/report.txt
		printf "\nIp: " >> /home/pi/report.txt
		hostname -I >> /home/pi/report.txt
		printf "\nActualizado a: " >> /home/pi/report.txt
		cat /home/pi/actualizacion.txt >> /home/pi/report.txt
		printf "\nActualizado a: " >> /home/pi/report.txt
		cat /home/pi/calendario.txt >> /home/pi/report.txt		
		mutt -s "Test $nombrepantalla" $correo < /home/pi/report.txt
		rm $file
#A partir de aquí suponemos que ya son ficheros para proyectar
#Si el fichero tiene extensión jpg lo mostramos en pantalla la duración establecida
elif [ $extension = 'jpg' ]
	then
		clear
		fim $file -a -c '{sleep 20;quit;};'
		clear
#En cualquier otro caso asumimos que se trata de un video y lo reproducimos
#Por defecto, al ser pantallas informativas,
#reproduciremos el vídeo sin sonido
#Si queremos sonido basta con poner al final del nombre de archivo AAA justo antes del punto de la extensión
else
	if [ ${fname: -3} = "AAA" ]
	then
		omxplayer  -b --blank --aspect-mode letterbox "$file" > /dev/null
	else
		omxplayer  -b --blank --aspect-mode letterbox --vol -6000 "$file" > /dev/null
	fi
fi
done
done
#Si salimos del bucle puede ser por algún fichero del tipo
#nombrepantalla.extensión y la extensión la tenemos en variable ejecuta
#La extensión deberá ser 001, 002, etc y según sea se ejecutará lo siguiente:
#Si es 001 apagamos la Raspberry
if [ $ejecuta = '001' ]
then
	sudo shutdown -h now
#Si es 002 reiniciamos:
elif [ $ejecuta = '002' ]
then
	sudo shutdown -r now
#Si es 003 borramos
elif [ $ejecuta = '003' ]
then
	clear
#Si hemos salido porque hay que apagar la pantalla:
elif [ $ejecuta = 'fin' ]
then
	echo standby 0 | cec-client -s -d 1
#En cualquier otro caso termina el script y borramos la pantalla
else
	clear
	echo standby 0 | cec-client -s -d 1
fi
#Podemos ir añadiendo las condiciones que queramos con elif
#Debemos evitar los ficheros de imagen y video con el nombre de la pantalla
#o el script se detendrá

Ficheros tratados en esta entrada

Listados con fechas

    • mananas.txt: fichero de texto con los días de apertura matinal
    • festivos.txt: fichero de texto con los días festivos
    • sabados.txt: fichero de texto con horario de sábados

Fichero de control de actualización de fechas

    • calendario.txt

Scripts

    • fecha.sh: script que activa o no proyecta.sh con argumento
    • actualizadia.sh: script que actualiza los calendarios de días en la Raspberry
    • proyecta.sh: modificación del script principal

Pantallas informativas (V): Informes de estado y actualizaciones

Es importante también saber qué es lo que pasa en nuestras Raspberry. Como comentábamos en la presentación de este proyecto, las pantallas estarán alejadas unas de otras y no podremos estar pendientes de si el fichero que hemos subido se está reproduciendo o no, necesitamos que la Raspberry “nos lo diga”.

La información nos la mandará la Raspbery por mail, para ello ha de instalarse el programa Mutt y el proceso para hacerlo con Gmail está descrito en esta entrada:

Mandando correos desde Raspberry con Mutt

Tal como pone en la entrada, se puede adaptar a otros servidores de correo de forma relativamente fácil.

Test

Bien, pues a partir de aquí damos por hecho que Mutt está instalado y funciona correctamente y comenzamos con un script para pedir información a la Raspberry y que nos diga:

      • Ficheros que tiene en el directorio para reproducir
      • IP
      • Fecha y hora de la última actualización

Vamos a comenzar creando un fichero de texto vacío en el home de pi en la Raspberry al que vamos a llamar report.txt

Y ahora preparamos el siguiente script:

#!/bin/bash	
printf "Test de funcionamiento\n\nArchivos en directorio:\n\n" > /home/pi/report.txt
ls /home/pi/Videos >> /home/pi/report.txt
printf "\nIp: " >> /home/pi/report.txt
hostname -I >> /home/pi/report.txt
mutt -s "Test Pantalla" [email protected] < /home/pi/report.txt

En la primera instrucción borramos el contenido de report.txt y le colocamos un par de líneas de introducción.

Las siguientes líneas irán añadiendo al fichero los siguientes datos:

    • Listado de ficheros en el directorio Videos
    • IP de la Raspberry (obtenida con hostname -I)

Y tras ello enviará el contenido del fichero report.txt al correo que hayamos puesto.

En este caso hemos llamado al fichero report01.sh así que probamos que funcione:

bash report01.sh

Comprobamos que a la dirección de correo que hayamos puesto en el script nos llega el mail con el listado del directorio y la IP y si todo ha ido bien seguimos. Retomaremos este script luego para añadirle lo referente a la última actualización que vamos a ver tras el siguiente apartado.

Cambio de IP

Antes de ir con el tema de las actualizaciones vamos a realizar otro script que será el que nos avise cuando haya un cambio de IP. Este script se ejecutará con crontab cada 15 minutos. El contenido es el siguiente:

El script se apoya en dos ficheros de texto que colocaremos en el home de pi de la Raspberry: ip.old e ip.new

Vemos que primero almacenamos en una variable el nombre de nuestra Raspberry. En la variable $correo tendremos que cambiar lo que hay por nuestro correo electrónico, igual que antes. Luego almacenamos la IP en ip.new y por último comparamos el contenido del fichero ip.new con ip.old y si hay alguna diferencia volcamos el contenido de ip.new en ip.old y mandamos un correo con la nueva IP

Aquí el código:

#!/bin/bash
#Guardamos en variables el nombre del hostname y el correo
nombrepantalla=$(hostname)
correo="[email protected]"
#Comprobamos la IP que tenemos asignada y la metemos al fichero new
hostname -I > /home/pi/ip.new
#Calculamos si hay diferencia pero sin salida
diff /home/pi/ip.new /home/pi/ip.old > /dev/null 2>&1
#Si hay alguna diferencia $? será 1, si fueran iguales será 0
if [ $? -eq 1 ]
then
	cat ip.new > ip.old
	mutt -s "Cambio IP $nombrepantalla" $correo < /home/pi/ip.new
fi

Llamaremos a este script: queip.sh

Actualizaciones

En la entrada II vimos la sincronización de carpetas e hicimos un script muy sencillo que llamamos actualiza.sh, ahora que la Raspberry puede mandar correos toca vitaminarlo, lo dejamos así:

Este script, como vemos, se apoya también en tres nuevos ficheros de texto que igual que antes colocaremos en /home/pi en la Raspberry, son los ficheros videos.new, videos.old y actualizacion.txt

Igual que como hemos hecho antes con la IP, aquí toca colocar nuestro correo electrónico y la IP del servidor, como vimos en la entrada II

En la línea 10 comprobaremos si la sincronización ha dado algún error o no y fijaremos el valor a dos variables que usaremos más adelante.

Antes de la sincronización hacemos un ls de Videos y después de la sincronización otro, ambos los guardamos en videos.old y videos.new respectivamente y en la línea 22 comparamos con diff -q ambos ficheros. La opción -q de diff nos va a decir si son o no son diferentes, así, en la línea 25 comprobamos tanto si los ficheros son diferentes como si ha habido algún error en la actualización, así, sólo mandaremos un correo si se ha actualizado algo o bien si no se ha podido actualizar, en el resto de casos, cuando no haya nada que actualizar y la conexión no haya devuelto un error, no se mandará correo.

En la línea 35 vemos que la fecha y hora de la actualización la guardaremos en el fichero actualizacion.txt que nos servirá para cuando pidamos un test.

Para el envío del correo nos apoyamos en el fichero report.txt comentado al inicio de esta entrada y ahora volveremos a ella, antes el código de actualiza.sh:

#!/bin/bash
#Guardamos en variables el nombre del hostname y el correo
nombrepantalla=$(hostname)
correo="[email protected]"
#Copiamos el contenido de la carpeta al archivo videos.old
ls /home/pi/Videos > /home/pi/videos.old
#Sincronizamos
rsync --delete -rtvu --timeout=60 -e "ssh -i /home/pi/.ssh/miclave" [email protected]:/home/pi/datos/pantallas/ /home/pi/Videos/
#Comprobamos si la sincronización ha dado errores o no
if [ $? -eq 0 ]
then
	resultado="Actualización correcta"
	resul="si"
else
	resultado="Error de actualización"
	resul="no"
fi
#Ahora volvemos a copiar el contenido de Videos, pero esta vez
#lo hacemos en el archivo videos.new
ls /home/pi/Videos > /home/pi/videos.new
#Y comparamos el contenido de los dos archivos
diff -q /home/pi/videos.old /home/pi/videos.new > /dev/null
#Si el contenido de los archivos es distinto
#O si se ha producido un error en la sincronización de carpetas
if [ $? -ne 0 ] || [ $resul = "no" ]
#Entonces mandamos el oportuno correo
then
	hostname -I > /home/pi/ip.new
	cp /home/pi/ip.new /home/pi/ip.old
	printf "$resultado de $nombrepantalla\n\nArchivos en directorio:\n\n" > /home/pi/report.txt
	ls /home/pi/Videos >> /home/pi/report.txt
	printf "\nIp: " >> /home/pi/report.txt
	cat /home/pi/ip.new >> /home/pi/report.txt
	printf "\nActualizado a: " >> /home/pi/report.txt
	date > actualizacion.txt
	cat /home/pi/actualizacion.txt >> /home/pi/report.txt
	mutt -s "$resultado de archivos $nombrepantalla" $correo < /home/pi/report.txt
fi

Volvemos a test

Ahora que ya hemos visto los informes sobre cambio de IP y las actualizaciones, volvemos al script de inicio y lo dejamos así:

Hemos añadido poca cosa, tan sólo el contenido del fichero actualizacion.txt para que nos diga la fecha y hora de la última actualización de contenidos ahora que ya tenemos lo anterior programado.

Podemos probar este script, pero sólo será una prueba pues su uso se integrará en el scrip principal, en proyecta.sh, no obstante ahí va:

#!/bin/bash	
printf "Test de funcionamiento\n\nArchivos en directorio:\n\n" > /home/pi/report.txt
ls /home/pi/Videos >> /home/pi/report.txt
printf "\nIp: " >> /home/pi/report.txt
hostname -I >> /home/pi/report.txt
printf "\nActualizado a: " >> /home/pi/report.txt
cat /home/pi/actualizacion.txt >> /home/pi/report.txt
mutt -s "Test Pantalla" [email protected] < /home/pi/report.txt

Modificando proyecta.sh

Es aquí cuando tenemos que integrar el “test de funcionamiento” en el script principal, y lo vamos a hacer así:

Comenzamos poniendo al principio una variable que almacene nuestro correo electrónico.

Y ahora, en un elif, si en el directorio Videos metemos un fichero que se llame test.txt (la extensión en realidad da lo mismo, pero mejor hacer un txt vacío) se ejecutará el código.

Vemos que lo hemos variado un poco, poniendo la variable $nombrepantalla y el correo también va por variable.

Una vez que nos envíe el correo borrará el fichero de la Raspberry.

Dos consideraciones: Esto afecta a todas las pantallas (a todas las Raspberrys) que estemos controlando, así que si tenemos cuatro, cada una nos enviará un correo. También habrá que estar atento a borrar el fichero text.txt del servidor, pues si no a la siguiente actualización lo volverían a cargar y de nuevo nos enviarían otros tantos correos.

El código que “de momento” va teniendo el script proyecta.sh es el siguiente:

#!/bin/bash
#Establecemos en estas variables:
#El nombre de la pantalla a gestionar
#El correo donde mandar los informes
nombrepantalla=$(hostname)
longitud=${#nombrepantalla}
correo="[email protected]"
ejecuta="bucle"
#Ocultamos el cursor
echo -e "\e[?1;0;0c"
clear
#Ejecutamos el bucle mientras $ejecuta sea "bucle"
while [ $ejecuta = "bucle" ]
do
#Refrescamos el directorio
cd .
#Recorremos el directorio
for file in /home/pi/Videos/*
do
#Metemos en variable el nombre y la extensión
extension="${file##*.}"
filename=$(basename $file)
fname="${filename%.*}"
#A continuación usaremos nombres de archivo que reservaremos para ejecutar diversas acciones
#Si el nombre coincide con el nombre de la pantalla pararemos el bucle
#borraremos el fichero y luego ejecutaremos la acción relacionada
if [ $fname = $nombrepantalla ]
	then
		ejecuta=$extension
		rm $file
		break
#Si el fichero comienza por XXX pero a continuación no se encuentra el nombre
#de la pantalla, lo ignoraremos.
elif [ ${fname:0:3} = "XXX" ] && [ ${fname:3:$longitud} != $nombrepantalla ]
	then
		:
#Si el nombre del fichero es test preparamos un informe que nos diga
#los ficheros que contiene el directorio, IP y la hora de actualización
elif [ $fname = 'test' ]
	then
		printf "Test de $nombrepantalla\n\nArchivos en directorio:\n\n" > /home/pi/report.txt
		ls /home/pi/Videos >> /home/pi/report.txt
		printf "\nIp: " >> /home/pi/report.txt
		hostname -I >> /home/pi/report.txt
		printf "\nActualizado a: " >> /home/pi/report.txt
		cat /home/pi/actualizacion.txt >> /home/pi/report.txt
		mutt -s "Test $nombrepantalla" $correo < /home/pi/report.txt
		rm $file
#A partir de aquí suponemos que ya son ficheros para proyectar
#Si el fichero tiene extensión jpg lo mostramos en pantalla la duración establecida
elif [ $extension = 'jpg' ]
	then
		clear
		fim $file -a -c '{sleep 20;quit;};'
		clear
#En cualquier otro caso asumimos que se trata de un video y lo reproducimos
#Por defecto, al ser pantallas informativas,
#reproduciremos el vídeo sin sonido
#Si queremos sonido basta con poner al final del nombre de archivo AAA justo antes del punto de la extensión
else
	if [ ${fname: -3} = "AAA" ]
	then
		omxplayer  -b --blank --aspect-mode letterbox "$file" > /dev/null
	else
		omxplayer  -b --blank --aspect-mode letterbox --vol -6000 "$file" > /dev/null
	fi
fi
done
done
#Si salimos del bucle puede ser por algún fichero del tipo
#nombrepantalla.extensión y la extensión la tenemos en variable ejecuta
#La extensión deberá ser 001, 002, etc y según sea se ejecutará lo siguiente:
#Si es 001 apagamos la Raspberry
if [ $ejecuta = '001' ]
then
	sudo shutdown -h now
#Si es 002 reiniciamos:
elif [ $ejecuta = '002' ]
then
	sudo shutdown -r now
#Si es 003 borramos
elif [ $ejecuta = '003' ]
then
	clear
#En cualquier otro caso termina el script y borramos la pantalla
else
	clear
fi
#Podemos ir añadiendo las condiciones que queramos con elif
#Debemos evitar los ficheros de imagen y video con el nombre de la pantalla
#o el script se detendrá

Ficheros tratados en esta entrada

Ficheros de texto en /home/pi

    • ip.new: de texto, lo creamos vacío, sirve para saber si cambia la IP
    • ip.old: de texto, lo creamos vacío, sirve para saber si cambia la IP
    • videos.new: de texto, lo creamos vacío, sirve para controlar las actualizaciones
    • videos.old: de texto, lo creamos vacío, sirve para controlar las actualizaciones
    • actualizacion.txt: de texto, lo creamos vacío, sirve para controlar las actualizaciones
    • report.txt: de texto, lo creamos vacío, contendrá el cuerpo de los email

Scripts

    • queip.sh: controla los cambios de IP
    • actualiza.sh: sincroniza el contenido de las carpetas
    • proyecta.sh: (continuación): script principal

Otros ficheros

    • test.txt: de texto, lo creamos vacío, sirve para pedir un test a las Raspberrys, se ha de subir a la carpeta origen del servidor

Pantallas informativas (IV): Automatización de todo el proceso

En las entradas anteriores hemos preparado la conexión con el servidor, la sincronización de carpetas, la reproducción de imágenes y de vídeos. Ahora toca ir juntando todo y comenzar a automatizar el proceso.

En esta entrada vamos a preparar el script para que, de momento, haga lo siguiente:

      • Reproducir en bucle las imágenes y vídeos
      • Reproducir archivos en una determinada pantalla
      • Reproducir vídeos sin sonido y opcionalmente con sonido
      • Salir del bucle
      • Reiniciar la Raspberry
      • Apagar la Raspberry

La automatización no va a verse sólo en esta entrada, ocupará también las siguientes porque vamos a ir añadiendo funcionalidades, pero el que nos ocupa podría decirse que es el fichero principal, el alma del proyecto.

Para la entrada que nos ocupa tenemos casi todas las piezas del puzzle, pero nos faltan algunas: ¿cómo le decimos que salga del bucle, se apague o reinicie a la raspberry?

Una solución podría ser que cargase un fichero sh y lo ejecutara, así podríamos hacer cualquier cosa. Si estuviéramos en una red privada y segura, sería una opción muy cómoda, pero para el entorno donde van a estar dando servicio las pantallas, ni de broma me parece que sea una buena opción poder cargar “cualquier” sh y que se ejecute.

La solución que se me ha ocurrido es “jugar” con los nombres de archivo y sus extensiones. Pongamos que quiero apagar la pantalla llamada “pantalla1”, pues subo al servidor un archivo que se llame “pantalla1.001” y cuando la Raspberry actualice sus archivos y lea este, que se apague. No es un apagado inmediato, pero es un apagado controlado.

Para que se reinicie utilizaremos la extensión “002” y para que simplemente salga, la “003”. Si queremos que haga más cosas podemos ir añadiendo extensiones.

¿Cómo hacemos que un archivo que carguen todas las pantallas lo reproduzca sólo una en concreto? Pues por ejemplo haciendo que el nombre del archivo comience por “XXX” y poniendo a continuación el nombre de la pantalla que lo ha de reproducir, por ejemplo: XXXpantalla4aviso01.jpg

Y para el audio de los videos, por defecto se reproducirán sin audio, pero si en algún caso en concreto lo queremos entonces tendremos que poner “AAA” justo antes de la extensión, por ejemplo: charla_juevesAAA.avi

Y ahora que lo tenemos todo definido vamos a ver cómo quedaría el código del script por partes:

Comenzamos metiendo en una variable el nombre de la pantalla, la longitud de dicho nombre y asignando el valor “bucle” a una tercera variable que usaremos tanto para ejecutar el bucle mientras mantenga ese valor o ejecutar determinadas acciones cuando cambie, por ejemplo por una extensión de archivo.

El echo de la línea 8 oculta, no del todo pero casi, el cursor. No queremos verlo parpadeando al reproducir un jpg

Comenzamos el bucle y por cada fichero que tengamos en Videos cargamos en una variable su extensión, la ruta completa del fichero y el nombre del fichero.

A continuación comprobamos si el nombre del fichero coincide con el nombre de la pantalla, y de ser así cambiamos el valor de la variable $ejecuta por la extensión del fichero. Esto por sí sólo ya evitaría que se ejecutara una nueva iteración del bucle, porque ya no se cumpliría el while, pero además ejecutamos un break y salimos en este mismo momento, no sin antes borrar el fichero.

En este punto vamos a dar un salto hasta el final del script para ver qué hace ahora la variable $ejecuta y cómo:

Fácil, como puede verse: if, elif, elif y else. De hecho hasta nos podríamos haber ahorrado el elif del 003, ya que tal como está ahora si le ponemos como extensión 003 o 887 va ha hacer un clear. Pero bueno, lo dejamos ahí porque en posteriores secciones aquí meteremos alguna cosa más.

Como $ejecuta ha tomado el valor de la extensión del archivo, pues si vale 001 apaga la Raspberry, con 002 la reinicia y simplemente hace un borrado de pantalla con 003 o cualquier otro.

Seguimos donde habíamos dejado antes el script. Ahora toca controlar los ficheros que van a una determinada pantalla, así:

Aunque en realidad lo que hacemos es justo lo contrario, es decir: Si el fichero comienza por XXX y a continuación no va el nombre de nuestra pantalla, no hacemos nada.

Y no lo borramos, porque si a la siguiente actualización sigue estando en el servidor, nos lo volvería a cargar, así que simplemente lo ignoramos.

Llegados a este punto ya tenemos que empezar a mostrar algo en la pantalla, que ya va siendo hora, así que comenzamos con los jpg:

Fácil, esto ya lo teníamos hecho de antes, aunque aquí ya les damos una duración de 20 segundos en pantalla, suficiente para publicitar un evento y que pueda leerse sin que se haga demasiado monótono. Ahora seguimos con los vídeos:

En primer lugar comprobamos si el nombre del fichero acaba en AAA, de ser así lo reproduciremos con audio con la instrucción que anteriormente habíamos probado.

En caso contrario se reproducirá sin sonido, para ello hay que añadir a la instrucción la opción –vol -6000

Y con esto ya está todo de momento. Este sería el script completo, por ahora:

#!/bin/bash
#Establecemos en estas variables:
#El nombre de la pantalla a gestionar
nombrepantalla=$(hostname)
longitud=${#nombrepantalla}
ejecuta="bucle"
#Ocultamos el cursor
echo -e "\e[?1;0;0c"
clear
#Ejecutamos el bucle mientras $ejecuta sea "bucle"
while [ $ejecuta = "bucle" ]
do
#Refrescamos el directorio
cd .
#Recorremos el directorio
for file in /home/pi/Videos/*
do
#Metemos en variable el nombre y la extensión
extension="${file##*.}"
filename=$(basename $file)
fname="${filename%.*}"
#A continuación usaremos nombres de archivo que reservaremos para ejecutar diversas acciones
#Si el nombre coincide con el nombre de la pantalla pararemos el bucle
#borraremos el fichero y luego ejecutaremos la acción relacionada
if [ $fname = $nombrepantalla ]
	then
		ejecuta=$extension
		rm $file
		break
#Si el fichero comienza por XXX pero a continuación no se encuentra el nombre
#de la pantalla, lo ignoraremos.
elif [ ${fname:0:3} = "XXX" ] && [ ${fname:3:$longitud} != $nombrepantalla ]
	then
		:
#A partir de aquí suponemos que ya son ficheros para proyectar
#Si el fichero tiene extensión jpg lo mostramos en pantalla la duración establecida
elif [ $extension = 'jpg' ]
	then
		clear
		fim $file -a -c '{sleep 20;quit;};'
		clear
#En cualquier otro caso asumimos que se trata de un video y lo reproducimos
#Por defecto, al ser pantallas informativas,
#reproduciremos el vídeo sin sonido
#Si queremos sonido basta con poner al final del nombre de archivo AAA justo antes del punto de la extensión
else
	if [ ${fname: -3} = "AAA" ]
	then
		omxplayer  -b --blank --aspect-mode letterbox "$file" > /dev/null
	else
		omxplayer  -b --blank --aspect-mode letterbox --vol -6000 "$file" > /dev/null
	fi
fi
done
done
#Si salimos del bucle puede ser por algún fichero del tipo
#nombrepantalla.extensión y la extensión la tenemos en variable ejecuta
#La extensión deberá ser 001, 002, etc y según sea se ejecutará lo siguiente:
#Si es 001 apagamos la Raspberry
if [ $ejecuta = '001' ]
then
	sudo shutdown -h now
#Si es 002 reiniciamos:
elif [ $ejecuta = '002' ]
then
	sudo shutdown -r now
#Si es 003 borramos
elif [ $ejecuta = '003' ]
then
	clear
#En cualquier otro caso termina el script y borramos la pantalla
else
	clear
fi
#Podemos ir añadiendo las condiciones que queramos con elif
#Debemos evitar los ficheros de imagen y video con el nombre de la pantalla
#o el script se detendrá

No está acabado pero casi. Más adelante lo completaremos con un par de cosas y por último lo afinaremos para que se ejecute desde crontab.


Ficheros tratados en esta entrada

    • proyecta.sh: nuestro script principal (preparación)