1 nouveauté hors suivi
Leçon

Concevoir un tableau de bord qui se lit

Jour 1 · Concevoir et construire · 30 min

Grafana s'apprend en une heure. Produire un écran qu'un exploitant lit en trois secondes demande davantage — et c'est là que se joue l'utilité de tout ce que vous avez construit depuis M1.

L'erreur de départ

Le réflexe est d'afficher tout ce qu'on a. Douze courbes, quatre jauges, deux camemberts. Le résultat est un écran que personne ne regarde — et donc une panne que personne ne voit passer.

La règle d'ordre : la forme d'abord, la couleur en dernier

La plupart des mauvais tableaux de bord commencent par le choix des couleurs. C'est l'inverse qu'il faut faire.

  1. Quel est le travail de la donnée ? Montrer une grandeur, distinguer des identités, signaler une polarité, donner un chiffre unique, montrer une évolution.
  2. La forme découle du travail. Une évolution appelle une courbe ; un état courant appelle un chiffre, pas un graphique.
  3. La couleur vient ensuite, et seulement pour le travail qu'elle fait.
Parfois, ce n'est pas un graphique

« Nombre de capteurs en ligne : 12 / 12 » n'a pas besoin d'une courbe. Un chiffre suffit, et il se lit plus vite.

Un graphique à une seule barre, ou un camembert à deux parts, sont toujours une erreur : le nombre est le graphique.

La hiérarchie de lecture

21,4 °C moyenne du parc 12 / 12 capteurs en ligne 2 alertes actives 3 min dernière réception Températures — 24 h Lyon Paris Détail par capteur capt-01 · 21,4 °C · il y a 12 s capt-07 · 22,9 °C · il y a 8 s … Chiffres d'état en haut · évolution au milieu · détail en bas
La hiérarchie de lecture : l'exploitant doit savoir en trois secondes si tout va bien, avant de chercher pourquoi.

Trois niveaux, du plus rapide au plus détaillé. L'exploitant descend seulement s'il a une raison de le faire.

L'erreur numéro un : le double axe

✗ Double axe °C 0-40 % 0-100 les courbes semblent corrélées — c'est le cadrage qui le décide ✓ Deux graphiques °C % chaque grandeur a son échelle — aucune corrélation suggérée
Le double axe est l'erreur la plus fréquente en supervision : deux échelles arbitraires inventent une corrélation qui n'existe pas.

Tracer la température et l'humidité sur un même graphique avec deux échelles verticales est tentant — Grafana le propose. C'est pourtant faux : l'alignement des deux échelles est arbitraire, donc le graphique invente une corrélation qui n'est pas dans les données. Changez une borne, la corrélation disparaît.

La parade : deux graphiques superposés partageant l'axe du temps, ou deux grandeurs ramenées à une base commune sur un seul axe.

La couleur, par le travail qu'elle fait

TravailType de paletteRègle
Identité — distinguer des capteurscatégorielleTeintes attribuées dans un ordre fixe, jamais recyclées
Magnitude — une carte de chaleurséquentielleUne seule teinte, du clair au foncé. Jamais d'arc-en-ciel
Polarité — écart à une consignedivergenteDeux teintes opposées + un gris neutre au milieu
État — normal, alerte, critiquestatutRéservée. Jamais réutilisée pour une série de données
Deux règles que Grafana ne vous imposera pas

La couleur suit l'entité, pas son rang. Si un filtre change le nombre de séries affichées, les couleurs des survivantes ne doivent pas bouger. Un exploitant qui a appris « Lyon est bleu » serait trompé.

Le rouge et le vert sont réservés aux états. Les utiliser pour distinguer deux capteurs, c'est faire croire que l'un va mal.

La palette du parcours

Quatre teintes, dans cet ordre. Elle a été vérifiée : bande de luminosité, saturation suffisante, et surtout séparation sous déficience visuelle — la paire la plus proche reste distinguable pour un daltonien de type protanope.

