Birdnet - tuto - comment repérer et écouter les oiseaux du jardin 🐦

Bonjour,

Suite à la découverte du projet Birdnet-pi, j’ai décidé de me lancer dans ce super projet.

L’idée de Birdnet est de mettre un place un micro qui écoute les sons extérieurs et par magie, où plutôt un développement d’algorithmes efficaces, Birdnet nous mets en avant les espèces d’oiseaux reconnus.
Je trouve cela merveilleux et j’arrive à donner des noms aux oiseaux qui se trouvent dans mon jardin et même mieux à les reconnaitre maintenant grâce à leurs chants.

Ils existent de nombreuses personnes qui ont travaillés pour le développement du logiciel, de l’intégration dans HA et pour la mise en place de card très bien.
Je ne m’attribue pas du tout leurs travaux, mais c’est plutôt un partage de mon expérience pour mettre en avant ce travail remarquable.

Il existe un addon home assistant que j’ai essayé d’utilisé, mais en terme de ressources, cela était trop limite, j’ai donc décidé d’avoir un raspberry pi 4B+ dédié.
Je ne développerais donc pas la partie addon-HA, ci dessous le lien qui en parle très bien:

Pour mon projet, je suis donc partie sur Birdnet-pi, sur un raspberry pi 4B+, équipé d’un micro usb.
Le tout est dehors et communique avec Home assistant par mqtt.

Voici les différents liens qui m’ont aidés avant de vous présenter un tuto de mise en place.

Le site officiel :

Le forum HA et le sujet Birdnet :

Le github : GitHub - Nachtzuster/BirdNET-Pi: A realtime acoustic bird classification system for the Raspberry Pi 5, 4B 3B+ 0W2 and more. Built on the TFLite version of BirdNET.

Un blog très complet sur le projet Birdnet-go, mais très utilie pour la configuration coté HA dans mon cas :

Tutoriel d’installation :

1- Installation birdnet sur rapberry pi 4 B+ :

Le mieux est de suivre la procédure d’installation décrite dans le github :

Pour faire simple :
A - Installer raspberry pi os à partir de pi imager
B- Faire les mises à jour :

sudo apt-get update
sudo apt-get upgrade

C - Installer votre micro si non reconnu.

D - Lancer la commande d’installation Birdnet-pi

curl -s ``https://raw.githubusercontent.com/Nachtzuster/BirdNET-Pi/main/newinstaller.sh`` | bash

Vous avez donc à ce stade un serveur web qui affiche l’interface web :
http://adresse_ip_du_pi (par exemple, pour ma part, http://192.168.1.32)

2- Mise en place de mqtt coté raspberry pi 4 B+ - Birdnet

Il faut installer un mqtt léger sur le raspberry-pi pour publier les informations à home assistant via le broker MQTT. J’ai choisi paho-mqtt.

Comme il s’agit d’environnement python, il faut créer un environnement virtuel pour exécuter les scripts python à l’intérieur, c’est la méthode recommandée par raspberry os :

A - Créez un environnement virtuel dans votre répertoire personnel :

Commande :

python3 -m venv birdnet_venv

B- Activez l’environnement virtuel :

source birdnet_venv/bin/activate

C- Installez paho-mqtt dans cet environnement :

pip install paho-mqtt

D- Créer le script Python

Créer un fichier nommé “birdnet_to_mqtt.py” :

Commande :
nano birdnet_to_mqtt.py

Mettez les éléments ci-dessous et adapter bien les XXXX relatifs à MQTT.

Script python :


import time
import re
import datetime
import json
import paho.mqtt.client as mqtt
import subprocess

# Configuration MQTT
MQTT_SERVER = "192.168.1.XXX"  # Adresse de votre Home Assistant
MQTT_PORT = 1883
MQTT_USER = "XXXX"    # Nom utilisateur mqtt de votre Home Assistant
MQTT_PASSWORD = "XXXXXX" # Mot de passe mqtt de votre Home Assistant
MQTT_TOPIC = "birdnet/detection" # Topic de base

# Regex pour capturer les informations de détection
RE_BIRD_ENTRY = re.compile(r".*\('([^']+)_\s*([^']+)', ([\d.]+)\)")

