Sure Petcare Katzenklappe mit Gladys über MQTT verbinden

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:

  1. Pollt die Sure Petcare-API alle 60 Sekunden über die Bibliothek surepy (dasselbe wie Home Assistant verwendet hat)

  2. Veröffentlicht auf MQTT den Zustand an Gladys: Position der Katze, Akkustand der Klappe

  3. 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 404 bei /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 das mqtt: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

  1. Ihre Katze passiert die Katzenklappe

  2. Die Katzenklappe erkennt den Chip (oder den RFID-Tag) und sendet die Information an den Sure Petcare Hub

  3. Der Hub überträgt die Information an die Sure Petcare Cloud

  4. Die Bridge pollt die Cloud-API über surepy (die gleiche Bibliothek, die Home Assistant verwendet hat)

  5. 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)

  6. 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!

Au taquet @David-Digitis!
Bravo für dieses Tutorium, das perfekt ist :ok_hand:
Nun, ich werde es nicht brauchen, da ich keine Katze oder Katzenklappe habe :rofl:

Kleine Frage: Da du das Tutorial für deine LEDs in Node.js gemacht hast, warum bist du nicht weitergegangen und auf Python umgestiegen?
Ich stelle die Frage, weil Gladys in JS ist und falls es eines Tages eine Integration gibt, wäre es umso einfacher, den Code direkt (oder fast) zu verwenden, anstatt ihn umwandeln zu müssen.

Und ich habe mich gefragt, ob du ein GitHub-Repo erstellen würdest, in dem du direkt den Code und ein Docker-Image hochlädst (und ghcr.io verwendest, indem du ein package erstellst)?

Ich habe einen Syno und habe bemerkt, dass du auch einen hast, sobald du /volume1/docker/... erwähnt hast.
Für Anfänger ist es nicht unbedingt einfach, die Dateien mit dem Code (Python, JS usw.) auf einer Plattform wie Syno oder einer anderen zu erstellen. Aber ein Docker-Befehl auszuführen, erscheint mir einfacher.
Glaubst du, das könnte gut für deine Entwicklungen sein?

Hallo :wink:

Ehrlich gesagt, wenn du mit analogen KIs gut organisiert / ausgebildet bist, kannst du viele Dinge sehr gut und sehr schnell erledigen. Ich habe nicht viel Verdienst, außer die Architekturideen zu haben.

Bei der verwendeten Sprache ist es ganz einfach, weil wir eine bestehende Bibliothek verwenden. Die gleiche, die auch HA verwendet.

Während ich dir diese Antwort schreibe, erstellt Claude gerade ein Repo auf meinem GitHub :laughing: