FR-Alert dans Home Assistant : recevoir les alertes officielles avant d’entrer dans la zone

Bonjour à toutes et tous,

Pourquoi ce projet ?

FR-Alert avertit surtout les personnes déjà présentes dans une zone dangereuse. Je voulais aller plus loin : être informé avant d’entrer dans un secteur concerné ou pouvoir suivre une situation à distance.

Les alertes peuvent concerner, par exemple : feu de forêt / inondation / accident industriel / fuite de produits dangereux / évacuation de population / attaque terroriste

L’objectif est donc simple : récupérer les alertes publiées sur le site officiel FR-Alert, les transmettre à Home Assistant et recevoir une notification avec le type de danger, le lieu, l’autorité émettrice et les consignes.

Lors des premiers tests, le système a notamment détecté une alerte réelle concernant un feu de forêt sur la presqu’île du Cap Ferret. Cela a permis de valider toute la chaîne, depuis la publication officielle jusqu’à la notification sur le téléphone.

Problème => pas d'API fournie par FR-Alert. Il a fallu réfléchir et bosser pas mal.

Au final, on obtient ça sur HA (alors que je suis basé à Lyon) :

Mise en place d'une VM Debian

J’ai créé une petite VM indépendante de Home Assistant afin d’y exécuter les services de veille.
Installation des outils nécessaires après l’installation minimale de Debian :

sudo apt update
sudo apt full-upgrade -y

sudo apt install -y \
  ca-certificates \
  curl \
  git \
  python3 \
  python3-venv \
  python3-pip \
  nftables \
  unattended-upgrades

Les mises à jour automatiques de sécurité sont ensuite activées :

sudo dpkg-reconfigure -plow unattended-upgrades
sudo systemctl enable --now unattended-upgrades

Pare-feu nftables : aucun service ne doit être accessible depuis Internet. Seul SSH est autorisé depuis le réseau local et le réseau WireGuard.

Fichier /etc/nftables.conf :

table inet filter {
    chain input {
        type filter hook input priority filter;
        policy drop;

        iifname "lo" accept
        ct state invalid drop
        ct state established,related accept

        ip protocol icmp accept
        meta l4proto ipv6-icmp accept

        udp sport 67 udp dport 68 accept

        iifname "enp0s5" \
        ip saddr { 192.168.0.0/24, 192.168.27.0/24 } \
        tcp dport 22 accept
    }

    chain forward {
        type filter hook forward priority filter;
        policy drop;
    }

    chain output {
        type filter hook output priority filter;
        policy accept;
    }
}

Activation :

sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables

Création d’un compte de service : le collecteur ne tourne pas avec mon compte utilisateur ni avec les droits root.

sudo useradd \
  --system \
  --home-dir /opt/webwatch \
  --shell /usr/sbin/nologin \
  webwatch

Création des dossiers :

sudo mkdir -p \
  /opt/webwatch/app \
  /opt/webwatch/venv \
  /var/lib/webwatch/fralert/alerts

sudo chown -R root:root /opt/webwatch/app
sudo chown -R webwatch:webwatch /var/lib/webwatch

Création de l’environnement Python :

sudo python3 -m venv /opt/webwatch/venv

sudo /opt/webwatch/venv/bin/pip install \
  --upgrade pip aiohttp

Le code reste protégé et appartient à root. Seul le dossier contenant les données et les archives est accessible en écriture au compte webwatch.

Gestion du certificat du site FR-Alert : le serveur FR-Alert ne fournissait pas toute la chaîne de certification TLS. Plutôt que de désactiver la vérification avec curl -k, j’ai créé un bundle dédié contenant les certificats Debian et le certificat intermédiaire nécessaire.

sudo mkdir -p /etc/webwatch/certs

curl \
  http://autorite.certigna.fr/servicesca_rootca.der \
  -o /tmp/certigna-services-ca.der

openssl x509 \
  -inform DER \
  -in /tmp/certigna-services-ca.der \
  -out /tmp/certigna-services-ca.pem

Installation du certificat :

sudo install \
  -o root \
  -g root \
  -m 0644 \
  /tmp/certigna-services-ca.pem \
  /etc/webwatch/certs/certigna-services-ca.pem

Création du bundle utilisé uniquement par le collecteur :