def on_connect(client, userdata, flags, rc):
    """Fonction appelée lors de la connexion au broker MQTT."""
    if rc == 0:
        print("Connecté au broker MQTT!")
    else:
        print("Échec de la connexion, code de retour:", rc)

def main():
    """Fonction principale pour lire les logs et publier sur MQTT."""
    client = mqtt.Client(protocol=mqtt.MQTTv311)
    client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
    client.on_connect = on_connect
    client.connect(MQTT_SERVER, MQTT_PORT, 60)
    client.loop_start()

    print("Démarrage de la surveillance des logs...")

    command = ["journalctl", "-f", "-o", "cat", "-u", "birdnet_analysis.service"]
    process = subprocess.Popen(command, stdout=subprocess.PIPE, text=True, bufsize=1)

    while True:
        line = process.stdout.readline()
        if not line:
            time.sleep(1)
            continue

        match = RE_BIRD_ENTRY.search(line)
        if match:
            scientific_name = match.group(1).strip()
            common_name = match.group(2).strip()
            confidence = float(match.group(3))

            # Ignore si l’espèce est dans la liste EXCLUDED_SPECIES
            if scientific_name in EXCLUDED_SPECIES:
                print(f"Ignoré : {common_name} ({scientific_name})")
                continue

            # Condition sur la confiance
            if confidence > 0.7: # Ajuster si vous voulez plus précis ou moins précis
                payload = {
                    "timestamp": datetime.datetime.now().isoformat(),
                    "scientific_name": scientific_name,
                    "common_name": common_name,
                    "confidence": confidence
                }

                json_payload = json.dumps(payload)
                client.publish(MQTT_TOPIC, json_payload)
                print(f"Publication : {common_name} ({scientific_name}) (confiance: {confidence:.2f})")

if __name__ == "__main__":
    main()

    client = mqtt.Client()
    client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
    client.on_connect = on_connect
    client.connect(MQTT_SERVER, MQTT_PORT, 60)
    client.loop_start()

    print("Démarrage de la surveillance des logs...")
    
    with open(SYSLOG_FILE_PATH, "r") as file:
        file.seek(0, 2) # Déplacer le curseur à la fin du fichier
        while True:
            line = file.readline()
            if not line:
                time.sleep(1)
                continue

            match = RE_BIRD_ENTRY.search(line)
            if match:
                log_data = match.group(1)
                
                # Le format du log peut varier, il faut l'adapter
                # Cet exemple est basé sur un format courant
                parts = log_data.split(';')
                if len(parts) >= 5:
                    timestamp = parts[0]
                    sci_name = parts[2]
                    com_name = parts[3]
                    confidence = float(parts[4])

                    payload = {
                        "timestamp": timestamp,
                        "scientific_name": sci_name,
                        "common_name": com_name,
                        "confidence": confidence
                    }

                    json_payload = json.dumps(payload)
                    client.publish(MQTT_TOPIC, json_payload)
                    print(f"Publication de la détection : {com_name} sur le topic {MQTT_TOPIC}")

if __name__ == "__main__":
    main()

Sauvegardez le fichier (Ctrl+O, puis Entrée, et Ctrl+X).

E - Rendre le script automatique au démarrage du pi :

Pour que le script Python qui transmet les détections d’oiseaux à MQTT se lance automatiquement au démarrage du Raspberry Pi, il faut créer un service systemd. Cela garantira que le script est toujours actif et qu’il redémarre en cas de problème.

Voici les étapes pour créer ce service.

E-1 : Créer le fichier de service systemd:

Créez un nouveau fichier de service dans le répertoire des services système :

sudo nano /etc/systemd/system/birdnet-mqtt.service

Ensuite, copiez et collez le contenu suivant dans le fichier. N’oubliez pas d’ajuster les chemins si necessaire :

[Unit]
Description=Service de publication des détections BirdNet sur MQTT
After=network.target
After=network-online.target

[Service]
ExecStart=/home/pi/birdnet_venv/bin/python /home/pi/birdnet_to_mqtt.py
WorkingDirectory=/home/pi
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

Avec :

ExecStart : C’est la commande qui sera exécutée pour lancer le service. Nous spécifions le chemin complet vers l’interpréteur Python de votre environnement virtuel (/home/pi/birdnet_venv/bin/python) et le chemin complet de votre script (/home/pi/birdnet_to_mqtt.py).