série 1  #2a78d6   bleu
série 2  #eb6834   orange
série 3  #1baf7a   vert d'eau
série 4  #eda100   jaune
Ce que la vérification a signalé

Le vert d'eau et le jaune ont un contraste inférieur à 3:1 sur fond clair. Ce n'est pas bloquant, mais cela oblige à une étiquette visible : légende présente, et libellé direct sur les courbes concernées. La couleur seule ne doit jamais porter l'identité.

Ce qui reste discret

  • Traits fins. 2 px pour une courbe, pas davantage.
  • Grille en filet léger, jamais en pointillés — le pointillé ajoute du bruit et se confond avec une série.
  • Pas de valeur sur chaque point. Étiqueter le dernier point, ou les extrêmes, suffit.
  • Le texte garde sa couleur de texte. Une valeur écrite dans la couleur de sa série devient illisible et redondante avec la pastille de légende.
✓ terminé
Leçon

Grafana en pratique

Jour 1 · Concevoir et construire · 20 min

Grafana lit des sources de données et affiche des panneaux. Trois notions suffisent à en faire le tour.

NotionCe que c'est
Source de donnéesLa connexion à InfluxDB, déclarée une fois
PanneauUne requête + une représentation. C'est l'unité de base
VariableUne valeur choisie en haut de l'écran qui s'injecte dans les requêtes

Les types de panneaux utiles en supervision

PanneauPour quoiPiège
Time seriesÉvolution d'une grandeurNe pas y empiler deux échelles
StatUn chiffre d'état : moyenne, dernier relevéPréciser la fonction employée
GaugeUne valeur par rapport à des bornesInutile si les bornes n'ont pas de sens physique
TableLe détail par capteurLe meilleur choix au-delà de sept séries
State timelineL'historique des états en ligne / hors ligne
Les variables changent tout

Plutôt que douze tableaux de bord, un seul avec une variable site et une variable capteur. Grafana peuple ces listes en interrogeant la base : les nouveaux capteurs apparaissent tout seuls.

Une requête Flux dans Grafana

C'est exactement le Flux de M3, avec les variables injectées par ${...} :

from(bucket: "releves")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r._measurement == "temperature")
  |> filter(fn: (r) => r.site == "${{site}}")
  |> aggregateWindow(every: v.windowPeriod, fn: mean)
Les deux variables offertes par Grafana

v.timeRangeStart et v.timeRangeStop reprennent le sélecteur de période en haut de l'écran. v.windowPeriod s'adapte à la largeur du panneau : le graphique reste lisible qu'on regarde une heure ou six mois, sans rien changer à la requête.

✓ terminé
Quiz

Quiz — Conception et Grafana

Jour 1 · Concevoir et construire · 8 min

1. Vous voulez afficher température et humidité sur la même période. Comment procéder ?

2. « 12 capteurs en ligne sur 12 ». Quelle représentation ?

3. Vous filtrez pour n'afficher que trois capteurs sur huit. Les couleurs des trois restants doivent :

4. Pourquoi ne pas utiliser rouge et vert pour distinguer deux sites ?

5. À quoi sert v.windowPeriod ?

✓ terminé
Travaux pratiques

TP 1 — Premiers panneaux

Jour 1 · Concevoir et construire · 1 h

Brancher Grafana sur InfluxDB et construire les quatre chiffres d'état.

Prérequis

