[TUTORIEL] - Script sh pour supervision RPI

Supervision complète d’un Raspberry Pi 3 via MQTT et Home Assistant

Ce script Bash permet de superviser un Raspberry Pi 3 et de publier ses métriques dans MQTT, afin qu’elles soient automatiquement intégrées dans Home Assistant grâce au mécanisme de MQTT Discovery.

L’objectif est d’obtenir une télémétrie complète du système :

  • état des feeders ADS-B

  • charge CPU

  • température

  • mémoire

  • disque

  • réseau

  • connectivité

  • alimentation du Raspberry Pi

  • nombre d’avions détectés

Toutes ces informations sont publiées automatiquement dans MQTT et deviennent immédiatement visibles dans Home Assistant sans configuration manuelle.


Principe de fonctionnement

Le script repose sur trois mécanismes principaux :

  1. Création automatique des capteurs Home Assistant via MQTT Discovery

  2. Collecte des métriques système

  3. Publication des données et des attributs dans MQTT

Chaque métrique devient un capteur Home Assistant associé à un topic MQTT spécifique.


Architecture des topics MQTT

Les données sont publiées dans une arborescence simple :

rpi3/<categorie>/<metrique>/state
rpi3/<categorie>/<metrique>/attributes

Exemples :

rpi3/system/cpu_temp/state
rpi3/system/cpu_temp/attributes

rpi3/system/ram_used/state
rpi3/system/ram_used/attributes

rpi3/feeders/fr24feed/state

state

contient la valeur brute du capteur

exemple :

54.2

attributes

contient des informations complémentaires au format JSON

exemple :

{
  "label": "54.2",
  "updated": "2026-01-30 20:15:10"
}

Cela permet dans Home Assistant d’afficher par exemple :

  • l’heure de dernière mise à jour

  • une étiquette lisible

  • d’autres métadonnées.


Création automatique des capteurs (MQTT Discovery)

La fonction :

publish_discovery()

crée automatiquement les capteurs dans Home Assistant en publiant un message dans :

homeassistant/sensor/<unique_id>/config

Exemple :

homeassistant/sensor/rpi3_system_cpu_temp/config

Le message publié contient un JSON décrivant le capteur, par exemple :

  • nom du capteur

  • topic MQTT

  • unité

  • icône

  • type de donnée

  • identifiant unique

Exemple simplifié :

{
  "name": "CPU Temp",
  "state_topic": "rpi3/system/cpu_temp/state",
  "unit_of_measurement": "°C",
  "device_class": "temperature",
  "icon": "mdi:thermometer"
}

Grâce à cela :

  • Home Assistant détecte automatiquement le capteur

  • aucune configuration YAML n’est nécessaire

  • les capteurs apparaissent immédiatement dans l’interface.


Publication des données

La fonction :

publish_value()

publie les informations dans MQTT.

Elle envoie deux messages :

1. valeur du capteur

rpi3/system/cpu_temp/state

2. attributs du capteur

rpi3/system/cpu_temp/attributes

Les attributs incluent :

  • un label lisible

  • la date de mise à jour.


Supervision des feeders ADS-B

Le script vérifie l’état de plusieurs services ADS-B :

  • ADSBExchange

  • FlightRadar24

  • Planefinder

  • FlightAware

  • AirNav

  • OpenSky

  • Dump1090

  • ADSBHub

Pour chaque feeder :

  1. le script vérifie si le service est actif via systemctl

  2. il publie :

1 = actif
0 = inactif

Exemple :

rpi3/feeders/fr24feed/state

Cela permet de voir immédiatement dans Home Assistant si un feeder est arrêté.


Métriques système collectées

Le script récupère différentes informations du Raspberry Pi.

CPU

  • charge 1 minute

  • charge 5 minutes

  • charge 15 minutes

source :

/proc/loadavg

Température

  • température CPU

  • température GPU

commande utilisée :

vcgencmd measure_temp

Fréquence GPU

vcgencmd measure_clock core