User et Group : Ces lignes indiquent que le service doit s’exécuter avec les permissions de l’utilisateur pi.

Restart=always : Cette ligne est importante car elle garantit que le service redémarrera automatiquement s’il s’arrête de manière inattendue.

Attention, visiblement sur pi os trexie, le nom d’utilisateur n’est pas pi mais admin. Il faut adapter les chemins du fichier systemd ci dessus en remplacement pi par admin si ce fichier ne fonctionne pas lors de l’installation.

E-2 : Activer et démarrer le service

Recharger la configuration systemd :

sudo systemctl daemon-reload

Activer le service pour qu’il se lance au démarrage :

sudo systemctl enable birdnet-mqtt.service

Démarrer le service :

sudo systemctl start birdnet-mqtt.service

E-3 : Vérifier le service :

Pour vérifier le statut du service birdnet-mqtt, utilisez la commande suivante :

sudo systemctl status birdnet-mqtt.service

Cette commande affichera des informations détaillées sur l’état du service, y compris s’il est en cours d’exécution (active (running)), s’il y a eu des erreurs, et les dernières entrées de son journal.

A ce stade, le rapberry pi Birdnet doit envoyer des informations à Home Assitant via mqtt.

3- Configuration coté Home assistant :

A - Configuration ds sensors dans configuration.yaml:

Dans votre fichier configuration.yaml, il faut ajouter des senors qui permettent de récupérer les informations envoyé par le raspberry pi Birdnet via le mqtt .

mqtt:
  sensor:
    - name: "Dernière détection d'oiseau"
      state_topic: "birdnet/detection"
      value_template: "{{ value_json.common_name }}"
      icon: "mdi:bird"
      json_attributes_topic: "birdnet/detection"
      unique_id: Derniere_detection_oiseau

    - name: "Confiance de la détection"
      state_topic: "birdnet/detection"
      value_template: "{{ (value_json.confidence * 100) | round(2) }}"
      unit_of_measurement: "%"
      icon: "mdi:percent"
      unique_id: Confiance_de_la_detection

    - name: "Nom scientifique"
      state_topic: "birdnet/detection"
      value_template: "{{ value_json.scientific_name }}"
      icon: "mdi:leaf"
      unique_id: Nom_scientifique

    - name: "Heure de la détection"
      state_topic: "birdnet/detection"
      value_template: "{{ value_json.timestamp }}"
      icon: "mdi:clock"
      device_class: timestamp
      unique_id: heure_de_la_detection

B - Card Lovelace :

Card très basique :

type: entities
title: Dernières détections d'oiseaux
state_color: false
entities:
  - entity: sensor.derniere_detection_doiseau
  - entity: sensor.nom_scientifique
  - entity: sensor.confiance_de_la_detection
  - entity: sensor.heure_de_la_detection

Sur les conversations :

et sur le blog de Kyle Niewiada

il y a de belles cards, avec des images des oiseaux.

C’est un peu complexe à mettre en place, mais c’est une bonne base pour améliorer les cards.
Un conseil il faut vraiment utiliser le type de card : type: markdown, je ne suis pas arrivé avec picture-card ou picture-element.

**A ce stade, vous devriez avoir lié Birdnet à Home assistant.


BONUS :
**

J’ai essayé de moderniser la page web de Birdnet-pi et j’ai créé une nouvelle page web :
Vous pouvez trouver mon site sur cette page là :

https://jpbirdnet.duckdns.org/

**
**
J’espère que vous serez émerveillé par ce beau projet et que vous trouverez du plaisir à reconnaitre les oiseaux situés à proximité de chez vous.

11 « J'aime »

Génial ! merci pour la découverte !

J’avais commencé la semaine dernière à implémenter bird_classification sur la nouvelle version de Frigate. Mais la résolution de mes caméras externes ne me donne pas des résultats satisfaisants.

Je pense que je vais réaiguiller mon projet !

2 « J'aime »

C’est un super projet que je ne connaissais pas.
Je vais essayer de trouver du temps pour mettre ça en place. Merci de la découverte

1 « J'aime »

Bonjour,

