Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .devcontainer/fhem-data/FHEM/99_MyUtils.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
##############################################
# $Id: 99_MyUtils.pm 1932 2012-01-28 18:15:28Z martinp876 $
package main;

use strict;
use warnings;
use JSON;
use Data::Dumper;

sub MyUtils_Initialize {
my ($hash) = @_;
}

# Enter you functions below _this_ line.

# MqttSignalduino_DispatchFromJSON wurde nach FHEM/lib/SD_Dispatch.pm verschoben.

1;
4 changes: 3 additions & 1 deletion .devcontainer/fhem-data/fhem_signalduino_example.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ attr global verbose 3
#
# This file is loaded by the FHEM container via CONFIGTYPE environment variable.

define autocreate autocreate

# 1. Define FHEMWEB instance to access FHEM via Browser (Port 8083)
define WEB FHEMWEB 8083 global
Expand Down Expand Up @@ -39,9 +40,10 @@ attr mqtt_broker autocreate simple
define PySignalDuino MQTT2_DEVICE
setuuid PySignalDuino 695e9c21-f33f-c986-4f81-a9f0ab37b6bcedf8
attr PySignalDuino IODev mqtt_broker
attr PySignalDuino readingList signalduino/v1/state/messages:.* { json2nameValue($EVENT, 'MSG_',$JSONMAP) }\
attr PySignalDuino readingList signalduino/v1/state/messages:.* { use FHEM::Devices::SIGNALDuino::Message;; FHEM::Devices::SIGNALDuino::Message::json2Dispatch($EVENT, $NAME);; json2nameValue($EVENT,'MSG_');; }\
signalduino/v1/responses:.* { json2nameValue($EVENT, 'RESP_') }\
signalduino/v1/errors:.* { json2nameValue($EVENT, 'ERR_') }
# attr PySignalDuino Clients :CUL_EM:CUL_FHTTK:CUL_TCM97001:CUL_TX:CUL_WS:Dooya:FHT:FLAMINGO:FS10:FS20:Fernotron:Hideki:IT:KOPP_FC:LaCrosse:OREGON:PCA301:RFXX10REC:Revolt:SD_AS:SD_Rojaflex:SD_BELL:SD_GT:SD_Keeloq:SD_RSL:SD_UT:SD_WS07:SD_WS09:SD_WS:SD_WS_Maverick:SOMFY:Siro:SIGNALduino_un:
attr PySignalDuino setList raw:textField signalduino/v1/commands/set/raw $EVTPART1 \
cc1101_reg:textField signalduino/v1/commands/set/cc1101_reg $EVTPART1 \
# System GET commands (noArg) \
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ pycache/
*.pyc
.venv/
.env/
temp_repo/
SIGNALDuino-Firmware/
.devcontainer/devcontainer.env
.devcontainer/.devcontainer.env
.devcontainer/mosquitto/data/
.devcontainer/mosquitto/log/
.devcontainer/fhem-data/*
!.devcontainer/fhem-data/FHEM
.devcontainer/fhem-data/FHEM/*
!.devcontainer/fhem-data/fhem_signalduino_example.cfg
!.devcontainer/fhem-data/FHEM/99_MyUtils.pm

.roo/mcp.json
8 changes: 8 additions & 0 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ Die SIGNALDuino-Firmware (Microcontroller-Code) wird in einem separaten Reposito
* **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.

[NOTE]
.JSON Output Schema
====
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.

include::./docs/architecture/snippets/json_output_schema_example.adoc[]
====

== Demo

=== MQTT-CLI-Integration
Expand Down
43 changes: 43 additions & 0 deletions docs/01_user_guide/mqtt_api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,49 @@ Alle nachfolgenden Beispiele verwenden `signalduino/v1` als Basis.
| Dekodierte Funknachrichten.
|===

=== Empfangene Nachrichten (Output-Schema)

Dekodierte Funksignale werden auf dem Topic `signalduino/v1/state/messages` publiziert.

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.

[source,json]
----
{
"data": "30E0A1AA4241DE6C000200000BC5",
"protocol_id": "125",
"raw": "MS;P0=-32001;P1=488;D=0101;CP=1;R=48;",
"metadata": {
"rssi": -74,
"freq_afc": 123
},
"protocol": {
"name": "WH31",
"id": "125",
"preamble": "W125#",
"format": "2-FSK",
"clock": 17257
}
}
----

==== Detailierte Felder der Output-Nachricht

[cols="1,3", options="header"]
|===
| Feld | Beschreibung
| `data`
| Der dekodierte, bereinigte Payload (Hex- oder Bit-String), *ohne* Protokoll-Preamble (z.B. `W125#`).
| `protocol_id`
| Die numerische oder alphanumerische ID des erkannten Protokolls.
| `raw`
| 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;`).
| `metadata`
| Allgemeine gerätespezifische Metadaten (z.B. `rssi`, `freq_afc`).
| `protocol`
| Ein Dictionary mit spezifischen Protokoll-Details, wie `name`, `id`, `preamble`, `format` und dem verwendeten `clock`-Wert.
|===

=== Request- und Response-Format

Alle Requests verwenden das folgende JSON-Format. Für einfache Befehle (meiste GETs) kann die Payload einfach `{}` sein.
Expand Down
21 changes: 21 additions & 0 deletions docs/03_protocol_reference/protocol_details.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@ PySignalduino unterstützt eine Vielzahl von Funkprotokollen im 433 MHz und 868

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

=== Dekodiertes Nachrichtenformat

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`.

[source,json]
----
{
"data": "30E0A1AA4241DE6C000200000BC5",
"protocol_id": "125",
"protocol": {
"name": "WH31",
"id": "125",
"preamble": "W125#",
"format": "2-FSK",
"clock": 17257
},
"metadata": { ... },
"raw": "MS;P0=-32001;P1=488;D=0101;CP=1;R=48;"
}
----

== Auszug unterstützter Protokolle

* **ID 10:** Oregon Scientific v2/v3 (Manchester, 433 MHz)
Expand Down
88 changes: 88 additions & 0 deletions docs/architecture/decisions/ADR-006-json-output-schema.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
= ADR-006: JSON Output Schema Refinement
:toc: macro
:toc-title: Inhaltsverzeichnis

== Status
* **Status:** Proposed
* **Datum:** 2026-01-11
* **Architecture Owner:** Roo (Architect)

== Kontext
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.

== Entscheidung
Wir werden das JSON-Output-Schema von `DecodedMessage` wie folgt anpassen:
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).
2. **Hinzufügen des `protocol` Feldes:** Ein neues Feld `protocol` vom Typ `dict` wird zur `DecodedMessage` hinzugefügt, um strukturierte, protokollspezifische Informationen zu enthalten.

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.

=== Details zur neuen Struktur (Präzisiert durch ADR-007)

[cols="1,1,4"]
|===
| Feld | Typ | Beschreibung

| `data`
| `str`
| Die bereinigten Nutzdaten (meist Hex-String) **ohne** Preamble/Postamble. Repräsentiert die protokollspezifischen Daten (ersetzt `payload`).

| `raw`
| `str`
| Die ursprüngliche, unveränderte Nachricht vom Signalduino (z.B. `MU;...`). Dient der vollständigen Nachvollziehbarkeit.

| `protocol`
| `object`
| Container für strukturierte Metadaten der erfolgreichen Protokolldemodulation.

| `protocol.id`
| `str`
| Die interne ID des Protokolls (z.B. `125`).

| `protocol.name`
| `str`
| Der menschenlesbare Name des Protokolls (aus `protocols.json`).

| `protocol.preamble`
| `str`
| Die Preamble (z.B. `W125#`) des erkannten Protokolls.

| `protocol.encoding`
| `str`
| Das Codierungsformat des Signals (z.B. `manchester`, `twostate`, `pulse`).

| `protocol.clock`
| `float`
| Der Takt-Wert in Mikrosekunden (`us`), der für die Demodulation verwendet wurde.

| `protocol.modulation`
| `str`
| (Optional) Modulationsart (z.B. `2-FSK`, `GFSK`) für FSK-Protokolle.

| `protocol.bitlength`
| `int`
| (Optional) Die tatsächliche Bitlänge der `data`-Nutzdaten, falls vom Protokoll bekannt/erzwungen. Unterstützt z.B. die Grothe-Constraint-Prüfung.

| `protocol.repeats`
| `int`
| (Optional) Die Anzahl der erkannten Wiederholungen dieses Pakets, relevant für Duplikaterkennung.
|===

=== Beispiel für die Datenstruktur (Präzisiert durch ADR-007)

include::../snippets/json_output_schema_example.adoc[]

== Konsequenzen
**Positiv:**
* Das `data`-Feld ist jetzt "sauber" und enthält nur die Nutzdaten.
* Protocolspezifische Metadaten sind standardisiert im `protocol`-Feld abrufbar.
* Vereinfacht die Integration mit Systemen, die strukturierte Daten erwarten.
* Das neue `raw`-Feld (siehe link:ADR-007-data-and-raw-fields.adoc[ADR-007]) ermöglicht besseres Debugging und vollständige Nachvollziehbarkeit.

**Negativ:**
* Dies ist ein **Breaking Change** für alle existierenden Konsumenten des `DecodedMessage`-Outputs, die darauf angewiesen sind, dass die Preamble im `payload` enthalten ist.
* 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.

== Alternativen
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.
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.
46 changes: 46 additions & 0 deletions docs/architecture/decisions/ADR-007-data-and-raw-fields.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
= ADR-007: Renaming Payload to Data and Adding Raw Field
:doctype: article
:encoding: utf-8
:lang: de
:status: Proposed
:decided-at: 2026-01-11
:decided-by: Roo (Architect)

[#adr-context]
== Kontext

Im Zuge der Definition des neuen JSON-Output-Schemas (siehe ADR-006) wurde Feedback gesammelt, das zwei wesentliche Verbesserungen vorschlägt:

. **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.
. **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.

[#adr-decision]
== Entscheidung

Wir passen das in ADR-006 definierte Schema wie folgt an:

. **Umbenennung `payload` zu `data`:**
Das Feld, das bisher `payload` hieß und die bereinigten Hex- oder Binärdaten (ohne Preamble) enthielt, wird in **`data`** umbenannt.

. **Einführung des Feldes `raw`:**
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.

=== Aktualisierte Datenstruktur

include::../snippets/json_output_schema_example.adoc[]

[#adr-consequences]
== Konsequenzen

=== Positive Konsequenzen
* **Klarere Semantik:** `data` ist ein neutralerer Begriff für den Dateninhalt und vermeidet die Überladung des Begriffs `payload`.
* **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.
* **Vollständigkeit:** Keine Information geht verloren; sowohl die Rohdaten als auch die interpretierten Daten stehen zur Verfügung.

=== Negative Konsequenzen
* **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.

[#adr-alternatives]
== Alternativen
* **Beibehaltung von `payload`:** Wurde verworfen, um die Ambiguität aufzulösen.
* **`raw` als Objekt:** Es wurde erwogen, `raw` strukturierter zu gestalten, aber für die einfache Nachvollziehbarkeit ist der originale String am wertvollsten.
Loading
Loading