Le contexte
J’ai migre toute ma domotique de Home Assistant vers Gladys. Un de mes sacrifices : la chatiere connectee Sure Petcare (Cat Flap Connect) qui n’a pas d’integration native dans Gladys.
Sur Home Assistant, l’integration Sure Petcare remontait la position du chat (dedans/dehors), la batterie de la chatiere, l’etat du verrou, etc. En passant sur Gladys, j’ai perdu tout ca.
La solution ? Le meme principe que pour mes bandeaux LED Magic Home : un bridge MQTT custom qui fait le lien entre l’API cloud Sure Petcare et Gladys.
Le bridge a ete developpe avec Claude Code (l’agent CLI d’Anthropic). Je ne m’en cache pas, ca fait partie de ma stack maintenant et ca m’a fait gagner un temps considerable sur la partie recherche API + code.
Update
Repos Github dispo :
L’architecture
Sure Petcare Cloud <--HTTPS--> sure-petcare-bridge (Python) <--MQTT--> Mosquitto <--> Gladys
Un seul container Python (~50 Mo RAM) qui :
-
Poll l’API Sure Petcare toutes les 60 secondes via la librairie surepy (la meme que Home Assistant utilisait)
-
Publie sur MQTT l’etat vers Gladys : position du chat, batterie de la chatiere
-
Log intelligent : n’affiche que les changements d’etat (pas de spam dans les logs)
Ce qu’il faut
-
Gladys avec l’integration MQTT configuree et connectee a un broker Mosquitto
-
Un compte Sure Petcare (celui de l’app smartphone)
-
Une chatiere Cat Flap Connect ou Pet Door Connect avec son Hub connecte au WiFi
-
Docker sur votre serveur
Etape 1 : Preparer les fichiers
Creez un dossier pour le bridge, par exemple /volume1/docker/sure-petcare-bridge/.
requirements.txt :
surepy>=0.9.0
paho-mqtt>=2.0
Dockerfile :
FROM python:3.12-slim
WORKDIR /app
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc libc6-dev && \
rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN apt-get purge -y gcc libc6-dev && apt-get autoremove -y
COPY bridge.py .
CMD ["python", "-u", "bridge.py"]
bridge.py :
#!/usr/bin/env python3
"""
Sure Petcare MQTT Bridge for Gladys Assistant v1.0
Polls Sure Petcare cloud API via surepy and publishes pet/device state
to Gladys via MQTT topics.
Environment variables:
SUREPETCARE_EMAIL (required)
SUREPETCARE_PASSWORD (required)
MQTT_HOST (default: localhost)
MQTT_PORT (default: 1883)
POLL_INTERVAL (default: 60, seconds)
LOG_LEVEL (default: INFO)
"""
import os
import sys
import signal
import asyncio
import logging
import paho.mqtt.client as paho
from surepy import Surepy
from surepy.enums import EntityType
# --- Configuration ---
EMAIL = os.environ.get('SUREPETCARE_EMAIL', '')
PASSWORD = os.environ.get('SUREPETCARE_PASSWORD', '')
MQTT_HOST = os.environ.get('MQTT_HOST', 'localhost')
MQTT_PORT = int(os.environ.get('MQTT_PORT', '1883'))
POLL_INTERVAL = int(os.environ.get('POLL_INTERVAL', '60'))
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
if not EMAIL or not PASSWORD:
sys.exit('[FATAL] SUREPETCARE_EMAIL et SUREPETCARE_PASSWORD requis')
logging.basicConfig(
level=getattr(logging, LOG_LEVEL.upper(), logging.INFO),
format='%(asctime)s [%(name)s] %(message)s',
datefmt='%H:%M:%S',
)
log = logging.getLogger('sure-bridge')
def slugify(name):
"""Nom -> slug compatible external_id Gladys."""
s = name.lower().replace(' ', '-').replace("'", '').replace('"', '')
for old, new in {'e': 'e', 'e': 'e', 'e': 'e', 'a': 'a', 'u': 'u', 'c': 'c'}.items():
s = s.replace(old, new)
return s
def publish(client, device_ext, feature_suffix, value):
"""Publie une valeur sur le topic MQTT Gladys."""
feat_ext = f'{device_ext}:{feature_suffix}'
topic = f'gladys/master/device/{device_ext}/feature/{feat_ext}/state'
client.publish(topic, str(value))
_prev_state = {}
def state_changed(key, **kwargs):
prev = _prev_state.get(key)
if prev != kwargs:
_prev_state[key] = kwargs
return True
return False
async def run():
# MQTT
try:
mqtt = paho.Client(paho.CallbackAPIVersion.VERSION2, client_id='sure-petcare-bridge')
except (AttributeError, TypeError):
mqtt = paho.Client(client_id='sure-petcare-bridge')
mqtt.on_connect = lambda *_: log.info(f'MQTT connecte ({MQTT_HOST}:{MQTT_PORT})')
mqtt.on_disconnect = lambda *_: log.warning('MQTT deconnecte, reconnexion auto...')
mqtt.reconnect_delay_set(min_delay=1, max_delay=30)
try:
mqtt.connect(MQTT_HOST, MQTT_PORT)
except Exception as e:
sys.exit(f'[FATAL] MQTT connexion impossible: {e}')
mqtt.loop_start()
# Sure Petcare
surepy = Surepy(email=EMAIL, password=PASSWORD)
log.info('Sure Petcare Bridge v1.0 demarre')
log.info(f'Compte: {EMAIL} | poll: {POLL_INTERVAL}s')
first_run = True
try:
while True:
try:
entities = await surepy.get_entities()
if first_run:
pets = [e for e in entities.values() if e.type == EntityType.PET]
flaps = [e for e in entities.values()
if e.type in (EntityType.CAT_FLAP, EntityType.PET_FLAP)]
hubs = [e for e in entities.values() if e.type == EntityType.HUB]
log.info(f'Decouvert: {len(pets)} animal(aux), {len(flaps)} chatiere(s), {len(hubs)} hub(s)')
for p in pets:
log.info(f' Animal: {p.name} (id={p.id})')
for f in flaps:
log.info(f' Chatiere: {f.name} (id={f.id})')
first_run = False
# Pets
for entity in entities.values():
if entity.type != EntityType.PET:
continue
slug = slugify(entity.name)
ext_id = f'mqtt:maison:pet-{slug}'
at_home = 1 if entity.at_home else 0
publish(mqtt, ext_id, 'presence', at_home)
if state_changed(f'pet-{entity.id}', at_home=at_home):
loc = 'dedans' if at_home else 'dehors'
log.info(f'[PET] {entity.name}: {loc}')
# Flaps
for entity in entities.values():
if entity.type not in (EntityType.CAT_FLAP, EntityType.PET_FLAP):
continue
slug = slugify(entity.name)
ext_id = f'mqtt:maison:chatiere-{slug}'
battery = getattr(entity, 'battery_level', None)
if battery is not None:
publish(mqtt, ext_id, 'battery', battery)
lock_state = getattr(entity, 'state', None)
lock_label = str(lock_state) if lock_state is not None else '?'
if state_changed(f'flap-{entity.id}', battery=battery, lock=lock_label):
log.info(f'[FLAP] {entity.name}: batterie={battery}% verrou={lock_label}')
except Exception as e:
log.error(f'Erreur poll: {e}')
await asyncio.sleep(POLL_INTERVAL)
finally:
mqtt.loop_stop()
mqtt.disconnect()
def main():
signal.signal(signal.SIGTERM, lambda *_: sys.exit(0))
try:
asyncio.run(run())
except (KeyboardInterrupt, SystemExit):
log.info('Arret.')
if __name__ == '__main__':
main()
Etape 2 : Builder et lancer
# Build l'image
docker build -t sure-petcare-bridge /chemin/vers/sure-petcare-bridge/
# Lancer le container
docker run -d \
--name sure-petcare-bridge \
--network host \
--restart unless-stopped \
-e SUREPETCARE_EMAIL=votre@email.com \
-e SUREPETCARE_PASSWORD=votre_mot_de_passe \
-e MQTT_HOST=localhost \
-e MQTT_PORT=1883 \
-e POLL_INTERVAL=60 \
sure-petcare-bridge:latest
Securite : ne mettez jamais vos credentials en dur dans le code. Toujours en variables d’environnement.
Verifier les logs
docker logs sure-petcare-bridge
Vous devriez voir :
12:15:21 [sure-bridge] MQTT connecte (localhost:1883)
12:15:21 [sure-bridge] Sure Petcare Bridge v1.0 demarre
12:15:21 [sure-bridge] Compte: votre@email.com | poll: 60s
12:15:23 [sure-bridge] Decouvert: 1 animal(aux), 1 chatiere(s), 1 hub(s)
12:15:23 [sure-bridge] Animal: Arwen (id=12345)
12:15:23 [sure-bridge] Chatiere: CatDoor (id=67890)
12:15:23 [sure-bridge] [PET] Arwen: dedans
12:15:23 [sure-bridge] [FLAP] CatDoor: batterie=51% verrou=Unlocked
Le warning
404sur/api/report/household/est normal — c’est un endpoint optionnel de l’API Sure Petcare que surepy tente d’appeler. Ca n’affecte rien.
Etape 3 : Creer les devices MQTT dans Gladys
Device 1 — Votre chat
Allez dans Integrations → MQTT → Appareils → Nouveau +
| Champ | Valeur |
|---|---|
| Nom | Arwen (ou le nom de votre chat) |
| ID externe | mqtt:maison:pet-arwen |
| Piece | (la ou votre chat traine le plus) |
Feature a ajouter :
| Champ | Valeur |
|---|---|
| Nom | Presence |
| ID externe | mqtt:maison:pet-arwen:presence |
| Categorie | Capteur de presence |
| Type | Binaire |
| Est-ce un capteur ? | Oui |
| Min | 0 |
| Max | 1 |
Convention de nommage : l’ID externe du device et de la feature doivent correspondre exactement a ce que le bridge publie. Le format est
mqtt:maison:pet-{nom-en-minuscules}. Si votre chat s’appelle « Moustache », ca donneramqtt:maison:pet-moustache.
Device 2 — La chatiere
| Champ | Valeur |
|---|---|
| Nom | Chatiere |
| ID externe | mqtt:maison:chatiere-catdoor |
| Piece | (la ou se trouve la chatiere) |
Feature a ajouter :
| Champ | Valeur |
|---|---|
| Nom | Batterie |
| ID externe | mqtt:maison:chatiere-catdoor:battery |
| Categorie | Batterie |
| Type | Entier |
| Est-ce un capteur ? | Oui |
| Min | 0 |
| Max | 100 |
| Unite | % |
Astuce : regardez les logs du bridge au premier demarrage pour trouver les bons slugs. Le bridge vous affiche le nom de chaque animal et chatiere detectes.
Etape 4 : Verifier dans le dashboard
Ajoutez les widgets sur votre dashboard Gladys :
-
Un widget avec la feature « Presence » du chat — affichera 1 (dedans) ou 0 (dehors)
-
Un widget « Jauge » pour la batterie de la chatiere
Le bridge poll toutes les 60 secondes. Quand votre chat passe la chatiere, la valeur change au prochain cycle de poll.
Comment ca marche
Le flux de donnees
-
Votre chat passe la chatiere
-
La chatiere detecte la puce (ou le tag RFID) et envoie l’info au Hub Sure Petcare
-
Le Hub transmet au cloud Sure Petcare
-
Le bridge poll l’API cloud via surepy (la meme librairie que Home Assistant utilisait)
-
Le bridge publie le nouvel etat sur MQTT :
gladys/master/device/mqtt:maison:pet-arwen/feature/mqtt:maison:pet-arwen:presence/state→0(dehors) -
Gladys recoit le message et met a jour le dashboard
Pourquoi du cloud polling ?
Sure Petcare n’expose pas d’API locale. Tout passe par leur cloud. C’est la meme limitation qu’on avait sur Home Assistant. Si les serveurs Sure Petcare tombent, plus de remontee d’info (mais l’app smartphone sera aussi HS).
Le polling a 60 secondes est un bon compromis : assez rapide pour savoir ou est le chat, pas assez agressif pour se faire rate-limit par l’API.
Le cache de changement
Le bridge ne spamme pas les logs. Il garde en memoire le dernier etat et ne log que quand quelque chose change. Les messages MQTT sont toujours publies (pour que Gladys ait toujours la derniere valeur), mais les logs restent propres.
Appareils compatibles
Le bridge detecte automatiquement tous les appareils de votre compte Sure Petcare :
-
Cat Flap Connect (chatiere a puce)
-
Pet Door Connect (porte pour animaux)
-
Hub (passerelle WiFi)
-
Feeder Connect / Felaqua (gamelle/fontaine — pas encore expose dans le bridge mais facile a ajouter)
Si vous avez plusieurs chats, le bridge creera un topic MQTT par animal. Il suffit de creer un device Gladys par chat.
Pourquoi MQTT et pas Matter / Matterbridge ?
On a aussi un plugin Matterbridge custom pour nos bandeaux LED Magic Home, donc la question s’est posee. On a etudie la piste serieusement avant de partir sur MQTT. Voila pourquoi on n’a pas retenu Matter pour la chatiere :
Sur les 3 infos qu’on voulait remonter, voici ce que Matter supporte cote Gladys aujourd’hui :
| Besoin | Cluster Matter | Supporte par Gladys ? |
|---|---|---|
| Chat dedans/dehors | BooleanState (ContactSensor) |
Oui — lu comme un binaire |
| Batterie | PowerSource |
Non — pas encore mappe dans Gladys |
| Dernier passage | aucun equivalent Matter | Non applicable |
En gros, 1 feature sur 3 passait par Matter. La batterie aurait necessite que Gladys implemente le cluster PowerSource (c’est standard, ca viendra probablement), et le « dernier passage » n’a tout simplement pas d’equivalent dans le protocole Matter.
Du coup, faire un hybride Matterbridge + MQTT pour compenser les manques, c’etait du bricolage pour pas grand-chose. Le bridge MQTT pur couvre 100% du besoin des le premier jour, sans attendre que des features soient ajoutees cote Gladys ou cote Matter.
Et soyons honnetes : pour du cloud polling a 60 secondes sur une chatiere, Matter n’apporte aucun avantage. Matter est fait pour du controle local temps reel — la c’est juste de la remontee de capteurs depuis un cloud tiers.
Limitations
-
Cloud only : pas de controle local, dependance aux serveurs Sure Petcare
-
Latence : entre 0 et 60 secondes selon le moment du poll (configurable via
POLL_INTERVAL) -
Lecture seule : le bridge ne gere pas (encore) le verrouillage/deverrouillage de la chatiere depuis Gladys. C’est techniquement possible avec surepy, si ca interesse du monde je peux ajouter la feature
-
Pas de dernier passage : Gladys n’a pas de feature « timestamp » native. Par contre, la feature presence dans Gladys enregistre quand la valeur a change — c’est de fait votre « dernier passage »
Stack technique
-
Python 3.12 (Docker slim)
-
surepy v0.9.0 — librairie Python pour l’API Sure Petcare (la meme que Home Assistant)
-
paho-mqtt v2.x — client MQTT
-
~50 Mo RAM en fonctionnement
Teste avec succes sur un NAS Synology DS1520+ avec Gladys v4, Mosquitto, et une Cat Flap Connect + Hub Sure Petcare.
Si vous avez des questions ou des ameliorations a suggerer, n’hesitez pas !