Très chouette tuto super détaillé, merci ! :folded_hands:
Je suis persuadé que cela va aider certains à la décision d’installer Birdnet parmi la communauté. :+1:

Juste pour compléter avec une alternative :
Au lieu de l’ajout d’un service chargé de MQTT et à défaut d’avoir des compétences en Python, j’ai opté pour l’utilisation d’Apprise qui est inclus dans le package de Birdnet en le configurant comme ceci dans l’interface d’admin (remplacer la zone en bleu par vos identifiants) :

et en dessous :

pour ceux qui souhaitent copier/coller :

{"Common_Name":"$comname","Scientific_Name":"$sciname","Confidence_Score":"$confidence","link":"$listenurl","Date":"$date","Time":"$time","Image":"$flickrimage","Common_Name":"$comname"}

Certes il y a une répétition à la fin de Common_name, c’est une façon de contourner un bug dans l’interface de Birdnet donc gardez cette ligne ainsi.
Il faut bien entendu renseigner une API key de FLickr pour que ca fonctionne.

Mes autres paramètres modifiés au cas où (mais rien à voir avec MQTT):

Enfin, pour ne pas avoir de problèmes d’accents dans Birdnet et dans HA, vérifiez que votre locale est en UTF-8 et non en 8859 :
sudo raspi-config
option 5: Options de localisation
L1: Locale
Choisir une “locale” en UTF8 (exm., fr_FR.UTF-8 UTF-8) à la place de celle par défaut qui est “fr_FR ISO-8859”.
valider puis choisir celle en UTF8
Confirmer et rebooter le RPI.

2 « J'aime »

J’adore cette idée…
mais rien ne remonte dans mon HA…

j’ai regardé les logs de mosquitto:

2026-01-07 15:02:51: New connection from 192.168.1.180:55619 on port 1883.
2026-01-07 15:02:51: New client connected from 192.168.1.180:55619 as auto-C5BE6455-D714-2DD9-8CF0-D862EAC4E9F2 (p2, c1, k60, u'mqtt-user').
2026-01-07 15:02:53: Client auto-C5BE6455-D714-2DD9-8CF0-D862EAC4E9F2 closed its connection.
2026-01-07 15:04:15: Saving in-memory database to /data//mosquitto.db.
2026-01-07 15:04:25: New connection from 172.30.32.2:56246 on port 1883.
2026-01-07 15:04:25: Client <unknown> closed its connection.
2026-01-07 15:04:47: New connection from 192.168.1.180:42269 on port 1883.
2026-01-07 15:04:47: New client connected from 192.168.1.180:42269 as auto-2143DB24-F326-87A8-7B92-467E11C6829A (p2, c1, k60, u'mqtt-user').
2026-01-07 15:04:52: Client auto-2143DB24-F326-87A8-7B92-467E11C6829A closed its connection.
2026-01-07 15:06:25: New connection from 172.30.32.2:57002 on port 1883.
2026-01-07 15:06:25: Client <unknown> closed its connection.
2026-01-07 15:07:17: New connection from 192.168.1.180:55021 on port 1883.
2026-01-07 15:07:17: New client connected from 192.168.1.180:55021 as auto-3712B27E-2C85-9FF8-280E-5CCBCF20AB32 (p2, c1, k60, u'mqtt-user').
2026-01-07 15:08:25: New connection from 172.30.32.2:46576 on port 1883.
2026-01-07 15:08:25: Client <unknown> closed its connection.
2026-01-07 15:10:25: New connection from 172.30.32.2:59676 on port 1883.
2026-01-07 15:10:25: Client <unknown> closed its connection.

j’ai bien mon birdnetpi qui cause, mais je retrouve pas de traces dans mqtt…

ni dans HA

y’a des log quelque part ?
j’ai mis ca sur un Pi4 avec une vieille webcam en guise de micro. (que je change dès que possible…)

C’est peut être une mise à jour de la structure du fichier où sont enregistrés les espèces :

Le fichier principal où sont enregistrées toutes les détections (date, heure, nom scientifique, nom commun, confiance) se trouve ici :
Chemin complet : /home/birdnet_zero/BirdNET-Pi/scripts/BirdDB.txt

Structure du fichier : C’est un fichier texte au format CSV (séparé par des points-virgules ;). Chaque ligne correspond à une détection.

