Skip to content
Merged
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
9 changes: 5 additions & 4 deletions .devcontainer/.devcontainer.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ MQTT_USERNAME=
# Optional: Leer lassen, wenn keine Authentifizierung erforderlich ist.
MQTT_PASSWORD=

# Das Basis-Topic für Signalduino Nachrichten.
# Nachrichten werden unter $MQTT_TOPIC/<protokoll_id> veröffentlicht.
# Befehle werden unter $MQTT_TOPIC/commands/# erwartet.
MQTT_TOPIC=signalduino/messages
# Das Basis-Topic (Root-Präfix) für MQTT-Kommunikation.
# Der Code hängt automatisch '/v1' an, um das Basis-Topic zu versionieren (z.B. 'signalduino/v1').
# Nachrichten werden unter $MQTT_TOPIC/v1/state/messages veröffentlicht.
# Befehle werden unter $MQTT_TOPIC/v1/commands/# erwartet.
MQTT_TOPIC=signalduino

# Signalduino Verbindungseinstellungen (für direkte Verwendung in main.py)
# Wähle entweder eine serielle Verbindung ODER eine TCP-Verbindung.
Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,18 @@ jobs:
with:
ruby-version: 3.4

# INSTALLATION: Installiere asciidoctor-kroki Gem für Mermaid-Unterstützung
- name: Install Asciidoctor Kroki Gem
run: gem install asciidoctor-kroki

- uses: reitzig/actions-asciidoctor@v2.0.2
with:
version: 2.0.26

- name: run asciidoctor
run: asciidoctor -R docs -D build/site/html -a docinfo=shared -a toc=left -a toclevels=2 'docs/index.adoc'
- 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'

- name: Setup Python for Sitemap Generation
uses: actions/setup-python@v5
Expand Down
8 changes: 4 additions & 4 deletions docs/01_user_guide/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ Die Hauptkomponenten sind:

Für einen schnellen Einstieg folgen Sie diesen Schritten:

1. **Installation**: Siehe link:installation.adoc[Installationsanleitung].
2. **Konfiguration**: Setzen Sie die erforderlichen Umgebungsvariablen (z.B. `SIGNALDUINO_SERIAL_PORT`, `MQTT_HOST`).
3. **Programm starten**: Führen Sie `python3 main.py` aus.
4. **MQTT‑Nachrichten überwachen**: Abonnieren Sie das Topic `signalduino/messages`, um dekodierte Signale zu empfangen.
. **Installation**: Siehe link:installation.adoc[Installationsanleitung].
. **Konfiguration**: Setzen Sie die erforderlichen Umgebungsvariablen (z.B. `SIGNALDUINO_SERIAL_PORT`, `MQTT_HOST`).
. **Programm starten**: Führen Sie `python3 main.py` aus.
. **MQTT‑Nachrichten überwachen**: Abonnieren Sie das Topic `signalduino/messages`, um dekodierte Signale zu empfangen.

Ausführliche Anleitungen finden Sie in den folgenden Kapiteln.

Expand Down
15 changes: 9 additions & 6 deletions docs/01_user_guide/installation.adoc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
= Installation
== Installation

[NOTE]
====
PySignalduino ist noch in Entwicklung. Es gibt bisher keine stabile Version – nutzen Sie die Software mit entsprechender Vorsicht.
PySignalduino ist noch in Entwicklung.
Es gibt bisher keine stabile Version – nutzen Sie die Software mit entsprechender Vorsicht.
====

== Voraussetzungen
Expand Down Expand Up @@ -30,7 +31,9 @@ Die einfachste Methode ist die Installation aus dem geklonten Repository im Entw

[source,bash]
----
include::../examples/bash/install_via_pip.sh[]
git clone https://github.com/Ein-Einfaches-Beispiel/PySignalduino.git
cd PySignalduino
pip install -e .
----

Dadurch wird das Paket `signalduino-mqtt` in Ihrer Python-Umgebung installiert und alle Runtime-Abhängigkeiten werden erfüllt.
Expand All @@ -41,7 +44,7 @@ Falls Sie das Paket nicht installieren, sondern nur die Abhängigkeiten nutzen m

[source,bash]
----
include::../examples/bash/install_requirements.sh[]
pip install -r requirements.txt
----

Die Datei `requirements.txt` enthält die gleichen Pakete wie oben aufgelistet.
Expand All @@ -52,7 +55,7 @@ Für Beiträge zum Projekt oder zum Ausführen der Tests installieren Sie zusät

[source,bash]
----
include::../examples/bash/install_dev_requirements.sh[]
pip install -r requirements-dev.txt
----

Dies installiert:
Expand All @@ -68,7 +71,7 @@ Dies installiert:

[source,bash]
----
include::../examples/bash/verify_installation.sh[]
python3 main.py --help
----

