Skip to content

Commit cd8e09c

Browse files
author
sidey79
committed
feat: Aktualisiere JSON-Ausgabe-Schema zur Verbesserung der Datenstruktur und -klarheit; entferne Preamble aus Payload und füge neues Feld 'raw' hinzu
1 parent d79252b commit cd8e09c

18 files changed

Lines changed: 435 additions & 33 deletions

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ pycache/
22
*.pyc
33
.venv/
44
.env/
5-
temp_repo/
65
SIGNALDuino-Firmware/
76
.devcontainer/devcontainer.env
87
.devcontainer/.devcontainer.env
98
.devcontainer/mosquitto/data/
109
.devcontainer/mosquitto/log/
11-
1210
.devcontainer/fhem-data/*
1311
!.devcontainer/fhem-data/FHEM
1412
.devcontainer/fhem-data/FHEM/*

README.adoc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,32 @@ Die SIGNALDuino-Firmware (Microcontroller-Code) wird in einem separaten Reposito
3939
* **Ausführbares Hauptprogramm** – `main.py` bietet eine sofort einsatzbereite Lösung mit Logging, Signalbehandlung und Timeout‑Steuerung.
4040
* **Komprimierte Datenübertragung** – Effiziente Payload‑Kompression für MQTT‑Nachrichten.
4141

42+
[NOTE]
43+
.JSON Output Schema
44+
====
45+
Decodierte Nachrichten werden als JSON-Objekte publiziert. Die Protokoll-Metadaten sind nun im Feld `protocol` enthalten, das `data`-Feld enthält die reinen, von der Protokoll-Preamble bereinigten Nutzdaten, und das `raw`-Feld enthält die unveränderte, rohe Nachrichtenzeichenkette der Firmware.
46+
47+
[source,json]
48+
----
49+
{
50+
"data": "30E0A1AA4241DE6C000200000BC5",
51+
"protocol_id": "125",
52+
"raw": "MS;P0=-32001;P1=488;D=0101;CP=1;R=48;",
53+
"metadata": {
54+
"rssi": -74,
55+
"freq_afc": 123
56+
},
57+
"protocol": {
58+
"name": "WH31",
59+
"id": "125",
60+
"preamble": "W125#",
61+
"format": "2-FSK",
62+
"clock": 17257
63+
}
64+
}
65+
----
66+
====
67+
4268
== Demo
4369

4470
=== MQTT-CLI-Integration

docs/01_user_guide/mqtt_api.adoc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,49 @@ Alle nachfolgenden Beispiele verwenden `signalduino/v1` als Basis.
3636
| Dekodierte Funknachrichten.
3737
|===
3838

39+
=== Empfangene Nachrichten (Output-Schema)
40+
41+
Dekodierte Funksignale werden auf dem Topic `signalduino/v1/state/messages` publiziert.
42+
43+
Die Payload folgt dem neuen, strukturierten Schema, bei dem protokollspezifische Metadaten im Feld `protocol` enthalten sind, das `data`-Feld die reinen, von der Protokoll-Preamble bereinigten Nutzdaten enthält, und das `raw`-Feld die unveränderte, rohe Nachrichtenzeichenkette der Firmware.
44+
45+
[source,json]
46+
----
47+
{
48+
"data": "30E0A1AA4241DE6C000200000BC5",
49+
"protocol_id": "125",
50+
"raw": "MS;P0=-32001;P1=488;D=0101;CP=1;R=48;",
51+
"metadata": {
52+
"rssi": -74,
53+
"freq_afc": 123
54+
},
55+
"protocol": {
56+
"name": "WH31",
57+
"id": "125",
58+
"preamble": "W125#",
59+
"format": "2-FSK",
60+
"clock": 17257
61+
}
62+
}
63+
----
64+
65+
==== Detailierte Felder der Output-Nachricht
66+
67+
[cols="1,3", options="header"]
68+
|===
69+
| Feld | Beschreibung
70+
| `data`
71+
| Der dekodierte, bereinigte Payload (Hex- oder Bit-String), *ohne* Protokoll-Preamble (z.B. `W125#`).
72+
| `protocol_id`
73+
| Die numerische oder alphanumerische ID des erkannten Protokolls.
74+
| `raw`
75+
| Die ursprüngliche, unmodifizierte Nachrichtenzeichenkette (`string`), die von der Firmware empfangen wurde (z.B. `MS;P0=-32001;P1=488;D=0101;CP=1;R=48;`).
76+
| `metadata`
77+
| Allgemeine gerätespezifische Metadaten (z.B. `rssi`, `freq_afc`).
78+
| `protocol`
79+
| Ein Dictionary mit spezifischen Protokoll-Details, wie `name`, `id`, `preamble`, `format` und dem verwendeten `clock`-Wert.
80+
|===
81+
3982
=== Request- und Response-Format
4083

4184
Alle Requests verwenden das folgende JSON-Format. Für einfache Befehle (meiste GETs) kann die Payload einfach `{}` sein.

docs/03_protocol_reference/protocol_details.adoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ PySignalduino unterstützt eine Vielzahl von Funkprotokollen im 433 MHz und 868
66

77
Die Datei `sd_protocols/protocols.json` ist die definitive Quelle für alle Protokollparameter (Timings, Preambles, Methoden).
88

9+
=== Dekodiertes Nachrichtenformat
10+
11+
Die dekodierten Nachrichten sind standardisierte JSON-Objekte, die protokollspezifische Metadaten im Feld `protocol` bereitstellen, den bereinigten Daten-Payload im Feld `data` (ohne Protokoll-Preamble), sowie die unveränderte, rohe Nachrichtenzeichenkette der Firmware im Feld `raw`.
12+
13+
[source,json]
14+
----
15+
{
16+
"data": "30E0A1AA4241DE6C000200000BC5",
17+
"protocol_id": "125",
18+
"protocol": {
19+
"name": "WH31",
20+
"id": "125",
21+
"preamble": "W125#",
22+
"format": "2-FSK",
23+
"clock": 17257
24+
},
25+
"metadata": { ... },
26+
"raw": "MS;P0=-32001;P1=488;D=0101;CP=1;R=48;"
27+
}
28+
----
29+
930
== Auszug unterstützter Protokolle
1031

1132
* **ID 10:** Oregon Scientific v2/v3 (Manchester, 433 MHz)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
= ADR-006: JSON Output Schema Refinement
2+
:toc: macro
3+
:toc-title: Inhaltsverzeichnis
4+
5+
== Status
6+
* **Status:** Proposed
7+
* **Datum:** 2026-01-11
8+
* **Architecture Owner:** Roo (Architect)
9+
10+
== Kontext
11+
Das bestehende JSON-Output-Schema für decodierte Funksignale (von `DecodedMessage` abgeleitet) enthält die Protokoll-Preamble (z.B. `W125#`) als Präfix im Feld `payload`. Dies erschwert die automatische Verarbeitung durch Downstream-Systeme (wie MQTT-Clients oder andere FHEM-Module), da diese die Preamble manuell entfernen müssen, um an die reinen Nutzdaten zu gelangen. Des Weiteren fehlt eine standardisierte Möglichkeit, protokollspezifische Metadaten (wie Protokollname, Format, Taktfrequenz) im Output bereitzustellen, ohne diese in der allgemeinen `metadata`-Struktur zu verstecken.
12+
13+
== Entscheidung
14+
Wir werden das JSON-Output-Schema von `DecodedMessage` wie folgt anpassen:
15+
1. **Nutzdaten-Bereinigung:** Die Protokoll-Preamble wird aus dem Nutzdatenfeld entfernt. Dieses Feld enthält nur noch die vom Protokolldecoder erzeugten reinen Daten (Hex- oder Bit-String).
16+
2. **Hinzufügen des `protocol` Feldes:** Ein neues Feld `protocol` vom Typ `dict` wird zur `DecodedMessage` hinzugefügt, um strukturierte, protokollspezifische Informationen zu enthalten.
17+
18+
Die Umbenennung des Nutzdatenfeldes von `payload` zu `data` sowie die Einführung des Feldes `raw` (für die ursprüngliche Nachricht) sind in link:ADR-007-data-and-raw-fields.adoc[ADR-007] dokumentiert, das dieses Schema ergänzt und präzisiert. Dieses ADR dient als Grundlage für die Einführung des `protocol`-Feldes und die Bereinigung des Nutzdateninhalts.
19+
20+
=== Details zur neuen Struktur (Präzisiert durch ADR-007)
21+
22+
| Feld | Typ | Beschreibung |
23+
|---|---|---|
24+
| `protocol_id` | `str` | Die numerische oder alphanumerische ID des erkannten Protokolls. |
25+
| `data` | `str` | Die bereinigten Nutzdaten **ohne** Preamble und Postamble (ersetzt `payload`). |
26+
| `raw` | `str` | Die ursprüngliche, unveränderte Nachricht vom Signalduino (z.B. `MU;...`). |
27+
| `protocol` | `dict` | Strukturierte Metadaten des Protokolls. |
28+
| `protocol.id` | `str` | Die ID des Protokolls (Redundanz zur einfachen Konsumierbarkeit). |
29+
| `protocol.name` | `str` | Der menschenlesbare Name des Protokolls (aus `protocols.json`). |
30+
| `protocol.preamble` | `str` | Die Preamble des Protokolls (z.B. `W125#`). |
31+
| `protocol.format` | `str` | Das Format des Signals (z.B. `manchester`, `twostate`, `2-FSK`) (aus `protocols.json`). |
32+
| `protocol.clock` | `int`/`float` | Der Clock-Wert, der für die Demodulation verwendet wurde (entweder `clockabs` oder `clockrange` Mittelwert/demodulierter Takt). |
33+
| `protocol.modulation` | `str` | (Optional) Modulationsart (z.B. `2-FSK`, `GFSK`) für FSK-Protokolle. |
34+
35+
=== Beispiel für die Datenstruktur (Präzisiert durch ADR-007)
36+
37+
[source,json]
38+
----
39+
{
40+
"data": "30E0A1AA4241DE6C000200000BC5",
41+
"raw": "MC;LL=-1017;LH=932;...",
42+
"protocol_id": "125",
43+
"metadata": {
44+
"rssi": -74,
45+
"freq_afc": 123
46+
},
47+
"protocol": {
48+
"name": "WH31",
49+
"id": "125",
50+
"preamble": "W125#",
51+
"format": "2-FSK",
52+
"clock": 17257
53+
}
54+
}
55+
----
56+
57+
== Konsequenzen
58+
**Positiv:**
59+
* Das `data`-Feld ist jetzt "sauber" und enthält nur die Nutzdaten.
60+
* Protocolspezifische Metadaten sind standardisiert im `protocol`-Feld abrufbar.
61+
* Vereinfacht die Integration mit Systemen, die strukturierte Daten erwarten.
62+
* Das neue `raw`-Feld (siehe link:ADR-007-data-and-raw-fields.adoc[ADR-007]) ermöglicht besseres Debugging und vollständige Nachvollziehbarkeit.
63+
64+
**Negativ:**
65+
* Dies ist ein **Breaking Change** für alle existierenden Konsumenten des `DecodedMessage`-Outputs, die darauf angewiesen sind, dass die Preamble im `payload` enthalten ist.
66+
* Alle Demodulations- und Parser-Logik muss angepasst werden, um die Preamble separat zu behandeln und das `protocol`-Feld sowie das neue `raw`-Feld zu befüllen.
67+
68+
== Alternativen
69+
1. **Preamble in `metadata` verschieben:** Hätte den Nutzdatenfeld gereinigt, aber die Protokolldetails weiterhin unstrukturiert gelassen. Abgelehnt, da ein dediziertes `protocol`-Feld die semantische Klarheit verbessert.
70+
2. **Beibehaltung der alten Struktur:** Hätte Abwärtskompatibilität gewährleistet, aber die Notwendigkeit für eine Nutzdatenreinigung durch jeden Konsumenten beibehalten. Abgelehnt, da die verbesserte Struktur die Wartbarkeit und zukünftige Erweiterbarkeit deutlich erhöht.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
= ADR-007: Renaming Payload to Data and Adding Raw Field
2+
:doctype: article
3+
:encoding: utf-8
4+
:lang: de
5+
:status: Proposed
6+
:decided-at: 2026-01-11
7+
:decided-by: Roo (Architect)
8+
9+
[#adr-context]
10+
== Kontext
11+
12+
Im Zuge der Definition des neuen JSON-Output-Schemas (siehe ADR-006) wurde Feedback gesammelt, das zwei wesentliche Verbesserungen vorschlägt:
13+
14+
1. **Ambiguität des Feldes `payload`:** Der Begriff `payload` wird in MQTT- und Messaging-Kontexten häufig für den gesamten Nachrichteninhalt verwendet. Die Verwendung von `payload` für die spezifischen, decodierten Hex-Daten kann daher zu Verwirrung führen.
15+
2. **Bedarf an Rohdaten:** Für Debugging-Zwecke und fortgeschrittene Analysen ist es notwendig, Zugriff auf die ursprüngliche, unveränderte Nachricht zu haben, wie sie vom Signalduino-Gerät empfangen wurde (z.B. der komplette `MU;...`- oder `MC;...`-String), bevor irgendeine Verarbeitung oder Parsing stattgefunden hat.
16+
17+
[#adr-decision]
18+
== Entscheidung
19+
20+
Wir passen das in ADR-006 definierte Schema wie folgt an:
21+
22+
1. **Umbenennung `payload` zu `data`:**
23+
Das Feld, das bisher `payload` hieß und die bereinigten Hex- oder Binärdaten (ohne Preamble) enthielt, wird in **`data`** umbenannt.
24+
25+
2. **Einführung des Feldes `raw`:**
26+
Es wird ein neues Feld **`raw`** auf der obersten Ebene des JSON-Objekts eingeführt. Dieses Feld enthält den ursprünglichen Nachrichten-String, der an den Parser übergeben wurde.
27+
28+
=== Aktualisierte Datenstruktur
29+
30+
[source,json]
31+
----
32+
{
33+
"data": "30E0A1AA4241DE6C000200000BC5", // Ehemals "payload"
34+
"raw": "MC;LL=-1017;LH=932;...", // Der originale Input-String
35+
"protocol_id": "125",
36+
"metadata": {
37+
"rssi": -74,
38+
"freq_afc": 123
39+
},
40+
"protocol": {
41+
"name": "WH31",
42+
"id": "125",
43+
"preamble": "W125#",
44+
"format": "2-FSK",
45+
"clock": 17257
46+
}
47+
}
48+
----
49+
50+
[#adr-consequences]
51+
== Konsequenzen
52+
53+
=== Positive Konsequenzen
54+
* **Klarere Semantik:** `data` ist ein neutralerer Begriff für den Dateninhalt und vermeidet die Überladung des Begriffs `payload`.
55+
* **Verbesserte Debugging-Möglichkeiten:** Durch das `raw`-Feld können Entwickler und User jederzeit nachvollziehen, was genau vom Gerät empfangen wurde, und Parser-Fehler leichter diagnostizieren.
56+
* **Vollständigkeit:** Keine Information geht verloren; sowohl die Rohdaten als auch die interpretierten Daten stehen zur Verfügung.
57+
58+
=== Negative Konsequenzen
59+
* **Breaking Change:** Dies ändert das gerade in ADR-006 vorgeschlagene Schema. Da ADR-006 jedoch noch neu ist, sollte der Anpassungsaufwand gering sein. Code, der bereits `payload` verwendet, muss auf `data` umgestellt werden.
60+
61+
[#adr-alternatives]
62+
== Alternativen
63+
* **Beibehaltung von `payload`:** Wurde verworfen, um die Ambiguität aufzulösen.
64+
* **`raw` als Objekt:** Es wurde erwogen, `raw` strukturierter zu gestalten, aber für die einfache Nachvollziehbarkeit ist der originale String am wertvollsten.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
= Architecture Proposal: JSON Output Schema Refinement
2+
:toc: macro
3+
:toc-title: Inhaltsverzeichnis
4+
5+
== Status
6+
* **Status:** Proposed
7+
* **Datum:** 2026-01-11
8+
* **Autor:** Roo (Architect)
9+
10+
== Kontext
11+
Das aktuelle JSON-Nachrichtenschema für decodierte Signale vermischt Protokoll-Metadaten (die Preamble) mit den eigentlichen Nutzdaten (Payload). Zudem fehlen strukturierte Informationen über das erkannte Protokoll, die für weiterverarbeitende Systeme (z.B. Home Assistant, FHEM via MQTT) nützlich wären.
12+
13+
Aktuelles Beispiel:
14+
[source,json]
15+
----
16+
{
17+
"payload": "W125#30E0A1AA4241DE6C000200000BC5",
18+
"metadata": { ... },
19+
"protocol_id": "125"
20+
}
21+
----
22+
23+
== Problemstellung
24+
1. **Verschmutzter Payload:** Der Payload enthält die Preamble (z.B. `W125#`), was nachgelagerte Parser zwingt, diese zu entfernen.
25+
2. **Fehlende Protokolldetails:** Es gibt kein dediziertes Feld, das alle relevanten statischen und dynamischen Eigenschaften des erkannten Protokolls zusammenfasst.
26+
27+
== Lösungsvorschlag
28+
29+
=== 1. Bereinigung des Payloads
30+
Die Preamble wird aus dem `payload`-Feld entfernt. Der Payload enthält nur noch die reinen Daten (Hex-String).
31+
32+
=== 2. Erweiterung um `protocol`-Objekt
33+
Ein neues Feld `protocol` wird eingeführt, das strukturierte Informationen enthält.
34+
35+
Neues Schema:
36+
[source,json]
37+
----
38+
{
39+
"payload": "30E0A1AA4241DE6C000200000BC5",
40+
"protocol_id": "125",
41+
"protocol": {
42+
"name": "WH31",
43+
"id": "125",
44+
"preamble": "W125#",
45+
"format": "manchester",
46+
"clock": 17257, // Optional: erkannter oder definierter Takt
47+
"modulation": "2-FSK" // Optional: aus Protokolldefinition
48+
},
49+
"metadata": {
50+
"rssi": -74,
51+
"freq_afc": 123
52+
}
53+
}
54+
----
55+
56+
== Betroffene Komponenten
57+
58+
=== `sd_protocols`
59+
Die Mixins für die Demodulation müssen angepasst werden, um die Preamble nicht mehr an den Payload anzuhängen, sondern separat zurückzugeben oder verfügbar zu machen.
60+
61+
* `sd_protocols/manchester.py`: `_demodulate_mc_data`
62+
* `sd_protocols/message_synced.py`: `demodulate_ms`
63+
* `sd_protocols/message_unsynced.py`: `demodulate_mu`
64+
* `sd_protocols/sd_protocols.py`: `demodulate_mc`, `demodulate_mn`
65+
66+
=== `signalduino/types.py`
67+
Die Klasse `DecodedMessage` muss um das Feld `protocol` erweitert werden.
68+
69+
[source,python]
70+
----
71+
@dataclass(slots=True)
72+
class DecodedMessage:
73+
protocol_id: str
74+
payload: str
75+
raw: RawFrame
76+
metadata: dict = field(default_factory=dict)
77+
protocol: dict = field(default_factory=dict) # Neu
78+
----
79+
80+
=== `signalduino/parser`
81+
Die Parser-Klassen (`MCParser`, `MSParser`, `MUParser`, `MNParser`) müssen sicherstellen, dass das `protocol`-Feld korrekt aus den Rückgabewerten der Protokollschicht befüllt wird.
82+
83+
== Migration
84+
Dies ist eine **Breaking Change** für Konsumenten, die erwarten, dass die Preamble Teil des Payloads ist. Die Versionsnummer sollte entsprechend (Minor oder Major, je nach Versionierungsstrategie) erhöht werden.
85+
86+
== Alternativen
87+
* **Status Quo beibehalten:** Führt zu unnötigem Parsing-Aufwand bei jedem Client.
88+
* **Preamble nur in Metadata:** Löst das Payload-Problem, bietet aber keine strukturierte Sicht auf das Protokoll.

signalduino/mqtt.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,9 +246,10 @@ def _raw_frame_to_dict(raw_frame: RawFrame) -> dict:
246246
message_dict["raw"] = _raw_frame_to_dict(message_dict["raw"])
247247

248248
# Remove empty or non-useful fields for publication
249-
message_dict.pop("raw", None) # Do not publish raw frame data by default
249+
# Note: 'raw' is now a string (ADR-007) and should be published.
250+
# The pop operation (line 249 in original) is removed to include it.
250251

251-
# Append preamble to payload for FHEM compatibility (PreambleProtocolID#HexData)
252+
# Append preamble to data for FHEM compatibility (PreambleProtocolID#HexData)
252253
preamble = ""
253254
if self._protocol_handler:
254255
try:
@@ -260,8 +261,8 @@ def _raw_frame_to_dict(raw_frame: RawFrame) -> dict:
260261
# Add new 'preamble' field
261262
message_dict["preamble"] = preamble
262263

263-
# Ensure payload is uppercase, but DO NOT prepend preamble anymore
264-
message_dict["payload"] = message.payload.upper()
264+
# Ensure data (formerly payload) is uppercase
265+
message_dict["data"] = message.data.upper()
265266

266267
return json.dumps(message_dict, indent=4)
267268

0 commit comments

Comments
 (0)