Il faut peut être mettre à jour le script pour publier avec le bon ordre ou nom :

Exemple de ligne formatant et envoyant le JSON au broker

mosquitto_pub -h « $MQTT_HOST » -t « $MQTT_TOPIC » -m « {"scientific_name":"$SCI_NAME", "common_name":"$COM_NAME", "confidence":"$CONFIDENCE"} »

Dans mon script on va chercher les logs de birdnet, c’est peut être pas la méthode la plus robuste, il faudrait peut être aller chercher directement le fichier txt…

Sinon j’ai demandé à Gemini et il me dit que mon script à des doublons

Double exécution : Tu as deux fois if name == « main »: à la suite. Le script lance la première partie, puis arrive sur la deuxième qui essaie d’ouvrir un fichier SYSLOG_FILE_PATH qui n’existe pas. Cela fait crasher le script instantanément.

Manque de boucle infinie stable : Le client MQTT se connecte, mais comme le reste du script rencontre une erreur, il s’arrête, ce qui force le broker à fermer la connexion.

Variables manquantes : La variable EXCLUDED_SPECIES est utilisée mais n’est définie nulle part dans ton code.

du coup il me propose ce script là birdnet_to_mqtt.py :

import time
import re
import datetime
import json
import paho.mqtt.client as mqtt
import subprocess

# --- CONFIGURATION ---
MQTT_SERVER = "192.168.1.XXX" # Ton IP Home Assistant
MQTT_PORT = 1883
MQTT_USER = "XXXX"
MQTT_PASSWORD = "XXXXXX"
MQTT_TOPIC = "birdnet/detection"
# Liste des espèces à ignorer (laisser vide si aucune)
EXCLUDED_SPECIES = ["Humain", "Bruit", "Vent"] 
CONFIDENCE_THRESHOLD = 0.7

# Regex pour capturer les informations de détection dans les logs
RE_BIRD_ENTRY = re.compile(r".*\('([^']+)_\s*([^']+)', ([\d.]+)\)")

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("✅ Connecté au broker MQTT avec succès !")
    else:
        print(f"❌ Échec de connexion, code : {rc}")

def main():
    # Setup MQTT
    client = mqtt.Client(protocol=mqtt.MQTTv311)
    client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
    client.on_connect = on_connect
    
    try:
        client.connect(MQTT_SERVER, MQTT_PORT, 60)
    except Exception as e:
        print(f"❌ Impossible de joindre le broker : {e}")
        return

    client.loop_start()

    print("🔭 Surveillance des logs BirdNET lancée...")

    # On écoute les logs de l'analyseur BirdNET
    command = ["journalctl", "-u", "birdnet_analysis.service", "-f", "-o", "cat"]
    process = subprocess.Popen(command, stdout=subprocess.PIPE, text=True, bufsize=1)

    try:
        for line in iter(process.stdout.readline, ''):
            match = RE_BIRD_ENTRY.search(line)
            if match:
                scientific_name = match.group(1).strip()
                common_name = match.group(2).strip()
                confidence = float(match.group(3))

                # Filtres
                if scientific_name in EXCLUDED_SPECIES:
                    continue

                if confidence >= CONFIDENCE_THRESHOLD:
                    payload = {
                        "timestamp": datetime.datetime.now().isoformat(),
                        "scientific_name": scientific_name,
                        "common_name": common_name,
                        "confidence": round(confidence, 4)
                    }

                    json_payload = json.dumps(payload)
                    client.publish(MQTT_TOPIC, json_payload)
                    print(f"🚀 Publié : {common_name} ({confidence*100:.1f}%)")
                    
    except KeyboardInterrupt:
        print("Arrêt du script...")
    finally:
        client.loop_stop()
        process.terminate()

if __name__ == "__main__":
    main()

alors, effectivement, j’ai l’arborescence /home/$USERNAME/BirdNET-Pi/
mais mon $USERNAME n’est pas birdnet_zero… c’est logique.

je vais regarder de plus pres, ca doit pas etre sorcier, l’interface web marche bien et je vois mes bébètes et leurs chants.

et le BirdDB.txt bien à jour, mais dans birdnetpi:~/BirdNET-Pi$

