Files
mass-hacs-gate-button/README.md
T
bot 988a30cbf7
Tests / pytest + ruff (3.12) (push) Has been cancelled
Validate / HACS validation (push) Has been cancelled
Validate / Hassfest validation (push) Has been cancelled
Initial commit: Main Gate Controller HACS integration
2026-09-07 23:19:12 +02:00

315 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Main Gate Controller
Eine vollständig über die Home-Assistant-UI konfigurierbare Custom Integration für
ein elektrisches Fußgängertor. Die Integration ersetzt eine bestehende Lösung aus
Skripten, Timern und Helpern und steuert das Tor indirekt über eine bereits
vorhandene Switch-Entity (z. B. `switch.shellyplusuni_8c4f00a6ce94_output_0`).
> Vor der ersten Veröffentlichung müssen die Platzhalter in `manifest.json`,
> `hacs.json`, dieser README und in `LICENSE` durch die tatsächlichen Werte
> (GitHub-Benutzername, Klarname) ersetzt werden. Die Platzhalter tragen das
> Präfix `YOUR_GITHUB_USERNAME`.
## Features
- **Mehrere Tore** – pro Tor wird ein eigener Config Entry angelegt.
- **Konfiguration über die UI** – inklusive modernem Entity-Selector für die
Switch-Entity, NumberSelectorn für Zeiten und EntitySelector für Notify-Entities.
- **Config Flow + Options Flow** – alle Parameter lassen sich nachträglich in
`Settings → Devices & Services → Main Gate Controller → Configure` ändern.
- **Mode: single** – ein laufender Ablauf kann nicht durch einen zweiten
Button-Press unterbrochen werden.
- **Robuste State Machine** – `closed → opening → open → closing → closed` mit
Debug-Log auf jedem Übergang. Eine fehlgeschlagene Benachrichtigung führt
nicht zum Abbruch. Ein fehlgeschlagener Switch-Service bricht den Ablauf ab
und setzt den Status zurück auf `closed`.
- **Synchronisation mit dem Sensor** – `remaining_seconds` wird während der
Offen-Phase jede Sekunde aktualisiert und steht sofort im Dashboard zur
Verfügung.
- **Restart-sicher** – nach einem Home-Assistant-Neustart beginnt der Status
mit `closed`, `running=false`. Ein ggf. unterbrochener Ablauf wird
protokolliert.
- **Diagnostics** – `home-assistant://developer-tools/...` Diagnose-Export ohne
sensible Daten.
## Ablauf
Wenn ein Tor geöffnet wird, führt die Integration exakt die folgende Sequenz
aus und nutzt ausschließlich Service-Calls auf `switch.turn_on` /
`switch.turn_off`:
```
1. Status: opening
2. (optional) Notification „Opening Main Gate (Pedestrians)“
3. switch.turn_off
4. warten (pulse_duration, Standard 1 s)
5. switch.turn_on
6. warten (pulse_duration)
7. switch.turn_off
8. Status: open
9. Countdown (open_duration, Standard 20 s), remaining_seconds zählt sekündlich herunter
10. Status: closing
11. (optional) Notification „Closing Main Gate“
12. switch.turn_on
13. warten (pulse_duration)
14. switch.turn_off
15. warten (pulse_duration)
16. Status: closed
```
## Installation via HACS (Custom Repository)
1. **HACS** in der Sidebar öffnen.
2. Auf das **⋮-Menü → Custom repositories** klicken.
3. Die URL dieses GitHub-Repositories eintragen, z. B.
`https://github.com/<your-username>/ha-main-gate-controller`.
4. **Typ** auf `Integration` stehen lassen.
5. **Repository hinzufügen** klicken.
6. In der HACS-Übersicht erscheint *Main Gate Controller* und kann über
**Download** installiert werden.
7. Falls erforderlich Home Assistant neu starten.
8. **Settings → Devices & Services → Add Integration** öffnen.
9. **Main Gate Controller** auswählen.
10. Das Gate konfigurieren (Name, Switch-Entity, Zeiten, optional Notifications).
## Manuelle Installation (ohne HACS)
```
custom_components/main_gate_controller/
```
in das Home-Assistant-Konfigurationsverzeichnis kopieren, Home Assistant
neu starten und denselben UI-Pfad ab Schritt 8 verwenden.
## Einrichtung
### Config Flow
Der Config Flow ist ein einzelner Schritt. Die folgenden Felder werden
abgefragt:
| Feld | Pflicht | Default | Beschreibung |
| ----------------------------- | :----: | -------------------------------------- | ----------------------------------------------------------------------- |
| `Gate name` | ✅ | `Main Gate` | Anzeigename – wird als Device-Name und Sensor-Entity-ID-Basis verwendet |
| `Gate switch` | ✅ | — | Switch-Entity zur Torsteuerung (Selector: `EntitySelector(domain="switch")`) |
| `Open duration (seconds)` | ✅ | `20` | Wie lange das Tor offengehalten wird |
| `Pulse duration (seconds)` | ✅ | `1.0` | Ein-/Aus-Pulsdauer der Switch-Impulse |
| `Send notifications` | ✅ | `false` | Aktiviert/deaktiviert alle Benachrichtigungen |
| `Notification targets` | ⛔ | leer | Notify-Entities (Selector: `EntitySelector(domain="notify", multiple=True)`) |
| `Opening notification text` | ⛔ | `Opening Main Gate (Pedestrians)` | Text der Öffnungs-Benachrichtigung |
| `Closing notification text` | ⛔ | `Closing Main Gate` | Text der Schließ-Benachrichtigung |
Wird `Send notifications` aktiviert ohne dass Targets ausgewählt sind, meldet
der Flow einen Fehler. Außerdem wird verhindert, dass dieselbe Switch-Entity
an mehrere Config Entries gebunden wird (Unique-ID-Schutz).
### Options Flow
Über **Settings → Devices & Services → Main Gate Controller → Configure**
lassen sich `Open duration`, `Pulse duration`, `Send notifications`,
`Notification targets` und beide Notification-Texte jederzeit nachjustieren.
Der Name und die Switch-Entity sind im Options Flow bewusst nicht editierbar;
dafür ist das Löschen und Neuanlegen eines Gates der vorgesehene Weg.
### Switch- und Notify-Domain
Die Integration geht davon aus, dass die ausgewählte Switch-Entity und die
ausgewählten Notify-Entities bereits in Home Assistant existieren (z. B. Shelly,
Sonoff, MQTT, Companion-App). Es gibt **keine** direkte Geräte-Abhängigkeit.
## Entities
Für jedes Gate (jeden Config Entry) wird ein gemeinsames Device angelegt mit
`manufacturer = Custom` und `model = Main Gate Controller`. Folgende Entities
gehören zum Device:
### Button `button.<gate>_open`
Startet den kompletten Ablauf. Friendly-Name: `Open`. Ist bereits ein Ablauf
aktiv, wird der Druck ignoriert (logischer `mode: single`). Wird sauber
protokolliert, nicht als Fehler.
### Sensor `sensor.<gate>_status`
Maschinenlesbarer State, standardmäßig einer von `closed`, `opening`, `open`,
`closing`. Attribute (immer vorhanden):
| Attribut | Typ | Beschreibung |
| -------------------- | -------------- | --------------------------------------------------- |
| `remaining_seconds` | `int \| null` | Sekunden bis zum automatischen Schließen |
| `duration` | `number` | Konfigurierte `Open duration` |
| `running` | `bool` | `True`, solange ein Ablauf läuft |
| `started_at` | `string\|null` | ISO 8601 UTC – Zyklusstart |
| `finishes_at` | `string\|null` | ISO 8601 UTC – berechnetes Ende der Öffnungsphase |
Während der `open`-Phase wird `remaining_seconds` ungefähr jede Sekunde
aktualisiert.
> **Bewusste Designentscheidung:** Es wird kein zusätzlicher
> `binary_sensor.<gate>_running` erzeugt. Das `running`-Attribut am
> Statussensor liefert dieselbe Information ohne eine weitere Entity, und
> `sensor.<gate>_status.options` enthält ohnehin alle Zustände.
## Beispiel Dashboard
Eine kompakte, einzelne Tile-Card reicht aus. Die Tile-Card unterstützt
das `state_content`-Feld, das beliebige Attribute der Entity anzeigen kann.
Wir übergeben den maschinenlesbaren State **und** die verbleibenden Sekunden:
```yaml
type: tile
entity: sensor.main_gate_status
name: Main Gate
icon: mdi:gate
state_content:
- state
- remaining_seconds
```
### Wer das Ergebnis in der „sprechenden“ Variante sehen will
Da der Sensor-State `closed/opening/open/closing` maschinenlesbar bleibt,
werden die deutschen Bezeichnungen „Geschlossen/Öffnet/Offen/Schließt“ nicht
im State selbst, sondern als Übersetzung im Frontend angezeigt. **Auf einer
Tile-Card sieht man also je nach Frontend-Sprache entweder die englische
oder die lokalisierte Variante – aber derselbe Sensor.**
Wer auf den englischen Buttons-State explizit verzichten will, kann alternativ
mit dem `mdi:clock-digital`-Icon und einer Text-Karte arbeiten:
```yaml
type: conditional
conditions:
- entity: sensor.main_gate_status
state: open
card:
type: markdown
content: >-
Open · {{ state_attr('sensor.main_gate_status', 'remaining_seconds') }}s
```
## Update-Prozess
HACS markiert neue Releases automatisch. Nach einer Aktualisierung empfiehlt
sich ein Home-Assistant-Neustart, weil die Integration während eines laufenden
Ablaufs nicht in-place ausgetauscht werden kann.
> Aktuell ist keine Reload-Annotation aktiv: ein bereits laufender Zyklus geht
> beim Update verloren und der Status wird auf `closed` zurückgesetzt. Das ist
> mit einem schief gestellten Tor-Position fehlertolerant (siehe
> *Einschränkungen*).
## Entwicklung
```bash
git clone https://github.com/<your-username>/ha-main-gate-controller
cd ha-main-gate-controller
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements_test.txt
# Statische Prüfung
ruff check custom_components tests
# Tests
pytest
```
Die Tests verwenden das Standard-Testwerkzeug
[`pytest-homeassistant-custom-component`](https://github.com/jaisenbe/pytest-homeassistant-custom-component),
welches eine bestimmte Version von Home Assistant pinnt. Damit lassen sich
Config-Flow, Entity-Lebenszyklus und asynchrone Service-Calls mit dem
echten HA-Testkern testen. Die Integration wird über den
`custom_components/`-Ordner direkt neben den Tests gefunden.
## Release-Prozess
1. Änderungen hochladen und prüfen, dass alle CI-Checks grün sind
(HACS validation, Hassfest, pytest).
2. Im Repository die Versionsnummer in `manifest.json` und im Tag
synchron anpassen. Der Release-Workflow
(`.github/workflows/release.yml`) erzwingt das. Ziel: `version: 0.1.0`
im Manifest ↔ Git-Tag `v0.1.0`.
3. Tag setzen und pushen:
```bash
git tag v0.1.0
git push origin v0.1.0
```
4. GitHub Actions baut das Release-Artefakt (Zip der Integration) und
veröffentlicht es auf der GitHub-Releases-Seite.
## Troubleshooting
### „cannot add entities“ / Integration lädt nicht
- Home Assistant neu starten, damit `custom_components/main_gate_controller`
einmal frisch gelesen wird.
- Logs:
```yaml
logger:
logs:
custom_components.main_gate_controller: debug
```
### Button bleibt ohne Wirkung
- Konfiguration prüfen: Settings → Devices & Services → Main Gate Controller →
Gerät → Entity → Status sollte `closed` sein. Wenn nicht, einmal manuell
über die Switch-Entity steuern, um sicher zu sein, dass das Relais
überhaupt reagiert.
### Benachrichtigungen kommen nicht an
- `Send notifications` aktiviert? Targets ausgewählt?
- Die Entity-Selector-Suche zeigt nur `notify.*`-Entities; bei
Companion-Apps darauf achten, dass die Mobile-App bereits ein
`notify.mobile_app_<phone>` angelegt hat.
### Status bleibt nach Stromausfall auf einer Phase hängen
- Die Integration ist **blind** für die reale Tor-Position: nach einem
Neustart beginnt `sensor.<gate>_status` mit `closed`. Wenn das Tor
während des Ausfalls halb offen geblieben ist, muss es von Hand
abgeschlossen werden.
## Architektur
```
custom_components/main_gate_controller/
├── __init__.py # async_setup_entry, async_unload_entry, DeviceInfo
├── manifest.json # Domain, Version, Codeowners, iot_class=calculated
├── const.py # Konstanten / Defaults
├── coordinator.py # MainGateController ← Zustandsmaschine, Countdown, Service-Calls
├── config_flow.py # MainGateConfigFlow + MainGateOptionsFlow + Selectors
├── button.py # button.<gate>_open
├── sensor.py # sensor.<gate>_status + Attribute
├── diagnostics.py # anonymisierter Diagnose-Export
├── strings.json # Übersetzungs-Schlüssel
└── translations/
├── en.json
└── de.json
```
Alle Service-Calls (`switch.turn_on/off`, `notify.send_message`) laufen
ausschließlich gegen die ausgewählten Home-Assistant-Entities. Es findet kein
direkter Zugriff auf Hersteller-Integrationen (z. B. Shelly) statt.
## Bekannte Einschränkungen / Design-Entscheidungen
- **Kein Binary-Sensor „Running“.** Der Statussensor liefert `running`
bereits als Attribut; eine zweite Entity würde keinen Mehrwert bieten.
- **Blind-Betrieb.** Die Integration hat keine Rückmeldung über die
tatsächliche Tor-Position. Wenn das Relais angezogen hat, heißt das
nicht zwingend, dass das Tor offen ist. Wer eine echtzeitnahe Statusanzeige
benötigt, sollte die Tor-Endlage zusätzlich an einen Home-Assistant-Sensor
melden und diesen Automations-Auslöser verwenden.
- **Restart mitten im Zyklus.** Beim Reload/Unload wird der laufende Zyklus
abgebrochen und der Status auf `closed` zurückgesetzt. Die reale
Tor-Position muss im Zweifel per Hand geprüft werden.
- **Switch-Fehler → safe default.** Schlägt ein `switch.turn_*`-Aufruf
fehl, wird der Zyklus abgebrochen und der Status auf `closed` gesetzt;
ein Alarm bleibt im Log. Im Zweifel ist das Relais möglicherweise
trotzdem ein- oder ausgeschaltet worden, das lässt sich aber nicht
garantiert ermitteln.