sudo sh -c '
cat \
  /etc/ssl/certs/ca-certificates.crt \
  /etc/webwatch/certs/certigna-services-ca.pem \
  > /etc/webwatch/certs/fr-alert-ca-bundle.pem
'

Cette méthode conserve une validation TLS normale sans modifier globalement les certificats reconnus par Debian.

Préparation du service de veille

Le script Python de surveillance est installé ici :
/opt/webwatch/app/fralert_watch.py
Les données sont stockées ici :

/var/lib/webwatch/fralert/
├── state.json
└── alerts/

Le fichier state.json conserve notamment les identifiants déjà vus afin d’éviter les doublons. Chaque nouvelle alerte est archivée sous forme HTML et JSON.

Le service systemd utilisé pour lancer le collecteur est le suivant :

[Unit]
Description=Surveillance du site officiel FR-Alert
Wants=network-online.target
After=network-online.target nftables.service

[Service]
Type=simple
User=webwatch
Group=webwatch
WorkingDirectory=/opt/webwatch/app

ExecStart=/opt/webwatch/venv/bin/python \
  /opt/webwatch/app/fralert_watch.py \
  --poll-seconds 60

Restart=on-failure
RestartSec=15

Environment=PYTHONUNBUFFERED=1

NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
LockPersonality=yes

CapabilityBoundingSet=
AmbientCapabilities=

RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
ReadWritePaths=/var/lib/webwatch/fralert
UMask=0077

[Install]
WantedBy=multi-user.target

Le fichier est enregistré sous :
/etc/systemd/system/fralert-watch.service
Puis le service est activé :

sudo systemctl daemon-reload
sudo systemctl enable --now fralert-watch.service

Vérification :

sudo systemctl status fralert-watch.service
sudo journalctl -u fralert-watch.service -n 30

La VM est alors prête à interroger régulièrement le site officiel FR-Alert, détecter les nouvelles publications et les archiver localement.

Réception des alertes dans Home Assistant

Côté HA, j’ai créé une automatisation déclenchée par un webhook local.

Le serveur Debian envoie un JSON à HA dès qu’une nouvelle alerte est détectée :

Site FR-Alert
      ↓
VM Debian
      ↓
Webhook local
      ↓
Automatisation Home Assistant
      ↓
Notification persistante + téléphone

Cette solution ne nécessite ni jeton API Home Assistant ni ouverture de port sur Internet.

Création du webhook dans HA :

Paramètres
→ Automatisations et scènes
→ Créer une automatisation
→ Ajouter un déclencheur
→ Webhook

Réglages utilisés :

  • méthode autorisée : POST ;
  • webhook limité au réseau local ;
  • identifiant généré par Home Assistant et conservé secret.

L’adresse complète du webhook est ensuite enregistrée sur Debian :
FRALERT_WEBHOOK_URL=http://IP_DE_HOME_ASSISTANT:8123/api/webhook/IDENTIFIANT_SECRET

Automatisation Home Assistant : exemple de YAML

alias: FR-Alert - Notification
description: Réception des alertes envoyées par la VM Debian

triggers:
  - trigger: webhook
    webhook_id: IDENTIFIANT_SECRET
    allowed_methods:
      - POST
    local_only: true

actions:
  - action: persistent_notification.create
    data:
      title: >-
        🚨 {{ trigger.json.get('headline', 'FR-Alert') }}
        · {{ trigger.json.get('area', 'Zone inconnue') }}

      message: |-
        📍 Lieu : {{ trigger.json.get('area', 'Non précisé') }}
        🏛️ Autorité : {{ trigger.json.get('authority', 'Non précisée') }}
        Type : {{ trigger.json.get('alert_type', 'Non précisé') }}

        {{ trigger.json.get('description', 'Aucune description disponible.') }}

        Identifiant : {{ trigger.json.get('alert_id', 'inconnu') }}
        {{ trigger.json.get('url', '') }}

  - action: notify.mobile_app_mon_telephone
    data:
      title: >-
        🚨 {{ trigger.json.get('headline', 'FR-Alert') }}
        · {{ trigger.json.get('area', 'Zone inconnue') }}

      message: |-
        📍 Lieu : {{ trigger.json.get('area', 'Non précisé') }}
        🏛️ Autorité : {{ trigger.json.get('authority', 'Non précisée') }}

        {{ trigger.json.get('description', 'Aucune description disponible.') }}

