PySignalduino ist modular aufgebaut und trennt die Protokolldefinitionen (JSON) strikt von der Verarbeitungslogik (Python). Seit der Migration zu asyncio (Version 0.9.0) folgt das System einer ereignisgesteuerten, asynchronen Architektur, die auf asyncio-Tasks und -Queues basiert. Dies ermöglicht eine effiziente Verarbeitung von Sensordaten, Kommandos und MQTT-Nachrichten ohne Blockierung.
Die Klasse SDProtocols (sd_protocols/sd_protocols.py) ist der zentrale Einstiegspunkt. Sie vereint Funktionalitäten durch Mehrfachvererbung von Mixins:
-
ProtocolHelpersMixin: Grundlegende Bit-Operationen.
-
ManchesterMixin: Spezifische Logik für Manchester-kodierte Signale (
mcBit2*Methoden). -
PostdemodulationMixin: Nachbearbeitung dekodierter Daten (
postDemo_*Methoden). -
RSLMixin: Handler für das RSL-Protokoll.
Die Datei sd_protocols/protocols.json enthält die statischen Definitionen. Jedes Protokoll besitzt eine ID und Eigenschaften wie:
-
format: Kodierung (z.B.manchester,twostate,pwm). -
preamble: Erkennungsmuster. -
method: Mapping auf die Python-Methode zur Dekodierung.
Der Ablauf bei Manchester-Signalen ist wie folgt:
1. Erkennung: Match anhand der Preamble/Muster.
2. Vorvalidierung: ManchesterMixin._demodulate_mc_data() prüft Länge und Taktung.
3. Dekodierung: Aufruf der spezifischen mcBit2*-Methode.
Hinweis: Einige Protokolle wie TFA (mcBit2TFA) oder Grothe (mcBit2Grothe) haben spezielle Anforderungen an die Längenprüfung oder Duplikatfilterung.
PySignalduino verwendet asyncio für alle E/A-Operationen, um parallele Verarbeitung ohne Thread-Overhead zu ermöglichen. Die Architektur basiert auf drei Haupt-Tasks, die über asynchrone Queues kommunizieren:
-
Reader-Task: Liest kontinuierlich Zeilen vom Transport (Seriell/TCP) und legt sie in der
_raw_message_queueab. -
Parser-Task: Entnimmt Rohzeilen aus der Queue, dekodiert sie über den
SignalParserund veröffentlicht Ergebnisse via MQTT oder ruft denmessage_callbackauf. -
Writer-Task: Verarbeitet Kommandos aus der
_write_queue, sendet sie an das Gerät und wartet bei Bedarf auf Antworten.
Zusätzlich gibt es spezielle Tasks für Initialisierung, Heartbeat und MQTT-Command-Listener.
-
_raw_message_queue(asyncio.Queue[str]): Rohdaten vom Reader zum Parser. -
_write_queue(asyncio.Queue[QueuedCommand]): Ausstehende Kommandos vom Controller zum Writer. -
_pending_responses(List[PendingResponse]): Verwaltet erwartete Antworten mit asyncio.Event für jede. -
_stop_event(asyncio.Event): Signalisiert allen Tasks, dass sie beenden sollen. -
_init_complete_event(asyncio.Event): Wird gesetzt, sobald die Geräteinitialisierung erfolgreich abgeschlossen ist.
Alle Ressourcen (Transport, MQTT-Client) implementieren aenter/aexit und werden mittels async with verwaltet. Der SignalduinoController selbst ist ein Kontextmanager, der die Lebensdauer der Verbindung steuert.
Die MQTT-Integration wurde auf eine versionierte, konsistente Befehlsschnittstelle umgestellt, basierend auf dem Architecture Decision Record (ADR-001, ADR-002).
Die Verarbeitung von eingehenden Befehlen erfolgt über ein dediziertes Command Dispatcher Pattern zur strikten Trennung von Netzwerk-Layer, Validierungslogik und Controller-Aktionen:
-
MqttPublisher (
signalduino/mqtt.py) empfängt eine Nachricht aufsignalduino/v1/commands/#. -
Der SignalduinoController leitet die rohe Payload an den MqttCommandDispatcher weiter.
-
Der Dispatcher (
signalduino/commands.py) validiert die Payload gegen ein JSON-Schema (ADR-002). -
Bei Erfolg wird die entsprechende asynchrone Methode im SignalduinoController aufgerufen.
-
Der Controller sendet serielle Kommandos (
W<reg><val>,V,CG) und verpackt die Firmware-Antwort. -
Die finale Antwort (
status: OKodererror: 400/500/502) wird an den Client zurückgesendet.
Alle Topics sind versioniert und verwenden das Präfix {MQTT_TOPIC}/v1.
Topic-Typ |
Topic-Struktur |
Zweck |
Command (Request) |
|
Steuerung und Abfrage von Parametern (z.B. |
Response (Success) |
|
Strukturierte Antwort auf Befehle ( |
Error (Failure) |
|
Strukturierte Fehlerinformationen ( |
Telemetry |
|
JSON-serialisierte, dekodierte Sensordaten ( |
Status |
|
Heartbeat- und Gerätestatus (z.B. |
Alle Requests (Commands) und Responses (Responses/Errors) verwenden eine standardisierte JSON-Struktur, die eine req_id zur Korrelation von Anfrage und Antwort erfordert.
{
"req_id": "uuid-12345",
"data": "V 3.5.7+20250219" // Nur in Responses
}Die vollständigen Architecture Decision Records (ADR) sind hier aufgelistet und werden im Dokument eingebettet:
../architecture/decisions/ADR-001-mqtt-get-frequency.adoc ../architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc ../architecture/decisions/ADR-003-cc1101-parameter-set-logic.adoc ../architecture/decisions/ADR-004-mqtt-response-parsing.adoc ../architecture/decisions/ADR-005-mqtt-cc1101-response-consistency.adoc
+-------------------+ +-------------------+ +-------------------+
| Transport | | Controller | | MQTT Publisher |
| (Serial/TCP) |----->| (asyncio Tasks) |----->| (aiomqtt) |
+-------------------+ +-------------------+ +-------------------+
^ | |
| v v
+-------------------+ +-------------------+ +-------------------+
| SIGNALDuino | | Parser | | MQTT Broker |
| Hardware |<-----| (SDProtocols) |<-----| (extern) |
+-------------------+ +-------------------+ +-------------------+-
Transport: Abstrahiert die physikalische Verbindung (asynchrone Lese-/Schreiboperationen).
-
Controller: Orchestriert die drei Haupt-Tasks und verwaltet die Queues.
-
Parser: Wendet die Protokoll‑Definitions‑JSON an und dekodiert Rohdaten.
-
MQTT Publisher: Stellt die Verbindung zum Broker her, publiziert Nachrichten und empfängt Kommandos.
-
Empfang: Hardware sendet Rohdaten → Transport liest Zeile → Reader‑Task legt Zeile in
_raw_message_queue. -
Verarbeitung: Parser‑Task entnimmt Zeile, erkennt Protokoll, dekodiert Nachricht.
-
Ausgabe: Dekodierte Nachricht wird an
message_callbackübergeben und/oder via MQTT publiziert. -
Kommando: Externe Quelle (MQTT oder API) ruft
send_commandauf → Kommando landet in_write_queue→ Writer‑Task sendet es an Hardware. -
Antwort: Falls Antwort erwartet wird, wartet der Controller auf das passende Event in
_pending_responses.
Alle Schritte sind asynchron und nicht‑blockierend; Tasks können parallel laufen, solange die Queues nicht leer sind.
Die Architektur wurde von einer threading‑basierten Implementierung (Version 0.8.x) zu einer reinen asyncio‑Implementierung migriert. Wichtige Änderungen:
-
Ersetzung von
threading.Threaddurchasyncio.Task -
Ersetzung von
queue.Queuedurchasyncio.Queue -
Ersetzung von
threading.Eventdurchasyncio.Event -
async/awaitin allen E/A‑Methoden -
Asynchrone Kontextmanager für Ressourcenverwaltung
Details zur Migration sind im Dokument ASYNCIO_MIGRATION.md zu finden.
Die PySignalduino-Dokumentation wird automatisch mit einer dynamischen Sitemap und branch-spezifischen robots.txt-Dateien versehen, um die Auffindbarkeit in Suchmaschinen zu verbessern.
Die Sitemap wird durch das Python-Skript tools/generate_sitemap.py generiert, das:
-
Den Build-Output-Ordner (
build/site/html) nach HTML-Dateien scannt -
Prioritäten (0.1–1.0) und Update-Frequenzen (
changefreq) basierend auf Dateipfaden zuweist -
Branch-spezifische Base-URLs unterstützt (main:
pysignalduino.rfd-fhem.github.io, preview:preview.rfd-fhem.github.io) -
Gültige XML-Sitemap gemäß sitemaps.org-Schema generiert
-
Fehlerbehandlung und Logging enthält
Das Skript kann manuell ausgeführt werden:
python tools/generate_sitemap.py --build-dir build/site/html --output sitemap.xml --branch mainDie Datei docs/robots.txt wird im CI/CD-Workflow branch-spezifisch angepasst:
-
main-Branch: Erlaubt Crawling aller Pfade, schließt Preview-/Develop-Pfade aus
-
preview-Branch: Verbietet Crawling des
/preview/-Pfads -
develop-Branch: Verbietet Crawling des
/develop/-Pfads
Zusätzlich wird ein Crawl-delay: 2 gesetzt, um Serverlast zu reduzieren.
Der GitHub Actions Workflow .github/workflows/docs.yml wurde erweitert, um:
-
Nach dem Asciidoctor-Build das Sitemap-Generierungsskript auszuführen
-
Die robots.txt in das Ausgabeverzeichnis zu kopieren und branch-spezifisch anzupassen
-
Bei Fehlern der Sitemap-Generierung nicht den gesamten Build fehlschlagen zu lassen (
continue-on-error: true)
Für den Fall, dass die dynamische Generierung fehlschlägt, stehen statische Vorlagen bereit:
-
docs/sitemap_template.xml– Grundlegende Sitemap mit den wichtigsten URLs -
docs/robots.txt– Generische robots.txt-Vorlage
Diese Dateien werden automatisch durch den CI/CD-Workflow verwendet.
-
Die Sitemap wird unter
https://pysignalduino.rfd-fhem.github.io/sitemap.xmlverfügbar sein -
Die robots.txt wird unter
https://pysignalduino.rfd-fhem.github.io/robots.txtverfügbar sein -
Es wird empfohlen, die Sitemap in der Google Search Console und Bing Webmaster Tools einzureichen