Archives par étiquette : dashboard

3 – Principe de base

Une page pour les afficher tous

Voici l’arborescence des fichiers nécessaires à l’affichage des différents modules.

  • index.php : page principale qui est appelée par le navigateur
  • ajax.php : contient le code exécuté lors des requêtes AJAX
  • inc.php : contient les fonctions PHP
  • javascript.js : contient les fonctions Javascript
  • style.css : feuille de style

Structure on-ne-peut-plus simple. La page « index.php » est appelée par le navigateur, et une fonction JavaScript se charge d’afficher le contenu de chaque module au bon endroit dans la page et de rafraichir l’affichage à intervalle régulier.

Note : Vous pourrez télécharger tous les fichiers dans une archive dans le dernier article du projet.

index.php

La page d’index se contente de positionner les balises <div> dans lesquelles seront affichés chaque module.

Dans la balise <head>, il y a une balise <meta> dont le but est de rafraichir la page toute les 3600 secondes (soit toutes les heures). Ainsi, toute la page sera rechargée, les compteurs JavaScript réinitialisés, les mise à jour du code sont prises en compte, etc…

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title>Jarvis</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <meta http-equiv="Content-Language" content="Fr"/>
    <meta name="Author" lang="fr" content="Torna"/>
    <meta name="Copyright" content="© Torna"/>
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Expires" content="0">
    <meta http-equiv="refresh" content="3600;url=index.php">
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
    <script type="text/javascript" src="javascript.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css"/>
  </head>
  <body>

    <div id="main">
      <!--div id="etalon">Jarvis <?php //echo date("Y-m-d H:i:s"); ?></div-->
      <div id="horloge"></div>
      <div id="meteo"></div>
      <div id="meteo_black"></div>
      <div id="ts3"></div>
      <div id="ping"></div>
      <div id="analytics"></div>
      <div id="vpn"></div>
      <div id="latency"></div>
      <div id="earth"><img id="img_earth" src="pict/blank.png"></div>
      <div id="moon"><img id="img_moon" src="pict/blank.png"></div>
      <div id="ifstat_oberon_up"><img id="img_oberon_up" src="pict/blank.png"></div>
      <div id="ifstat_oberon_down"><img id="img_oberon_down" src="pict/blank.png"></div>
    </div>

    <?php include('inc.php'); echo analytics(); ?>

  </body>
</html>

Pour les modules qui affichent des images (phases terre et lune, graphique de bande passante), il convient d’insérer une image vide « blank.png » afin d’éviter l’affichage d’une erreur dans la page en attendant que la requète AJAX chargée de l’affichage de l’image soit terminée.

ajax.php

C’est ce fichier qui est appelé par les requètes AJAX et qui est chargé d’appeler la bonne fonction à exécuter.

<?php

  header('Content-type: text/html; charset=utf-8');
  require_once('inc.php');

  if(isset($_REQUEST['block'])){$block = $_REQUEST['block'];}else{$block = 'none';}

  /////////////////////////////////////////////////
  //  LATENCY
  /////////////////////////////////////////////////

  if($block == 'latency'){
    echo latency();
  }

  /////////////////////////////////////////////////
  //  TS3
  /////////////////////////////////////////////////

  else if($block == 'ts3'){
    echo ts3();
  }

  /////////////////////////////////////////////////
  //  METEO
  /////////////////////////////////////////////////

  else if($block == 'meteo'){
    echo meteo();
  }

  /////////////////////////////////////////////////
  //  PING
  /////////////////////////////////////////////////

  else if($block == 'ping'){
    echo ping();
  }

  /////////////////////////////////////////////////
  //  VPN PPTPD
  /////////////////////////////////////////////////

  else if($block == 'vpn'){
    echo vpn();
  }

  /////////////////////////////////////////////////
  //  IFSTAT
  /////////////////////////////////////////////////

  else if($block == 'ifstat'){
    imagickHisto ($_REQUEST['max'], $_REQUEST['eth'], $_REQUEST['up_down']);
  }

?>

Un bloc de code pour chaque module. En fonction de la variable $block passée en paramètre de la requète AJAX, on appelle la fonction ciblée.

inc.php

Ce fichier contient les fonctions appelées par le fichier ajax.php. Même principe, un bloc de code pour chaque module.

En gros, une fonction par module 🙂 Le détail de chaque fonction fera l’objet d’un article à suivre.

javascript.js

Ce fichier contient les fonctions JavaScript qui vont lancer les requêtes AJAX à intervalle réuglier.

/* initialisation des fonctions de tous les modules au chargement de la page */
$(document).ready(function() {
   fonction_nom_du_module_1 ();
   fonction_nom_du_module_2 ();
   ...
});