Votre pile de M3 doit tourner : broker, capteurs, InfluxDB alimenté par Telegraf.

  1. Ajoutez Grafana à votre docker-compose.yml plutôt que de le lancer à part — la pile doit rester reproductible :
      grafana:
        image: grafana/grafana:11.1.0
        ports: ["3000:3000"]
        environment:
          - GF_SECURITY_ADMIN_PASSWORD=thermeo2026
        volumes:
          - grafana-data:/var/lib/grafana
  2. Ouvrez http://localhost:3000. Dans Connections → Data sources, ajoutez InfluxDB en choisissant le langage de requête Flux, l'URL de votre conteneur, l'organisation thermeo et votre jeton.
  3. Cliquez sur Save & test : la connexion doit être confirmée avant d'aller plus loin.
  4. Créez un tableau de bord et, avant de toucher à Grafana, écrivez sur papier les quatre chiffres qu'un exploitant de Thermeo doit voir en trois secondes. Justifiez chacun.
  5. Construisez le premier panneau Stat : moyenne du parc sur la dernière heure.
    from(bucket: "releves")
      |> range(start: -1h)
      |> filter(fn: (r) => r._measurement == "temperature")
      |> mean()
  6. Construisez les trois autres : nombre de capteurs ayant émis dans les dix dernières minutes, nombre d'alertes actives, âge du dernier relevé reçu.
  7. Disposez-les en une seule rangée en haut de l'écran, comme dans le schéma de la leçon.
  8. Vérifiez la lisibilité : demandez à quelqu'un de regarder l'écran trois secondes, puis de dire si tout va bien. S'il hésite, la disposition est à revoir.
Questions de réflexion — à préparer par écrit
  • Pourquoi préciser la fonction d'agrégation dans le titre d'un panneau Stat ?
  • Vos quatre chiffres suffisent-ils à décider s'il faut intervenir ?
✓ terminé
Travaux pratiques

TP 2 — Le tableau de bord de supervision

Jour 1 · Concevoir et construire · 1 h 30

Assembler un écran complet, avec variables et palette maîtrisée.

  1. Ajoutez sous vos chiffres d'état un panneau Time series des températures sur 24 heures, une courbe par site.
  2. Appliquez la palette de la leçon. Dans Panel → Overrides, fixez la couleur par série nommée, jamais par ordre d'affichage :
    Lyon    -> #2a78d6
    Paris   -> #eb6834
    Nantes  -> #1baf7a
    Lille   -> #eda100
  3. Vérifiez la règle : filtrez pour masquer Lyon, et confirmez que Paris reste orange au lieu de devenir bleu.
  4. Réglez l'épaisseur des traits à 2 px, retirez le remplissage sous les courbes, et passez la grille en filet léger — jamais en pointillés.
  5. Activez la légende et le libellé de la dernière valeur. C'est ce qu'impose l'avertissement de contraste signalé dans la leçon : l'identité ne doit pas reposer sur la seule couleur.
  6. Créez une variable site alimentée par la base, dans Dashboard settings → Variables, de type Query :
    import "influxdata/influxdb/schema"
    schema.tagValues(bucket: "releves", tag: "site")
  7. Injectez-la dans vos requêtes avec ${{site}} et vérifiez que le sélecteur apparaît en haut de l'écran. Ajoutez un nouveau capteur sur un nouveau site : il doit apparaître dans la liste sans rien modifier.
  8. Ajoutez en bas un panneau Table avec la dernière valeur de chaque capteur et son ancienneté. Au-delà de sept séries, c'est la table qui reste lisible, pas le graphique.
  9. Faites délibérément l'erreur : ajoutez un panneau où la température et l'humidité partagent un graphique à deux axes. Regardez-le, puis remplacez-le par deux panneaux empilés. Conservez une capture des deux pour votre rendu.
    # a produire puis remplacer : le double axe est l'anti-patron le plus courant
  10. Exportez le tableau de bord en JSON et versionnez-le.
Questions de réflexion — à préparer par écrit
  • Sur vos deux captures, qu'est-ce que le double axe suggérait que les deux panneaux ne suggèrent plus ?
  • Au-delà de combien de capteurs votre graphique devient-il illisible ?
  • Comment un exploitant daltonien lit-il votre écran ?
✓ terminé
Point de contrôle

Point de contrôle — fin du jour 1

Jour 1 · Concevoir et construire · 15 min

