Events und Automationen

Event-System
Abschnitt betitelt „Event-System“Code: backend/src/justai/events/.
- Typisiert und versioniert: Jeder Event-Typ steht im Katalog
(
events/catalog.py, abrufbar unterGET /api/events/catalog) mit Beschreibung, Dauerhaftigkeit, Priorität, Sensibilität und optional einem Payload-Schema. Plugins registrieren eigene Typen nur unterplugin.<id>.*. - Felder:
id(UUIDv7),type,ts,source(Art, Raum, Gerät, Endpoint, Session, Plugin),payload,correlation_id,caused_by,hop,dedup_key,priority,sensitivity,durability. - Bus (
events/bus.py): Jeder Abonnent hat eine eigene begrenzte Queue und einen eigenen Worker. Ein langsamer oder fehlerhafter Abonnent blockiert niemanden. Duplikate (gleiche ID oder gleicherdedup_keyinnerhalb der TTL) werden verworfen. - Persistenz: Dauerhafte Events speichert der
EventStorein PostgreSQL vor der Zustellung (Aufbewahrung konfigurierbar, Standard 90 Tage). - Schleifenschutz: Folgeevents erben
hop + 1undcaused_by. Über dem globalen Hop-Limit werden Events gestoppt undsystem.event_loop_detectedgemeldet. - Anzeige: Admin-UI → Diagnose (Live-Strom über WebSocket, Filter, Historie).
Manuelles Auslösen zum Testen:
POST /api/events.
Automationen
Abschnitt betitelt „Automationen“Code: backend/src/justai/automation/. Eine Automation ist ein validiertes
JSON-Dokument (Modell: automation/model.py, Schema:
GET /api/automations/schema):
{ "trigger": {"type": "event", "event": "vision.person.recognized", "match": {"person_id": "person-…"}}, "conditions": [{"type": "time_window", "start": "07:00", "end": "22:00"}, {"type": "no_active_conversation", "room": "trigger"}], "actions": [{"type": "assistant.invoke", "instruction": "Begrüße die Person mit Namen.", "room": "trigger"}], "options": {"once": true, "cooldown_s": 0, "max_runs_per_hour": 20}}Event-Typen von Plugins stehen im Katalog, solange das Plugin läuft (auch aus der
Sandbox, Besitzer plugin:<id>). Der Assistent zählt sie mit dem Tool events.types
auf, damit z. B. „Wenn es anfängt zu regnen, sag es mir im Wohnzimmer“ mit einem selbst
gebauten Regen-Plugin funktioniert. Eine Zuhörpause („hör eine Stunde nicht zu“) legt
eine einmalige at-Automation an, die das Zuhören wieder einschaltet.
Auslöser
Abschnitt betitelt „Auslöser“| Typ | Felder |
|---|---|
event |
event (Wildcards erlaubt, z. B. vision.object.* oder plugin.gen-rain-monitor.*), match (Payload-Felder müssen gleich sein), optional room_id |
cron |
5 Felder: Minute Stunde Tag Monat Wochentag (0 = Sonntag) |
interval |
seconds (≥ 30) |
at |
Zeitpunkt (einmalig; die Automation wird nach dem Lauf deaktiviert) |
Zeitangaben gelten in der Zeitzone aus Einstellungen → System (Standard
Europe/Rome).
Bedingungen
Abschnitt betitelt „Bedingungen“time_window (Start, Ende, optional Wochentage), payload (Pfad, Operator
== != > >= < <= in contains, Wert), no_active_conversation (Raum),
visual_state (Zustandsdetektor ist in Zustand X).
| Typ | Wirkung |
|---|---|
assistant.invoke |
startet eine proaktive Gesprächssession ohne Wakeword im Raum (trigger = Raum des auslösenden Events). Bevorzugt wird der Endpoint, dessen Kamera das Event ausgelöst hat. Die Anweisung geht an das Modell; mit start_conversation: true (Standard) hört die Session danach normal zu, mit false nur noch 4 s. |
speak |
Text im Raum sagen. Standard (start_conversation: true): in der Stimme des Assistenten als Beginn eines Gesprächs; die Person kann direkt antworten. false: reine Durchsage über die lokale Sprachausgabe. |
display.show |
Dokument auf dem Display im Raum zeigen |
tool |
Tool mit Argumenten aufrufen (Rechte der Automation) |
event.emit |
eigenes Event veröffentlichen |
wait |
warten (max. 1 h) |
once (nach dem ersten erfolgreichen Lauf deaktivieren; erneutes Aktivieren macht
die Regel wieder scharf), cooldown_s, priority
(0–3), max_retries, not_before (Auslöser vorher ignorieren, z. B. „wenn du
mich ab morgen 9 Uhr siehst“), expires_at (danach deaktivieren),
max_runs_per_hour. Zeitpunkte ohne Zeitzone gelten in der Systemzeitzone.
Ausführung
Abschnitt betitelt „Ausführung“Event ─► Trigger-Match (im Speicher) ─► Bedingungen ─► Schutz: eigene Events ignorieren · Kettenlänge ≤ 4 · once · cooldown · Runs/Stunde ─► Job in der Postgres-Queue (überlebt Neustarts, Wiederholungen mit Backoff) ─► Aktionen nacheinander ─► Lauf-Protokoll (automation_runs) ─► automation.run.*-EventsAusgaben einer Automation unterliegen denselben Regeln wie Gespräche: Der Raum kommt aus dem Trigger oder der Konfiguration, nie aus einer Modellausgabe.
- Im Gespräch: Tool
automation.create, z. B. „Wenn du Anna das nächste Mal siehst, begrüße sie mit Namen.“ Regeln aufvision.person.recognizedbekommen automatisch die Bedingungaway_s >= 600(mindestens 10 Minuten nicht gesehen), sonst würde die Begrüßung irgendwann auslösen, während die Person noch vor der Kamera sitzt (so im Live-Test passiert). Andere Abstände kann das Modell setzen („wenn ich heimkomme“ ≥ 3600, „jedes Mal“ ≥ 0). Das Tool akzeptiert Personennamen (wird zur Personen-ID aufgelöst),self(die sprechende, erkannte Person) undhere(der aktuelle Raum). Fehlendetype-Angaben werden aus den Feldern ergänzt. Validierungsfehler gehen mit Details an das Modell zurück, damit es korrigieren kann. - Admin-UI → Automationen: Liste, JSON-Editor mit Schema-Validierung, Aktivieren/Deaktivieren, Jetzt ausführen, Lauf-Historie.
- Tools für den Assistenten:
automation.list,automation.set_enabled,automation.delete(mit Bestätigung).
- Unit (
tests/unit/test_automation_model.py): gültige/ungültige Definitionen, Ergänzung fehlender Typen, Cron-Matching, Vergleichsoperatoren. - E2E (
tests/e2e/test_simulated_rooms.py::test_automation_greets_recognised_person_in_the_trigger_room): Ein (eingespeistes)vision.person.recognizedim Wohnzimmer startet dort eine proaktive Session mit Begrüßung, die Küche bleibt still,oncedeaktiviert die Automation danach. - Unit:
not_before/expires_atmit lokalen Zeitangaben ohne Zeitzone. - Live per Textchat (2026-10-16): „Wenn du mich das nächste Mal ab morgen um 9:00
siehst, erinnere mich bitte, dass ich den Müll rausbringen soll.“ ergibt
Personen-Trigger,
speak,once,not_before: 2026-10-17T09:00:00und die Abwesenheitsbedingung. - Manuell gegen die Docker-Instanz: „Wenn du Anna Test das nächste Mal siehst,
begrüße sie bitte mit ihrem Namen.“ legt eine korrekte Automation an
(Event-Trigger mit aufgelöster Personen-ID,
assistant.invoke,once). - Die vollständige Kette mit echter Gesichtserkennung an der Kamera ist noch nicht getestet (VISION.md).