/* initialisation des variables timeout pour chaque module */
var timeout_nom_du_module_1; 
var timeout_nom_du_module_2;
...

/* définition de la fonction pour le module nom_du_module_1 */

function nom_du_module_1 ()
{
  $.ajax({
    async : false,
    type: "GET",
    url: "./ajax.php",
    data: "block=nom_du_module_1",
    success: function(html){
      $("#nom_du_module_1").html(html);
    }
  });

  g = setTimeout("nom_du_module_1()", 10000);
}

Je ne mets ici que la structure gobale du fichier pour expliquer le principe. Le fichier complet sera disponible au téléchargement dans le dernier article.

style.css

Ce fichier contient la feuille de style de la page, c’est elle qui contient toutes les options de mise en forme des éléments de la page (position, taille, couleurs, etc…).

/* init */

body
{
  background-color  : rgba(0, 0, 50, 1);
  font-family       : Verdana, "Courier New", "Lucida Console";
  font-size         : 14px;
  font-weight       : normal;
  padding           : 0px;
  margin            : 0px;
  overflow          : hidden;
}

div, table, tr, td, th, h1, h2, h3, a, span, p, pre, fieldset, input, textarea, form, img, select
{
  font-family       : Verdana, "Courier New", "Lucida Console";
  font-size         : 14px;
  font-weight       : normal;
  padding           : 0px;
  margin            : 0px;
  color             : #FFF;
}

/* main */

div#main
{
  width             : 1366px;
  height            : 768px;
  position          : relative;
  overflow          : hidden;
  background-color  : rgba(0, 0, 0, 1);
  color             : #FFF;
}

La div qui a pour id main est la div qui contient toutes les autres div de chaque module. Ici, je fixe sa taille en pixel à la résolution native de mon écran 1366×768 px.

La couleur de fond (background-color) est fixée au noir mais vous pouvez choisir ce qui vous plaît 🙂

1 – Raspberry Pi Home Dash Screen

J’ai toujours été passionné par la domotique et par tout ce qui touche à l’informatique dans la maison. J’adore les stations météo qui affichent tout un tas d’informations, les horloges high-tech qui font calendrier et qui sonnent quand vous recevez un mail, les cadres photos numériques, etc,… En revanche, vous ne trouverez chez moi aucun de ces appareils (ou presque). En effet, je trouve que ce qui existe aujourd’hui est soit beaucoup trop limités, soit très limités et beaucoup trop chers 😀

En parallèle, j’aime avoir une vue globale sur ce qui se passe sur mon réseau informatique à la maison. Savoir comment se porte ma bande passante, connaitre l’état de mes serveurs et NAS, savoir qui est connecté sur mon serveur TeamSpeak ou sur mon VPN, etc…

J’adore également savoir quelle est la phase de la lune, et connaître l’ensoleillement de la terre au cours de la journée, l’heure du lever et du coucher du soleil… Les écrans de contrôle de la NASA, comme on les voit dans les films, me font un certain effet 😉

Donc voilà, un projet relativement simple mais très fun, un écran tableau de bord pour regrouper toutes ces données 🙂

Bien en vue au dessus du bar qui sépare ma cuisine de mon séjour, voici ce que ça donne :

IMG_5393

Cette photo montre la chose dans sa version finale, proprement installée au mur.

En détail

Voilà la liste des informations qui sont représentées sur cet écran :

  • Phase de la terre avec cartographie des nuages en temps réel
  • Phase de la lune
  • Heure
  • Date
  • Météo du jour
  • Prévisions météo à 3 jours
  • Etat des serveurs et NAS
  • Clients TeamSpeak connectés
  • Clients VPN connectés
  • Bande passante internet

J’ajoute de temps en temps des fonctionnalités, tant qu’il reste de la place pour afficher des informations. L’important c’est que ce soit visible/lisible depuis n’importe où dans la pièce.

Voici ce que ça donne en détail (cliquez pour agrandir) :

dashscreen_final

Le matériel nécessaire

Contrairement à mes autres projets, ici un Raspberry Pi et un écran suffisent… D’ailleurs, vous pouvez utiliser autre chose qu’un Pi, notamment un vieux eeePC ou PC portable pour rester dans le compacte 🙂 Une tour standard fera également l’affaire si vous pouvez la planquer. J’ai simplement ajouté un dongle WiFi pour connecter le Pi au réseau car aucune prise RJ45 n’arrive à cet endroit. Un support mural pour accrocher l’écran au mur ainsi qu’une goulotte extra plate pour masquer les câbles d’alimentation. Si vous choisissez de poser votre écran au dessus de votre frigo par exemple, vous économiserez l’achat de ces deux accessoires.