Vérifiez avant de continuer
Grafana est dans mon docker-compose.yml et lit InfluxDB
Quatre chiffres d'état en haut de l'écran, chacun justifié
Les couleurs sont fixées par série nommée, pas par ordre d'affichage
Masquer une série ne repeint pas les autres — vérifié
Légende et libellés visibles : l'identité ne repose pas sur la couleur seule
Une variable site alimente les requêtes depuis la base
J'ai produit un double axe puis l'ai remplacé, avec les deux captures
Mon tableau de bord est exporté et versionné
✓ terminé
Leçon

Alerter sans noyer

Jour 2 · Alerter et sortir du cadre · 20 min

Vous savez déjà déclencher une alerte : en M2 dans l'objet, en M3 dans Node-RED. Grafana en propose une troisième, et il faut savoir laquelle choisir.

Réagit enVoitBon pour
Objet (M2)immédiatsa seule mesureSécurité, réaction locale
Node-RED (M3)quelques secondestous les messages qui passentCorrélation entre capteurs, actions
Grafana (M4)à chaque évaluation, souvent la minutel'historique en baseTendances, absence de données, seuils sur durée
Ce que seul Grafana peut faire

Alerter sur une durée : « la température dépasse 24 °C depuis plus de quinze minutes ». Ni l'objet ni le moteur de règles n'ont l'historique pour cela.

Et alerter sur une absence : « aucune donnée reçue depuis dix minutes » — la condition No Data, qui traite le silence comme un signal.

Les états d'une alerte

ÉtatSignification
NormalLa condition n'est pas remplie
PendingElle l'est, mais pas encore assez longtemps
AlertingElle l'est depuis la durée exigée — la notification part
No DataLa requête ne renvoie rien : à traiter explicitement
Pending est votre hystérésis

L'état Pending joue exactement le rôle de l'hystérésis vue en M2 : il empêche qu'un franchissement passager déclenche une alerte. Une condition vraie pendant 5 minutes avant de basculer élimine l'essentiel du bavardage.

Ce qui rend une alerte utile

  • Elle est actionnable. Si personne ne peut rien faire en la recevant, elle ne doit pas exister.
  • Elle dit où et quoi. « Alerte température » ne vaut rien ; « capt-07, étage 2, Lyon : 26,3 °C depuis 20 min » se traite.
  • Elle se referme. Une alerte qui ne signale jamais son retour à la normale laisse croire au problème permanent.
La fatigue d'alerte

C'est le vrai risque, et il est silencieux. Un exploitant qui reçoit trente notifications par jour finit par toutes les ignorer — y compris celle qui comptait.

Mieux vaut trois alertes justes que trente approximatives. Si une alerte se déclenche souvent sans action derrière, ce n'est pas le seuil qu'il faut ajuster : c'est l'alerte qu'il faut supprimer.

✓ terminé
Travaux pratiques

TP 3 — Des alertes qu'on peut suivre

Jour 2 · Alerter et sortir du cadre · 1 h

Créer des règles d'alerte durables et annoter les incidents.

  1. Créez une première règle dans Alerting → Alert rules : température moyenne supérieure à 24 °C.
    from(bucket: "releves")
      |> range(start: -15m)
      |> filter(fn: (r) => r._measurement == "temperature")
      |> group(columns: ["capteur"])
      |> mean()
  2. Réglez la condition sur IS ABOVE 24, l'évaluation toutes les minutes, et surtout « for » à 15 minutes — c'est ce délai qui joue le rôle de l'hystérésis.
  3. Rédigez le résumé de l'alerte avec les étiquettes, pour qu'elle soit actionnable :
    Capteur {{{{ $labels.capteur }}}} — {{{{ $values.B }}}} °C depuis 15 min
  4. Faites monter un capteur au-dessus du seuil et observez la bascule Normal → Pending → Alerting. Chronométrez : la notification ne doit pas partir avant les quinze minutes.
  5. Créez une seconde règle sur l'absence de données : configurez le comportement No Data en Alerting, puis coupez un capteur et vérifiez le déclenchement.
  6. Comparez avec les deux autres mécanismes que vous connaissez : le testament MQTT de M1 et la requête de détection de M3. Chronométrez les trois sur la même coupure et consignez les délais.
  7. Configurez un point de contact — un webhook vers un service de test suffit — et vérifiez qu'une notification part réellement.
  8. Ajoutez enfin une annotation automatique : les périodes d'alerte doivent apparaître en surbrillance sur vos graphiques, pour relire un incident après coup.