Sie sollten eine Ausgabe mit allen verfügbaren Kommandozeilenoptionen sehen.
Expand Down
9 changes: 4 additions & 5 deletions docs/01_user_guide/mqtt_api.adoc
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
= MQTT API Reference
:doctype: book
:icons: font
:toc: left
:toclevels: 2
:doctype: book :icons: font :toc: left :toclevels: 2
:sectnums:

[[_mqtt_introduction]]
Expand All @@ -12,7 +9,9 @@ Die MQTT-Schnittstelle ermöglicht die Steuerung des PySignalduino-Gateways und

=== Topics und Struktur

Die Basis aller Topics ist `<base_topic>/v1`, wobei `<base_topic>` standardmäßig `signalduino` ist. Alle Beispiele verwenden `signalduino/v1` als Basis.
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`.
Alle nachfolgenden Beispiele verwenden `signalduino/v1` als Basis.

|===
| Zweck | Topic-Format | Anmerkungen
Expand Down
7 changes: 5 additions & 2 deletions docs/01_user_guide/usage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ include::../../main.py[lines=55..84]

==== Heartbeat-Funktionalität

Der Publisher sendet regelmäßig einen Heartbeat („online“) unter `{topic}/status`, solange die Verbindung besteht. Bei Verbindungsabbruch wird „offline“ gepublished.
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

Expand Down Expand Up @@ -106,7 +107,9 @@ Die folgenden Befehle werden unterstützt (Auswahl):

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

Expand Down
9 changes: 6 additions & 3 deletions docs/02_developer_guide/architecture.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

== Übersicht

PySignalduino ist modular aufgebaut und trennt die Protokolldefinitionen (JSON) strikt von der Verarbeitungslogik (Python). Seit der Migration zu asyncio (Version 0.9.0) folgt das System einer ereignisgesteuerten, asynchronen Architektur, die auf asyncio-Tasks und -Queues basiert. Dies ermöglicht eine effiziente Verarbeitung von Sensordaten, Kommandos und MQTT-Nachrichten ohne Blockierung.
PySignalduino ist modular aufgebaut und trennt die Protokolldefinitionen (JSON) strikt von der Verarbeitungslogik (Python).
Seit der Migration zu asyncio (Version 0.9.0) folgt das System einer ereignisgesteuerten, asynchronen Architektur, die auf asyncio-Tasks und -Queues basiert.
Dies ermöglicht eine effiziente Verarbeitung von Sensordaten, Kommandos und MQTT-Nachrichten ohne Blockierung.

== Kernkomponenten

Expand Down Expand Up @@ -154,9 +156,10 @@ Die Sitemap wird durch das Python-Skript `tools/generate_sitemap.py` generiert,

Das Skript kann manuell ausgeführt werden:

```bash
[source,bash]
----
python tools/generate_sitemap.py --build-dir build/site/html --output sitemap.xml --branch main
```
----

=== robots.txt-Konfiguration

Expand Down
35 changes: 24 additions & 11 deletions docs/02_developer_guide/contribution.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

[NOTE]
====
Da PySignalduino noch in aktiver Entwicklung ist, können sich Code-Strukturen und APIs schnell ändern. Bitte synchronisieren Sie Ihren Fork regelmäßig mit dem upstream-Repository.
Da PySignalduino noch in aktiver Entwicklung ist, können sich Code-Strukturen und APIs schnell ändern.
Bitte synchronisieren Sie Ihren Fork regelmäßig mit dem upstream-Repository.
====

Beiträge zum Projekt sind willkommen!

== Workflow

1. **Fork & Clone:** Projekt forken und lokal klonen.
2. **Branch:** Feature-Branch erstellen (`git checkout -b feature/mein-feature`).
3. **Entwicklung:** Änderungen implementieren.
4. **Tests:** Sicherstellen, dass alle Tests bestehen (`pytest`).
5. **Pull Request:** PR auf GitHub öffnen.
. **Fork & Clone:** Projekt forken und lokal klonen.
. **Branch:** Feature-Branch erstellen (`git checkout -b feature/mein-feature`).
. **Entwicklung:** Änderungen implementieren.
. **Tests:** Sicherstellen, dass alle Tests bestehen (`pytest`).
. **Pull Request:** PR auf GitHub öffnen.

== Entwicklungsumgebung

Expand All @@ -23,7 +24,7 @@ Das Projekt verwendet `poetry` für die Abhängigkeitsverwaltung. Installieren S

[source,bash]
----
include::../../examples/bash/install_dev_deps.sh[]
include::../examples/bash/install_dev_deps.sh[]
----

Oder verwenden Sie `poetry install` (falls Poetry konfiguriert ist).
Expand All @@ -42,25 +43,37 @@ Das Projekt folgt PEP 8. Verwenden Sie `black` für automatische Formatierung un

[source,bash]
----
include::../../examples/bash/format_code.sh[]
include::../examples/bash/format_code.sh[]
----

Es gibt keine strikte CI-Prüfung, aber konsistenter Stil wird erwartet.

=== Dokumentationsstil

Für alle AsciiDoc-Dateien im `docs/` Verzeichnis ist die Regel "Ein Satz pro Zeile" verpflichtend.
Dies erleichtert die Überprüfung von Änderungen mittels `git diff`.

[IMPORTANT]
====
Jeder Satz muss auf einer neuen Zeile beginnen.
Ein Satz endet typischerweise mit einem Punkt (`.`), Ausrufezeichen (`!`) oder Fragezeichen (`?`).
Achten Sie darauf, dass Codeblöcke und Tabellen nicht betroffen sind.
====

== Tests ausführen

Das Projekt nutzt `pytest`. Stellen Sie sicher, dass `requirements-dev.txt` installiert ist.

[source,bash]
----
include::../../examples/bash/run_pytest.sh[]
include::../examples/bash/run_pytest.sh[]
----

Für spezifische Testmodule:

[source,bash]
----
include::../../examples/bash/run_specific_tests.sh[]
include::../examples/bash/run_specific_tests.sh[]
----

=== Asyncio-Tests
Expand Down Expand Up @@ -91,7 +104,7 @@ Coverage-Bericht generieren:

[source,bash]
----
include::../../examples/bash/coverage_report.sh[]
include::../examples/bash/coverage_report.sh[]
----

Der Bericht wird im Verzeichnis `htmlcov/` erstellt.
Expand Down
3 changes: 1 addition & 2 deletions docs/02_developer_guide/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

Dieser Abschnitt beschreibt die Architektur, wie man zur Entwicklung beitragen kann (Contributing) und wie man Tests durchführt.

include::architecture.adoc[]
include::contribution.adoc[]
include::architecture.adoc[] include::contribution.adoc[]

== Weitere Ressourcen

Expand Down
6 changes: 3 additions & 3 deletions docs/03_protocol_reference/protocol_details.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ Die Datei `sd_protocols/protocols.json` ist die definitive Quelle für alle Prot

== Neues Protokoll hinzufügen

1. Definition in `protocols.json` ergänzen.
2. Dekodierungsmethode implementieren (z.B. in `sd_protocols/manchester.py`).
3. Tests hinzufügen.
. Definition in `protocols.json` ergänzen.
. Dekodierungsmethode implementieren (z.B. in `sd_protocols/manchester.py`).
. Tests hinzufügen.
10 changes: 4 additions & 6 deletions docs/architecture/decisions/ADR-001-mqtt-get-frequency.adoc
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
= 001. Implementierung des MQTT-Befehls get/frequency
:author: Roo
:revdate: 2026-01-03
:status: Accepted
:author: Roo :revdate: 2026-01-03 :status: Accepted

[[status]]
== Status
Expand All @@ -26,9 +24,9 @@ $$f_{RF} = \frac{26}{65536} \times F_{REG} \, \text{MHz}$$

Wir implementieren den `get/frequency` Befehl als Teil der `MqttHandler`- und `Commands`-Klassen.

1. *MQTT Topic*: Der Befehl wird über `cmd/get/frequency` empfangen (komplettes Topic: `<base_topic>/commands/get/frequency`).
2. *Antwort Topic*: Die Antwort wird über das etablierte Antwort-Topic (`<base_topic>/responses`) veröffentlicht, um Konsistenz mit dem bestehenden `get/system/version` Befehl zu gewährleisten. Der Payload muss das Feld `command` enthalten, um die Herkunft zu kennzeichnen.
3. *Berechnungslogik*: Die Berechnung wird exakt nach der CC1101-Formel unter Verwendung von $F_{XOSC} = 26 \, \text{MHz}$ durchgeführt.
. *MQTT Topic*: Der Befehl wird über `cmd/get/frequency` empfangen (komplettes Topic: `<base_topic>/commands/get/frequency`).
. *Antwort Topic*: Die Antwort wird über das etablierte Antwort-Topic (`<base_topic>/responses`) veröffentlicht, um Konsistenz mit dem bestehenden `get/system/version` Befehl zu gewährleisten. Der Payload muss das Feld `command` enthalten, um die Herkunft zu kennzeichnen.
. *Berechnungslogik*: Die Berechnung wird exakt nach der CC1101-Formel unter Verwendung von $F_{XOSC} = 26 \, \text{MHz}$ durchgeführt.
* Wir erstellen eine asynchrone Methode in `signalduino/hardware.py` (z.B. `get_frequency_registers()`) zum Auslesen von FREQ2, FREQ1, FREQ0.
* 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.
Expand Down
15 changes: 5 additions & 10 deletions docs/architecture/decisions/ADR-002-mqtt-command-dispatcher.adoc
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
= 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)
:doctype: article :encoding: utf-8 :lang: de :status: Accepted :decided-at: 2026-01-04 :decided-by: Roo (Architekt)

[[kontext]]
== Kontext
Expand All @@ -20,9 +15,9 @@ Die zentrale Verwaltung der Befehle und deren Validierung ist eine bewährte Met

Die hartcodierte `if/elif`-Logik in `signalduino/mqtt.py` wird durch die Verwendung des `MqttCommandDispatcher` ersetzt.

1. Der `MqttPublisher` in `signalduino/mqtt.py` wird eine Instanz des `MqttCommandDispatcher` erhalten.
2. Die Methode `_handle_command` in `MqttPublisher` wird umgeschrieben, um den eingehenden Befehlspfad und Payload direkt an `MqttCommandDispatcher.dispatch()` zu übergeben.
3. Die Fehler- und Erfolgsantworten werden vom `MqttCommandDispatcher` zurückgegeben und von `MqttPublisher` an die entsprechenden MQTT-Topics (`/responses` und `/errors`) publiziert.
. Der `MqttPublisher` in `signalduino/mqtt.py` wird eine Instanz des `MqttCommandDispatcher` erhalten.
. 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]]
== Konsequenzen
Expand All @@ -40,4 +35,4 @@ Die hartcodierte `if/elif`-Logik in `signalduino/mqtt.py` wird durch die Verwend
[[alternativen]]
== 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.
* **Anderer Dispatch-Mechanismus:** Die Verwendung des vorhandenen `MqttCommandDispatcher` ist die pragmatischste Lösung, da die Klasse bereits existiert und die Validierungsinfrastruktur bietet.
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
= 003. Verwendung von CC1101-Standardformeln für Frequenz und Datenrate
:revdate: 2026-01-04
:status: Accepted
:revdate: 2026-01-04 :status: Accepted

== Context
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`).