mode: queued
max: 10

Le collecteur Debian transmet notamment :

{
  "headline": "Incendie - Feu de forêt",
  "area": "Presqu'île du Cap Ferret",
  "authority": "Préfecture de la Gironde",
  "alert_type": "Réel",
  "description": "Message et consignes officielles...",
  "alert_id": "FR-ALERT.1784874174.90000.0",
  "url": "https://fr-alert.gouv.fr/les-alertes/..."
}

HA utilise directement ces champs dans les modèles Jinja avec :
{{ trigger.json.get('area', 'Zone inconnue') }}

Le résultat est une notification contenant le type de danger, le lieu, l’autorité, les consignes et le lien vers la publication officielle :

Voilà :slightly_smiling_face:
J'espère que l'idée vous aura plu !

@ +

C'est pas trop anxiogène de recevoir toutes les alertes de France et de Navarre systématiquement ?

Remarque, ça peut rassurer et de savoir qu'il y a pire situation quelque part.

L'idée est de filtrer dans HA ensuite par département/commune pour ne recevoir que les alertes qui m'intéressent. Pour l'instant, je suis en phase de recette, je laisse tout arriver.

C'est vrai que tout recevoir en permanence risque d'alimenter ma parano :sweat_smile:

Mais sur le principe générale, le principe est de "sécuriser" les déplacements plutôt que générer du stress en permanence. On n'a pas besoin d'en rajouter.

Ouais le filtre ne va pas être simple, la géolocalisation n'est pas très claire.

A vrai dire j'ai du mal à comprendre comment on peut mettre à disposition des données si pourries mais c'est ma déformation professionnelle.

Oui, c’est exactement le point délicat. :thinking:

Les données sont bien publiées sur le site officiel FR-Alert, mais elles ne sont pas fournies sous une forme vraiment normalisée et facilement exploitable. Selon les alertes, le lieu peut apparaître comme une commune, un secteur, un massif, un site industriel ou simplement dans le texte libre.

Dans l’exemple du Cap Ferret, la localisation était présente sous la forme Presqu’île du Cap Ferret (P1), mais sans champ clair du type département ou code INSEE. J’ai donc dû extraire le bloc situé avant « Informations complémentaires ».

Pour la géolocalisation, je pense partir sur un filtrage prudent basé sur le lieu, le texte et l’autorité émettrice. Les alertes dont la zone reste ambiguë seraient notifiées par défaut, afin d’éviter de rater un événement important.

Et oui, je partage ton avis : pour un dispositif officiel, une API ou au minimum des données structurées avec commune, département et coordonnées aurait été beaucoup plus propre.

Surtout que ce sont des alertes envoyées sur des mobiles, donc ils devraient connaître toutes les antennes concernées.

Bref, c'est affligeant ...

Après quelques jours de tests, je vais finalement abandonner ce projet de veille FR-Alert via Home Assistant.

Techniquement, tout fonctionne très bien : la VM Debian surveille le site officiel, détecte les nouvelles alertes, les analyse, récupère le type de danger, la zone, l’autorité émettrice, puis les transmet à Home Assistant et au téléphone en quelques secondes.

Le problème est ailleurs : la publication sur le site FR-Alert peut arriver très longtemps après l’événement réel.

En analysant les alertes archivées, j’ai constaté plusieurs décalages de plusieurs heures, voire plusieurs jours. Exemple concret : une alerte concernant un incendie à Papeete, déclenchée le 20 août, n’est apparue sur le site FR-Alert que le 26 août. Une fois publiée, ma VM l’a récupérée en une vingtaine de secondes et HA l’a transmise quasi instantanément, mais six jours après l’événement, cela n’a évidemment plus aucun intérêt opérationnel.

Or le but initial était justement d’être averti en temps réel ou quasi temps réel avant d’entrer dans une zone dangereuse, ou de suivre une situation en cours à distance.

Le site public FR-Alert n’est donc manifestement pas une source suffisamment temps réel pour cet usage.