Date;Time;Sci_Name;Com_Name;Confidence;Lat;Lon;Cutoff;Week;Sens;Overlap
2026-01-07;11:00:13;Corvus monedula;Eurasian Jackdaw;0.8264;47.2145;-1.5512;0.7;2;1.25;0.0
2026-01-07;11:28:02;Corvus corone;Corneille noire;0.7475;47.4143;-1.6690;0.7;2;1.25;0.0
2026-01-07;11:28:11;Corvus corone;Corneille noire;0.7057;47.4143;-1.6690;0.7;2;1.25;0.0
2026-01-07;11:48:39;Corvus corone;Corneille noire;0.7966;47.4143;-1.6690;0.7;2;1.25;0.0
2026-01-07;13:51:59;Buteo buteo;Buse variable;0.9073;47.4147;-1.6690;0.7;2;1.25;0.0
2026-01-07;16:16:29;Chloris chloris;Verdier d'Europe;0.7138;47.4147;-1.6690;0.7;2;1.25;0.0
2026-01-07;16:16:32;Erithacus rubecula;Rougegorge familier;0.7893;47.4147;-1.6690;0.7;2;1.25;0.0

tout n’est pas perdu :wink:
mais je vois que mosquitto_pub n’existe pas… j’ai du oublié de l’installer…

Pardon mon message n’était peut être pas clair, il y a deux façons de récupérer les informations, soit par le fichier txt soit par les logs du Service birdnet_analysis.

C’est la commande suivant dans mon script :

command = [« journalctl », « -f », « -o », « cat », « -u », « birdnet_analysis.service »]

En fait le script écoute le service et transmet l’information.

Le plus robuste serait sûrement de se baser sur le fichier txt qui est généré par birdnet, c’est celui utilisé par l’interface web original de birdnet pi.

Pour voir exactement ce que le script voit en temps réel, tape cette commande dans ton terminal :

journalctl -u birdnet_analysis.service -f -o cat

Sinon tu peux trouver les logs de birdnet pi:

Le log d’analyse :
/home/birdnetpi/BirdNET-Pi/log/scripts/analysis.log
(C’est ici que BirdNET écrit chaque détection au moment où elle se produit).
Le log d’extraction :
/home/birdnetpi/BirdNET-Pi/log/scripts/extraction.log
La base de données :
/home/birdnetpi/BirdNET-Pi/scripts/BirdDB.txt

Remplace birdnetpi par ton nom sur le pi. J’ai mis birdnetpi ici car c’est ce que je vois dans tes logs mais moi j’ai birdnet_zero

En espérant être plus clair

1 « J'aime »

Merci d avoir pris le temps de nous donner toutes ces informations détaillées.

J étais à la recherche de ce type d application. Je vais l implémenter sur mon HA.

1 « J'aime »

Bon, ca avance

j’ai lancé un /usr/bin/mosquitto_pub -h 192.168.1.25 -u user -P Pwd -t "birdnet/detection" -m '{"scientific_name":"Corvus monedula", "common_name":"Choucas des tours", "confidence":"0.8475"}'
ce qui correspond à la ligne 2026-01-08;11:36:33;Corvus monedula;Choucas des tours;0.8475;47.xxxx;-1.yyyy;0.7;2;1.25;0.0
et HA voit çà:

bon, la confiance, c’est pas encore çà !!! on croirais voir nos hommes politiques…

par contre en automatique, je n’ai rien qui remonte.

J’ai essayé la technique de @Marsupilami à savoir d’utiliser Apprise, mais sans « Image »:« $flickrimage » vu que l’API est pour les comptes payants.

j’arrive bien à avoir les updates dans mqtt en automatique.
en passant, j’ai retiré ma vieille webcam logitec et mis un petit micro cravate à 10€ sur amazon, j’ai multiplié par 20 le nombre de détections… :bird: :dove: :eagle:

le seul truc qui me reste c’est de récupérer les entrées mqtt dans HA, vu que le template de @jpjobe ne va pas avec apprise et que le pyton n’est pas mon fort.

Je pense qu’il y a du avoir une mise à jour entre la version que j’ai installé et la version que tu as, c’est pour cela que mon code python n’est pas tout a fait adapté.
Peux tu m’envoyer ton fichier .txt et je vais essayer d’adapter le code python?