Mémoire

RAM utilisée et RAM totale via :

free -m

Disque

espace utilisé et libre via :

df -h /

Réseau

trafic réseau total :

/sys/class/net/<interface>/statistics/
  • rx_bytes

  • tx_bytes


Connectivité

Deux tests de connectivité sont effectués :

  • ping du routeur local

  • ping de Google DNS (8.8.8.8)

Cela permet de distinguer :

  • un problème réseau local

  • un problème Internet.


Adresse IP

Le script publie l’adresse IP actuelle du Raspberry Pi :

hostname -I

Détection du nombre d’avions ADS-B

Le script interroge l’API locale de dump1090 :

http://localhost:8080/data/aircraft.json

Il compte les avions vus dans les 60 dernières secondes.

Commande utilisée :

jq '[.aircraft[] | select(.seen < 60)] | length'

Cela donne une estimation du trafic aérien actif.


Surveillance de l’alimentation du Raspberry Pi

Le script utilise :

vcgencmd get_throttled

Si le Raspberry Pi manque de puissance électrique, un flag est activé.

Le capteur renvoie :

1 = alimentation OK
0 = sous-alimentation détectée

Organisation des capteurs dans Home Assistant

Tous les capteurs sont regroupés sous un device unique :

Raspberry Pi 3

avec les métadonnées :

  • modèle

  • fabricant

  • version du script.


Avantages de cette approche

  • configuration automatique dans Home Assistant

  • architecture MQTT simple et lisible

  • script léger et portable

  • supervision complète du Raspberry Pi

  • idéal pour les stations ADS-B


Exemple de résultat dans Home Assistant

Une fois le script exécuté, on obtient par exemple :

  • CPU Temp

  • GPU Temp

  • RAM Used

  • Disk Free

  • Net In / Net Out

  • Ping Router

  • Ping Google

  • Aircraft Active

  • état des feeders ADS-B

Le tout regroupé dans un seul appareil Raspberry Pi 3.

Aadapter selon vos besoins :slight_smile:

#!/bin/bash
# ============================================
# status3_full.sh - Supervision complète RPi3 via MQTT HA
# ADS-B + métriques système + réseau + GPU + I/O
# ============================================

GREEN="\033[0;32m"
RED="\033[0;31m"
RESET="\033[0m"

BROKER="192.168.1.87"
USER="mqtt-user"
PASS="mqtt-pass"

echo "=== Supervision RPi3 complète ==="

# --------------------------------------------
# MQTT Discovery
# --------------------------------------------
publish_discovery() {
    local name="$1"
    local service="$2"
    local topic_base="$3"

    local unique_id="rpi3_${topic_base}_${service}"
    local discovery_topic="homeassistant/sensor/${unique_id}/config"

    # Définition des unités, device_class, state_class et icône selon le service
    local unit="" icon="" device_class="" state_class=""

    case "$service" in
        cpu_temp|gpu_temp)
            unit="°C"
            state_class="measurement"
            device_class="temperature"
            icon="mdi:thermometer"
            ;;
        cpu_load1|cpu_load5|cpu_load15)
            state_class="measurement"
#            device_class="none"
	     icon="mdi:cpu-64-bit"
            state_class="measurement"
            ;;
        ram_used|ram_total)
            unit="MB"
            icon="mdi:memory"
            state_class="measurement"
            ;;
        disk_used|disk_free)
            unit="GB"
            icon="mdi:harddisk"
            state_class="measurement"
            ;;
        net_in|net_out)
            unit="Bytes"
            state_class="total_increasing"
            icon="mdi:network"