Um die Steuerung der CC1101-Parameter zu konsolidieren, wurden die Spezialbefehle (`X4C`, `X5C`) in der Python-Implementierung durch generische Register-Writes (`W`) ersetzt, wobei die Umrechnungslogik (z.B. Index in Registerwert) in Python implementiert wurde.
Um die Steuerung der CC1101-Parameter zu konsolidieren, wurden die Spezialbefehle (`X4C`, `X5C`) in der Python-Implementierung durch generische Register-Writes (`W`) ersetzt, wobei die Umrechnungslogik (z.B.
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
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).
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).

Diese Registerwerte werden dann über generische CC1101-Schreibbefehle des Signalduino-Protokolls (`W<RegisterAddress><Value>`) übertragen.

Expand All @@ -28,4 +29,4 @@ Nach dem Senden aller Register-SET-Befehle muss die Methode `SignalduinoCommands
* **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:**
* _Ablehnungsgrund:_ Die `SignalduinoCommands`-Klasse ist der logische Ort für die Umrechnung von physikalischen Einheiten in serielle Protokolle, da sie die Schnittstelle zum physischen Gerät darstellt. Die `SignalduinoController`-Klasse soll nur die MQTT-Payload entpacken.
* _Ablehnungsgrund:_ Die `SignalduinoCommands`-Klasse ist der logische Ort für die Umrechnung von physikalischen Einheiten in serielle Protokolle, da sie die Schnittstelle zum physischen Gerät darstellt. Die `SignalduinoController`-Klasse soll nur die MQTT-Payload entpacken.
11 changes: 2 additions & 9 deletions docs/architecture/proposals/mqtt_factory_reset_and_settings.adoc
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
= Architektur-Proposal: MQTT-basierter Factory Reset und Hardware-Status
:doctype: article
:encoding: utf-8
:lang: de
:author: Roo (Architekt)
:email: roo@pythonsignalduino.com
:revnumber: 1.0
:revdate: 2026-01-04
:xrefstyle: full
:doctype: article :encoding: utf-8 :lang: de :author: Roo (Architekt) :email: roo@pythonsignalduino.com :revnumber: 1.0 :revdate: 2026-01-04 :xrefstyle: full

[[status]]
== Status
Expand Down Expand Up @@ -156,4 +149,4 @@ Es wird ein kleines Python-Helfer-Tool (z.B. `signalduino-mqtt-cli`) entworfen,
* `sd-mqtt-cli get hardware-status --req-id <ID> --parameter bandwidth` (Sendet `get/cc1101/bandwidth`)
* `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.)

Dieses Tool würde die `MqttPublisher` Logik des Hauptprogramms in einem CLI-Kontext nachbilden, um PUBLISH/SUBSCRIBE für Request/Response zu handhaben.
Dieses Tool würde die `MqttPublisher` Logik des Hauptprogramms in einem CLI-Kontext nachbilden, um PUBLISH/SUBSCRIBE für Request/Response zu handhaben.
Loading
Loading