Auto-héberger le bridge
Le bridge est un petit service dans un conteneur. Il parle à votre console Protect, tient la machine à états de la porte, sert l’app et exécute les règles d’alerte. Il tourne sur toute machine allumée en permanence, avec Docker, capable de joindre votre console : un NAS, un Raspberry Pi ou un VPS, sur amd64 et arm64.
Vous n’êtes pas obligé de l’utiliser. L’app se connecte directement à votre console Protect et fonctionne sur votre réseau domestique sans rien installer d’autre. Le bridge sert si vous voulez des invitations familiales qui ne distribuent jamais votre clé Protect, un journal d’activité par nom, des règles d’alerte exécutées sur votre propre matériel, ou des raccourcis fiables en arrière-plan. Les deux façons de se connecter, comparées.
Liste de contrôle
À avoir sous la main : l’adresse de votre console, une nouvelle clé d’API Protect, l’adresse locale de la machine qui fera tourner le bridge, et la sortie de relais qui commande la porte. Suivez ensuite la liste ; chaque étape renvoie à sa section.
- Sortie de relais en Pulse, capteur monté en Garage, clé d’API créée
- Le bridge joint la console sur le port 443 ; la console et le téléphone joignent le bridge sur le 8787
- Compose et
.envtéléchargés, deux secrets générés, bridge démarré /healthzrépond"ready":true, depuis l’hôte et depuis une autre machine- Téléphone appairé avec l’invitation du journal du conteneur
- Deux règles Alarm Manager créées, et un mouvement de porte apparaît dans le journal d’activité
- ntfy configuré, si vous voulez des alertes sur un téléphone en veille ou à l’extérieur
- Volume de données dans vos sauvegardes,
.envdans un gestionnaire de mots de passe
Avant de commencer
- Configurez une sortie de relais en Pulse. Dans Protect, ouvrez le relais et configurez en Pulse, type porte de garage, la sortie câblée sur les bornes du bouton-poussoir de votre moteur. Notez de quelle sortie il s’agit : la numérotation commence à 1 dans l’app, mais les identifiants de l’API commencent à 0.
- Montez le capteur et réglez son type de montage sur Garage. C’est ce que lit le bridge pour décider si la porte est ouverte. Avec un autre type de montage, l’appairage automatique ne le retiendra pas.
- Créez une clé d’API Protect. Console → Réglages → Control Plane → Intégrations →
Créer une clé d’API. Copiez-la immédiatement : elle n’est plus affichée ensuite.
Cette clé peut lire toutes les caméras de votre console. Traitez-la comme un mot de passe donnant accès à votre domicile, parce que c’est fonctionnellement ce qu’elle est.
Comment le bridge trouve votre porte. Avec exactement une sortie de relais de type
porte de garage et un seul capteur monté en Garage, le bridge les trouve seul et vous n’avez rien à
configurer. S’il y a plusieurs candidats, il ne peut pas deviner : épinglez-les avec
DOOR_RELAY_ID, DOOR_OUTPUT_ID (l’identifiant de l’API : la sortie 1 dans l’app
vaut 0) et DOOR_SENSOR_ID. Une seconde porte est toujours déclarée
explicitement, jamais devinée ; voir deux portes. Une fois le bridge lancé, il
liste chaque relais et capteur avec son identifiant :
curl -s -H "Authorization: Bearer <BRIDGE_TOKENS>" http://<hôte-du-bridge>:8787/v1/discovery Appui sur le relais. Sur Protect 7.2.x, le relais bascule au lieu d’envoyer une
impulsion : le bridge l’enclenche puis le relâche lui-même. C’est le réglage par défaut,
RELAY_PULSE_MODE=emulated. Seulement si votre relais envoie réellement une impulsion de
lui-même, mettez RELAY_PULSE_MODE=native.
Qui doit joindre quoi
Tout reste sur votre réseau. Si le bridge et la console sont sur des VLAN différents, votre pare-feu doit autoriser les deux premières lignes, dans le sens indiqué.
| De | Vers | Port | Pourquoi |
|---|---|---|---|
| Bridge | Console Protect | TCP 443 (HTTPS) | Lire le capteur, actionner le relais. Obligatoire. |
| Console Protect | Bridge | TCP 8787 (HTTP) | Webhooks Alarm Manager, pour que le bridge réagisse instantanément. Recommandé ; il interroge la console de toute façon. |
| Téléphone | Bridge | TCP 8787 (HTTP) | L’app, les widgets, la Watch et les Raccourcis. Obligatoire : sur votre Wi-Fi, ou via un VPN ou un tunnel à l’extérieur. |
| Téléphone | Bridge | UDP 5353 (mDNS) | Trouver le bridge sans saisir son adresse. Facultatif ; demande le réseau de l’hôte, et ne traverse pas les VLAN sauf si votre passerelle relaie le mDNS. |
| Bridge | ntfy (le vôtre, ou ntfy.sh) | TCP 8788, ou 443 pour ntfy.sh | Alertes push vers un téléphone en veille ou à l’extérieur. Facultatif ; voir alertes. |
| Bridge | ghcr.io | TCP 443 | Télécharger l’image, à l’installation et aux mises à jour. |
Le téléphone doit être sur un réseau qui peut joindre le bridge, avec l’isolation des clients désactivée sur ce Wi-Fi. Ne redirigez jamais un port d’Internet vers le bridge ; voir depuis l’extérieur.
Le lancer
L’image est ghcr.io/pcrausaz/garage-opener-bridge. Le docker-compose.yml
et le .env.example de référence se
trouvent dans le dépôt du bridge. Placez-les dans un même dossier :
mkdir garage-bridge && cd garage-bridge
curl -fsSLO https://raw.githubusercontent.com/pcrausaz/garage-opener-bridge/main/selfhost/docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/pcrausaz/garage-opener-bridge/main/selfhost/.env.example -o .env
Ouvrez .env et renseignez les valeurs du début. Deux d’entre elles sont des secrets à
générer, pas à inventer. Lancez cette commande deux fois et collez chaque résultat à sa place :
openssl rand -hex 24 # .env : les valeurs qui comptent
GARAGE_BRIDGE_TAG=latest # voir « mises à jour et sauvegardes » plus bas
PROTECT_URL=https://192.168.1.1
PROTECT_API_KEY=… # la clé que vous venez de créer
BRIDGE_TOKENS=… # premier résultat d'openssl : votre jeton admin
WEBHOOK_SECRET=… # second résultat : le secret de chemin Alarm Manager
TZ=Europe/Zurich .env est lu comme des valeurs brutes, pas exécuté comme un script shell : une ligne comme
BRIDGE_TOKENS=$(openssl rand -hex 24) enregistrerait ces caractères tels quels. Collez le
résultat, pas la commande.
docker compose up -d
curl -s localhost:8787/healthz
Vous devriez obtenir {"ok":true,"ready":true,…}, avec la version
réellement en cours d’exécution. Si ready vaut false, le bridge tourne mais
n’a pas encore joint la console, et le message indique pourquoi. Lancez le même curl depuis
une autre machine, avec l’adresse de l’hôte au lieu de localhost, pour vérifier aussi le
chemin réseau.
Avec Dockhand, Portainer ou un autre gestionnaire de stacks
Collez le docker-compose.yml de référence comme stack et définissez les mêmes variables
dans l’environnement de la stack, au lieu d’un fichier .env. Le fichier liste chaque
réglage explicitement, rien d’autre n’est nécessaire. Une variable absente du fichier n’atteint jamais
le bridge.
Deux portes sur un seul bridge
À partir du bridge 0.6, un seul bridge pilote deux portes : plus besoin d’un second bridge. Chaque porte
a besoin de sa propre sortie de relais, sur le même relais ou sur un autre, et de son propre capteur
monté en Garage. La première porte garde ses réglages DOOR_* ; la seconde est décrite par
DOOR2_* :
# .env : deux portes (bridge 0.6 ou ultérieur)
DOOR_NAME=Porte gauche
DOOR_RELAY_ID=…
DOOR_OUTPUT_ID=0 # identifiant de l'API : sortie 1 dans Protect
DOOR_SENSOR_ID=…
DOOR2_NAME=Porte droite
DOOR2_RELAY_ID=… # le même relais, ou un second
DOOR2_OUTPUT_ID=1 # identifiant de l'API : sortie 2 dans Protect
DOOR2_SENSOR_ID=… # le capteur de cette porte
# DOOR2_INTERIOR_CAMERA_ID=… # facultatif : l'image sur l'écran de cette porte
# DOOR2_DRIVEWAY_CAMERA_ID=… # facultatif : la caméra qui voit l'allée
Avec deux sorties de garage et deux capteurs, le bridge ne les apparie pas tout seul : deviner quel
capteur surveille quelle porte, c’est le meilleur moyen d’ouvrir la mauvaise. Il affiche au démarrage
un appariement suggéré, par nom, et attend que vous l’écriviez comme ci-dessus. Les identifiants
viennent du même appel /v1/discovery. DOOR_NAME et DOOR2_NAME
sont les noms que l’app, les notifications et le journal d’activité donnent à chaque porte.
- Fichier compose. Le compose de référence ne transmet que les variables qu’il liste :
utilisez celui publié avec la version 0.6 ou ultérieure, ou ajoutez les lignes
DOOR2_*à votre copie. - Alarm Manager. Le second capteur a besoin de ses propres règles d’ouverture et de fermeture, qui publient vers la même adresse. Le bridge distingue les portes d’après le capteur qui a émis l’événement.
- Caméras. Deux portes peuvent désigner la même caméra.
DOOR_INTERIOR_CAMERA_IDetDOOR2_INTERIOR_CAMERA_IDchoisissent l’image affichée sur l’écran de chaque porte dans l’app. - Téléphones. Personne n’a besoin de s’appairer à nouveau. Chacun choisit sa porte par défaut dans l’app (version 1.1 ou ultérieure) ; une app plus ancienne continue de piloter la première porte, exactement comme avant.
Nous n’avons nous-mêmes qu’une seule porte : la seconde n’est testée que sur le simulateur. Une seconde sortie de relais réelle et les événements Alarm Manager d’un second capteur restent non vérifiés tant qu’une personne équipée de deux portes ne nous a pas fait de retour. Si c’est vous, dites-nous comment cela s’est passé.
À propos des jetons
BRIDGE_TOKENS est l’identifiant qui ouvre votre porte de garage. Le bridge refuse de
démarrer sur une valeur de moins de 24 caractères, ou sur quoi que ce soit de devinable. Générez-la.
Les téléphones de votre famille n’ont pas besoin d’une entrée ici : ils rejoignent par
invitation, plus bas.
Réseau de l’hôte, ou pas
L’app trouve le bridge via Bonjour, et le multicast ne traverse pas le réseau bridge de Docker : le
compose de référence utilise donc network_mode: host. Si ce n’est pas possible chez vous,
par exemple sur Docker Desktop pour macOS ou Windows, publiez plutôt 8787:8787, mettez
BONJOUR=false et PUBLIC_URL=http://<adresse-de-l’hôte>:8787. Sans le
réseau de l’hôte, le bridge ne voit que l’adresse de son conteneur, et c’est elle que porteraient sinon
les liens d’invitation.
Appairer votre téléphone
Ne copiez pas votre jeton admin dans l’app. Tant qu’aucun téléphone n’a rejoint, le bridge affiche une invitation à usage unique au démarrage :
docker logs garage-bridge | tail -20
Sur le premier écran de l’app, touchez Rejoindre avec une invitation et collez le lien
garageopener://join…, ou simplement le code. Ouvrir le lien sur le téléphone revient au
même. L’invitation porte l’adresse du bridge : PUBLIC_URL si vous l’avez définie, sinon
l’adresse locale de l’hôte. Elle est valable quinze minutes ; redémarrez le conteneur pour en obtenir
une nouvelle. Dès qu’un téléphone a rejoint, l’affichage cesse et les téléphones suivants sont invités
depuis l’app elle-même.
Laisser la console informer le bridge
Le bridge interroge la console régulièrement, mais les webhooks le font réagir en moins d’une seconde. Créez deux règles sur la console :
- Ouvrez Protect → Alarm Manager → Create Alarm.
-
Nommez-la
garage-door-opened. Déclencheur : Sensor → Contact → Opened, sur le capteur de votre porte de garage. -
Sous Actions, ajoutez Webhook, choisissez POST,
décochez Send thumbnail et collez :
http://<hôte-du-bridge>:8787/v1/webhooks/alarm-manager/<WEBHOOK_SECRET> - Recommencez avec
garage-door-closedet Contact → Closed. Gardez les mots opened et closed dans les noms.
Vérifiez en faisant bouger la porte (ou seulement le capteur) : le journal d’activité de l’app affiche une entrée Capteur en moins d’une seconde. Le bouton Test alarm de Protect envoie un exemple générique, pas l’événement réel : il ne sert pas à ce test.
Si rien n’arrive : un secret erroné renvoie un simple 404, vérifiez donc d’abord le secret, et que rien ne s’y est ajouté au collage. Vérifiez ensuite que la console peut joindre l’hôte du bridge (voir qui doit joindre quoi).
Alertes quand le téléphone est en veille
Le bridge évalue les règles d’alerte en permanence. L’app les affiche quand elle est ouverte ou qu’elle se rafraîchit en arrière-plan, mais c’est iOS qui décide quand. Pour une notification qui atteint à coup sûr un téléphone en veille ou loin de chez vous, faites publier le bridge sur ntfy et abonnez-vous au sujet dans l’app ntfy :
- ntfy.sh : définissez
NTFY_URL=https://ntfy.shet unNTFY_TOPIClong et aléatoire (openssl rand -hex 16). Le nom du sujet est la seule chose qui le garde privé. Redémarrez le bridge et abonnez-vous au sujet dans l’app ntfy. - Votre propre ntfy : le compose de référence en inclut un, qui n’admet personne tant
que vous n’avez pas créé d’utilisateur. Dans
.env, définissezNTFY_URL=http://127.0.0.1:8788(là où le bridge publie ;http://ntfy:80si vous publiez les ports au lieu du réseau de l’hôte),NTFY_BASE_URL=http://<adresse-de-l’hôte>:8788(là où votre téléphone le joint) etNTFY_TOPIC. Puis :
Placez le jeton dansdocker compose --profile ntfy up -d docker exec -it garage-ntfy ntfy user add --role=user garage # demande un mot de passe docker exec -it garage-ntfy ntfy access garage '<NTFY_TOPIC>' read-write docker exec -it garage-ntfy ntfy token add garage # affiche tk_….envsousNTFY_TOKENet relancezdocker compose --profile ntfy up -d. Dans l’app ntfy, ajoutez le serveurNTFY_BASE_URL, connectez-vous en tant quegarageet abonnez-vous au sujet.
Avec PUBLIC_URL défini, les notifications ntfy portent des boutons Close now et
Hold 2h qui appellent directement votre bridge.
Certificats
Votre console présente un certificat auto-signé : la valeur par défaut est donc
PROTECT_TLS=insecure, ce qui convient sur un réseau local. Épinglez-le correctement une
fois en route :
openssl s_client -connect 192.168.1.1:443 -servername 192.168.1.1 </dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha256
Placez le résultat dans PROTECT_TLS. Vous pouvez coller la sortie d’openssl telle quelle
(sha256 Fingerprint=AB:CD:…) : les deux-points et la casse sont ignorés, et la valeur est
enregistrée sous la forme fingerprint:abcd…. Si votre console possède un vrai certificat,
utilisez system. Le bridge refuse de démarrer avec insecure
si PROTECT_URL n’est pas une adresse locale, car cela exposerait votre clé d’API à
quiconque se trouve sur le chemin.
Y accéder depuis l’extérieur
Définissez PUBLIC_URL sur l’adresse que le téléphone utilisera : c’est elle que portent les
liens d’invitation. Ensuite, choisissez :
| Approche | Remarques |
|---|---|
| VPN vers votre domicile (WireGuard sur l’UDM, ou Tailscale) | Recommandé. Rien n’est exposé ; le téléphone rejoint simplement votre réseau. |
| Tunnel Cloudflare | Vrai nom d’hôte, vrai certificat, aucun port entrant. Mettez TRUST_PROXY=true. |
| Votre propre reverse proxy | Caddy, nginx ou Traefik en terminaison TLS. Mettez TRUST_PROXY=true, et désactivez la mise en tampon sur /v1/events, sinon l’état de la porte accusera du retard. |
| Redirection de port | Non. Cela place sur l’internet public un appareil qui ouvre votre garage, derrière un seul jeton, en général sans TLS. |
TRUST_PROXY compte davantage qu’il n’y paraît. Sans lui, toutes les requêtes semblent
venir du proxy : la limite par adresse sur les invitations devient cinq par minute pour tout votre
foyer. Avec lui, alors que rien en amont ne filtre X-Forwarded-For, un appelant peut
déclarer l’adresse qu’il veut : ne l’activez donc que si un proxy que vous contrôlez est réellement
en place.
Mises à jour et sauvegardes
latest est la valeur par défaut et convient à la plupart des gens. Docker ne met jamais un
conteneur à jour tout seul : latest signifie simplement que vous obtiendrez la version
courante au prochain pull que vous déciderez de faire. Vous restez donc sur les correctifs de
sécurité sans avoir à suivre les publications.
docker compose pull && docker compose up -d
curl -s localhost:8787/healthz # vérifiez la version récupérée
Épinglez plutôt une version exacte, GARAGE_BRIDGE_TAG=0.6.0, si vous voulez que les mises à
jour soient une décision et non un effet de bord d’un pull, ou si vous utilisez un outil de
mise à jour automatique comme Watchtower. Avec latest et une mise à jour automatique
combinés, une version exigeant un changement de configuration redémarrerait votre bridge sur un refus
de démarrer, sans que vous le voyiez.
Dans tous les cas, lisez le changelog avant un pull. Tout ce qui demande de toucher à la configuration figure sous Action required, et le bridge échoue bruyamment au démarrage plutôt que de tourner dans un état dégradé.
Sauvegardez le volume garage-bridge-data. Il contient les membres de la
famille, les invitations en cours et le journal d’activité. Sans lui, chaque téléphone doit être
appairé à nouveau. Votre .env est la seule autre chose à conserver, et sa place est dans un
gestionnaire de mots de passe plutôt que dans une sauvegarde partagée.
Sources et licence
Le bridge et son contrat OpenAPI sont open source sous licence Apache-2.0. Vous le faites tourner sur votre propre matériel avec un identifiant vers votre console : vous devez pouvoir le lire plutôt que de le croire sur parole, y compris l’audit de sécurité mené avant sa publication.
Les apps iPhone et Android ne sont pas open source, et le service d’alertes cloud non plus. Ce service est quelque chose que nous exploitons, pas que vous faites tourner ; il ne détient aucun identifiant vers votre console et ne peut rien ouvrir ni fermer. Ce qu’il conserve est décrit intégralement sur la page de confidentialité.
Hors périmètre
Plus de deux portes par bridge. La vidéo en direct ; l’app affiche à la place des images fixes de la caméra. UniFi Protect uniquement, sans autres relais ni autres hubs. Aucune intégration Home Assistant. Une installation sans Docker (Node 22 directement) fonctionnera mais n’est pas prise en charge : Docker est le chemin testé, et le seul que couvrent ces instructions.
Bloqué ? La FAQ couvre les cas courants, et support@garageopener.app vous met en contact avec une personne.
Dernière mise à jour: