Der Kontext
Ich habe meine gesamte Smart-Home-Automatisierung von Home Assistant zu Gladys migriert. Einer meiner Opfergänge: die vernetzte Katzenklappe Sure Petcare (Cat Flap Connect), die keine native Integration in Gladys hat.
Bei Home Assistant meldete die Sure Petcare-Integration die Position der Katze (innen/außen), den Akkustand der Klappe, den Zustand des Schlosses usw. Durch den Wechsel zu Gladys habe ich all das verloren.
Die Lösung? Dasselbe Prinzip wie bei meinen Magic Home LED-Streifen: eine benutzerdefinierte MQTT-Brücke, die die Verbindung zwischen der Sure Petcare Cloud-API und Gladys herstellt.
Die Brücke wurde mit Claude Code (dem CLI-Agenten von Anthropic) entwickelt. Ich verstecke es nicht, das ist jetzt Teil meiner Stack und hat mir viel Zeit bei der API-Recherche und dem Codieren erspart.
Update
GitHub-Repo verfügbar:
Die Architektur
Sure Petcare Cloud <--HTTPS--> sure-petcare-bridge (Python) <--MQTT--> Mosquitto <--> Gladys
Ein einzelner Python-Container (~50 MB RAM), der:
-
Pollt die Sure Petcare-API alle 60 Sekunden über die Bibliothek surepy (dasselbe wie Home Assistant verwendet hat)
-
Veröffentlicht auf MQTT den Zustand an Gladys: Position der Katze, Akkustand der Klappe
-
Intelligentes Logging: Zeigt nur Zustandsänderungen an (kein Spam in den Logs)
Was benötigt wird
-
Gladys mit der Integration MQTT konfiguriert und verbunden mit einem Mosquitto-Broker
-
Ein Sure Petcare-Konto (das aus der Smartphone-App)
-
Eine Cat Flap Connect oder Pet Door Connect mit ihrem Hub, der mit dem WiFi verbunden ist
-
Docker auf Ihrem Server
Schritt 1: Dateien vorbereiten
Erstellen Sie einen Ordner für die Brücke, z.B. /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 für Gladys Assistant v1.0
Pollt die Sure Petcare Cloud-API über surepy und veröffentlicht den Haustier-/Gerätezustand
an Gladys über MQTT-Topics.
Umgebungsvariablen:
SUREPETCARE_EMAIL (erforderlich)
SUREPETCARE_PASSWORD (erforderlich)
MQTT_HOST (Standard: localhost)
MQTT_PORT (Standard: 1883)
POLL_INTERVAL (Standard: 60, Sekunden)
LOG_LEVEL (Standard: 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
# --- Konfiguration ---
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 und SUREPETCARE_PASSWORD erforderlich')
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):
"""Name -> slug kompatibel mit Gladys external_id."""
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):
"""Veröffentlicht einen Wert auf dem MQTT-Topic von 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 verbunden ({MQTT_HOST}:{MQTT_PORT})')
mqtt.on_disconnect = lambda *_: log.warning('MQTT getrennt, automatische Wiederverbindung...')
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-Verbindung nicht möglich: {e}')
mqtt.loop_start()
# Sure Petcare
surepy = Surepy(email=EMAIL, password=PASSWORD)
log.info('Sure Petcare Bridge v1.0 gestartet')
log.info(f'Konto: {EMAIL} | Polling: {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'Entdeckt: {len(pets)} Tier(e), {len(flaps)} Katzenklappe(n), {len(hubs)} Hub(s)')
for p in pets:
log.info(f' Tier: {p.name} (id={p.id})')
for f in flaps:
log.info(f' Katzenklappe: {f.name} (id={f.id})')
first_run = False
# Tiere
for entity in entities.values():
if entity.type != EntityType.PET:
continue
slug = slugify(entity.name)
ext_id = f'mqtt:haus: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 = 'innen' if at_home else 'außen'
log.info(f'[PET] {entity.name}: {loc}')
# Katzenklappen
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:haus:katzenklappe-{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}% verriegelt={lock_label}')
except Exception as e:
log.error(f'Polling-Fehler: {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('Beendet.')
if __name__ == '__main__':
main()
Schritt 2: Bauen und starten
# Bild bauen
docker build -t sure-petcare-bridge /pfad/zu/sure-petcare-bridge/
# Container starten
docker run -d \
--name sure-petcare-bridge \
--network host \
--restart unless-stopped \
-e SUREPETCARE_EMAIL=ihre@email.com \
-e SUREPETCARE_PASSWORD=ihre_passwort \
-e MQTT_HOST=localhost \
-e MQTT_PORT=1883 \
-e POLL_INTERVAL=60 \
sure-petcare-bridge:latest
Sicherheit: Setzen Sie niemals Ihre Anmeldedaten hart in den Code. Immer in Umgebungsvariablen.
Logs überprüfen
docker logs sure-petcare-bridge
Sie sollten sehen:
12:15:21 [sure-bridge] MQTT verbunden (localhost:1883)
12:15:21 [sure-bridge] Sure Petcare Bridge v1.0 gestartet
12:15:21 [sure-bridge] Konto: ihre@email.com | Poll: 60s
12:15:23 [sure-bridge] Gefunden: 1 Tier(e), 1 Katzenklappe(n), 1 Hub(s)
12:15:23 [sure-bridge] Tier: Arwen (id=12345)
12:15:23 [sure-bridge] Katzenklappe: CatDoor (id=67890)
12:15:23 [sure-bridge] [PET] Arwen: drinnen
12:15:23 [sure-bridge] [FLAP] CatDoor: batterie=51% verriegelt=Unlocked
Die Warnung
404bei/api/report/household/ist normal — dies ist ein optionales API-Endpoint von Sure Petcare, das surepy versucht aufzurufen. Es beeinträchtigt nichts.
Schritt 3: MQTT-Geräte in Gladys erstellen
Gerät 1 — Ihre Katze
Gehen Sie zu Integrationen → MQTT → Geräte → Neu +
| Feld | Wert |
|---|---|
| Name | Arwen (oder der Name Ihrer Katze) |
| Externe ID | mqtt:haus:pet-arwen |
| Raum | (wo Ihre Katze am meisten herumlungert) |
Feature hinzufügen:
| Feld | Wert |
|---|---|
| Name | Anwesenheit |
| Externe ID | mqtt:haus:pet-arwen:presence |
| Kategorie | Anwesenheitssensor |
| Typ | Binär |
| Ist dies ein Sensor? | Ja |
| Min | 0 |
| Max | 1 |
Namenkonvention: Die externe ID des Geräts und des Features müssen genau dem entsprechen, was der Bridge veröffentlicht. Das Format ist
mqtt:haus:pet-{name-in-kleinbuchstaben}. Wenn Ihre Katze „Moustache“ heißt, ergibt dasmqtt:haus:pet-moustache.
Gerät 2 — Die Katzenklappe
| Feld | Wert |
|---|---|
| Name | Katzenklappe |
| Externe ID | mqtt:haus:chatiere-catdoor |
| Raum | (wo sich die Katzenklappe befindet) |
Feature hinzufügen:
| Feld | Wert |
|---|---|
| Name | Batterie |
| Externe ID | mqtt:haus:chatiere-catdoor:battery |
| Kategorie | Batterie |
| Typ | Ganzzahl |
| Ist dies ein Sensor? | Ja |
| Min | 0 |
| Max | 100 |
| Einheit | % |
Tipp: Schauen Sie sich die Logs der Bridge beim ersten Start an, um die richtigen Slugs zu finden. Die Bridge zeigt Ihnen den Namen jedes erkannten Tieres und jeder Katzenklappe an.
Schritt 4: Im Dashboard überprüfen
Fügen Sie die Widgets zu Ihrem Gladys-Dashboard hinzu:
-
Ein Widget mit dem Feature „Anwesenheit“ der Katze — zeigt 1 (drinnen) oder 0 (draußen) an
-
Ein „Anzeige“-Widget für die Batterie der Katzenklappe
Die Bridge pollt alle 60 Sekunden. Wenn Ihre Katze die Katzenklappe passiert, ändert sich der Wert im nächsten Poll-Zyklus.
Wie es funktioniert
Der Datenfluss
-
Ihre Katze passiert die Katzenklappe
-
Die Katzenklappe erkennt den Chip (oder den RFID-Tag) und sendet die Information an den Sure Petcare Hub
-
Der Hub überträgt die Information an die Sure Petcare Cloud
-
Die Bridge pollt die Cloud-API über surepy (die gleiche Bibliothek, die Home Assistant verwendet hat)
-
Die Bridge veröffentlicht den neuen Zustand auf MQTT:
gladys/master/device/mqtt:haus:pet-arwen/feature/mqtt:haus:pet-arwen:presence/state→0(draußen) -
Gladys empfängt die Nachricht und aktualisiert das Dashboard
Warum Cloud-Polling?
Sure Petcare bietet keine lokale API an. Alles läuft über ihre Cloud. Es ist die gleiche Einschränkung, die wir bei Home Assistant hatten. Wenn die Sure Petcare-Server ausfallen, gibt es keine Rückmeldung (aber die Smartphone-App wäre auch offline).
Das Polling alle 60 Sekunden ist ein guter Kompromiss: schnell genug, um zu wissen, wo sich die Katze befindet, aber nicht zu aggressiv, um von der API rate-gegrenzt zu werden.
Der Änderungs-Cache
Die Bridge spamt nicht die Logs. Sie speichert den letzten Zustand im Speicher und loggt nur, wenn sich etwas ändert. MQTT-Nachrichten werden immer veröffentlicht (damit Gladys immer den letzten Wert hat), aber die Logs bleiben sauber.
Kompatible Geräte
Die Bridge erkennt automatisch alle Geräte Ihres Sure Petcare-Kontos:
-
Cat Flap Connect (Chip-Katzenklappe)
-
Pet Door Connect (Tier-Tür)
-
Hub (WiFi-Gateway)
-
Feeder Connect / Felaqua (Futter-/Wasserstation — noch nicht in der Bridge exponiert, aber einfach hinzuzufügen)
Wenn Sie mehrere Katzen haben, erstellt die Bridge ein MQTT-Topic pro Tier. Sie müssen einfach ein Gladys-Gerät pro Katze erstellen.
Warum MQTT und nicht Matter / Matterbridge?
Wir haben auch ein benutzerdefiniertes Matterbridge-Plugin für unsere Magic Home LED-Streifen, daher stellte sich die Frage. Wir haben die Matter-Option ernsthaft in Betracht gezogen, bevor wir uns für MQTT entschieden. Hier ist der Grund, warum wir uns nicht für Matter entschieden haben:
Von den drei Informationen, die wir zurückmelden wollten, unterstützt Matter heute auf Gladys-Seite:
| Bedarf | Matter-Cluster | Von Gladys unterstützt? |
|---|---|---|
| Katze drinnen/draußen | BooleanState (ContactSensor) |
Ja — wird als binär gelesen |
| Batterie | PowerSource |
Nein — noch nicht in Gladys abgebildet |
| Letzter Durchgang | kein Matter-Äquivalent | Nicht anwendbar |
Kurz gesagt, 1 Feature von 3 würde über Matter laufen. Die Batterie würde erfordern, dass Gladys den Cluster PowerSource implementiert (es ist standardmäßig, es wird wahrscheinlich kommen), und der „letzte Durchgang“ hat einfach kein Äquivalent im Matter-Protokoll.
Daher wäre ein hybrides Matterbridge + MQTT, um die Lücken zu füllen, Makulatur. Die reine MQTT-Bridge deckt 100% des Bedarfs ab dem ersten Tag ab, ohne dass Features auf Gladys- oder Matter-Seite hinzugefügt werden müssen.
Und seien wir ehrlich: Für ein Cloud-Polling alle 60 Sekunden an einer Katzenklappe bringt Matter keinen Vorteil. Matter ist für lokale Echtzeitsteuerung gemacht — hier geht es nur um die Rückmeldung von Sensoren von einer Drittanbieter-Cloud.
Einschränkungen
-
Nur Cloud: Keine lokale Steuerung, Abhängigkeit von den Sure Petcare-Servern
-
Latenz: Zwischen 0 und 60 Sekunden, je nach Poll-Zeitpunkt (konfigurierbar über
POLL_INTERVAL) -
Nur Lesen: Die Bridge verwaltet (noch) nicht das Verriegeln/Entriegeln der Katzenklappe von Gladys aus. Technisch möglich mit surepy, wenn es Interesse gibt, kann ich das Feature hinzufügen
-
Kein letzter Durchgang: Gladys hat kein natives „Zeitstempel“-Feature. Allerdings speichert das Anwesenheits-Feature in Gladys, wann sich der Wert geändert hat — das ist Ihr „letzter Durchgang“
Technische Stack
-
Python 3.12 (Docker slim)
-
surepy v0.9.0 — Python-Bibliothek für die Sure Petcare API (die gleiche wie Home Assistant)
-
paho-mqtt v2.x — MQTT-Client
-
~50 Mo RAM im Betrieb
Erfolgreich getestet auf einem NAS Synology DS1520+ mit Gladys v4, Mosquitto und einer Cat Flap Connect + Sure Petcare Hub.
Wenn Sie Fragen oder Verbesserungsvorschläge haben, zögern Sie nicht!