Questions de réflexion — à préparer par écrit
  • Vos trois mécanismes de détection ont des délais différents. Lequel garderiez-vous, et faut-il vraiment les trois ?
  • Quelle alerte de votre tableau de bord risque de créer de la fatigue, et pourquoi ?
  • Une alerte se déclenche la nuit. Qui la reçoit, et que peut-il faire ?
✓ terminé
Leçon

Quand Grafana ne suffit plus

Jour 2 · Alerter et sortir du cadre · 20 min

Grafana est excellent pour superviser. Il l'est beaucoup moins dès qu'on sort de ce cadre.

BesoinGrafanaInterface sur mesure
Superviser, explorer l'historiqueidéalcoûteux à refaire
Écran public affiché en permanencepossible mais rigideadapté
Piloter — envoyer une consignenon prévunécessaire
Intégrer à un logiciel métieriframe, sans plusnatif
Utilisateurs non techniquesinterface denseà leur mesure
Le critère qui tranche

Est-ce que l'utilisateur doit agir depuis l'écran ? S'il ne fait que regarder, Grafana suffit et coûte une journée. S'il doit envoyer une consigne, valider un incident ou déclencher une action, il faut une interface — et un développement.

L'architecture minimale

Trois pièces, pas davantage :

  1. Une API qui interroge InfluxDB et expose des données prêtes à afficher. Elle ne renvoie jamais la base brute au navigateur.
  2. Une page web qui les affiche.
  3. Un canal temps réel pour que la page se mette à jour sans rechargement.

Interrogation périodique ou WebSocket

Interrogation périodiqueWebSocket
Mise en œuvrequelques lignesun serveur qui maintient les connexions
Latencela période choisieimmédiate
Chargeconstante, même sans donnée nouvelleproportionnelle aux événements
Quand le retenirrafraîchissement de quelques secondesvraie réactivité requise
Ne partez pas sur WebSocket par réflexe

Pour un tableau de bord qui se rafraîchit toutes les cinq secondes, l'interrogation périodique est plus simple, plus robuste et se débogue trivialement. Le WebSocket se justifie quand la seconde compte — une alarme, un pilotage.

La règle qui vaut pour les deux

Le navigateur ne doit jamais recevoir le jeton InfluxDB. C'est l'API qui détient les identifiants et n'expose que ce dont la page a besoin. Un jeton dans du JavaScript est un jeton public.

✓ terminé
Quiz

Quiz — Alertes et interfaces

Jour 2 · Alerter et sortir du cadre · 7 min

1. Quelle alerte seul Grafana peut porter ?

2. À quoi correspond l'état Pending ?

3. Quel est le principal risque d'un système d'alerte mal réglé ?

4. Votre page web doit afficher les relevés. Où mettre le jeton InfluxDB ?

✓ terminé
Travaux pratiques

TP 4 — Une interface sur mesure

Jour 2 · Alerter et sortir du cadre · 1 h 30

