[Retour d'expérience + Proposition] Vidéosurveillance locale universelle dans Gladys — go2rtc + Frigate (détection person/chien/cheval... sans cloud)

Bonjour à tous ! :wave:

Je viens partager un retour d’expérience complet qui s’est transformé en projet d’intégration pour Gladys. Au menu : comment j’ai fait fonctionner en 100 % local une caméra que TP-Link a volontairement bridée (pas de RTSP, pas d’ONVIF), avec de la détection d’objets IA (personne, chien, chat… et même cheval :horse:), le tout sur un mini-PC qui fait déjà tourner 2 Gladys et un Home Assistant. Et à la fin, la proposition de développement que je compte lancer.

Accrochez-vous, c’est un peu long, mais tout est reproductible. :coffee:


:warning: La problématique de départ

J’ai plusieurs caméras Tapo, dont une C660 (4K, pan/tilt, solaire/batterie — mais branchée sur secteur chez moi, la batterie n’est qu’un secours).

Je voulais la même chose que tout le monde ici : contrôler mes caméras et recevoir les alertes de détection (personne, animal…) en local, dans Gladys / Node-RED via MQTT, sans dépendre du cloud TP-Link.

Premier mur : sur toute la gamme batterie/solaire (C425, C460, C660, C645D, D230…), TP-Link désactive RTSP et ONVIF. C’est assumé et documenté par le constructeur : « économie d’énergie ». Le fait que la caméra soit alimentée en permanence sur secteur n’y change rien, c’est le firmware qui décide. :man_facepalming:

Vérification qui ne laisse aucun doute :

$ nmap -Pn -p 443,554,2020,8800 10.6.0.222

PORT     STATE  SERVICE
443/tcp  open   https
554/tcp  closed rtsp       ← RTSP fermé
2020/tcp closed onvif      ← ONVIF fermé
8800/tcp open   ???        ← tiens tiens... 👀

RTSP et ONVIF fermés… mais un mystérieux port 8800 ouvert.

Second mur, valable même pour les caméras Tapo avec RTSP (ma C520WS par exemple) : la remontée des événements de détection via ONVIF est cassée sur plusieurs modèles (bug firmware connu). Donc même en RTSP, pas d’alertes fiables en local.

:bulb: Le contournement : go2rtc parle le « Tapo »

Le port 8800, c’est le protocole propriétaire TP-Link — celui qu’utilise l’app Tapo. Et il se trouve que go2rtc (le couteau suisse vidéo d’AlexxIT, embarqué dans Frigate ET dans Home Assistant) sait le parler nativement via une source tapo://.

L’architecture complète devient :

Caméra (tapo://, rtsp://, onvif://, ...) → go2rtc → Frigate (détection IA) → MQTT → Gladys / Node-RED

Et le point clé : c’est Frigate qui fait la détection d’objets, pas le firmware de la caméra. Donc plus besoin d’ONVIF, plus besoin du cloud, et une détection meilleure que celle de TP-Link — l’app Tapo me dit « animal » pour tout, Frigate distingue person, dog, cat, horse (labelmap COCO, 91 classes). Pour ma part j’ai besoin de détecter précisément les chevaux, autant dire que ça change tout. :racehorse:

:hammer_and_wrench: Les pièges rencontrés (pour vous éviter des heures de debug)

Ça n’a pas marché du premier coup, et chaque piège vaut le coup d’être documenté :

1. Le mot de passe doit être URL-encodé. La source c’est tapo://MOT_DE_PASSE@IP avec le mot de passe de votre compte cloud Tapo (pas de « compte caméra » sur ces modèles). Si votre mdp contient #, ^, %, @… il faut l’encoder (#%23, etc.), sinon go2rtc tronque l’URL silencieusement. Astuce :

bash

python3 -c "import urllib.parse,getpass; print(urllib.parse.quote(getpass.getpass('mdp: '), safe=''))"

2. Le flux principal 4K = écran noir. Bug connu (SPS/PPS non propagés, cf. issue go2rtc #2202) : la caméra envoie bien la vidéo mais go2rtc n’arrive pas à la parser. Le substream fonctionne parfaitement :

tapo://MDP_ENCODE@10.6.0.222?channel=0&subtype=1

640×360, c’est moche à l’œil mais c’est exactement ce que veut Frigate pour la détection (les modèles travaillent en 300-640 px de toute façon). Le 4K reste sur la carte SD de la caméra pour la relecture.

3. Les timestamps pourris (LE piège). Le flux tapo génère des DTS non monotones → ffmpeg s’emballe à 190 % de CPU, le watchdog Frigate le tue en boucle, aucun clip ne s’enregistre. Le correctif qui a tout réparé :

yaml

input_args: -avoid_negative_ts make_zero -fflags +genpts+discardcorrupt -rtsp_transport tcp -use_wallclock_as_timestamps 1

La clé c’est -use_wallclock_as_timestamps 1 : ffmpeg ignore les timestamps de la source et les régénère.

4. L’iGPU Intel change tout. Mon serveur est un modeste Beelink U59 (Celeron N5105) qui héberge déjà HA + 2 Gladys. En détecteur CPU, il était à genoux (86 % système). En activant OpenVINO sur l’iGPU + décodage VAAPI :

Métrique CPU iGPU (OpenVINO + VAAPI)
Inférence 94,6 ms 15,8 ms
Process détecteur 191,8 % 8,5 %
CPU système 86 % 22 %
Frames jetées 7/s 0

Un Celeron à 22 % de CPU qui fait de la détection d’objets temps réel en plus de HA et 2 Gladys. :exploding_head:

:white_check_mark: Le résultat

Côté MQTT, Frigate publie tout ce dont on rêve :

frigate/c660/person            → 1 / 0  (binaire, parfait pour Gladys)
frigate/c660/dog               → 1 / 0
frigate/events                 → JSON riche (label, score, box, trajectoire, zones...)
frigate/reviews                → alertes avec thumbnail
frigate/stats                  → santé complète (fps, inférence, stockage...)

Détection validée en réel (person à 0,93 de score, dog reconnu là où Tapo dit « animal »), clips enregistrés, snapshots, zéro cloud. Une caméra « officiellement impossible à intégrer » qui fonctionne mieux en local que les modèles « compatibles ». :muscle:

:gear: Configurations complètes validées (docker-compose + config Frigate) — cliquez pour déplier

docker-compose.yml :

yaml

services:
  frigate:
    container_name: frigate
    image: ghcr.io/blakeblackshear/frigate:stable
    restart: unless-stopped
    shm_size: "256mb"
    devices:
      - /dev/dri/renderD128:/dev/dri/renderD128
    volumes:
      - ./config:/config
      - ./storage:/media/frigate
      - type: tmpfs
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports:
      - "8971:8971"   # UI (HTTPS depuis la 0.17 !)
      - "8554:8554"   # RTSP restream
      - "1984:1984"   # go2rtc

config/config.yml :

yaml

mqtt:
  enabled: true
  host: <votre_mosquitto>
  user: xxx
  password: xxx

detectors:
  ov:
    type: openvino
    device: GPU

model:
  width: 300
  height: 300
  input_tensor: nhwc
  input_pixel_format: bgr
  path: /openvino-model/ssdlite_mobilenet_v2.xml
  labelmap_path: /openvino-model/coco_91cl_bkgr.txt

go2rtc:
  streams:
    c660:
      - tapo://MDP_URL_ENCODE@10.6.0.222?channel=0&subtype=1

cameras:
  c660:
    ffmpeg:
      hwaccel_args: preset-vaapi
      inputs:
        - path: rtsp://127.0.0.1:8554/c660
          input_args: -avoid_negative_ts make_zero -fflags +genpts+discardcorrupt -rtsp_transport tcp -use_wallclock_as_timestamps 1
          roles: [detect, record]
    detect:
      enabled: true
      fps: 5
      width: 640
      height: 360
    objects:
      track: [person, dog, cat, horse]
    record:
      enabled: true
      retain:
        days: 7
        mode: motion
    snapshots:
      enabled: true
      retain:
        default: 14

Notes d’exploitation :

  • Frigate 0.17 : l’UI est en HTTPS sur le port 8971 (certificat auto-signé), compte admin généré au premier démarrage (mdp dans les logs).
  • shm_size : 128 Mo ne suffit pas, même pour 1 caméra.
  • Les caméras Tapo limitent les flux concurrents → toujours consommer via go2rtc (qui mutualise en 1 connexion), jamais en direct.
  • Petit warning connection reset by peer périodique sur le port 8800 : la caméra coupe de temps en temps, go2rtc reconnecte tout seul. Non bloquant.
---

:rocket: Et maintenant : proposition d’intégration Gladys

Tout ça fonctionne, mais soyons honnêtes : entre le nmap, l’URL-encoding, les input_args ffmpeg et la config OpenVINO, ce n’est pas accessible au commun des mortels. Et c’est exactement le genre de complexité que Gladys sait masquer. :heart:

Je compte donc développer une intégration « Vidéosurveillance locale » (nouveau service ou extension de rtsp-camera, à discuter) dont le principe est de ne PAS réinventer la roue :

  • go2rtc = couche d’abstraction protocolaire (il parle rtsp://, tapo://, onvif://, Ring, Nest, Dahua, USB… et c’est maintenu par AlexxIT + une grosse communauté). Une intégration = toutes les caméras.
  • Frigate = détection d’objets + enregistrements + MQTT.
  • Gladys = orchestration, configuration simple, et exposition des features.

Ce que ferait l’intégration

  1. Gestion des conteneurs Frigate/go2rtc depuis l’UI Gladys (installation, démarrage, restart) — sur le même modèle que ce qui existe déjà pour Zigbee2MQTT.
  2. Configuration intelligente automatique : détection du matériel (iGPU Intel présent ? → OpenVINO + VAAPI ; sinon CPU), calcul du shm_size selon le nombre de caméras, défauts stables — le tout surchargeable en mode expert.
  3. Ajout de caméra simplissime : nom, type (RTSP / Tapo / ONVIF…), IP, mot de passe (encodé automatiquement !), choix des objets à détecter (person, dog, cat, horse… multi-select). L’intégration génère la config Frigate et applique les correctifs connus (substream Tapo, input_args timestamps…) sans que l’utilisateur ait à savoir que ça existe.
  4. Features Gladys créées automatiquement par caméra :
  • la caméra live classique ;
  • un binaire de détection par type d’objet (frigate/<cam>/person → capteur présence Gladys → scènes !) ;
  • une « caméra image » par type : la dernière image capturée de chaque détection (probablement un nouveau type de feature image-only, sans live — à discuter).
  1. Santé : statut par caméra, compteur de reconnexions, stats Frigate remontées.

Questions ouvertes pour la communauté (et @pierre-gilles :slight_smile:)

  • Nouveau service ou extension de rtsp-camera ? Mon intuition : nouveau service (le périmètre est très différent), mais l’existant doit rester le chemin simple pour qui ne veut qu’un flux RTSP.
  • Nouveau type de feature « caméra image-only » (affichage de la dernière image, pas de player live) : ça vous semble la bonne modélisation pour « dernière détection de type X » ?
  • Politique conteneurs compagnons : Frigate embarque son propre go2rtc, donc un seul conteneur suffit. OK pour suivre le pattern Z2M ?
  • Live view : intégration du web component video-stream.js de go2rtc (WebRTC/MSE) avec fallback snapshots pour les flux capricieux. Question du proxy via le server Gladys à creuser (mixed content HTTPS/HTTP).

Je démarre le développement par une phase d’analyse de l’existant, puis un MVP (conteneur + caméra RTSP générique + binaires de détection MQTT), puis les sources avancées (tapo://, onvif://) et le live. PRs petites et découpées, comme d’habitude.

Tous les retours sont bienvenus : cas d’usage, caméras que vous aimeriez voir supportées, avis sur les questions ci-dessus… Et si certains veulent tester le setup manuel en attendant, les configs complètes sont dans le déplié ci-dessus — je réponds aux questions ! :raised_hands:

À très vite, Terdious

Petit complément important, parce que le cas de ma Tapo C520WS illustre bien que cette approche ne concerne pas que les caméras bridées sans RTSP. :point_down:

Le cas C520WS : RTSP OK… mais détection inutilisable en local

La C520WS est une caméra filaire, donc sur le papier c’est la situation idéale : RTSP et ONVIF officiellement supportés, image et live fonctionnels sans aucun contournement (rtsp://user:pass@IP/stream1, compte caméra classique dans l’app).

Sauf que… la remontée des événements de détection via ONVIF est cassée au niveau du firmware sur ce modèle. :sob: Le symptôme est documenté chez plusieurs utilisateurs : la connexion ONVIF est coupée ~10 secondes après la requête PullMessage, alors que les événements apparaissent bien dans l’app Tapo (et que des modèles comme les C110/C210 les envoient correctement). TP-Link a publié des firmwares censés corriger (C520WS V1 : 1.3.2, V2 : 1.1.1), mais les retours restent aléatoires.

Résultat : j’ai le flux vidéo en local, mais aucun moyen fiable de savoir en local QUE quelque chose est détecté, ni QUOI. Ce qui est quand même le cœur du besoin pour de la domotique…

Pourquoi l’architecture go2rtc + Frigate règle ça aussi

C’est exactement là que l’approche du post prend tout son sens : puisque la détection est faite par Frigate (sur le flux RTSP), le bug ONVIF de la caméra devient… complètement sans objet. On n’a plus besoin que le firmware daigne nous envoyer ses événements :

  • flux RTSP natif → Frigate → détection person / dog / cat / horse (mon vrai besoin sur cette caméra, qui surveille une zone où passent les chevaux — l’app Tapo ne sait dire que « animal » :horse:) ;
  • événements riches sur MQTT (frigate/c520ws/person, frigate/events avec score, position, trajectoire…) → directement exploitables dans Gladys ;
  • et cerise : le pipeline est strictement identique à celui de la C660. Une seule architecture pour la caméra « impossible » ET la caméra « compatible mais buggée ». C’est exactement ce qui me conforte dans l’idée d’une intégration unique.

Et le pilotage PTZ ?

La C520WS est motorisée, et à terme je veux aussi pouvoir la bouger depuis Gladys (suivre une zone, presets…). Bonne nouvelle : autant l’ONVIF événementiel est cassé sur ce modèle, autant l’ONVIF PTZ fonctionne, lui. Deux pistes que je vais évaluer :

  1. ONVIF PTZ via Frigate : Frigate sait piloter le PTZ des caméras ONVIF (bloc onvif: dans la config caméra, port 2020 chez Tapo) — ça resterait dans le même outil ;
  2. pytapo en direct (l’API locale Tapo, port 443) : plus complet (presets, patrouille, sirène, privacy mode…), c’est la lib qu’utilise l’intégration HA Tapo Control.

Ça fera partie de la réflexion pour l’intégration : la détection passe par Frigate/MQTT dans tous les cas, mais le contrôle (PTZ, sirène…) mérite peut-être un canal dédié selon les marques. À modéliser proprement côté features Gladys (boutons/directions ?).

Prochaine étape

Je lance prochainement les essais sur la C520WS : intégration dans le même Frigate que la C660 (le Beelink a largement la marge maintenant :muscle:), détection horse en conditions réelles, puis premiers tests PTZ. Retour complet ici dès que c’est fait — avec les chiffres et les pièges éventuels, comme pour la C660.

Si d’autres ici ont des Tapo filaires (C210, C310, C320WS, C520WS…) et veulent comparer le comportement ONVIF de leur firmware, je suis preneur ! :raised_hands:

J’ai une C610 sur batterie et ca ma toujours saoulé de ne pas avoir de RTSP sur ce type de caméra.
Si besoin j’ai aussi une C500 et une C210 donc je pourrais tester sans problème.

Au top !!

Pour le moment j’ai tout testé en dehors de gladys, du coup je lance Claude Fable sur le dev maintenant (pour tenter de finir mes crédits ce soir ^^) !!

Je tiens au courant quand j’ai un truc de test !!

Chez moi j’ai mini pc dédié sur lequel tourne Frigate, je vais donc suivre ça de près.
Ce qui m’interroge :

  • beaucoup d’utilisateurs de Frigate comme moi utilisent un Google Coral (même si c’est moins vrai récemment), il faudrait pouvoir le prendre en compte non ?
  • j’ai des caméras Reolink, dont la configuration pour Frigate fût parfois laborieuse, si l’intégration à Gladys se veut la plus user-friendly possible, il faudrait faciliter l’édition du yaml non ? Sinon on va perdre des gens en route ^^

Voilà pour mes premières interrogations !