Pour l’écran, choisissez le moins cher. Préférez les écrans LED, plus fin et qui consomment moins. Ils offrent généralement un angle de vue plus large (pratique quand on se balade dans la pièce), ainsi qu’une luminosité plus élevée. Si vous souhaitez l’accrocher au mur, il doit biensur disposer des fixations de type VESA. Pour la taille, c’est comme vous voulez, et cela dépend de ce que vous voulez afficher. Sachez toutefois qu’un écran de plus de 20-22″ devient vite encombrant… Je vous conseille donc une écran entre 16″ et 20″, au format 16/9 (plus esthétique qu’un 4/3), avec un bord fin (toujours par souci d’esthétisme 😉 ).

Voici en détail ce que j’ai choisi :

  • Un Raspberry Pi Modèle B 512Mo
  • Une carte mémoire SDHC 8Go
  • Un dongle WiFi USB NetGear WNA1000M
  • Un écran LCD AOC e941Vwa 18,5″ (n’importe quel écran peut convenir)
  • Un support mural VESA pour écranAavara EL1010
  • Une goulotte plate pour cacher les câbles

Dans les prochains articles, j’expliquerai comment procéder pour créer facilement ce type d’écran et je publierai tout mon code source en détaillant le fonctionnement de chaque module.

Comme toujours, et j’insiste, je compte sur vous pour alimenter la chose, partager vos avis et idées d’amélioration. Si vous réalisez quelque chose de ce type, ce pourrait être génial de créer un soft avec une bibliothèque qui regrouperait tous les modules créés. 🙂

IMG_5389

10 – Le serveur de monitoring

Au départ je souhaitais pouvoir prendre la main à distance sur le Pi pour m’assurer que tout fonctionne bien durant la semaine. Cependant, les contraintes d’autonomie du Pi sur batterie me forçant à ne temporiser sont fonctionnement 2 minutes toutes les 10 minutes, la fenêtre de tir pour choper le Pi lorsqu’il est up est franchement serrée.

Sans compter sur l’autonomie, je ne pourrai pas surveiller le Pi à longueur de journée pour voir si tout fonctionne bien, que rien ne gêne les prises de vue, etc,… Il faut donc que le Pi se manifeste tout seul, régulièrement : c’est le principe du « battement de coeur ». Le Pi envoi de lui même des battements de coeur réguliers, et si les battements de coeur s’arrêtent, c’est que quelque chose à mal tourné.

L’idée est donc d’utiliser un serveur distant, qui accusera réception de ces battements de coeur, et m’enverra un mail pour me dire que tout va bien. Chaque battement de coeur sera accompagné d’une miniature de la dernière photo prise. Tout est enregistré dans une base de données, et j’ai prévu une page qui récapitule toutes les battements de coeur enregistrés avec leur photo miniature. Ce qui me permettra également d’avoir un aperçu des travaux en direct.

Inutile d’envoyer un battement de coeur à chaque prise de vue. Un mail toutes les 10 minutes, c’est trop. Deux mails par heure, c’est mieux 🙂

Serveur web – Apache

Très concrètement, le serveur de monitoring est un simple serveur web Apache, accompagné d’une base de données MySQL et de quelques scripts PHP.

Je ne détail pas ici l’installation et la configuration de ces composants. C’est un sujet bien à part qui mériterait un chapitre complet. Si vous voulez faire pareil, vous pouvez utiliser un hébergement mutualisé chez Free ou OVH (les ftp Free mis à disposition des Freenautes font également parfaitement l’affaire).

Base de données

Je n’ai besoin que d’une seule table pour enregistrer chaque battement de coeur ainsi que la photo associée. Voici le schéma de la table heartbeat que j’utilise :