Le projet reste techniquement intéressant et pourrait servir à de l’archivage ou de la veille historique, mais ce n’était pas mon objectif. Je préfère donc arrêter là plutôt que de maintenir une usine qui fonctionne parfaitement… pour recevoir certaines alertes trop tard.

Au moins, les tests auront permis de valider clairement cette limite.

280.0 h | Incendie - Feu de forêt | Commune de Correns
événement→site ≈ 167.1 h | site→VM 406483.0 s | VM→HA 0.006 s
278.6 h | Incendie - Feu de forêt | Commune de Montfort-sur-Argens
événement→site ≈ 165.7 h | site→VM 406482.9 s | VM→HA 0.006 s
277.5 h | Incendie - Feu de forêt | Commune de Chateauvert
événement→site ≈ 164.6 h | site→VM 406482.8 s | VM→HA 0.017 s
276.9 h | Incendie - Feu de forêt | Commune de Correns, Montfort-sur-Argens, Le V
événement→site ≈ 164.0 h | site→VM 406483.7 s | VM→HA 0.006 s
275.2 h | Incendie - Feu de forêt | Commune de Montfort-sur-Argens, Carces - Sud
événement→site ≈ 162.2 h | site→VM 406659.6 s | VM→HA 0.006 s
253.9 h | Incendie - Feu de forêt | Commune de Le Val-Vins (Nord Est)
événement→site ≈ 141.0 h | site→VM 406660.6 s | VM→HA 0.006 s
229.6 h | Incendie - | zone incendie SAFE - FR_57242 – Gandrange - 5
événement→site ≈ 116.7 h | site→VM 406660.5 s | VM→HA 0.020 s
148.6 h | Incendie - | Zone de Motu Uta, commune de Papeete, société
événement→site ≈ 148.6 h | site→VM 21.6 s | VM→HA 0.020 s
131.6 h | Incendie - Feu de forêt | Commune du BARP
événement→site ≈ 131.6 h | site→VM 27.6 s | VM→HA 0.021 s
130.4 h | Incendie - Feu de forêt | Commune de CESTAS
événement→site ≈ 130.4 h | site→VM 48.6 s | VM→HA 0.020 s
130.1 h | Incendie - Feu de forêt | Communes de BIGANOS, MIOS et Le BARP
événement→site ≈ 130.0 h | site→VM 135.0 s | VM→HA 0.021 s
118.0 h | Incendie - Feu de forêt | SUD-EST de la commune BRIGNOLES, quartiers de
événement→site ≈ 118.0 h | site→VM 32.6 s | VM→HA 0.019 s
116.9 h | Incendie - Feu de forêt | Commune BRIGNOLES, quartiers TOMBAREL / PELIC
événement→site ≈ 116.9 h | site→VM 32.4 s | VM→HA 0.021 s
110.2 h | Incendie - Feu de forêt | Commune de SAINT-JEAN-D’ILLAC
événement→site ≈ 110.2 h | site→VM 27.6 s | VM→HA 0.020 s
109.3 h | Incendie - Feu de forêt | MASSIF ILLARATA CAVIU LIVIU (Corse-du-Sud)
événement→site ≈ 109.3 h | site→VM 24.6 s | VM→HA 0.018 s
107.0 h | Incendie - Feu de forêt | Communes de SAINT-AUBIN DE MEDOC / SAINT-MEDA
événement→site ≈ 106.9 h | site→VM 204.5 s | VM→HA 0.006 s
105.2 h | Incendie - Feu de forêt | Commune de MERIGNAC (OUEST ROCADE)
événement→site ≈ 105.2 h | site→VM 210.4 s | VM→HA 0.007 s
105.1 h | Incendie - Feu de forêt | Commune Le HAILLAN
événement→site ≈ 105.1 h | site→VM 215.3 s | VM→HA 0.018 s
104.8 h | Incendie - Feu de forêt | Commune EYSINE
événement→site ≈ 104.8 h | site→VM 85.9 s | VM→HA 0.025 s

Effectivement, par rapport à ce que tu avais en tête, ça n'a pas beaucoup de sens de continuer le projet. C'était un projet intéressant. Merci de l'avoir fait vivre à travers les pages de ce forum. Qui sait, un jour, ce site, FR Alert ou un autre, deviendra un peu plus temps réel et ton projet pourra être réactivé.

Tu as aussi les deux intégrations :