Construire l'API et la page temps réel, et savoir quand cela se justifie.

  1. Écrivez l'API avec FastAPI. Elle interroge InfluxDB et n'expose que le nécessaire :
    from fastapi import FastAPI
    from fastapi.middleware.cors import CORSMiddleware
    from influxdb_client import InfluxDBClient
    import os
    
    app = FastAPI()
    app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"])
    
    cli = InfluxDBClient(url="http://localhost:8086",
                         token=os.environ["INFLUX_TOKEN"],   # jamais en dur
                         org="thermeo")
    
    FLUX = '''
    from(bucket: "releves")
      |> range(start: -10m)
      |> filter(fn: (r) => r._measurement == "temperature")
      |> group(columns: ["capteur"])
      |> last()
    '''
    
    @app.get("/api/etat")
    def etat():
        tables = cli.query_api().query(FLUX)
        return [{"capteur": r.values["capteur"],
                 "valeur": r.get_value(),
                 "instant": r.get_time().isoformat()}
                for t in tables for r in t.records]
  2. Lancez et vérifiez :
    INFLUX_TOKEN="<jeton>" uvicorn api:app --reload --port 8000
    curl -s http://localhost:8000/api/etat | python3 -m json.tool
  3. Écrivez une page HTML autonome qui interroge cette API toutes les cinq secondes et affiche une tuile par capteur : nom, valeur, ancienneté. Reprenez la palette et la hiérarchie de la leçon du jour 1.
  4. Traitez le cas de l'erreur : si l'API ne répond pas, la page doit le dire clairement plutôt que d'afficher des valeurs périmées comme si elles étaient fraîches. C'est le défaut le plus dangereux d'un écran de supervision.
  5. Grisez toute tuile dont la dernière valeur date de plus de deux minutes : une donnée périmée affichée comme actuelle vaut moins que pas de donnée.
  6. Ajoutez un endpoint POST /api/consigne qui publie une consigne en message MQTT retenu — le motif du jumeau vu en M3 — et un bouton dans la page. C'est ce que Grafana ne sait pas faire.
  7. Ajoutez l'API et la page à votre docker-compose.yml.
  8. Rédigez une note d'une page : ce que vous mettez dans Grafana, ce que vous mettez dans l'interface sur mesure, et pourquoi. Chiffrez la charge de développement de chaque option.
Questions de réflexion — à préparer par écrit
  • Vous avez écrit vingt lignes d'API et une page. Combien de jours pour égaler Grafana ?
  • Votre page interroge toutes les cinq secondes. Que change un WebSocket, concrètement ?
  • Comment un exploitant distingue-t-il « 21,4 °C il y a 3 s » de « 21,4 °C il y a 3 h » sur votre écran ?
✓ terminé
Point de contrôle

Point de contrôle — fin du module M4

Jour 2 · Alerter et sortir du cadre · 20 min

Vérifiez avant de continuer
Ma règle d'alerte passe bien par Pending avant Alerting
Le résumé d'alerte nomme le capteur et la valeur — il est actionnable
Une alerte sur absence de données se déclenche à la coupure d'un capteur
J'ai chronométré les trois mécanismes de détection : testament, requête, Grafana
Les périodes d'alerte apparaissent en annotation sur mes graphiques
Mon API expose les données sans jamais livrer le jeton au navigateur
Ma page signale une API injoignable et grise les valeurs périmées
Le bouton de consigne publie bien un message retenu
Ma note Grafana / sur-mesure est rédigée et chiffrée
Revue de fin de module — lundi 17 août, 15 h 00

Votre encadrant regardera votre tableau de bord trois secondes et vous dira ce qu'il en a retenu. Si ce n'est pas ce que vous vouliez montrer, c'est la conception qu'il faut revoir, pas la configuration.

M5 enchaîne mardi 18 : sécurité IoT et déploiement industriel.

✓ terminé

Mon avancement

Signature d'émargement

Demander de l'aide à votre encadrant

Étape concernée :

Avant d'envoyer

Cherchez seul pendant 30 minutes et notez ce que vous avez tenté. Ce que vous avez essayé fait partie de ce que vous présentez — c'est ce qui distingue l'autonomie de l'abandon.