C’est tout bon !!!

1 « J'aime »

Du coup voici le partage du script et du sensors ha mis à jour suites aux échanges.

Script birdnet_to_mqtt

import time
import re
import datetime
import json
import paho.mqtt.client as mqtt
import subprocess

# ================= CONFIGURATION =================
MQTT_SERVER = "192.168.1.XXX"  # <---  ADRESSE IP HA
MQTT_PORT = 1883
MQTT_USER = "XXXX"             # <---  UTILISATEUR mosquitto ha
MQTT_PASSWORD = "XXXXX"        # <---  MOT DE PASSE mosquitto ha
MQTT_TOPIC = "birdnet/detection"
CONFIDENCE_THRESHOLD = 0.7     # Seuil de confiance (0.7 = 70%)
# =================================================

# REGEX AMÉLIORÉE : Elle cible la ligne finale de détection qui est la plus fiable.
# Format ciblé : [birdnet_analysis][INFO] 2026-01-14;16:42:23;Sci_Name;Com_Name;0.7019;...
RE_BIRD_ENTRY = re.compile(r"INFO\] \d{4}-\d{2}-\d{2};\d{2}:\d{2}:\d{2};([^;]+);([^;]+);([\d.]+)")

# Liste des espèces à ignorer (Nom Scientifique)
EXCLUDED_SPECIES = ["Humain", "Bruit", "Vent"]

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("Connecté au broker MQTT avec succès !")
    else:
        print(f"Échec de connexion MQTT, code : {rc}")

def main():
    # Setup MQTT avec l'API compatible (v1)
    client = mqtt.Client(protocol=mqtt.MQTTv311)
    client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
    client.on_connect = on_connect

    try:
        client.connect(MQTT_SERVER, MQTT_PORT, 60)
    except Exception as e:
        print(f" Impossible de joindre le broker : {e}")
        return

    client.loop_start()
    print("🔭 Surveillance des logs BirdNET lancée (Nouvelle Regex)...")

    # Écoute des logs du service d'analyse
    command = ["journalctl", "-u", "birdnet_analysis.service", "-f", "-o", "cat"]
    process = subprocess.Popen(command, stdout=subprocess.PIPE, text=True, bufsize=1)

    try:
        for line in iter(process.stdout.readline, ''):
            match = RE_BIRD_ENTRY.search(line)
            if match:
                scientific_name = match.group(1).strip()
                common_name = match.group(2).strip()
                confidence = float(match.group(3))

                print(f"Analyse vue : {common_name} ({confidence:.2f})", end="")

                # Filtres
                if scientific_name in EXCLUDED_SPECIES:
                    print(" Ignoré (Liste exclusion)")
                    continue

                if confidence >= CONFIDENCE_THRESHOLD:
                    payload = {
                        "timestamp": datetime.datetime.now().isoformat(),
                        "scientific_name": scientific_name,
                        "common_name": common_name,
                        "confidence": round(confidence, 4)
                    }

                    json_payload = json.dumps(payload)
                    client.publish(MQTT_TOPIC, json_payload)
                    print(f" PUBLIÉ sur MQTT !")
                else:
                    print(f" Rejeté (Confiance {confidence:.2f} < {CONFIDENCE_THRESHOLD})")
                    
    except KeyboardInterrupt:
        print("\nArrêt du script...")
    finally:
        client.loop_stop()
        process.terminate()

if __name__ == "__main__":
    main()

Sensor Heure de la détection dans configuration.yaml

- name: "Heure de la détection"
      state_topic: "birdnet/detection"
      # On découpe la chaîne pour ne garder que les caractères de l'index 11 à 19
      value_template: "{{ value_json.timestamp[11:19] }}"
      icon: "mdi:clock"
      unique_id: heure_de_la_detection

Penser à mettre à jour la card avec les mêmes noms de sensors

Bonjour,

J’ai essayé de configurer de la même façon car je n’arrive pas à publier en mqtt et j’ai cette erreur