CREATE TABLE `heartbeat` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date` int(11) NOT NULL,
  `log` text COLLATE utf8_bin NOT NULL,
  `img` text COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;

Description des champs :

  • id : un simple indexe auto incrémenté
  • date : champ entier pour stocker la date du battement de coeur sous la forme d’un timestamp
  • log : champt text permettant d’enregistrer des infos diverses
  • img : champ text contenant le nom de la miniature de la photo associée au battement de coeur

Fonctions et fichier de configuration

J’ai rassembler les fonctions génériques que j’utilise dans le fichier func.php que vous pouvez télécharger ici : func.php.tar.gz

L’ensemble des paramètres de configuration des scripts PHP sont rassemblés dans le fichier conf.php que vous pouvez télécharger ici : conf.php.tar.gz

Script PHP heartbeat.php

C’est ce script qui va être appelé par la commande curl comme expliqué dans le chapitre précédent. Sont rôle est de réceptionner la photo miniature, d’enregistrer la date de réception, et d’envoyer un mail.

Je vais à l’essentiel, le code de ce script est donc très minimaliste :

<?php

header('Content-type: text/html; charset=utf-8');

// inclusion des fichiers requis
require_once('conf.php');
require_once('func.php');

// initialisation de la session
$session_id = sessionStart();
// connexion à la base de données
$db_connect = dbConnect();
// nettoie la variable $_REQUEST
$_REQUEST   = clearRequest ($_REQUEST);

// enregistre la tentative de connexion dans un fichier de log
logIt('access', 'heartbeat connection ...');

// vérifie que le token donné correspond à celui attendu ;)
if(!empty($_REQUEST['token']) && $_REQUEST['token'] == $conf['token']['heartbeat']) {

  // si le fichier est bien réceptionné
  if (is_uploaded_file($_FILES['img']['tmp_name'])) {
    if (move_uploaded_file($_FILES['img']['tmp_name'], $conf['upload_dir'].'/'.$_FILES['img']['name'])) {

      $field_data = array (
                              'date'  => time(),
                              'img'   => $_FILES['img']['name'],
                              'log'   => ''
                          );
      // enregistre le battement de coeur en base de données dans la table heartbeat
      $add        = addData ('heartbeat', $field_data);

      // titre du mail à envoyer
      $mail_title = 'Pi TimeLapse - HeartBeat '.date("d.m.y H:i:s");

      // corps du mail à envoyer, contenant la date et la minature
      $mail_core  = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
                      <html>
                        <head>
                          <title>'.$conf['site_name'].'</title>
                          <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
                          <meta http-equiv="Content-Language" content="Fr" />
                          <META name="Author" lang="fr" content="MagdiWeb" />
                          <META name="Copyright" content="© MagdiWeb" />
                        </head>
                        <body>
                          '.$mail_title.'<br>
                          '.$field_data['log'].'<br>
                          <img src="http://pi_timelapse/'.$conf['upload_dir'].'/'.$_FILES['img']['name'].'">

                        </body>
                      </html>';

      // envoie le mail
      $mail       = mailIt ('mon_adresse_mail@mail.fr', $mail_title, $mail_core);
      logIt('access', $mail_title);
    }
    else {
      logIt('access', 'unable to mv uploaded file '.$_FILES['img']['name']);
    }
  }
  else{
    logIt('access', 'error during file upload ...');
  }

}
else{
  logIt('access', 'wrong heartbeat token ...');
}

// écrit l'ensemble des messages/logs dans un fichier
writeLog ();

?>

Voici une screenshot du mail que je reçois sur mon smartphone lors de la réception d’un battement de coeur de la part du Pi (sur la photo on voit un bout de mon bureau,… c’est pour les tests 😉 :

photo

Dashboard

J’ai également prévu une page type dashboard qui affiche toutes les miniatures reçues avec les dates. Pour le moment, je ne publie pas cette page, mais lorsque le chantier commencera, je la synchroniserai avec ce blog afin que vous puissiez suivre en directe l’évolution des travaux, et constater que le dispositif fonctionne bien 🙂

Toujours aussi minimaliste, le but est simplement d’afficher l’ensemble des miniatures sous forme d’une mosaïque d’images.

<?php

header('Content-type: text/html; charset=utf-8');

require_once('conf.php');
require_once('func.php');

$session_id = sessionStart();
$db_connect = dbConnect();
$_REQUEST   = clearRequest ($_REQUEST);

if(!empty($_REQUEST['token']) && $_REQUEST['token'] == $conf['token']['index']) {

$heartbeats = selectAll ('heartbeat');

?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title><?php echo $conf['site_name']; ?></title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <meta http-equiv="Content-Language" content="Fr" />
    <META name="Author" lang="fr" content="MagdiWeb" />
    <META name="Copyright" content="© MagdiWeb" />  
    <link rel="stylesheet" type="text/css" href="style.css" />
  </head>
  <body>
    <?php
      $nb_heartbeat = sizeof($heartbeats);
      echo '('.$nb_heartbeat.' beats)';
    ?>
    <table>
    <?php
      $c = 0;
      foreach($heartbeats as $heartbeat) {
        if($c == 0){echo '<tr>';}
        echo '<td>'.humanDate ($heartbeat['date'], 'full').'<br><img src="'.$conf['upload_dir'].'/'.$heartbeat['img'].'"></td>';
        $c++;
        if($c == 4){$c=0; echo '</tr>';}
      }
    ?>
    </table>

  </body>
</html>

<?php

}else{
  logIt('access', 'wrong index token ...');
}

writeLog ();

?>

Voici une screenshot de ce que cela donne pour l’instant (toujours avec des photos de test 😉 :

Screenshot-1