HomeKit-Integration – Neue Funktionen

Hallo zusammen,

nachdem ich die externe Integration für meine Heizungen Neomitis durchgeführt habe, habe ich die Nutzung meines Pro-Kontos Claude fortgesetzt :slight_smile:

Ich habe mich daran gemacht, alles, was möglich ist, von Homekit auf Gladys zu portieren.

Es gibt X PRs. Ich konnte sie noch nicht testen, daher sind Tester herzlich willkommen :wink:

Alles wurde vollständig von Claude erledigt. Wenn ich einige Dinge falsch gemacht habe, bin ich für eure Kritik und Hilfe zur Korrektur offen (Programmierung ist nicht mein Beruf).

Nachfolgend findet ihr einen Überblick darüber, was bereits erledigt ist und was noch zu tun ist (von Claude):

## Stand der Dinge

Der Gladys HomeKit Bridge unterstützte 12 Gerätetypen. Der Rest — Rauchmelder, Schlösser, Thermostate, Batterien, Schalter — blieb auf dem iPhone unsichtbar, **ohne Fehlermeldung** : Die Geräte erschienen einfach nicht.

**Gemerged:** [#2781](feat(homekit): expose light, CO, CO2 and air quality sensors by Dreamthy · Pull Request #2781 · GladysAssistant/Gladys · GitHub) — Helligkeitssensoren, CO-, CO2- und Luftqualitätssensoren.

**In Überprüfung:**

- [#2792](feat(homekit): expose sirens as a Switch by Dreamthy · Pull Request #2792 · GladysAssistant/Gladys · GitHub) — **Sirene** (`Switch`)

- [#2793](feat(homekit): expose smoke sensors by Dreamthy · Pull Request #2793 · GladysAssistant/Gladys · GitHub) — **Rauchmelder** (`SmokeSensor`)

- [#2794](https://github.com/GladysAssistant/Gladys/pull/2794) — **Schloss** (`LockMechanism`)

- [#2795](https://github.com/GladysAssistant/Gladys/pull/2795) — **Knopf, Fernbedienung** (`StatelessProgrammableSwitch`)

- [#2796](feat(homekit): expose fans as a Fanv2 by Dreamthy · Pull Request #2796 · GladysAssistant/Gladys · GitHub) — **Ventilator** (`Fanv2`)

- [#2797](https://github.com/GladysAssistant/Gladys/pull/2797) — **Batteriestand** (`Battery`)

- [#2798](feat(homekit): expose PM2.5 and PM10 densities on the air quality sensor by Dreamthy · Pull Request #2798 · GladysAssistant/Gladys · GitHub) — **PM2.5- und PM10-Partikel** (`AirQualitySensor`)

- [#2799](https://github.com/GladysAssistant/Gladys/pull/2799) — **Thermostat und Klimaanlage** (`Thermostat`)

Bei den Integrationen betrifft es hauptsächlich **Zigbee2mqtt**, **Z-Wave**, **Matter** und **Xiaomi**, sowie **Nuki** für Schlösser, **Netatmo** und **MELCloud** für Thermostate und **Bluetooth** für Batterien.

Eine neunte PR ist in Arbeit: die **Auswahl der angezeigten Geräte**. Heute zeigt der Bridge alles an, was er anzeigen kann, was bei einer großen Installation das Haus-App überflutet. Sie fügt eine Option hinzu, um nur eine Auswahl anzuzeigen.

Jede PR deckt eine Kategorie ab, ist alleine lesbar und hat 100 % Testabdeckung für die hinzugefügten Zeilen.

—

## Aktuelle Abdeckung

`hap-nodejs`, die von Gladys und Homebridge verwendete Bibliothek, unterstützt 73 Dienste. Aber nicht alle sind Gerätetypen: 38 sind Protokoll-„Plumbing“ (Pairing, Transport, Firmware), Teile, die nur innerhalb anderer Dienste existieren, oder veraltete Duplikate.

Es bleiben also **35 tatsächlich anzeigbare Dienste**. Die Bridge unterstützte **12** vor diesem Projekt, sie wird **15** mit den laufenden PRs unterstützen — also **43 %**, plus zusätzliche Merkmale für bestehende Dienste.

### Woher kommt die Zahl 73 — und warum ist die Apple-Dokumentation nicht die richtige Referenz

Klassische Falle beim Start: Die HomeKit-Dokumentation auf der Apple-Entwicklerseite beschreibt das **HomeKit-Framework**, das zum Schreiben einer iOS-App dient, die *steuert* Zubehörteile. Eine Bridge hingegen basiert auf **HAP**, dem HomeKit Accessory Protocol.

Apple veröffentlicht eine *HomeKit Accessory Protocol Specification (Non-Commercial Version)*, deren Release R2 die letzte öffentliche Version ist. Die kommerzielle Version unterliegt einer NDA und ist dem MFi-Programm vorbehalten.

Interessantes Detail, das im Code von `hap-nodejs` gefunden wurde: Sie transkribiert nicht das PDF der Spezifikation, sondern **generiert** ihre Definitionen aus Ressourcen von Apple, die auf macOS vorhanden sind — `HomeKitDaemon.framework` und die Metadaten des HomeKit Accessory Simulators.

Praktische Konsequenz: Sie folgt dem, was iOS tatsächlich implementiert, nicht dem, was die R2 von 2019 beschreibt. Dort finden sich Dienste, die mit „seit iOS 15“ annotiert sind, aber in der öffentlichen Spezifikation fehlen. Und das ergibt ein lokal überprüfbares Referenzwerk mit Merkmalen, Grenzen und Einheiten.

—

## Fünf Fallstricke für alle, die beitragen möchten

### 1. Eine Kategorie reicht nicht aus: Es ist das Paar Kategorie + Typ

Der hinterlistigste der fünf. Ein Gladys-Feature hat eine **Kategorie** und einen **Typ**. Die Bridge überprüft, ob das Paar in ihrer Zuordnungstabelle existiert. Wenn der Typ nicht vorhanden ist, wird das Feature ignoriert — **ohne Fehler oder Log**.

Dabei kann eine Integration die Kategorie eines Features **ohne dessen Typ zu ändern***. Zwei reale Fälle, beide während des Prozesses gefunden:

- **Z-Wave** klassifiziert binäre Sensoren als `co2-sensor`, wobei sie `type: binary` beibehalten. Ich hatte `co2-sensor` für dezimale und ganze Typen, aber nicht für binäre Typen gemappt → alle Z-Wave-CO2-Detektoren blieben unsichtbar. Gefunden bei der Überprüfung der ersten PR.

- **Nuki** meldet seinen Batteriestand als `lock:integer`, während andere `sensor:integer` verwenden. Nur die erwarteten Typen zu mappen, hätte alle Nuki-Schlösser stillschweigend ausgeschlossen.

Die Methode, die dies vermeidet: Die **tatsächlich von den Integrationen erzeugten Paare** auflisten und niemals die „offensichtlichen“ Typen einer Kategorie annehmen.

### 2. Einheiten lassen sich nicht erraten

HomeKit erwartet Partikeldichten in **µg/m³**. Gladys lässt eine Integration sie in Milligramm, Mikrogramm **oder** Nanogramm pro Kubikmeter deklarieren. Ohne Umrechnung wird ein Sensor in mg/m³ tausendmal zu niedrig angezeigt, und nichts zeigt dies an.

Ähnliches gilt für VOCs, aber mit einem anderen Ausgang: Gladys speichert sie in **ppb**, HomeKit will µg/m³. Die Umrechnung erfordert die molare Masse der Verbindung, die ein generisches „VOC“-Feature nicht trägt. Kein Faktor kann ehrlich gewählt werden, daher bleiben VOCs außerhalb der Bridge, absichtlich.

### 3. Die Bridge verzögert standardmäßig 5 Sekunden

Nützlich, um das iPhone nicht mit Statusänderungen zu überfluten. Fatal für einen **Knopf** : Ein Druck ist ein Ereignis, kein Zustand. Mit 5 Sekunden Verzögerung reagiert HomeKit zu spät oder verschluckt den Druck, wenn ein zweiter folgt. Der Verzögerungswert muss explizit auf null gesetzt werden.

### 4. Gladys ist manchmal reicher als HomeKit

`BUTTON_STATUS` zählt **über hundert Werte** : Klick, Doppelklick, Langdruck, aber auch Drehung, Schütteln, Richtungspfeile, Helligkeitsgesten. HomeKit kennt nur **drei**.

Die getroffene Wahl: Nur die drei hochladen, die eine exakte Entsprechung haben, und **die anderen ignorieren** statt sie auf eine der drei zu reduzieren. Bei jemandem die falsche Automatisierung auszulösen ist schlimmer, als gar keine auszulösen.

### 5. Ein Gerät, ein Dienst, nicht drei

HomeKit modelliert in einem einzigen Dienst, was Gladys in mehrere Kategorien aufteilt:

- Der Dienst `Thermostat` ← Heizsollwert, Kühlsollwert, Modus, Temperatursensor

- Der Dienst `AirQualitySensor` ← Luftqualitätsindex, PM2.5, PM10

- Der Dienst `Battery` ← Stand, Batterie-Niedrig-Alarm

Ohne Gruppierung zeigt die Haus-App drei Kacheln für ein einziges Gerät an. Dies geschah mit einem Matter-Ventilator: Er erschien als **drei separate Ventilatoren**.

—

## Was blockiert ist und warum

Das sind Grenzen des **Gladys-Kerns**, nicht der Bridge.

**`GarageDoorOpener`, `Outlet`, `Door`, `Window`** — es gibt keine `device_class` im Gladys-Modell, um zu sagen « dieser Rollladen ist eine Garagentür » oder « diese Steckdose ist eine Steckdose, kein Schalter ». Das ist das strukturellste Problem: Es blockiert vier Dienste auf einmal. Das Heben übersteigt bei weitem die HomeKit-Brücke und verdient eine eigene Diskussion.

**`SecuritySystem`** — die Hausalarmanlage ist kein Gerät: Sie lebt in `t_house.alarm_mode`, aber die Brücke iteriert über die Geräte.

**`Valve`** — die sieben `water-valve`-Typen sind *alle schreibgeschützt*: Durchfluss, Bewässerungsvolumen, Betriebszustand. Kein Öffnungsbefehl. Eine nicht bedienbare Ventilkachel wäre schlimmer als nichts — und diese Ventile sind bereits steuerbar, ihr Befehl läuft über ein `switch:binary`-Feature, das die Brücke bereits ausgesetzt hat.

**`OccupancySensor`** — HomeKit erwartet einen stabilen Booleschen Wert, während Gladys die Anwesenheit in `sensor:push` meldet, ein Ereignis ohne dauerhaften Zustand. Man müsste einen Zustand mit einer willkürlich gewählten Abschaltdauer synthetisieren: Das ist Geschäftslogik, kein Mapping.

**`HeaterCooler`** — die Kategorie `heater` hat nur einen Typ, `pilot-wire-mode`. Der Pilotdraht ist eine französische Besonderheit ohne HomeKit-Äquivalent.

**`Television` und `CameraRTPStreamManagement`** — bewusst außerhalb des Umfangs: Das sind eigenständige Projekte, keine Mapping-Ergänzungen.

—

## Warum nicht eine externe Integration?

Das ist **heute nicht möglich**, aus drei strukturellen Gründen:

  1. Die Brücke muss sich im **mDNS im lokalen Netzwerk** anmelden, damit das iPhone sie entdeckt. Eine externe Integration läuft in einem isolierten Netzwerk, in dem Multicast nicht funktioniert.

  2. Sie muss **auf einem festen Port hören**, der vom LAN aus erreichbar ist.

  3. Sie muss **alle Geräte** aller Integrationen sehen, während das externe Modell jede Integration in ihren eigenen Namensraum isoliert.

Achtung vor einer häufigen Verwechslung: Es gibt Diskussionen über eine Integration **HomeKit Controller**, die in die *andere* Richtung geht — HomeKit-Zubehör von Gladys aus steuern. Diese wäre extern portierbar. Die Brücke nicht.

—

## Wo helfen?

**Auf echten Geräten testen.** Das ist der dringendste Bedarf. Alles ist durch automatisierte Tests abgedeckt, aber die echte Paarung wurde nicht auf allen Gerätetypen validiert. Wenn Sie ein Schloss, einen Thermostat, einen Ventilator oder einen Rauchmelder und ein iPhone haben, ist Ihr Feedback wertvoller als jeder Unit-Test.

**PRs lesen**, insbesondere die Mapping-Entscheidungen: Gassensor-Schwellenwerte, Luftqualitätsbänder, Schlosszustände. Sie sind alle in den Beschreibungen dokumentiert und begründet.

**Noch machbar, aber nicht gemacht:** `FilterMaintenance`, zur HEPA-Filterüberwachung. Heute nur ein Hersteller, also geringer Wert — aber ein einfacher Beitrag für alle, die einsteigen wollen.

Danke für deine PRs @jeromeme !! :smiley:

Ich habe die automatischen Cursor-Reviews für deine PRs gestartet!

Danke @pierre-gilles für die Reviews, das hat die Korrektur einiger Punkte ermöglicht.

Aktualisierter Status:

Korrigiert — 25 Fehler

Erste Überprüfungsrunde (15)

  1. Eine Verzögerung von 0 s auf 5 s durch ein || — der Rauchmelder-Fix war toter Code
  2. Zwei aufeinanderfolgende Einfachklicks: Der zweite wurde verschluckt
  3. Ein Lüfter schrieb in ein schreibgeschütztes Feature
  4. Ausschalten ohne vorheriges Lesen → Wiedereinschalten mit voller Geschwindigkeit
  5. Das Schloss vergaß den Befehl nach der ersten Abfrage
  6. Ein schreibgeschütztes Schloss akzeptierte trotzdem Befehle
  7. Stummer Akku wurde als schwach gemeldet (null <= 20 ist true in JS)
  8. Nicht zusammenhängende Klimatisierungsmodi: „Heiß“ wurde bei einer Klimaanlage mit nur Kalt angeboten
  9. Temperaturvorgabe verknüpft auf einem Gerät, das keine hat → Absturz bei der ersten Abfrage
  10. Leere Modusliste, von HAP abgelehnt
  11. Keine Begrenzung auf dem Benachrichtigungspfad: Ein durchdrehender Sensor ließ die Brücke abstürzen
  12. 5 Sekunden Verzögerung bei einem Rauchmelder (gemeldet von Pierre-Gilles)
  13. Route /device nicht getestet
  14. Schreibreihenfolge: Ein teilweiser Fehler ließ alle Geräte exponiert
  15. Der Fehler von /device zog den Pairing-QR-Code mit

Zweite Runde (8)

  1. Zwei Alarme im selben Moment: Der erste ging verloren
  2. Ein Schlossbefehl überschrieb den tatsächlichen Zustand — Nuki wurde als verriegelt gemeldet, während es sich dreht
  3. Mehrfach-Tasten-Fernbedienung: Alle Drücke auf Taste 1
  4. Matter-Tasten völlig stumm
  5. Die Rohgeschwindigkeit des Lüfters überschrieb die angezeigte Prozentzahl
  6. Unterschreiten von 20 % Akku nie gemeldet, nur bei der nächsten Abfrage
  7. Thermostat nie „im Ruhezustand“ zwischen seinen beiden Vorgaben
  8. Automatikmodus auf einer Klimaanlage geschrieben, die ihn nicht deklariert

Heute Morgen (2)

  1. Lange Drücke von Xiaomi nie übertragen
  2. Vier bereits gemergte Tests durch ein Rebase gelöscht — die CI blieb grün

Status

  • 3 PRs gemergt: #2781, #2792, #2798
  • 7 PRs offen, alle grün, ohne Konflikte
  • 5 genehmigt von Cursor: #2793, #2796, #2797, #2799, #2800
  • 2 in Wartestellung auf seine neue Runde: #2794, #2795
  • 46 Review-Threads beantwortet
  • 2 Tracking-Issues offen: #2806, #2812

Was noch auf der HomeKit-Brücke zu tun ist

Testen

  • Ein echtes iPhone paaren. Nichts wurde jemals auf echtem Gerät getestet:
    alles wird durch automatisierte Tests validiert. Es wird eine native Gladys-Installation
    benötigt — kein Docker, Multicast mDNS durchdringt die VM nicht.
    Priorität für Schlösser, Thermostate und Fernbedienungen.
  • Überprüfen der Accessoire-Namen: HAP lehnt Emojis und Satzzeichen ab und gibt nur eine Warnung aus. Das Accessoire erscheint dann nie, ohne Fehler.

Überprüfen

Sieben PRs offen, alle grün, alle separat überprüfbar: #2793 Rauchmelder,
#2794 Schloss, #2795 Taste, #2796 Lüfter, #2797 Batterie, #2799 Thermostat,
#2800 Geräteauswahl.

Zwei offene Baustellen für alle

  • #2806 — thermostat:mode und thermostat:operating-state mappen. Die Typen existieren im Kern seit #2752, aber keine Integration produziert sie
    noch. Gleichzeitig mit der ersten, die sie ausgibt (#2730 auf der Z-Wave-Seite).
  • #2812 — HomeKit-Dienste nach Feature statt nach Typ indizieren. Ein einziger Fix würde Mehrfach-Tasten-Fernbedienungen, Mehrfach-Jalousien und Thermostate mit mehreren Sonden freischalten.

Durch den Gladys-Kern blockiert

Diese HomeKit-Dienste sind ohne eine Weiterentwicklung des Gerätemodells nicht erreichbar. Der erste ist der strukturierendste: Er würde vier auf einmal freischalten.

  • GarageDoorOpener, Outlet, Door, Window — Es fehlt ein device_class, um eine Steckdose von einem Schalter oder eine Garagentür von einer Jalousie zu unterscheiden.
  • SecuritySystem — Der Alarm lebt in t_house.alarm_mode, nicht in einem Gerät.
  • Valve — Die sieben Typen water-valve sind alle schreibgeschützt. Diese Ventile bleiben über switch:binary steuerbar.
  • OccupancySensor — Gladys meldet die Anwesenheit ohne persistenten Zustand.
  • HeaterCooler — Die Kategorie heater hat nur den Pilotdraht, eine französische Besonderheit ohne HomeKit-Äquivalent.

Machbar, nicht gemacht

  • FilterMaintenance (HEPA-Filter) — Ein einziger Produzent, geringer Wert.

Vielen Dank für all diese PRs :slight_smile: Das ist wirklich cool !!

Alles ist für mich in Ordnung, es ist in master gemerged!

Vielleicht sind unsere Funktionen einfach nicht präzise genug :slight_smile:

Zögere nicht, für jeden Feature-Typ, den du in Gladys haben möchtest, einen Feature-Request in Demande de fonctionnalités zu erstellen, das kann absolut hinzugefügt werden!

Eigentlich sehe ich da kein Problem, die Integration könnte den Alarmmodus in Homekit abbilden?

Was meinst du damit?

Kein Problem, ich hoffe, dass alles gut funktioniert :slight_smile:

Für den Alarm werde ich sicherstellen, dass ein Gerät in Homekit mit der folgenden Zuordnung erstellt wird:

  • Gladys / disarmed = Homekit / DISARMED
  • Gladys / armed = Homekit / AWAY_ARM
  • Gladys / partially-armed = Homekit / STAY_ARM
  • Gladys / panic = Homekit / ALARM_TRIGGERED
    Nur der Zustand Homekit / NIGHT_ARM würde nicht integriert werden.

Für die device_class werde ich das mit Claude untersuchen und gegebenenfalls eine Anfrage stellen :wink:

Für den OccupancySensor handelt es sich um einen Fehler, der von Claude gemeldet wurde:

Zitat
Ich hatte `presence-sensor` ausgeschlossen, weil ich dachte, dass es keinen Zustand zu lesen gibt, da sein Typ `SENSOR.PUSH` ist.
Beim Lesen von `lan-manager.scanPresence.js` ist das falsch: Es sendet tatsächlich 1, wenn das Gerät erkannt wird, und 0, wenn es verschwindet. Es ist ein echter Boolescher Wert und lässt sich direkt auf `OccupancySensor` abbilden. Gleiches gilt für Bluetooth. Ich werde den Pull Request erstellen.