#            state_class="measurement"
            ;;
        pi_power)
            icon="mdi:flash"
            state_class="measurement"
            ;;
        ping_router|ping_google)
            icon="mdi:network-pos"
            state_class="measurement"
            ;;
        aircraft_active|adsb_aircraft_count)
            unit="aircraft"
            icon="mdi:airplane"
            state_class="measurement"
            ;;
        ip_address)
            icon="mdi:lan"
            ;;
        *)
            icon="mdi:help-circle"
            state_class="measurement"
            ;;
    esac

    # Construction du payload JSON
    local payload="{\"name\":\"$name\",\"state_topic\":\"rpi3/$topic_base/$service/state\",\"unique_id\":\"$unique_id\",\"object_id\":\"$unique_id\""

    # Ajouter unit_of_measurement seulement si définie
    [[ -n "$unit" ]] && payload+=",\"unit_of_measurement\":\"$unit\""

    # Ajouter device_class seulement si défini
    [[ -n "$device_class" ]] && payload+=",\"device_class\":\"$device_class\""

    # Ajouter state_class seulement si défini
    [[ -n "$state_class" ]] && payload+=",\"state_class\":\"$state_class\""

    # Champs fixes
    payload+=",\"icon\":\"$icon\",\"json_attributes_topic\":\"rpi3/$topic_base/$service/attributes\",\"value_template\":\"{{ value }}\",\"device\":{\"identifiers\":[\"rpi3\"],\"name\":\"Raspberry Pi 3\",\"model\":\"Raspberry Pi 3 B+\",\"manufacturer\":\"Raspberry Pi Foundation\",\"sw_version\":\"status.sh v0.0.1 20260130\"}}"

    # Debug
    #echo "DEBUG: Publishing to $discovery_topic"
    #echo "DEBUG: Payload -> $payload"

    # Publication MQTT
    mosquitto_pub -h "$BROKER" -u "$USER" -P "$PASS" -t "$discovery_topic" -m "$payload" -r
}

# --------------------------------------------
# Publier valeurs MQTT
# --------------------------------------------
publish_value() {
    local topic_base="$1"
    local service="$2"
    local value="$3"
    local label="$4"

    timestamp=$(date +"%Y-%m-%d %H:%M:%S")

    # Publier la valeur simple dans state_topic
    mosquitto_pub -h "$BROKER" -u "$USER" -P "$PASS" \
        -t "rpi3/$topic_base/$service/state" -m "$value"

    # Publier les attributs dans un topic séparé
    payload=$(jq -n \
        --arg lbl "$label" \
        --arg ts "$timestamp" \
        '{label: $lbl, updated: $ts}')

    mosquitto_pub -h "$BROKER" -u "$USER" -P "$PASS" \
        -t "rpi3/$topic_base/$service/attributes" -m "$payload"
}

# --------------------------------------------
# ADS-B Feeders
# --------------------------------------------
feeders=(
"adsbexchange-feed:ADSBExchange Feed"
"adsbexchange-mlat:ADSBExchange MLAT"
"adsbexchange-stats:ADSBExchange Stats"
"fr24feed:FR24"
"pfclient:Planefinder"
"piaware:FlightAware Piaware"
"rbfeeder:AirNav RBFeeder"
"dump1090-fa:Dump1090-fa"
"opensky-feeder:OpenSky"
"adsbhub.sh:ADSBhub Script"
)

for f in "${feeders[@]}"; do
    service="${f%%:*}"
    name="${f##*:}"
    publish_discovery "$name" "$service" "feeders" "$service"

    # Vérification service ou script
    if [ "$service" == "adsbhub.sh" ]; then
        pgrep -f /usr/bin/adsbhub.sh >/dev/null && status="active" || status="inactive"
    else
        status=$(sudo systemctl is-active "$service" 2>/dev/null || echo "inactive")
    fi

    value=$([ "$status" == "active" ] && echo 1 || echo 0)
    label=$([ "$status" == "active" ] && echo "OK" || echo "KO")
    publish_value "feeders" "$service" "$value" "$label"
done