[apprise][INFO] Loaded 1 entries from file:///tmp/phpi1kgjnicn9i7elbXy20?encoding=utf-8&cache=yes
[apprise][ERROR] Unhandled Notification Exception
Traceback (most recent call last):
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/apprise/apprise.py", line 673, in _notify_sequential
    result = server.notify(**kwargs)
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/apprise/plugins/base.py", line 468, in notify
    the_calls = [self.send(**kwargs2) for kwargs2 in send_calls]
                 ~~~~~~~~~^^^^^^^^^^^
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/apprise/plugins/mqtt.py", line 370, in send
    self.client.connect(
    ~~~~~~~~~~~~~~~~~~~^
        self.host,
        ^^^^^^^^^^
        port=self.port,
        ^^^^^^^^^^^^^^^
        keepalive=self.mqtt_keepalive,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/paho/mqtt/client.py", line 1435, in connect
    return self.reconnect()
           ~~~~~~~~~~~~~~^^
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/paho/mqtt/client.py", line 1598, in reconnect
    self._sock = self._create_socket()
                 ~~~~~~~~~~~~~~~~~~~^^
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/paho/mqtt/client.py", line 4609, in _create_socket
    sock = self._create_socket_connection()
  File "/home/admin/BirdNET-Pi/birdnet/lib/python3.13/site-packages/paho/mqtt/client.py", line 4640, in _create_socket_connection
    return socket.create_connection(addr, timeout=self._connect_timeout, source_address=source)
           ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/socket.py", line 840, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
               ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/socket.py", line 977, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
               ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
socket.gaierror: [Errno -2] Name or service not known

Je ne sais pas quoi faire, merci par avance pour votre aide
Super projet par ailleurs !

Hello,

J’ai un lampadaire dans la jardin qui vient de se faire décapiter, donc arrivée électrique dispo.
J’ai un pi4 qui traine à la maison, et je me dis que je peux bien l’exploiter pour écouter les « zoizo »
Par contre, il faut un micro extérieur, vous avez des réf ? J’ai vu l’info du micro-cravate

Bonne idée
Voici celui que j’ai

1 « J'aime »

Bonjour,
As tu configurer mqtt dans birdnet directement avec Apprise?
Ou seulement utilisé mon script v2?

Il faut soit l’un soit l’autre sinon il y a conflit.

L’erreur gaierror dans le log mentionne /home/admin/BirdNET-Pi/birdnet/lib/python3.13/…
Cela est sûrement une erreur qui dit que birdnet-pi essai de ce connecter à ta box mais n’y arrive pas.

Peux tu me confirmer que tu arrives à te connecter à ton pi en ssh? Et que tu as bien mis cette adresse dans script? Pour ma part c’est 192.168.1.32.

Si tu arrives à te connecter en ssh sur le même réseau que ta box, alors cela vient du mqtt.

Donc si c’est mqtt, soit tu as mal renseigné le nom de ton broker et mots de passe, soit tu as mis les deux solutions d’envoi en mqtt, mon script et apprise.
Je n’ai pas essayé apprise, donc sur ce point je ne peux pas aider mais si c’est sur mon script, alors vérifie bien que tu as remplacé le XX par tes éléments.

Autre possibilité, utilisé bien l’adresse ip et non le nom en .local de type homeassistant.local.
Ca me fait bigger quand je veux me connecter avec le nom .local, j’utilise que l’adresse ip que j’ai fixé dans ma box pour que l’adresse du pi 'e change pas.

Sinon partage moi ton script birdnet_to_mqtt en message privé pour que je regarde et j’apporterai une solution à tous si je trouve

Mais comme cela je pense qu’il y a les deux solutions mqtt qui sont mise en place (apprise + script).

J’ai mis ces deux là:

c’est de tres bonne qualité pour enregistrer.

1 « J'aime »

Bonjour,
Merci pour ce tuto, j’ai simplement installé l’addon birdnet-go sous home assistant avec comme micro celui de ma caméra réolink, ca semblait mal parti hier soir et aujourd’hui le résultat estassez impressionnant je dois dire plus, de 1200 détections :laughing:
Je viens de basculer les enregistrement sous mon NAS car à cette vitesse le disque va exploser.

1 « J'aime »

Oui il y a un addon mais je n’avais pas de micro à l’extérieur donc j’ai mis mon raspberry pi dehors avec le micro, voilà pourquoi j’ai proposé cette approche.

1 « J'aime »