diff --git a/.devcontainer/fhem-data/fhem_signalduino_example.cfg b/.devcontainer/fhem-data/fhem_signalduino_example.cfg index 529f1fc..5e93c36 100755 --- a/.devcontainer/fhem-data/fhem_signalduino_example.cfg +++ b/.devcontainer/fhem-data/fhem_signalduino_example.cfg @@ -25,12 +25,16 @@ attr WEB stylesheetPrefix dark define eventTypes eventTypes ./log/eventTypes.txt setuuid eventTypes 695e9c21-f33f-c986-9ac3-190c47641a98acb9 + +# tag::broker_config[] # 4. Define the MQTT Client (Broker Connection) # 'mqtt' is the hostname of the broker service in docker-compose.yml define mqtt_broker MQTT2_CLIENT mqtt:1883 setuuid mqtt_broker 695e9c21-f33f-c986-e617-d7301881c4685bc6 attr mqtt_broker autocreate simple +# end::broker_config[] +# tag::device_config[] # 5. Define the SignalDuino MQTT Device define PySignalDuino MQTT2_DEVICE setuuid PySignalDuino 695e9c21-f33f-c986-4f81-a9f0ab37b6bcedf8 @@ -75,6 +79,7 @@ attr PySignalDuino setList raw:textField signalduino/v1/commands/set/raw $EVTPAR # Maintenance commands \ factory_reset:noArg signalduino/v1/commands/set/factory_reset attr PySignalDuino stateFormat state +# end::device_config[] # Map JSON payload to readings # Define setter commands diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index ec3606c..2e34cad 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -31,7 +31,10 @@ jobs: - name: run asciidoctor with Kroki extension # Registriere die Kroki-Extension (-r asciidoctor-kroki) und aktiviere sie (-a kroki=). # Mermaid-Diagramme werden mit dem Kroki-Server gerendert. - run: asciidoctor -R docs -D build/site/html -a docinfo=shared -a toc=left -a toclevels=2 -r asciidoctor-kroki -a kroki= 'docs/index.adoc' + # Verwendung von -R docs, um die Verzeichnisstruktur unter build/site/html beizubehalten + run: | + shopt -s globstar + asciidoctor -R docs -D build/site/html -a docinfo=shared -a toc=left -a toclevels=2 -r asciidoctor-kroki -a kroki= docs/**/*.adoc - name: Setup Python for Sitemap Generation uses: actions/setup-python@v5 diff --git a/README.adoc b/README.adoc new file mode 100644 index 0000000..ce1f942 --- /dev/null +++ b/README.adoc @@ -0,0 +1,93 @@ += PySignalduino – Asynchrone MQTT-Bridge für SIGNALDuino +:sectlinks: + +Dieses Projekt ist eine moderne Python-Implementierung der SIGNALDuino-Protokolle mit vollständiger *asyncio*-Unterstützung und integrierter *MQTT-Bridge*. Es ermöglicht die Kommunikation mit SIGNALDuino-Hardware (über serielle Schnittstelle oder TCP) und veröffentlicht empfangene Signale sowie empfängt Steuerbefehle über MQTT. + +=== Entwicklungsstatus + +[NOTE] +.Entwicklungsstatus +==== +PySignalduino befindet sich noch in aktiver Entwicklung und hat noch kein offizielles Release veröffentlicht. Die API kann sich zwischen Versionen ändern. Entwickler sollten bei der Verwendung Vorsicht walten lassen und auf mögliche Breaking Changes vorbereitet sein. +==== + +=== PySignalduino vs. Original +PySignalduino ist keine direkte Portierung, sondern eine Neuimplementierung mit folgenden Unterschieden: +* *Asynchrone Verarbeitung*:: Statt Threads wird `asyncio` verwendet. +* *MQTT-Integration*:: Eingebaute MQTT-Bridge für nahtlose Integration in IoT-Ökosysteme. +* *Moderne Python-Praktiken*:: Typisierung, strukturierte Logging, Konfiguration über Umgebungsvariablen. + +== Controller-Code und Firmware + +Die SIGNALDuino-Firmware (Microcontroller-Code) wird in einem separaten Repository entwickelt: + +* *GitHub Repository*:: https://github.com/RFD-FHEM/SIGNALDuino +* *Aktuelle Version*:: v4.0.0 +* *Unterstützte Hardware*:: +** Arduino Nano mit CC1101 +** ESP32-basierte Boards (z.B. ESP32-DevKitC) +** Maple Mini (STM32) +* *Build-Anleitungen*:: Das Repository enthält PlatformIO-Konfigurationen und Arduino-IDE-Projektdateien für einfache Kompilierung. + +== Hauptmerkmale + +* **Vollständig asynchron** – Basierend auf `asyncio` für hohe Performance und einfache Integration in asynchrone Anwendungen. +* **MQTT-Integration** – Automatisches Publizieren dekodierter Nachrichten in konfigurierbare Topics und Empfang von Steuerbefehlen (z.B. `version`, `set`, `mqtt`). +* **Unterstützte Transporte** – Serielle Verbindung (über `pyserial-asyncio`) und TCP-Verbindung. +* **Umfangreiche Protokollbibliothek** – Portierung der originalen FHEM‑SIGNALDuino‑Protokolle mit `SDProtocols` und `SDProtocolData`. +* **Konfiguration über Umgebungsvariablen** – Einfache Einrichtung ohne Codeänderungen. +* **Ausführbares Hauptprogramm** – `main.py` bietet eine sofort einsatzbereite Lösung mit Logging, Signalbehandlung und Timeout‑Steuerung. +* **Komprimierte Datenübertragung** – Effiziente Payload‑Kompression für MQTT‑Nachrichten. + +== Demo + +=== MQTT-CLI-Integration + +image::docs/pysignalduino_cli.apng[PySignalduino CLI Demo] + +=== FHEMweb-Integration + +image::docs/pysignalduino_fhemweb.apng[PySignalduino FHEMweb Demo] + +== Projektstruktur + +[source,text] +---- +PySignalduino/ +├── signalduino/ # Hauptpaket +│ ├── controller.py # Asynchroner Controller +│ ├── mqtt.py # MQTT‑Publisher/Subscriber +│ ├── transport.py # Serielle/TCP‑Transporte (asyncio) +│ ├── commands.py # Befehlsimplementierung +│ └── ... +├── sd_protocols/ # Protokollbibliothek (SDProtocols) +├── tests/ # Umfangreiche Testsuite +├── docs/ # Dokumentation (AsciiDoc) +├── main.py # Ausführbares Hauptprogramm +├── pyproject.toml # Paketkonfiguration +└── requirements*.txt # Abhängigkeiten +---- + +== Dokumentation + +Die vollständige Dokumentation und alle Anleitungen finden Sie auf der Hauptseite: + +* xref:docs/index.adoc[Zur vollständigen PySignalduino Dokumentation] + +== SEO & Sitemap + +Die Dokumentation wird automatisch mit einer dynamischen Sitemap (`sitemap.xml`) und branch‑spezifischen `robots.txt`‑Dateien versehen, um die Auffindbarkeit in Suchmaschinen zu verbessern. + +* *Sitemap‑Generierung*:: Das Skript xref:tools/generate_sitemap.py[tools/generate_sitemap.py] scannt den Build‑Output, weist Prioritäten und Update‑Frequenzen zu und generiert eine valide XML‑Sitemap gemäß sitemaps.org. +* *Branch‑spezifische URLs*:: Für die Branches `main`, `preview` und `develop` werden unterschiedliche Base‑URLs verwendet. +* *Integration in CI/CD*:: Der GitHub Actions Workflow link:.github/workflows/docs.yml[.github/workflows/docs.yml] generiert die Sitemap automatisch nach jedem Build und passt die `robots.txt` entsprechend an. + +Weitere Details zur Architektur finden Sie im xref:docs/02_developer_guide/architecture.adoc#dokumentations-infrastruktur-sitemap--seo[Architektur‑Dokument]. + +== Lizenz + +Dieses Projekt steht unter der MIT‑Lizenz – siehe xref:LICENSE[LICENSE] für Details. + +== Danksagung + +Basierend auf der originalen FHEM‑SIGNALDuino‑Implementierung von [@Sidey79](https://github.com/Sidey79) und der Community. \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index a0bb61c..0000000 --- a/README.md +++ /dev/null @@ -1,220 +0,0 @@ -# PySignalduino – Asynchrone MQTT-Bridge für SIGNALDuino - -Dieses Projekt ist eine moderne Python-Implementierung der SIGNALDuino-Protokolle mit vollständiger **asyncio**-Unterstützung und integrierter **MQTT-Bridge**. Es ermöglicht die Kommunikation mit SIGNALDuino-Hardware (über serielle Schnittstelle oder TCP) und veröffentlicht empfangene Signale sowie empfängt Steuerbefehle über MQTT. - -## Projektgeschichte - -PySignalduino ist Teil des **RFD-FHEM**-Ökosystems, das ursprünglich als Perl-basierte Lösung für die Hausautomationssoftware FHEM begann. Die Entwicklung lässt sich in folgende Meilensteine unterteilen: - -### Ursprung: RFD-FHEM und SIGNALDuino -- **2010er Jahre**: Die RFD-FHEM-Community entwickelte Hardware- und Softwarelösungen für die Funkkommunikation mit 433/868 MHz Geräten. -- **SIGNALDuino-Hardware**: Ein Arduino-basierter Transceiver mit CC1101 Funkmodul, der als kostengünstige Alternative zu kommerziellen Lösungen entstand. -- **Perl-Implementierung**: Die ursprüngliche Protokollimplementierung erfolgte in Perl als FHEM-Modul `00_SIGNALduino.pm`. - -### Migration zu Python -- **2020er Jahre**: Mit der wachsenden Popularität von Python und MQTT entstand der Bedarf nach einer moderneren, asynchronen Lösung. -- **PySignalduino**: Diese Bibliothek portiert die Perl-Protokolle (`SD_Protocols.pm`, `SD_ProtocolData.pm`) in eine native Python-Implementierung. -- **Asynchrone Architektur**: Vollständige `asyncio`-Integration für bessere Performance und einfachere Integration in moderne IoT-Systeme. - -### Community-Entwicklung -- **Open Source**: Das Projekt wird von einer aktiven Community auf GitHub gepflegt und weiterentwickelt. -- **Firmware-Entwicklung**: Die SIGNALDuino-Firmware wird parallel im Repository [RFD-FHEM/SIGNALDuino](https://github.com/RFD-FHEM/SIGNALDuino) entwickelt. -- **Version 3.5.0**: Die aktuelle Firmware-Version bietet erweiterte Funktionen wie WiFi-Unterstützung für ESP32-basierte Boards. - -### Entwicklungsstatus - -> **⚠️ Entwicklungsstatus** -> -> PySignalduino befindet sich noch in aktiver Entwicklung und hat noch kein offizielles Release veröffentlicht. Die API kann sich zwischen Versionen ändern. Entwickler sollten bei der Verwendung Vorsicht walten lassen und auf mögliche Breaking Changes vorbereitet sein. - -### PySignalduino vs. Original -PySignalduino ist keine direkte Portierung, sondern eine Neuimplementierung mit folgenden Unterschieden: -- **Asynchrone Verarbeitung**: Statt Threads wird `asyncio` verwendet. -- **MQTT-Integration**: Eingebaute MQTT-Bridge für nahtlose Integration in IoT-Ökosysteme. -- **Moderne Python-Praktiken**: Typisierung, strukturierte Logging, Konfiguration über Umgebungsvariablen. - -## Controller-Code und Firmware - -Die SIGNALDuino-Firmware (Microcontroller-Code) wird in einem separaten Repository entwickelt: - -- **GitHub Repository**: https://github.com/RFD-FHEM/SIGNALDuino -- **Aktuelle Version**: v3.5.0 -- **Unterstützte Hardware**: - - Arduino Nano mit CC1101 - - ESP32-basierte Boards (z.B. ESP32-DevKitC) - - Maple Mini (STM32) -- **Build-Anleitungen**: Das Repository enthält PlatformIO-Konfigurationen und Arduino-IDE-Projektdateien für einfache Kompilierung. - -## Hauptmerkmale - -* **Vollständig asynchron** – Basierend auf `asyncio` für hohe Performance und einfache Integration in asynchrone Anwendungen. -* **MQTT-Integration** – Automatisches Publizieren dekodierter Nachrichten in konfigurierbare Topics und Empfang von Steuerbefehlen (z.B. `version`, `set`, `mqtt`). -* **Unterstützte Transporte** – Serielle Verbindung (über `pyserial-asyncio`) und TCP-Verbindung. -* **Umfangreiche Protokollbibliothek** – Portierung der originalen FHEM‑SIGNALDuino‑Protokolle mit `SDProtocols` und `SDProtocolData`. -* **Konfiguration über Umgebungsvariablen** – Einfache Einrichtung ohne Codeänderungen. -* **Ausführbares Hauptprogramm** – `main.py` bietet eine sofort einsatzbereite Lösung mit Logging, Signalbehandlung und Timeout‑Steuerung. -* **Komprimierte Datenübertragung** – Effiziente Payload‑Kompression für MQTT‑Nachrichten. - -## Installation - -### Voraussetzungen - -* Python 3.8 oder höher -* pip (Python-Paketmanager) - -### Paketinstallation - -1. Repository klonen: - ```bash - git clone https://github.com/.../PySignalduino.git - cd PySignalduino - ``` - -2. Abhängigkeiten installieren (empfohlen in einer virtuellen Umgebung): - ```bash - pip install -e . - ``` - - Dies installiert das Paket im Entwicklermodus inklusive aller Runtime‑Abhängigkeiten: - * `pyserial` - * `pyserial-asyncio` - * `aiomqtt` (asynchrone MQTT‑Client‑Bibliothek) - * `python-dotenv` - * `requests` - -3. Für Entwicklung und Tests zusätzlich: - ```bash - pip install -r requirements-dev.txt - ``` - -## Schnellstart - -1. **Umgebungsvariablen setzen** (optional). Erstelle eine `.env`‑Datei im Projektverzeichnis: - ```bash - SIGNALDUINO_SERIAL_PORT=/dev/ttyUSB0 - MQTT_HOST=localhost - LOG_LEVEL=INFO - ``` - -2. **Programm starten**: - ```bash - python3 main.py --serial /dev/ttyUSB0 --mqtt-host localhost - ``` - - Oder nutze die Umgebungsvariablen: - ```bash - python3 main.py - ``` - -3. **Ausgabe beobachten**. Das Programm verbindet sich mit dem SIGNALDuino, initialisiert die Protokolle und beginnt mit dem Empfang. Dekodierte Nachrichten werden im Log ausgegeben und – sofern MQTT konfiguriert ist – an den Broker gesendet. - -## Konfiguration - -### Umgebungsvariablen - -| Variable | Beschreibung | Beispiel | -|----------|--------------|----------| -| `SIGNALDUINO_SERIAL_PORT` | Serieller Port (z.B. `/dev/ttyUSB0`) | `/dev/ttyACM0` | -| `SIGNALDUINO_BAUD` | Baudrate (Standard: `57600`) | `115200` | -| `SIGNALDUINO_TCP_HOST` | TCP‑Host (alternativ zu Serial) | `192.168.1.10` | -| `SIGNALDUINO_TCP_PORT` | TCP‑Port (Standard: `23`) | `23` | -| `MQTT_HOST` | MQTT‑Broker‑Host | `mqtt.eclipseprojects.io` | -| `MQTT_PORT` | MQTT‑Broker‑Port (Standard: `1883`) | `1883` | -| `MQTT_USERNAME` | Benutzername für MQTT‑Authentifizierung | `user` | -| `MQTT_PASSWORD` | Passwort für MQTT‑Authentifizierung | `pass` | -| `MQTT_TOPIC` | Basis‑Topic für Publikation/Subscription | `signalduino/` | -| `LOG_LEVEL` | Logging‑Level (DEBUG, INFO, WARNING, ERROR, CRITICAL) | `DEBUG` | - -### Kommandozeilenargumente - -Alle Umgebungsvariablen können auch als Argumente übergeben werden (sie haben Vorrang). Eine vollständige Liste erhält man mit: - -```bash -python3 main.py --help -``` - -Wichtige Optionen: -* `--serial PORT` – Serieller Port -* `--tcp HOST` – TCP‑Host -* `--mqtt-host HOST` – MQTT‑Broker -* `--mqtt-topic TOPIC` – Basis‑Topic -* `--timeout SECONDS` – Automatisches Beenden nach N Sekunden -* `--log-level LEVEL` – Logging‑Level - -## MQTT‑Integration - -### Publizierte Topics - -* `{basis_topic}/decoded` – JSON‑Nachricht jedes dekodierten Signals. -* `{basis_topic}/raw` – Rohdaten (falls aktiviert). -* `{basis_topic}/status` – Statusmeldungen (Verbunden/Getrennt/Fehler). - -### Abonnierte Topics (Befehle) - -* `{basis_topic}/cmd/version` – Liefert die Firmware‑Version des SIGNALDuino. -* `{basis_topic}/cmd/set` – Sendet einen `set`‑Befehl an den SIGNALDuino. -* `{basis_topic}/cmd/mqtt` – Steuert die MQTT‑Integration (z.B. Kompression an/aus). - -Die genauen Payload‑Formate und weitere Befehle sind in der [Befehlsreferenz](docs/03_protocol_reference/commands.adoc) dokumentiert. - -## Projektstruktur - -``` -PySignalduino/ -├── signalduino/ # Hauptpaket -│ ├── controller.py # Asynchroner Controller -│ ├── mqtt.py # MQTT‑Publisher/Subscriber -│ ├── transport.py # Serielle/TCP‑Transporte (asyncio) -│ ├── commands.py # Befehlsimplementierung -│ └── ... -├── sd_protocols/ # Protokollbibliothek (SDProtocols) -├── tests/ # Umfangreiche Testsuite -├── docs/ # Dokumentation (AsciiDoc) -├── main.py # Ausführbares Hauptprogramm -├── pyproject.toml # Paketkonfiguration -└── requirements*.txt # Abhängigkeiten -``` - -## Entwicklung - -### Tests ausführen - -```bash -pytest -``` - -Für Tests mit Coverage‑Bericht: - -```bash -pytest --cov=signalduino --cov=sd_protocols -``` - -### Beitragen - -Beiträge sind willkommen! Bitte erstelle einen Pull‑Request oder öffne ein Issue im Repository. - -## Dokumentation - -* [Installationsanleitung](docs/01_user_guide/installation.adoc) -* [Benutzerhandbuch](docs/01_user_guide/usage.adoc) -* [Asyncio‑Migrationsleitfaden](docs/ASYNCIO_MIGRATION.md) -* [Protokollreferenz](docs/03_protocol_reference/protocol_details.adoc) -* [Befehlsreferenz](docs/01_user_guide/usage.adoc#_command_interface) - -## SEO & Sitemap - -Die Dokumentation wird automatisch mit einer dynamischen Sitemap (`sitemap.xml`) und branch‑spezifischen `robots.txt`‑Dateien versehen, um die Auffindbarkeit in Suchmaschinen zu verbessern. - -* **Sitemap‑Generierung:** Das Skript `tools/generate_sitemap.py` scannt den Build‑Output, weist Prioritäten und Update‑Frequenzen zu und generiert eine valide XML‑Sitemap gemäß sitemaps.org. -* **Branch‑spezifische URLs:** Für die Branches `main`, `preview` und `develop` werden unterschiedliche Base‑URLs verwendet. -* **Integration in CI/CD:** Der GitHub Actions Workflow `.github/workflows/docs.yml` generiert die Sitemap automatisch nach jedem Build und passt die `robots.txt` entsprechend an. - -Weitere Details zur Architektur finden Sie im [Architektur‑Dokument](docs/02_developer_guide/architecture.adoc#dokumentations-infrastruktur-sitemap--seo). - -## Lizenz - -Dieses Projekt steht unter der MIT‑Lizenz – siehe [LICENSE](LICENSE) für Details. - -## Danksagung - -Basierend auf der originalen FHEM‑SIGNALDuino‑Implementierung von [@Sidey79](https://github.com/Sidey79) und der Community. \ No newline at end of file diff --git a/docs/01_user_guide/index.adoc b/docs/01_user_guide/index.adoc index 0fd85d1..9768d3a 100644 --- a/docs/01_user_guide/index.adoc +++ b/docs/01_user_guide/index.adoc @@ -1,4 +1,5 @@ = Benutzer-Leitfaden +:sectlinks: :leveloffset: 1 Der Benutzer-Leitfaden enthält Anweisungen zur Installation, Konfiguration und grundlegenden Verwendung von PySignalduino. @@ -42,7 +43,37 @@ PySignalduino folgt einer modular aufgebauten Architektur: [source,plantuml] ---- -include::../../docs/diagrams/architecture.puml[] +@startuml +skinparam componentStyle uml2 + +package "PySignalduino Core" { + [Controller] as ctrl + [Command API] as cmd + [Parser Layer] as parser + [Transport Layer] as transport + [Protocol Lib] as proto + [MQTT Client] as mqtt +} + +node "Hardware" { + [SIGNALDuino] as hw +} + +cloud "Infrastructure" { + [MQTT Broker] as broker +} + +ctrl -down-> transport : manages +ctrl -right-> parser : feeds raw data +parser -right-> proto : uses for decoding +ctrl -up-> mqtt : publishes events +ctrl -left-> cmd : uses +cmd -down-> transport : sends commands + +transport <--> hw : Serial / TCP +mqtt <--> broker : WiFi / LAN + +@enduml ---- Die Hauptkomponenten sind: @@ -78,6 +109,6 @@ Für einen schnellen Einstieg folgen Sie diesen Schritten: Ausführliche Anleitungen finden Sie in den folgenden Kapiteln. -include::installation.adoc[] -include::usage.adoc[] -include::mqtt_api.adoc[] \ No newline at end of file +include::installation.adoc[leveloffset=+1] +include::usage.adoc[leveloffset=+1] +include::mqtt_api.adoc[leveloffset=+1] diff --git a/docs/01_user_guide/installation.adoc b/docs/01_user_guide/installation.adoc index 755b024..daef3d1 100644 --- a/docs/01_user_guide/installation.adoc +++ b/docs/01_user_guide/installation.adoc @@ -1,4 +1,5 @@ == Installation +:sectlinks: [NOTE] ==== @@ -86,6 +87,6 @@ Details finden Sie in der [DevContainer-Dokumentation](devcontainer_env.md). Nach der Installation können Sie: -1. Die [Schnellstart-Anleitung](../index.adoc#_schnellstart) befolgen. -2. Die [Konfiguration über Umgebungsvariablen](../usage.adoc#_konfiguration) einrichten. -3. Die [MQTT-Integration](../usage.adoc#_mqtt_integration) testen. \ No newline at end of file +* Die xref:#_schnellstart[Schnellstart-Anleitung] befolgen. +* Die xref:#_konfiguration[Konfiguration über Umgebungsvariablen] einrichten. +* Die xref:#_mqtt_integration[MQTT-Integration] testen. diff --git a/docs/01_user_guide/mqtt_api.adoc b/docs/01_user_guide/mqtt_api.adoc index 616e2cf..706b502 100644 --- a/docs/01_user_guide/mqtt_api.adoc +++ b/docs/01_user_guide/mqtt_api.adoc @@ -1,13 +1,16 @@ -= MQTT API Reference -:doctype: book :icons: font :toc: left :toclevels: 2 +:doctype: book +:icons: font +:toc: left +:toclevels: 2 :sectnums: +:sectlinks: [[_mqtt_introduction]] -== Einführung += Einführung Die MQTT-Schnittstelle ermöglicht die Steuerung des PySignalduino-Gateways und den Empfang von dekodierten Nachrichten. Alle Befehle und Antworten verwenden das JSON-Format. -=== Topics und Struktur +== Topics und Struktur Der **Standard-Topic** für alle MQTT-Operationen ist `signalduino/v1`. Dieser Wert kann über die Umgebungsvariable `MQTT_TOPIC` oder den CLI-Parameter `--mqtt-topic` angepasst werden. Wenn nur der Basis-Topic (z.B. `foo`) gesetzt wird, ist der finale Topic immer versionsspezifisch: `foo/v1`. @@ -316,21 +319,10 @@ Eine vollständige Beispielkonfiguration finden Sie in der Datei `.devcontainer/ [source,fhem] ---- # 1. Verbindung zum Broker herstellen (falls noch nicht vorhanden) -define mqtt_broker MQTT2_CLIENT mqtt:1883 -attr mqtt_broker autocreate simple +include::../../.devcontainer/fhem-data/fhem_signalduino_example.cfg[tags=broker_config] # 2. PySignalduino Device definieren -define PySignalDuino MQTT2_DEVICE -attr PySignalDuino IODev mqtt_broker - -# 3. Readings für empfangene Nachrichten extrahieren -# Wandelt JSON-Payload automatisch in Readings um -attr PySignalDuino readingList signalduino/v1/state/messages:.* { json2nameValue($EVENT, '', $JSONMAP) } - -# 4. Senden von Befehlen ermöglichen -attr PySignalDuino setList raw:textField signalduino/v1/commands/set/raw $EVTPART1 \ -cc1101_reg:textField signalduino/v1/commands/set/cc1101_reg $EVTPART1 \ -version:noArg signalduino/v1/commands/get/system/version +include::../../.devcontainer/fhem-data/fhem_signalduino_example.cfg[tags=device_config] ---- === Wichtige Hinweise diff --git a/docs/01_user_guide/usage.adoc b/docs/01_user_guide/usage.adoc index 7ac8ae1..bb3238b 100644 --- a/docs/01_user_guide/usage.adoc +++ b/docs/01_user_guide/usage.adoc @@ -1,6 +1,5 @@ -= Verwendung und Konfiguration - -== Grundlegende Nutzung +=== Grundlegende Nutzung +:sectlinks: Die Hauptklasse `SDProtocols` stellt die Schnittstelle zur Protokollverarbeitung bereit. @@ -9,11 +8,11 @@ Die Hauptklasse `SDProtocols` stellt die Schnittstelle zur Protokollverarbeitung include::../../sd_protocols/sd_protocols.py[lines=25..47] ---- -== Integration +=== Integration PySignalduino ist als Bibliothek konzipiert, die beispielsweise in MQTT-Bridges oder Home-Automation-Skripten verwendet werden kann. Sie übernimmt die Erkennung und Dekodierung der Rohdaten. -=== Logging +==== Logging Für Debugging-Zwecke können Sie eine eigene Callback-Funktion registrieren: @@ -22,11 +21,11 @@ Für Debugging-Zwecke können Sie eine eigene Callback-Funktion registrieren: include::../../sd_protocols/sd_protocols.py[lines=162..170] ---- -=== MQTT Integration +==== MQTT Integration PySignalduino bietet eine integrierte MQTT-Integration über die Klasse `MqttPublisher`. Diese ermöglicht das Veröffentlichen dekodierter Nachrichten an einen MQTT-Broker und das Empfangen von Befehlen über MQTT-Topics. -==== Einrichtung und Konfiguration +===== Einrichtung und Konfiguration Die MQTT-Verbindung wird automatisch initialisiert, wenn die Umgebungsvariable `MQTT_HOST` gesetzt ist. Folgende Umgebungsvariablen können konfiguriert werden: @@ -44,7 +43,7 @@ Der `MqttPublisher` wird innerhalb des `SignalduinoController` verwendet und ste include::../../main.py[lines=55..84] ---- -==== MQTT-Topics +===== MQTT-Topics * `{topic}/messages` – JSON‑kodierte dekodierte Nachrichten (DecodedMessage) * `{topic}/commands/#` – Topic für eingehende Befehle (Wildcard-Subscription) @@ -52,23 +51,23 @@ include::../../main.py[lines=55..84] * `{topic}/errors` – Fehlerantworten. * `{topic}/status` – Heartbeat‑ und Statusmeldungen (optional) -==== Heartbeat-Funktionalität +===== Heartbeat-Funktionalität Der Publisher sendet regelmäßig einen Heartbeat („online“) unter `{topic}/status`, solange die Verbindung besteht. Bei Verbindungsabbruch wird „offline“ gepublished. -==== Beispiel: Manuelle Nutzung des MqttPublisher +===== Beispiel: Manuelle Nutzung des MqttPublisher [source,python] ---- include::../../tests/test_mqtt.py[lines=112..116] ---- -=== Command Interface +==== Command Interface PySignalduino stellt eine umfangreiche Befehls-API zur Steuerung des SIGNALDuino-Firmware-Geräts bereit. Die Klasse `SignalduinoCommands` kapselt alle verfügbaren seriellen Befehle und bietet eine asynchrone Schnittstelle. -==== Verfügbare Befehle +===== Verfügbare Befehle Die folgenden Befehle werden unterstützt (Auswahl): @@ -105,13 +104,13 @@ Die folgenden Befehle werden unterstützt (Auswahl): * `send_xfsk(params)` – xFSK senden (SN...) * `send_message(message)` – Vorkodierte Nachricht senden -==== Persistenz-Funktionalität +===== Persistenz-Funktionalität Befehle, die die Hardware-Konfiguration ändern (z. B. `write_register`, `set_patable`), werden in der Regel im EEPROM des SIGNALDuino persistent gespeichert. Die Persistenz wird durch die Firmware gewährleistet; PySignalduino sendet lediglich die entsprechenden Kommandos. -==== Nutzung über MQTT +===== Nutzung über MQTT Wenn MQTT aktiviert ist, können Befehle über das Topic `{base_topic}/commands/{command}` gesendet werden. Die Basis für Antworten ist `{base_topic}/responses` (Erfolg) oder `{base_topic}/errors` (Fehler). Das `base_topic` ist standardmäßig `signalduino/v1`. @@ -186,27 +185,25 @@ mosquitto_pub -h localhost -t "signalduino/v1/commands/get/system/version" -m '{ # Antwort empfängst du auf signalduino/v1/responses ---- -==== Code-Beispiel: Direkte Nutzung der Command-API - -==== Code-Beispiel: Direkte Nutzung der Command-API +===== Code-Beispiel: Direkte Nutzung der Command-API [source,python] ---- include::../../tests/test_controller.py[lines=120..130] ---- -==== Beispiel: Asynchrone Context-Manager Nutzung +===== Beispiel: Asynchrone Context-Manager Nutzung [source,python] ---- include::../../main.py[lines=55..84] ---- -== API-Referenz (Auszug) +=== API-Referenz (Auszug) Die folgenden Klassen und Schnittstellen sind für die Integration besonders relevant: -=== MqttPublisher +==== MqttPublisher Die Klasse `signalduino.mqtt.MqttPublisher` bietet eine asynchrone Context-Manager-Schnittstelle zur Kommunikation mit einem MQTT-Broker. @@ -218,7 +215,7 @@ Die Klasse `signalduino.mqtt.MqttPublisher` bietet eine asynchrone Context-Manag * **Context-Manager:** `async with MqttPublisher() as publisher:` -=== SignalduinoCommands +==== SignalduinoCommands Die Klasse `signalduino.commands.SignalduinoCommands` kapselt alle seriellen Befehle für die SIGNALDuino-Firmware. @@ -226,7 +223,7 @@ Die Klasse `signalduino.commands.SignalduinoCommands` kapselt alle seriellen Bef * **Alle Methoden sind asynchron** (`async def`) und geben entweder `str` (Antwort) zurück oder `None` (keine Antwort erwartet) * **Umfang:** Systembefehle, Konfiguration, Senden von Nachrichten (siehe Abschnitt „Command Interface“) -=== Asynchrone Context-Manager-Schnittstelle +==== Asynchrone Context-Manager-Schnittstelle Sowohl `SignalduinoController` als auch `MqttPublisher` und die Transportklassen (`TcpTransport`, `SerialTransport`) implementieren das asynchrone Context-Manager-Protokoll (`__aenter__`/`__aexit__`). Dies gewährleistet eine sichere Ressourcenverwaltung (Verbindungsauf‑/abbau, Hintergrundtasks). @@ -237,7 +234,7 @@ Beispiel für verschachtelte Context-Manager: include::../../main.py[lines=55..84] ---- -=== Weitere Klassen +==== Weitere Klassen * `SignalduinoController` – Zentrale Steuerungsklasse, koordiniert Transport, Parser, MQTT und Befehle * `TcpTransport`, `SerialTransport` – Asynchrone Transportimplementierungen für TCP bzw. serielle Verbindungen @@ -245,29 +242,29 @@ include::../../main.py[lines=55..84] Eine vollständige API-Dokumentation kann mit `pydoc` oder mittels Sphinx generiert werden. -== Troubleshooting +=== Troubleshooting Dieser Abschnitt beschreibt häufige Probleme und deren Lösungen. -=== MQTT-Verbindungsprobleme +==== MQTT-Verbindungsprobleme * **Keine Verbindung zum Broker:** Stellen Sie sicher, dass die Umgebungsvariablen `MQTT_HOST` und `MQTT_PORT` korrekt gesetzt sind. Der Broker muss erreichbar sein und keine Authentifizierung erfordern (oder Benutzername/Passwort müssen gesetzt sein). * **Verbindung bricht ab:** Überprüfen Sie die Netzwerkverbindung und Broker-Konfiguration. Der MQTT-Client (`aiomqtt`) versucht automatisch, die Verbindung wiederherzustellen. Falls die Verbindung dauerhaft abbricht, prüfen Sie Firewall-Einstellungen und Broker-Logs. * **MQTT-Nachrichten werden nicht empfangen:** Stellen Sie sicher, dass das Topic `{topic}/commands/#` abonniert ist. Der Command-Listener startet automatisch, wenn MQTT aktiviert ist. Überprüfen Sie die Log-Ausgabe auf Fehler. -=== Asyncio-spezifische Probleme +==== Asyncio-spezifische Probleme * **`RuntimeError: no running event loop`:** Tritt auf, wenn asyncio-Funktionen außerhalb eines laufenden Event-Loops aufgerufen werden. Stellen Sie sicher, dass Ihr Code innerhalb einer asyncio-Coroutine läuft und `asyncio.run()` verwendet wird. Verwenden Sie `async with` für Context-Manager. * **Tasks hängen oder werden nicht abgebrochen:** Alle Hintergrundtasks sollten auf das `_stop_event` reagieren. Bei manuell erstellten Tasks müssen Sie `asyncio.CancelledError` abfangen und Ressourcen freigeben. * **Deadlocks in Queues:** Wenn eine Queue voll ist und kein Consumer mehr liest, kann `await queue.put()` blockieren. Stellen Sie sicher, dass die Consumer-Tasks laufen und die Queue nicht überfüllt wird. Verwenden Sie `asyncio.wait_for` mit Timeout. -=== Verbindungsprobleme zum SIGNALDuino-Gerät +==== Verbindungsprobleme zum SIGNALDuino-Gerät * **Keine Antwort auf Befehle:** Überprüfen Sie die serielle oder TCP-Verbindung. Stellen Sie sicher, dass das Gerät eingeschaltet ist und die korrekte Baudrate (115200) verwendet wird. Testen Sie mit einem Terminal-Programm, ob das Gerät auf `V` (Version) antwortet. * **Timeout-Errors:** Die Standard-Timeout für Befehle beträgt 2 Sekunden. Bei langsamen Verbindungen kann dies erhöht werden. Falls Timeouts trotzdem auftreten, könnte die Verbindung instabil sein. * **Parser erkennt keine Protokolle:** Überprüfen Sie, ob die Rohdaten im erwarteten Format ankommen (z.B. `+MU;...`). Stellen Sie sicher, dass die Protokolldefinitionen (`protocols.json`) geladen werden und das Protokoll aktiviert ist. -=== Logging und Debugging +==== Logging und Debugging Aktivieren Sie Debug-Logging, um detaillierte Informationen zu erhalten: @@ -278,7 +275,7 @@ include::../../main.py[lines=21..30] Die Log-Ausgabe zeigt den Status von Transport, Parser und MQTT. -=== Bekannte Probleme und Workarounds +==== Bekannte Probleme und Workarounds * **Entwicklungsstatus**: Da PySignalduino noch in aktiver Entwicklung ist, können sich Verhalten und API zwischen Commits ändern. Bei unerwartetem Verhalten prüfen Sie bitte die aktuelle Codebasis und melden Sie Issues auf GitHub. @@ -286,4 +283,4 @@ Die Log-Ausgabe zeigt den Status von Transport, Parser und MQTT. * **Windows und asyncio:** Unter Windows kann es bei seriellen Verbindungen zu Problemen mit asyncio kommen. Verwenden Sie `asyncio.ProactorEventLoop` oder weichen Sie auf TCP-Transport aus. * **Memory Leaks:** Bei langem Betrieb können asyncio-Tasks Speicher verbrauchen. Stellen Sie sicher, dass abgeschlossene Tasks garbage-collected werden. Verwenden Sie `asyncio.create_task` mit Referenzen, um Tasks später abbrechen zu können. -Bei weiteren Problemen öffnen Sie bitte ein Issue auf GitHub mit den relevanten Logs und Konfigurationsdetails. \ No newline at end of file +Bei weiteren Problemen öffnen Sie bitte ein Issue auf GitHub mit den relevanten Logs und Konfigurationsdetails. diff --git a/docs/02_developer_guide/architecture.adoc b/docs/02_developer_guide/architecture.adoc index 9e5c1d2..9bbeeec 100644 --- a/docs/02_developer_guide/architecture.adoc +++ b/docs/02_developer_guide/architecture.adoc @@ -1,6 +1,5 @@ -= Architektur - == Übersicht +:sectlinks: 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. @@ -95,8 +94,14 @@ Alle Requests (Commands) und Responses (Responses/Errors) verwenden eine standar ---- === Wichtige Architekturentscheidungen -* link:../architecture/decisions/adr-001-mqtt-topic-structure.md[ADR-001: MQTT Topic Struktur und Versionierung] -* link:../architecture/decisions/adr-002-command-dispatcher.md[ADR-002: Command Dispatcher Pattern und JSON-Schema-Validierung] +Die vollständigen Architecture Decision Records (ADR) sind hier aufgelistet und werden im Dokument eingebettet: + +// ADRs 001 bis 005 werden direkt eingebunden +include::../architecture/decisions/ADR-001-mqtt-get-frequency.adoc[leveloffset=+3] +include::../architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc[leveloffset=+3] +include::../architecture/decisions/ADR-003-cc1101-parameter-set-logic.adoc[leveloffset=+3] +include::../architecture/decisions/ADR-004-mqtt-response-parsing.adoc[leveloffset=+3] +include::../architecture/decisions/ADR-005-mqtt-cc1101-response-consistency.adoc[leveloffset=+3] == Komponentendiagramm (Übersicht) diff --git a/docs/02_developer_guide/contribution.adoc b/docs/02_developer_guide/contribution.adoc index fbcb73e..861ee8d 100644 --- a/docs/02_developer_guide/contribution.adoc +++ b/docs/02_developer_guide/contribution.adoc @@ -1,4 +1,3 @@ -= Beitrag leisten (Contributing) [NOTE] ==== @@ -9,6 +8,7 @@ Bitte synchronisieren Sie Ihren Fork regelmäßig mit dem upstream-Repository. Beiträge zum Projekt sind willkommen! == Workflow +:sectlinks: . **Fork & Clone:** Projekt forken und lokal klonen. . **Branch:** Feature-Branch erstellen (`git checkout -b feature/mein-feature`). diff --git a/docs/02_developer_guide/index.adoc b/docs/02_developer_guide/index.adoc index bff708f..07a0ffc 100644 --- a/docs/02_developer_guide/index.adoc +++ b/docs/02_developer_guide/index.adoc @@ -1,4 +1,5 @@ = Entwickler-Leitfaden +:sectlinks: Dieser Abschnitt beschreibt die Architektur, wie man zur Entwicklung beitragen kann (Contributing) und wie man Tests durchführt. diff --git a/docs/03_protocol_reference/index.adoc b/docs/03_protocol_reference/index.adoc index 487328c..e6f8a86 100644 --- a/docs/03_protocol_reference/index.adoc +++ b/docs/03_protocol_reference/index.adoc @@ -1,4 +1,5 @@ = Protokoll-Referenz +:sectlinks: Eine detaillierte Referenz aller unterstützten Protokolle, deren Implementierungsdetails und bekannten Einschränkungen. diff --git a/docs/03_protocol_reference/protocol_details.adoc b/docs/03_protocol_reference/protocol_details.adoc index 66d0fef..e111ef7 100644 --- a/docs/03_protocol_reference/protocol_details.adoc +++ b/docs/03_protocol_reference/protocol_details.adoc @@ -1,8 +1,8 @@ -= Protokolldetails PySignalduino unterstützt eine Vielzahl von Funkprotokollen im 433 MHz und 868 MHz Bereich. == Protokolldefinition +:sectlinks: Die Datei `sd_protocols/protocols.json` ist die definitive Quelle für alle Protokollparameter (Timings, Preambles, Methoden). diff --git a/docs/architecture/decisions/ADR-001-mqtt-get-frequency.adoc b/docs/architecture/decisions/ADR-001-mqtt-get-frequency.adoc index f65c66e..d81e5ba 100644 --- a/docs/architecture/decisions/ADR-001-mqtt-get-frequency.adoc +++ b/docs/architecture/decisions/ADR-001-mqtt-get-frequency.adoc @@ -6,7 +6,7 @@ Angenommen. -[[context]] +[#adr-context] == Kontext Das PySignalduino-Projekt benötigt eine Methode, um die aktuell im CC1101-Transceiver eingestellte Funkfrequenz über das MQTT-Interface abzufragen, hauptsächlich für Diagnose- und Statuszwecke. @@ -19,7 +19,7 @@ $$f_{RF} = \frac{F_{XOSC}}{2^{16}} \times F_{REG}$$ Bei $F_{XOSC} = 26 \, \text{MHz}$ ergibt sich: $$f_{RF} = \frac{26}{65536} \times F_{REG} \, \text{MHz}$$ -[[decision]] +[#adr-decision] == Entscheidung Wir implementieren den `get/frequency` Befehl als Teil der `MqttHandler`- und `Commands`-Klassen. @@ -31,7 +31,7 @@ Wir implementieren den `get/frequency` Befehl als Teil der `MqttHandler`- und `C * Wir implementieren die Berechnung in `signalduino/commands.py` (z.B. `get_frequency()`), um die Abhängigkeit der Hardware vom Command Layer zu kapseln. * Das Ergebnis wird auf 4 Dezimalstellen gerundet (in MHz), um eine hohe Genauigkeit bei der Anzeige zu gewährleisten. -[[consequences]] +[#adr-consequences] == Konsequenzen * *Positiv*: Benutzer können die eingestellte Frequenz einfach über MQTT abfragen, was die Diagnose erleichtert. @@ -39,7 +39,7 @@ Wir implementieren den `get/frequency` Befehl als Teil der `MqttHandler`- und `C * *Negativ*: Es müssen neue Methoden in `signalduino/hardware.py`, `signalduino/commands.py` und `signalduino/mqtt.py` implementiert werden. * *Negativ*: Die Hardware-Klasse muss um die Logik zum Lesen der drei Register erweitert werden, möglicherweise durch eine neue Abstraktionsebene, falls dies in Zukunft für andere Dreifachregister notwendig wird. -[[alternatives]] +[#adr-alternatives] == Alternativen * *Alternative 1: Berechnung auf der Hardware-Ebene:* diff --git a/docs/architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc b/docs/architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc index 7f10576..b1185d4 100644 --- a/docs/architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc +++ b/docs/architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc @@ -1,7 +1,7 @@ = ADR 002: Verwendung des MqttCommandDispatcher für die MQTT-Befehlsbehandlung :doctype: article :encoding: utf-8 :lang: de :status: Accepted :decided-at: 2026-01-04 :decided-by: Roo (Architekt) -[[kontext]] +[#adr-context] == Kontext Die MQTT-Befehlsbehandlung in `signalduino/mqtt.py` erfolgt derzeit über eine hartcodierte `if/elif`-Kette in der Methode `_handle_command` (Zeile 108). Diese Struktur ist schwer wartbar und skaliert schlecht, sobald neue Befehle hinzugefügt werden müssen, wie es für Factory Reset und das Abrufen von Hardware-Einstellungen erforderlich ist. @@ -10,7 +10,7 @@ Der Code enthält bereits einen generischen, schema-validierenden Befehls-Dispat Die zentrale Verwaltung der Befehle und deren Validierung ist eine bewährte Methode, um die Robustheit und Erweiterbarkeit der Schnittstelle zu gewährleisten. -[[entscheidung]] +[#adr-decision] == Entscheidung Die hartcodierte `if/elif`-Logik in `signalduino/mqtt.py` wird durch die Verwendung des `MqttCommandDispatcher` ersetzt. @@ -19,7 +19,7 @@ Die hartcodierte `if/elif`-Logik in `signalduino/mqtt.py` wird durch die Verwend . Die Methode `_handle_command` in `MqttPublisher` wird umgeschrieben, um den eingehenden Befehlspfad und Payload direkt an `MqttCommandDispatcher.dispatch()` zu übergeben. . Die Fehler- und Erfolgsantworten werden vom `MqttCommandDispatcher` zurückgegeben und von `MqttPublisher` an die entsprechenden MQTT-Topics (`/responses` und `/errors`) publiziert. -[[konsequenzen]] +[#adr-consequences] == Konsequenzen === Positive Konsequenzen @@ -32,7 +32,7 @@ Die hartcodierte `if/elif`-Logik in `signalduino/mqtt.py` wird durch die Verwend * **Refactoring-Aufwand:** Die bestehende Logik in `signalduino/mqtt.py` muss entfernt und durch den Dispatcher-Aufruf ersetzt werden. * **Kopplung an Controller:** Der Dispatcher ist direkt an den `SignalduinoController` gekoppelt (was bereits der Fall war und akzeptiert wird). -[[alternativen]] +[#adr-alternatives] == Alternativen * **Beibehaltung der if/elif-Kette:** Dies wurde abgelehnt, da es gegen die Prinzipien der Wartbarkeit und der Single Responsibility Principle (SRP) verstößt. * **Anderer Dispatch-Mechanismus:** Die Verwendung des vorhandenen `MqttCommandDispatcher` ist die pragmatischste Lösung, da die Klasse bereits existiert und die Validierungsinfrastruktur bietet. \ No newline at end of file diff --git a/docs/architecture/decisions/ADR-003-cc1101-parameter-set-logic.adoc b/docs/architecture/decisions/ADR-003-cc1101-parameter-set-logic.adoc index beb0a99..54b2892 100644 --- a/docs/architecture/decisions/ADR-003-cc1101-parameter-set-logic.adoc +++ b/docs/architecture/decisions/ADR-003-cc1101-parameter-set-logic.adoc @@ -1,7 +1,8 @@ = 003. Verwendung von CC1101-Standardformeln für Frequenz und Datenrate :revdate: 2026-01-04 :status: Accepted -== Context +[#adr-context] +== Kontext Die Implementierung der MQTT SET-Befehle für CC1101-Parameter (Frequenz, Datenrate) erfordert die Umrechnung von physikalischen Werten (MHz, kBaud) in die spezifischen Registerwerte des CC1101-Chips. Andere Parameter wie Bandbreite, Sensitivity und Rampl verwendeten früher spezielle, abstraktere Kommandos des Signalduino-Firmware-Protokolls (`C101`, `X4C`, `X5C`). @@ -12,7 +13,8 @@ Index in Registerwert) in Python implementiert wurde. Für Frequenz und Datenrate existiert keine solche Abstraktion im Signalduino-Protokoll, oder die vorhandene Logik ist unvollständig/unzureichend für eine präzise Steuerung. -== Decision +[#adr-decision] +== Entscheidung Die Umrechnung von Frequenz (MHz) in die drei Registerwerte (FREQ2, FREQ1, FREQ0) und die Umrechnung der Datenrate (kBaud) in MDMCFG4/MDMCFG3-Registerwerte erfolgt *direkt* in der Python-Implementierung von `SignalduinoCommands` unter Verwendung der im CC1101-Datenblatt definierten Standardformeln (z.B. Freq = f_xosc * FREQ / 2^16). @@ -20,12 +22,14 @@ Diese Registerwerte werden dann über generische CC1101-Schreibbefehle des Signa Nach dem Senden aller Register-SET-Befehle muss die Methode `SignalduinoCommands.cc1101_write_init()` aufgerufen werden, um die CC1101-Konfiguration erneut in das Chip-Register zu schreiben und die Änderungen zu aktivieren. -== Consequences +[#adr-consequences] +== Konsequenzen * **Positiv:** Gewährleistet maximale Präzision bei der Einstellung von Frequenz und Datenrate, da die direkte CC1101-Berechnung verwendet wird. Die Logik ist in Python gekapselt und leicht testbar (Unit Tests). * **Negativ:** Erhöht die Komplexität der `SignalduinoCommands`-Klasse, da sie nun die CC1101-Register-Berechnungslogik enthalten muss. * **Neutral:** Alle CC1101-Set-Befehle, die Register schreiben (einschließlich Rampl und Sensitivity), verwenden nun die generischen `W`-Befehle. Dies konsolidiert die Logik in Python (phys. Wert -> Registerwert) und vereinfacht die Implementierung auf Kosten des Verzichts auf spezifische Firmware-Spezialbefehle (`X4C`, `X5C`). Der `C101`-Befehl für die Bandbreite wird vorerst beibehalten, da er eine Abstraktion der Firmware darstellt. -== Alternatives Considered +[#adr-alternatives] +== Alternativen * **Alternative 1: Nur Signalduino-Spezialbefehle verwenden:** * _Ablehnungsgrund:_ Für Frequenz und Datenrate gibt es keine oder keine ausreichend präzisen/dokumentierten Signalduino-Spezialbefehle, die eine Einstellung über MQTT in physikalischen Einheiten (MHz, kBaud) ermöglichen. * **Alternative 2: Berechnung in die Controller-Klasse verschieben:** diff --git a/docs/architecture/decisions/ADR-004-mqtt-response-parsing.adoc b/docs/architecture/decisions/ADR-004-mqtt-response-parsing.adoc index 0ab57f0..3cb23b3 100644 --- a/docs/architecture/decisions/ADR-004-mqtt-response-parsing.adoc +++ b/docs/architecture/decisions/ADR-004-mqtt-response-parsing.adoc @@ -2,7 +2,8 @@ :revdate: 2026-01-06 :author: Roo -== 1. Kontext +[#adr-context] +== Kontext Die MQTT-Befehle `get/cc1101/*` (z.B. `get/cc1101/config`) und `get/config/decoder` schlagen mit Timeouts fehl, obwohl die serielle Kommunikation mit der SIGNALDuino-Firmware die Antworten empfängt. Die Ursache liegt darin, dass der `MqttCommandDispatcher` eine strukturierte JSON-Payload (ein Python-Dictionary) als `data`-Feld in der MQTT-Antwort erwartet. Die zugrundeliegenden `SignalduinoCommands` Methoden geben jedoch in diesen Fällen den *rohen* String der seriellen Firmware-Antwort zurück. @@ -14,7 +15,8 @@ Betroffene Befehle und ihre Rohantwortformate: Zusätzlich müssen alle `get` Befehle, die einen rohen String zurückgeben, angepasst werden, um die Konsistenz des MQTT-API zu gewährleisten. -== 2. Entscheidung +[#adr-decision] +== Entscheidung Wir werden die `SignalduinoCommands` Methoden, die serielle GET-Befehle ausführen, so modifizieren, dass sie die rohe Firmware-Antwort parsen und ein konsistentes Python-Dictionary (`Dict[str, Any]`) zurückgeben. Dieses Dictionary wird dann vom `MqttCommandDispatcher` als JSON-Payload im `data`-Feld der MQTT-Antwort verwendet. @@ -35,7 +37,8 @@ Dies stellt sicher, dass alle erfolgreichen `GET` Anfragen über MQTT eine struk 3. **Weitere einfache GET-Befehle:** * Methoden wie `get_version`, `get_free_ram`, `get_uptime` geben bereits einen geparsten Wert zurück (String oder Int), der korrekt gekapselt wird. Diese Methoden bleiben unverändert, da sie bereits einen strukturierten Wert zurückgeben, der indirekt im `data`-Feld des MQTT-Payloads landet. -== 3. Konsequenzen +[#adr-consequences] +== Konsequenzen === Positive * **Behebung der Timeouts:** Die MQTT GET-Befehle für Konfigurationen werden korrekt beantwortet und die Timeouts behoben. @@ -46,7 +49,8 @@ Dies stellt sicher, dass alle erfolgreichen `GET` Anfragen über MQTT eine struk * **Refactoring:** Es müssen kleinere Refactorings in [`signalduino/commands.py`](signalduino/commands.py) durchgeführt werden, um die Rückgabetypen der Methoden anzupassen. * **Tests/Dokumentation:** Die zugehörigen Unittests in [`tests/test_mqtt_commands.py`](tests/test_mqtt_commands.py) und die MQTT API Dokumentation in [`docs/01_user_guide/mqtt_api.adoc`](docs/01_user_guide/mqtt_api.adoc) müssen aktualisiert werden. -== 4. Alternativen +[#adr-alternatives] +== Alternativen 1. **Alternative 1: Parsing im `MqttCommandDispatcher`:** Die Rohergebnisse als `str` beibehalten und das Parsen spezifischer Befehlsantworten direkt im `MqttCommandDispatcher` durchführen. * *Nachteil:* Vermischt die Zuständigkeiten. Der Dispatcher sollte nur das Routing und die Validierung übernehmen, während die `SignalduinoCommands` die Logik für die Kommunikation und das Parsen der Firmware-spezifischen Antworten enthalten sollten. diff --git a/docs/architecture/decisions/ADR-005-mqtt-cc1101-response-consistency.adoc b/docs/architecture/decisions/ADR-005-mqtt-cc1101-response-consistency.adoc index d1f7aa9..fa11d50 100644 --- a/docs/architecture/decisions/ADR-005-mqtt-cc1101-response-consistency.adoc +++ b/docs/architecture/decisions/ADR-005-mqtt-cc1101-response-consistency.adoc @@ -2,7 +2,7 @@ :doctype: article :encoding: utf-8 :lang: de :status: Proposed :decided-at: 2026-01-07 :decided-by: Roo :toc: left -[[kontext]] +[#adr-context] == Kontext Aktuell weichen die JSON-Antwortstrukturen für die Abfrage einzelner CC1101-Parameter via MQTT (z.B. Topic `get/cc1101/bandwidth`) von der Struktur der Gesamt-Abfrage (Topic `get/cc1101/settings`) ab. @@ -12,7 +12,7 @@ Aktuell weichen die JSON-Antwortstrukturen für die Abfrage einzelner CC1101-Par Diese Inkonsistenz erschwert die automatisierte Verarbeitung der Antworten, da Clients je nach Abfragetyp unterschiedliche JSON-Pfade parsen müssen. Ziel ist eine konsistente Struktur, bei der die JSON-Knotennamen für die einzelnen Parameter in beiden Abfragetypen identisch sind. -[[entscheidung]] +[#adr-decision] == Entscheidung Die JSON-Antwortstruktur für alle CC1101-Parameter-Abfragen wird vereinheitlicht. Die Schlüsselnamen der einzelnen Parameter in der JSON-Antwort werden in beiden Abfragetypen (Einzelparameter und Gesamt-Settings) identisch verwendet. Es wird entschieden, die Schlüssel der Einzelparameter ohne umschließendes Wrapper-Objekt zu verwenden. @@ -32,7 +32,7 @@ Die JSON-Antwortstruktur für alle CC1101-Parameter-Abfragen wird vereinheitlich ``` Die `settings`-Antwort ist somit eine direkte Aggregation der Einzelparameter-Antworten. -[[konsequenzen]] +[#adr-consequences] == Konsequenzen === Positive Konsequenzen @@ -43,7 +43,7 @@ Die `settings`-Antwort ist somit eine direkte Aggregation der Einzelparameter-An * **Breaking Change:** Bestehende Clients, die sich auf eine Wrapper-Struktur wie `{"cc1101": {...}}` bei der Gesamt-Abfrage (`get/cc1101/settings`) verlassen, müssen angepasst werden. * **Migration:** Die Server-Logik für die MQTT-Antworten in der PySignalduino-Implementierung muss entsprechend geändert werden. -[[alternativen]] +[#adr-alternatives] == Alternativen * **Alternative A: Wrapper in Einzelabfragen beibehalten:** Man könnte die Einzelabfrage um den CC1101-Wrapper erweitern (z.B. `get/cc1101/bandwidth` -> `{"cc1101": {"bandwidth": "X kHz"}}`). Dies wurde abgelehnt, da es unnötige Verschachtelung für Einzelwerte einführt und die Lesbarkeit des Payloads verschlechtert. * **Alternative B: Einzelabfragen als reiner Wert:** Die Antwort könnte nur den reinen Wert zurückgeben (z.B. `get/cc1101/bandwidth` -> `"X kHz"`). Dies wurde abgelehnt, da es das JSON-Format verlässt und der Parametername im Payload verloren ginge, was die Eindeutigkeit erschwert. diff --git a/docs/architecture/proposals/mqtt_set_commands.adoc b/docs/architecture/proposals/mqtt_set_commands.adoc index b264042..29e6206 100644 --- a/docs/architecture/proposals/mqtt_set_commands.adoc +++ b/docs/architecture/proposals/mqtt_set_commands.adoc @@ -24,8 +24,8 @@ Das Ziel ist es, eine konsistente, zuverlässige Kette vom MQTT-Topic bis zum se |`signalduino/commands.py` (`SignalduinoCommands`) |Niedrigstufige Kommunikation mit dem Signalduino-Gerät; Umrechnung von physikalischen Werten in CC1101-Registerwerte. -|Implementierung neuer/vollständiger Methoden: `set_frequency`, `set_datarate`. -|Prüfung und ggf. Vervollständigung/Anpassung von: `set_bwidth`, `set_sens`, `set_rampl`. +|Implementierung neuer/vollständiger Methoden: `set_frequency`, `set_datarate`. + +Prüfung und ggf. Vervollständigung/Anpassung von: `set_bwidth`, `set_sens`, `set_rampl`. |=== == 3. Architektonischer Flow und Sequenz diff --git a/docs/architecture/templates/adr_template.adoc b/docs/architecture/templates/adr_template.adoc index 5fa9941..24d49c5 100644 --- a/docs/architecture/templates/adr_template.adoc +++ b/docs/architecture/templates/adr_template.adoc @@ -1,17 +1,17 @@ = ADR {Nummer}: {Titel} :doctype: article :encoding: utf-8 :lang: de :status: {Status} :decided-at: {Datum} :decided-by: {Entscheidungsträger} -[[kontext]] +[#adr-context] == Kontext {Beschreibe den technischen Kontext, der zur Entscheidung geführt hat. Was sind die aktuellen Probleme oder die neuen Anforderungen?} -[[entscheidung]] +[#adr-decision] == Entscheidung {Beschreibe die getroffene Entscheidung. Dies sollte klar und prägnant sein.} -[[konsequenzen]] +[#adr-consequences] == Konsequenzen {Beschreibe die positiven und negativen Konsequenzen der Entscheidung.} @@ -24,6 +24,6 @@ * * -[[alternativen]] +[#adr-alternatives] == Alternativen {Hier können kurz abgelehnte Alternativen aufgeführt werden.} \ No newline at end of file diff --git a/docs/index.adoc b/docs/index.adoc index 6d0842d..621b9ad 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -1,6 +1,7 @@ = SIGNALduino - Projektübersicht :doctype: book :homepage: index.html :description: Der SIGNALduino ist ein vielseitiges Funkmodul zum Empfangen und Senden von 433/868MHz Signalen in Smart-Home-Umgebungen wie FHEM. :keywords: SIGNALduino, FHEM, 433MHz, 868MHz, Smart Home, Funkprotokolle, DIY, Arduino, ESP32 :toc: left :toclevels: 2 +:sectlinks: :experimental: Er dient als Signal-Transceiver und Protokolldekoder. @@ -16,7 +17,6 @@ Dadurch werden verschiedenste proprietäre Funkprotokolle für die Nutzung in Sm Die verfügbare Hardware-Basis reicht von einfachen Arduino/nanoCUL-Lösungen bis hin zu erweiterten Varianten wie dem Maple-SignalDuino und dem ESP32-SignalDuino, die erweiterte Funktionen (z.B. WLAN) bieten. .Übersicht: Hardware und Funktion - [mermaid] ---- graph TD @@ -33,7 +33,7 @@ graph TD subgraph "PySignalduino (Software-Verarbeitung)" F[Empfangen / Senden] - G[Protokolldemodulation] + H[Dekodierung Protokolle] end diff --git a/docs/pysignalduino_cli.apng b/docs/pysignalduino_cli.apng new file mode 100644 index 0000000..18b82df Binary files /dev/null and b/docs/pysignalduino_cli.apng differ diff --git a/docs/pysignalduino_fhemweb.apng b/docs/pysignalduino_fhemweb.apng new file mode 100644 index 0000000..6a09e6e Binary files /dev/null and b/docs/pysignalduino_fhemweb.apng differ diff --git a/tests/tools/test_generate_sitemap.py b/tests/tools/test_generate_sitemap.py index 3d6eb8b..3e6dc2f 100644 --- a/tests/tools/test_generate_sitemap.py +++ b/tests/tools/test_generate_sitemap.py @@ -37,16 +37,16 @@ def test_index_html(self): assert get_priority_for_path('index.html') == 1.0 def test_user_guide_installation(self): - assert get_priority_for_path('user-guide/installation.html') == 0.9 + assert get_priority_for_path('01_user_guide/installation.html') == 0.9 def test_user_guide_usage(self): - assert get_priority_for_path('user-guide/usage.html') == 0.9 + assert get_priority_for_path('01_user_guide/usage.html') == 0.9 def test_protocol_reference_index(self): - assert get_priority_for_path('protocol-reference/index.html') == 0.8 + assert get_priority_for_path('03_protocol_reference/index.html') == 0.8 def test_developer_guide_architecture(self): - assert get_priority_for_path('developer-guide/architecture.html') == 0.8 + assert get_priority_for_path('02_developer_guide/architecture.html') == 0.8 def test_examples_general(self): assert get_priority_for_path('examples/some-example.html') == 0.3 @@ -64,7 +64,7 @@ def test_index_html(self): assert get_changefreq_for_path('index.html') == 'monthly' def test_user_guide_installation(self): - assert get_changefreq_for_path('user-guide/installation.html') == 'yearly' + assert get_changefreq_for_path('01_user_guide/installation.html') == 'yearly' def test_changelog(self): assert get_changefreq_for_path('changelog.html') == 'weekly' @@ -100,14 +100,14 @@ def test_single_html_file(self): assert files[0]['path'] == 'index.html' def test_nested_html_files(self): - (self.build_dir / 'user-guide').mkdir() - (self.build_dir / 'user-guide' / 'installation.html').write_text('') + (self.build_dir / '01_user_guide').mkdir() + (self.build_dir / '01_user_guide' / 'installation.html').write_text('') (self.build_dir / 'examples' / 'bash').mkdir(parents=True) (self.build_dir / 'examples' / 'bash' / 'coverage-report.html').write_text('') files = scan_html_files(self.build_dir) paths = [f['path'] for f in files] - assert 'user-guide/installation.html' in paths + assert '01_user_guide/installation.html' in paths assert 'examples/bash/coverage-report.html' in paths def test_ignore_hidden_files(self): @@ -145,27 +145,27 @@ def test_url_generation_index(self): assert url['changefreq'] == 'monthly' def test_url_generation_nested(self): - self.create_test_html('user-guide/installation.html') + self.create_test_html('01_user_guide/installation.html') html_files = scan_html_files(self.build_dir) urls = generate_sitemap_urls(html_files, 'https://example.com') assert len(urls) == 1 url = urls[0] - assert url['loc'] == 'https://example.com/user-guide/installation' + assert url['loc'] == 'https://example.com/01_user_guide/installation' assert url['priority'] == '0.9' assert url['changefreq'] == 'yearly' def test_url_generation_directory_index(self): - self.create_test_html('user-guide/index.html') + self.create_test_html('01_user_guide/index.html') html_files = scan_html_files(self.build_dir) urls = generate_sitemap_urls(html_files, 'https://example.com') assert len(urls) == 1 - assert urls[0]['loc'] == 'https://example.com/user-guide' + assert urls[0]['loc'] == 'https://example.com/01_user_guide' def test_multiple_urls(self): self.create_test_html('index.html') - self.create_test_html('user-guide/installation.html') + self.create_test_html('01_user_guide/installation.html') self.create_test_html('examples/basic-usage.html') html_files = scan_html_files(self.build_dir) @@ -174,7 +174,7 @@ def test_multiple_urls(self): assert len(urls) == 3 locs = [u['loc'] for u in urls] assert 'https://example.com' in locs - assert 'https://example.com/user-guide/installation' in locs + assert 'https://example.com/01_user_guide/installation' in locs assert 'https://example.com/examples/basic-usage' in locs class TestXmlSitemapGeneration: diff --git a/tools/generate_sitemap.py b/tools/generate_sitemap.py index 4e67fb8..38b307c 100644 --- a/tools/generate_sitemap.py +++ b/tools/generate_sitemap.py @@ -34,14 +34,14 @@ # Basierend auf der tatsächlichen Dokumentationsstruktur und erwarteten HTML-Dateien PRIORITY_MAP = { 'index.html': 1.0, - 'user-guide/installation.html': 0.9, - 'user-guide/usage.html': 0.9, - 'user-guide/index.html': 0.8, - 'developer-guide/architecture.html': 0.8, - 'developer-guide/contribution.html': 0.7, - 'developer-guide/index.html': 0.8, - 'protocol-reference/protocol-details.html': 0.7, - 'protocol-reference/index.html': 0.8, + '01_user_guide/installation.html': 0.9, + '01_user_guide/usage.html': 0.9, + '01_user_guide/index.html': 0.8, + '02_developer_guide/architecture.html': 0.8, + '02_developer_guide/contribution.html': 0.7, + '02_developer_guide/index.html': 0.8, + '03_protocol_reference/protocol_details.html': 0.7, + '03_protocol_reference/index.html': 0.8, 'examples/basic-usage.html': 0.6, 'examples/mqtt-integration.html': 0.6, 'examples/command-api-example.html': 0.5, @@ -78,14 +78,14 @@ CHANGEFREQ_MAP = { 'index.html': 'monthly', - 'user-guide/installation.html': 'yearly', - 'user-guide/usage.html': 'yearly', - 'user-guide/index.html': 'yearly', - 'developer-guide/architecture.html': 'yearly', - 'developer-guide/contribution.html': 'yearly', - 'developer-guide/index.html': 'yearly', - 'protocol-reference/protocol-details.html': 'monthly', - 'protocol-reference/index.html': 'monthly', + '01_user_guide/installation.html': 'yearly', + '01_user_guide/usage.html': 'yearly', + '01_user_guide/index.html': 'yearly', + '02_developer_guide/architecture.html': 'yearly', + '02_developer_guide/contribution.html': 'yearly', + '02_developer_guide/index.html': 'yearly', + '03_protocol_reference/protocol_details.html': 'monthly', + '03_protocol_reference/index.html': 'monthly', 'examples/': 'yearly', 'examples/bash/': 'yearly', 'migration/': 'never', @@ -123,11 +123,11 @@ def get_priority_for_path(file_path: str) -> float: return 0.2 elif normalized.startswith('migration/'): return 0.1 - elif normalized.startswith('developer-guide/'): + elif normalized.startswith('02_developer_guide/'): return 0.5 - elif normalized.startswith('user-guide/'): + elif normalized.startswith('01_user_guide/'): return 0.7 - elif normalized.startswith('protocol-reference/'): + elif normalized.startswith('03_protocol_reference/'): return 0.6 else: return 0.5 @@ -148,11 +148,11 @@ def get_changefreq_for_path(file_path: str) -> str: return 'yearly' elif normalized.startswith('migration/'): return 'never' - elif normalized.startswith('developer-guide/'): + elif normalized.startswith('02_developer_guide/'): return 'yearly' - elif normalized.startswith('user-guide/'): + elif normalized.startswith('01_user_guide/'): return 'yearly' - elif normalized.startswith('protocol-reference/'): + elif normalized.startswith('03_protocol_reference/'): return 'monthly' else: return 'yearly'