# --------------------------------------------
# Pi System métriques
# --------------------------------------------
system_metrics=(
"pi_power:Pi Power"
"cpu_load1:CPU Load 1min"
"cpu_load5:CPU Load 5min"
"cpu_load15:CPU Load 15min"
"cpu_temp:CPU Temp"
"gpu_temp:GPU Temp"
"gpu_freq:GPU Freq"
"ram_used:RAM Used"
"ram_total:RAM Total"
"disk_used:Disk Used"
"disk_free:Disk Free"
"ip_address:IP Address"
"ping_router:Ping Router"
"ping_google:Ping Google"
"net_in:Net In"
"net_out:Net Out"
"aircraft_active:Aircraft Active (<60s)"
)

ROUTER="192.168.1.1"
NET_IF="enxb827ebc51e31"

for s in "${system_metrics[@]}"; do
    service="${s%%:*}"
    name="${s##*:}"
    publish_discovery "$name" "$service" "system" "$service"

    case "$service" in
        pi_power)
            output=$(/usr/bin/vcgencmd get_throttled)
            if [ "$output" == "throttled=0x0" ]; then value=1; label="OK"; else value=0; label="Under powered"; fi
            ;;
        cpu_load1)
            value=$(cut -d ' ' -f1 /proc/loadavg)
            label="$value"
            ;;
        cpu_load5)
            value=$(cut -d ' ' -f2 /proc/loadavg)
            label="$value"
            ;;
        cpu_load15)
            value=$(cut -d ' ' -f3 /proc/loadavg)
            label="$value"
            ;;
        cpu_temp)
            value=$(/usr/bin/vcgencmd measure_temp | cut -d '=' -f2 | tr -d "'C")
            label="${value}"
            ;;
        gpu_temp)
            value=$(/usr/bin/vcgencmd measure_temp | cut -d '=' -f2 | tr -d "'C")
            label="${value}"
            ;;
        gpu_freq)
            value=$(/usr/bin/vcgencmd measure_clock core | cut -d '=' -f2 | awk '{print $1/1000000}')
            label="${value} MHz"
            ;;
        ram_used)
            value=$(free -m | awk '/Mem:/ {print $3}')
            label="${value} MB"
            ;;
        ram_total)
            value=$(free -m | awk '/Mem:/ {print $2}')
            label="${value} MB"
            ;;
        disk_used)
            value=$(df -h / | awk 'NR==2 {print $3}' | sed 's/G//')
            label="$value GB"
            ;;
        disk_free)
            value=$(df -h / | awk 'NR==2 {print $4}' | sed 's/G//')
            label="$value GB"
            ;;
        ip_address)
            value=$(hostname -I | awk '{print $1}')
            label="$value"
            ;;
        ping_router)
            ping -c 1 $ROUTER >/dev/null 2>&1 && value=1 && label="OK" || value=0 && label="KO"
            ;;
        ping_google)
            ping -c 1 8.8.8.8 >/dev/null 2>&1 && value=1 && label="OK" || value=0 && label="KO"
            ;;
        net_in)
            value=$(cat /sys/class/net/$NET_IF/statistics/rx_bytes)
            label="$value Bytes"
            ;;
        net_out)
            value=$(cat /sys/class/net/$NET_IF/statistics/tx_bytes)
            label="$value Bytes"
            ;;
        aircraft_active)
            value=$(curl -s http://localhost:8080/data/aircraft.json | jq '[.aircraft[] | select(.seen < 60)] | length' 2>/dev/null || echo 0)
            label="$value aircraft"
            ;;
    esac

    publish_value "system" "$service" "$value" "$label"
done

# --------------------------------------------
# Services critiques (seul SSH)
# --------------------------------------------
critical_services=(
"ssh:SSH"
)

for c in "${critical_services[@]}"; do
    service="${c%%:*}"
    name="${c##*:}"
    publish_discovery "$name" "$service" "system" "$service"
    status=$(sudo systemctl is-active "$service" 2>/dev/null || echo "inactive")
    value=$([ "$status" == "active" ] && echo 1 || echo 0)
    label=$([ "$status" == "active" ] && echo "OK" || echo "KO")
    publish_value "system" "$service" "$value" "$label"
done

echo "=== Fin supervision complète RPi3 ==="
2 « J'aime »