Skip to content

Commit 4aaed61

Browse files
author
sidey79
committed
feat: implement mqtt commands for factory reset and settings retrieval
Refactor MQTT handling to use MqttCommandDispatcher. Add CLI tool for MQTT commands. Add tests and architecture documentation.
1 parent 36708f2 commit 4aaed61

File tree

12 files changed

+776
-130
lines changed

12 files changed

+776
-130
lines changed

.roo/mcp.json

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1 @@
1-
{
2-
"mcpServers": {
3-
"filesystem": {
4-
"command": "npx",
5-
"args": [
6-
"-y",
7-
"@modelcontextprotocol/server-filesystem",
8-
"/workspaces/PySignalduino"
9-
]
10-
},
11-
"git": {
12-
"command": "uvx",
13-
"args": [
14-
"mcp-server-git",
15-
"--repository",
16-
"/workspaces/PySignalduino"
17-
]
18-
}
19-
}
20-
}
1+
{"mcpServers":{"filesystem":{"command":"npx","args":["-y","@modelcontextprotocol/server-filesystem","/workspaces/PySignalduino"],"alwaysAllow":["edit_file","read_text_file","search_files","read_multiple_files"]},"git":{"command":"uvx","args":["mcp-server-git","--repository","/workspaces/PySignalduino"],"alwaysAllow":["git_diff_unstaged","git_checkout"]}}}

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"host": "mqtt",
5353
"port": 1883,
5454
"clientId": "vsmqtt_client_db93",
55-
"savedSubscriptions": ['signalduino/']
55+
"savedSubscriptions": ['signalduino/v1/responses','signalduino/v1/commands','signalduino/v1/errors']
5656
}
5757
]
5858
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
= ADR 002: Verwendung des MqttCommandDispatcher für die MQTT-Befehlsbehandlung
2+
:doctype: article
3+
:encoding: utf-8
4+
:lang: de
5+
:status: Accepted
6+
:decided-at: 2026-01-04
7+
:decided-by: Roo (Architekt)
8+
9+
[[kontext]]
10+
== Kontext
11+
12+
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.
13+
14+
Der Code enthält bereits einen generischen, schema-validierenden Befehls-Dispatcher, den `MqttCommandDispatcher` (definiert in `signalduino/commands.py`). Dieser Dispatcher ist so konzipiert, dass er Befehlspfade gegen eine zentrale `COMMAND_MAP` prüft, Payloads validiert und die Ausführung an die entsprechenden Methoden im `SignalduinoController` delegiert.
15+
16+
Die zentrale Verwaltung der Befehle und deren Validierung ist eine bewährte Methode, um die Robustheit und Erweiterbarkeit der Schnittstelle zu gewährleisten.
17+
18+
[[entscheidung]]
19+
== Entscheidung
20+
21+
Die hartcodierte `if/elif`-Logik in `signalduino/mqtt.py` wird durch die Verwendung des `MqttCommandDispatcher` ersetzt.
22+
23+
1. Der `MqttPublisher` in `signalduino/mqtt.py` wird eine Instanz des `MqttCommandDispatcher` erhalten.
24+
2. Die Methode `_handle_command` in `MqttPublisher` wird umgeschrieben, um den eingehenden Befehlspfad und Payload direkt an `MqttCommandDispatcher.dispatch()` zu übergeben.
25+
3. Die Fehler- und Erfolgsantworten werden vom `MqttCommandDispatcher` zurückgegeben und von `MqttPublisher` an die entsprechenden MQTT-Topics (`/responses` und `/errors`) publiziert.
26+
27+
[[konsequenzen]]
28+
== Konsequenzen
29+
30+
=== Positive Konsequenzen
31+
* **Erweiterbarkeit:** Neue MQTT-Befehle (wie Factory Reset und Hardware Settings) können einfach durch Hinzufügen eines Eintrags zur `COMMAND_MAP` und der zugehörigen Controller-Methode hinzugefügt werden, ohne `signalduino/mqtt.py` zu ändern.
32+
* **Trennung der Zuständigkeiten:** Die Verarbeitung der Befehlslogik (Validierung, Mapping, Ausführung) wird von der reinen MQTT-Transportlogik getrennt.
33+
* **Validierung:** Alle eingehenden MQTT-Payloads werden automatisch gegen definierte JSON-Schemata validiert, was die Fehleranfälligkeit der Implementierung reduziert.
34+
* **Konsistente Fehlerbehandlung:** Erfolgs- und Fehlerantworten werden an zentraler Stelle standardisiert.
35+
36+
=== Negative Konsequenzen
37+
* **Refactoring-Aufwand:** Die bestehende Logik in `signalduino/mqtt.py` muss entfernt und durch den Dispatcher-Aufruf ersetzt werden.
38+
* **Kopplung an Controller:** Der Dispatcher ist direkt an den `SignalduinoController` gekoppelt (was bereits der Fall war und akzeptiert wird).
39+
40+
[[alternativen]]
41+
== Alternativen
42+
* **Beibehaltung der if/elif-Kette:** Dies wurde abgelehnt, da es gegen die Prinzipien der Wartbarkeit und der Single Responsibility Principle (SRP) verstößt.
43+
* **Anderer Dispatch-Mechanismus:** Die Verwendung des vorhandenen `MqttCommandDispatcher` ist die pragmatischste Lösung, da die Klasse bereits existiert und die Validierungsinfrastruktur bietet.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
= Architektur-Proposal: MQTT-basierter Factory Reset und Hardware-Status
2+
:doctype: article
3+
:encoding: utf-8
4+
:lang: de
5+
:author: Roo (Architekt)
6+
:email: roo@pythonsignalduino.com
7+
:revnumber: 1.0
8+
:revdate: 2026-01-04
9+
:xrefstyle: full
10+
11+
[[status]]
12+
== Status
13+
14+
|===
15+
|Status|Datum der letzten Änderung|Entscheidungsträger
16+
17+
|Draft
18+
|2026-01-04
19+
|Roo (Architekt)
20+
|===
21+
22+
[[zusammenfassung]]
23+
== 1. Zusammenfassung (Executive Summary)
24+
25+
Dieses Proposal beschreibt die Einführung von zwei neuen MQTT-Funktionalitäten: die Durchführung eines Factory Resets auf dem Signalduino-Gerät und das Abrufen der aktuellen CC1101-Hardware-Einstellungen (Frequenz, Bandbreite, Verstärkung, Empfindlichkeit, Datenrate) über MQTT. Die Implementierung basiert auf dem Refactoring des MQTT-Befehls-Handlings, indem der vorhandene `MqttCommandDispatcher` zentral in `signalduino/mqtt.py` verwendet wird.
26+
27+
[[problem-definition]]
28+
== 2. Problemstellung und Motivation
29+
30+
Aktuell müssen Hardware-Einstellungen direkt über die serielle Konsole des Signalduino-Geräts abgefragt werden. Für einen Factory Reset (Serial Command `e (EEPROM Defaults)`) fehlt ein hochstufiger, zugänglicher Mechanismus. Die bestehende MQTT-Befehlslogik in `signalduino/mqtt.py` ist eine unstrukturierte `if/elif`-Kette, die eine Erweiterung erschwert. Die Motivation ist, eine vollständig über MQTT fernsteuerbare und auslesbare Schnittstelle für die Geräteeinstellungen zu schaffen.
31+
32+
[[ziele]]
33+
== 3. Ziele
34+
35+
1. **Refactoring:** Ersetze die `if/elif`-Logik in `signalduino/mqtt.py` durch den [`MqttCommandDispatcher`](signalduino/commands.py:193) (siehe xref:ADR-002-mqtt-command-dispatcher.adoc[ADR 002]).
36+
2. **Factory Reset:** Definiere und implementiere den MQTT-Befehl für den Signalduino Factory Reset (`e`).
37+
3. **Hardware-Status-Abruf:** Implementiere neue Controller-Methoden und MQTT-Befehle, um die aktuellen CC1101-Einstellungen (Freq, Bandwidth, rAmpl, sens, DataRate) auszulesen.
38+
4. **Tooling:** Entwirf die Schnittstelle für ein CLI-Helfer-Tool zum Testen und Steuern dieser Befehle.
39+
40+
[[vorgeschlagene-architektur]]
41+
== 4. Vorgeschlagene Architektur
42+
43+
Die Architektur nutzt die bereits existierende Schichtenarchitektur von PySignalduino (MQTT Publisher -> Controller -> Serial Commands). Der Schlüssel liegt in der Zentralisierung des Befehls-Routings im `MqttCommandDispatcher`.
44+
45+
=== 4.1. Komponenten-Diagramm (Mermaid)
46+
47+
[mermaid]
48+
----
49+
graph TD
50+
A[MQTT Client] --> B(MqttPublisher / Listener);
51+
B --> C{MqttCommandDispatcher};
52+
C --> D[SignalduinoController];
53+
D --> E[SignalduinoCommands (Serial API)];
54+
E --> F[Signalduino Hardware];
55+
56+
subgraph Signal Path (Commands)
57+
B -- Refactored Handler --> C
58+
C -- Payload Validation / Routing --> D
59+
D -- High-Level Call --> E
60+
E -- Low-Level Serial --> F
61+
end
62+
----
63+
64+
=== 4.2. Sequenz-Diagramm (Mermaid)
65+
66+
Dieses Diagramm zeigt den Ablauf für den Factory Reset und das Abrufen der Bandbreite.
67+
68+
[mermaid]
69+
----
70+
sequenceDiagram
71+
participant Mq as MQTT Client (Tool)
72+
participant Mqp as MqttPublisher (signalduino/mqtt.py)
73+
participant Disp as MqttCommandDispatcher
74+
participant Ctrl as SignalduinoController
75+
participant Cmd as SignalduinoCommands
76+
participant SDU as Signalduino Hardware
77+
78+
group Factory Reset (Command)
79+
Mq->>Mqp: PUBLISH (Topic: .../commands/command/factory_reset, Payload: {"req_id": "123"})
80+
Mqp->>Disp: dispatch("command/factory_reset", payload)
81+
Disp->>Ctrl: command_factory_reset(payload)
82+
Ctrl->>Cmd: send_command("e")
83+
Cmd->>SDU: Serial: e
84+
SDU-->>Cmd: Serial: OK / Timeout
85+
Cmd-->>Ctrl: Result
86+
Ctrl-->>Disp: Response Data
87+
Disp-->>Mqp: Result Dict
88+
Mqp->>Mq: PUBLISH (Topic: .../responses, Payload: success: true, req_id: "123")
89+
end
90+
91+
group Hardware Status (GET)
92+
Mq->>Mqp: PUBLISH (Topic: .../commands/get/cc1101/bandwidth, Payload: {"req_id": "456"})
93+
Mqp->>Disp: dispatch("get/cc1101/bandwidth", payload)
94+
Disp->>Ctrl: get_cc1101_bandwidth(payload)
95+
Ctrl->>Cmd: read_cc1101_register(0x10)
96+
Cmd->>SDU: Serial: C10
97+
SDU-->>Cmd: Serial: C10 = 02 (Beispiel)
98+
Cmd->>Cmd: Decode to Bandwidth (z.B. 102 kHz)
99+
Cmd-->>Ctrl: 102 (kHz)
100+
Ctrl-->>Disp: 102
101+
Disp-->>Mqp: Response Data
102+
Mqp->>Mq: PUBLISH (Topic: .../responses, Payload: data: 102, req_id: "456")
103+
end
104+
----
105+
106+
[[schnittstellen]]
107+
== 5. Betroffene Schnittstellen (APIs, MQTT Topics)
108+
109+
=== 5.1. Neue MQTT Topics (PUBLISH an)
110+
* `signalduino/v1/commands/command/factory_reset`
111+
* `signalduino/v1/commands/get/cc1101/bandwidth`
112+
* `signalduino/v1/commands/get/cc1101/rampl`
113+
* `signalduino/v1/commands/get/cc1101/sensitivity`
114+
* `signalduino/v1/commands/get/cc1101/datarate`
115+
116+
=== 5.2. `SignalduinoCommands` Erweiterungen (Serial API)
117+
Neue Methoden in [`SignalduinoCommands`](signalduino/commands.py:20), die das Lesen der CC1101-Register kapseln und die Rohwerte in nutzbare Einheiten (kHz, dB) umrechnen:
118+
* `factory_reset()` (Serial Command `e`)
119+
* `get_bwidth()` (liest Register `0x10` und berechnet die Bandbreite)
120+
* `get_rampl()` (liest Register `0x1B` und decodiert die Verstärkung)
121+
* `get_sens()` (liest Register `0x1D` und decodiert die Empfindlichkeit)
122+
* `get_datarate()` (liest Register `0x10` und `0x11` und berechnet die Datenrate)
123+
124+
=== 5.3. `MqttCommandDispatcher.COMMAND_MAP` Erweiterungen
125+
Neue Einträge in der Map zur Weiterleitung der obigen MQTT Topics an die entsprechenden Controller-Methoden.
126+
127+
[[alternativen]]
128+
== 6. Alternativen in Betracht gezogen
129+
130+
* **Kein Refactoring:** Das Beibehalten der `if/elif`-Kette in `signalduino/mqtt.py` wurde abgelehnt, da es die Wartbarkeit reduziert und dem Architekturprinzip der Trennung der Zuständigkeiten widerspricht (siehe ADR-002).
131+
* **Keine Abfrage einzelner Werte:** Stattdessen nur einen Sammelbefehl (`get/cc1101/status`) implementieren. Dies wurde abgelehnt, da es die Konsistenz mit dem bereits vorhandenen `get/cc1101/frequency` bricht und nicht die Flexibilität für clientspezifische Abfragen bietet.
132+
133+
[[auswirkungen]]
134+
== 7. Auswirkungen und Migration
135+
136+
* **Bestehender Code:** Die Methode `MqttPublisher._handle_command` in `signalduino/mqtt.py` muss vollständig refaktorisiert werden, um den Dispatcher zu verwenden. Die bestehende Logik für `get/system/version` und `get/cc1101/frequency` wird entfernt und über den Dispatcher abgewickelt.
137+
* **Abhängigkeiten:** Keine neuen externen Abhängigkeiten erforderlich.
138+
139+
[[implementierungsplan]]
140+
== 8. Implementierungs-Plan
141+
142+
Der detaillierte Implementierungsplan wird in Phase 2 erstellt, basiert aber auf den folgenden High-Level-Schritten:
143+
144+
1. **Refactoring:** Initialisiere den `MqttCommandDispatcher` in `MqttPublisher.__init__` und aktualisiere `MqttPublisher._handle_command` zur Verwendung des Dispatchers.
145+
2. **Controller-Erweiterung:** Füge die High-Level-Methoden `command_factory_reset`, `get_cc1101_bandwidth`, `get_cc1101_rampl`, `get_cc1101_sensitivity`, `get_cc1101_datarate` zum `SignalduinoController` hinzu.
146+
3. **Serial Commands:** Implementiere die entsprechenden Low-Level-Methoden (`factory_reset`, `get_bwidth`, `get_rampl`, `get_sens`, `get_datarate`) in [`SignalduinoCommands`](signalduino/commands.py:20) inklusive der Register-Decodierungslogik.
147+
4. **Dispatcher-Aktualisierung:** Erweitere `COMMAND_MAP` in `signalduino/commands.py` um die neuen Befehle und deren Schemata.
148+
149+
[[cli-tool]]
150+
== 9. CLI Tool Design
151+
152+
Es wird ein kleines Python-Helfer-Tool (z.B. `signalduino-mqtt-cli`) entworfen, das über die Kommandozeile MQTT-Befehle senden kann. Dieses Tool wird die neuen Funktionen demonstrieren und zur Verifikation dienen.
153+
154+
=== 9.1. Befehlsdesign
155+
* `sd-mqtt-cli reset --req-id <ID>` (Sendet `command/factory_reset`)
156+
* `sd-mqtt-cli get hardware-status --req-id <ID> --parameter bandwidth` (Sendet `get/cc1101/bandwidth`)
157+
* `sd-mqtt-cli get hardware-status --all --req-id <ID>` (Optional: Implementiert einen Batch-Abruf oder ruft alle einzelnen GET-Befehle sequenziell ab und gibt das konsolidierte Ergebnis aus.)
158+
159+
Dieses Tool würde die `MqttPublisher` Logik des Hauptprogramms in einem CLI-Kontext nachbilden, um PUBLISH/SUBSCRIBE für Request/Response zu handhaben.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
= ADR {Nummer}: {Titel}
2+
:doctype: article
3+
:encoding: utf-8
4+
:lang: de
5+
:status: {Status}
6+
:decided-at: {Datum}
7+
:decided-by: {Entscheidungsträger}
8+
9+
[[kontext]]
10+
== Kontext
11+
12+
{Beschreibe den technischen Kontext, der zur Entscheidung geführt hat. Was sind die aktuellen Probleme oder die neuen Anforderungen?}
13+
14+
[[entscheidung]]
15+
== Entscheidung
16+
17+
{Beschreibe die getroffene Entscheidung. Dies sollte klar und prägnant sein.}
18+
19+
[[konsequenzen]]
20+
== Konsequenzen
21+
22+
{Beschreibe die positiven und negativen Konsequenzen der Entscheidung.}
23+
24+
=== Positive Konsequenzen
25+
*
26+
*
27+
28+
=== Negative Konsequenzen
29+
*
30+
*
31+
32+
[[alternativen]]
33+
== Alternativen
34+
{Hier können kurz abgelehnte Alternativen aufgeführt werden.}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
= Architektur-Proposal: {Proposal-Titel}
2+
:doctype: article
3+
:encoding: utf-8
4+
:lang: de
5+
:author: {Autor}
6+
:email: {Email}
7+
:revnumber: 1.0
8+
:revdate: {Datum}
9+
:xrefstyle: full
10+
11+
[[status]]
12+
== Status
13+
14+
|===
15+
|Status|Datum der letzten Änderung|Entscheidungsträger
16+
17+
|Draft
18+
|{Datum}
19+
|{Entscheidungsträger}
20+
|===
21+
22+
[[zusammenfassung]]
23+
== 1. Zusammenfassung (Executive Summary)
24+
25+
{Kurze Zusammenfassung des Proposals.}
26+
27+
[[problem-definition]]
28+
== 2. Problemstellung und Motivation
29+
30+
{Detaillierte Beschreibung des Problems, das gelöst werden soll, und der Grund, warum diese Lösung benötigt wird.}
31+
32+
[[ziele]]
33+
== 3. Ziele
34+
35+
{Klare, messbare Ziele des Proposals.}
36+
37+
[[vorgeschlagene-architektur]]
38+
== 4. Vorgeschlagene Architektur
39+
40+
{Detaillierte Beschreibung der vorgeschlagenen Architektur, Komponenten und deren Interaktion.}
41+
42+
=== 4.1. Komponenten-Diagramm (Mermaid)
43+
44+
[mermaid]
45+
----
46+
{Mermaid Diagramm}
47+
----
48+
49+
=== 4.2. Sequenz-Diagramm (Mermaid)
50+
51+
[mermaid]
52+
----
53+
{Mermaid Diagramm für den Hauptablauf}
54+
----
55+
56+
[[schnittstellen]]
57+
== 5. Betroffene Schnittstellen (APIs, MQTT Topics)
58+
59+
{Auflistung und Beschreibung aller neuen oder geänderten Schnittstellen.}
60+
61+
[[alternativen]]
62+
== 6. Alternativen in Betracht gezogen
63+
64+
{Beschreibung der in Betracht gezogenen Alternativen und der Gründe für deren Ablehnung.}
65+
66+
[[auswirkungen]]
67+
== 7. Auswirkungen und Migration
68+
69+
{Beschreibung der Auswirkungen auf bestehenden Code und des Migrationspfads.}
70+
71+
[[implementierungsplan]]
72+
== 8. Implementierungs-Plan
73+
74+
{High-Level Implementierungsschritte (Verweis auf Implementierungs-Plan in Phase 2).}

0 commit comments

Comments
 (0)