First version finished. master
authorBenjamin Braatz <benjamin.braatz@graph-it.com>
Wed, 23 Aug 2023 08:34:38 +0000 (10:34 +0200)
committerBenjamin Braatz <benjamin.braatz@graph-it.com>
Wed, 23 Aug 2023 08:34:38 +0000 (10:34 +0200)
.gitignore
conf.json
controlpi_plugins/readjson.py
doc/Test.png [new file with mode: 0644]
doc/index.md

index 6ece8bc1690c94904f8aa041e8132e4391f3562c..4cb81f4095af522e1a9869f5982aee940d908623 100644 (file)
@@ -1,2 +1,3 @@
 /venv/
 /controlpi_readjson.egg-info/
+__pycache__/
index 559c7023f456bc264353c48288e960ac8f66e517..d8d6c1d499501cd1901027935d039c95dcf5dd8f 100644 (file)
--- a/conf.json
+++ b/conf.json
@@ -1,16 +1,10 @@
 {
-    "Debug": {
-        "plugin": "WSServer",
-        "web": {
-            "/": {
-                "module": "controlpi_plugins.wsserver",
-                "location": "Debug"
-            }
-        }
-    },
     "Log": {
         "plugin": "Log",
         "filter": [{}]
     },
-    <...>
+    "SerialTest": {
+        "plugin": "ReadJSON",
+        "device": "/tmp/serialout"
+    }
 }
index 72284c9f6565f1231ae1d117143d2c92bd990db7..e8cbf0d127bd3347b11b088299e9829a754a220f 100644 (file)
@@ -1,41 +1,58 @@
-"""ControlPi Plugin for <PURPOSE>."""
+"""ControlPi Plugin for Reading JSON from a Serial Device."""
 import asyncio
+import json
+import serial  # type: ignore
+import serial_asyncio  # type: ignore
 
 from controlpi import BasePlugin, Message, MessageTemplate
 from controlpi.baseplugin import JSONSchema
 
 
-class Example(BasePlugin):
-    """ControlPi plugin for <PURPOSE>."""
+class ReadJSON(BasePlugin):
+    """ControlPi plugin for reading JSON from a serial device."""
 
     CONF_SCHEMA: JSONSchema = {'properties':
-                               {'init': {'type': 'boolean',
-                                         'default': False}},
-                               'required': []}
+                               {'device': {'type': 'string'},
+                                'baudrate': {'type': 'integer',
+                                             'enum': [2400, 4800, 9600,
+                                                      19200, 38400, 57600,
+                                                      115200],
+                                             'default': 115200},
+                                'parity': {'type': 'string',
+                                           'enum': ['even', 'odd', 'none'],
+                                           'default': 'none'},
+                                'stopbits': {'type': 'integer',
+                                             'minimum': 1,
+                                             'maximum': 2,
+                                             'default': 1}},
+                               'required': ['device']}
 
     def process_conf(self) -> None:
-        """Register bus client."""
-        self._state = self.conf['init']
-        self.bus.register(self.name, 'Example',
-                          [MessageTemplate({'event':
-                                            {'const': 'changed'},
-                                            'state':
-                                            {'type': 'boolean'}}),
-                           MessageTemplate({'state':
-                                            {'type': 'boolean'}})],
-                          [([MessageTemplate({'target':
-                                              {'const': self.name},
-                                              'command':
-                                              {'const': 'get state'}})],
-                            self._get_state)])
-
-    async def _get_state(self, message) -> None:
-        await self.bus.send(Message(self.name, {'state': self._state}))
+        """Process configuration and register bus client."""
+        # Translate string in configuration to constant from serial module:
+        if self.conf['parity'] == 'even':
+            self._parity = serial.PARITY_EVEN
+        elif self.conf['parity'] == 'odd':
+            self._parity = serial.PARITY_ODD
+        elif self.conf['parity'] == 'none':
+            self._parity = serial.PARITY_NONE
+        # Register message bus client:
+        # Can send arbitrary messages, receives nothing.
+        self.bus.register(self.name, 'ReadJSON', [MessageTemplate()], [])
 
     async def run(self) -> None:
-        """Run main loop of the plugin."""
+        """Run main loop of the plugin reading the serial device."""
+        reader, writer = await serial_asyncio.open_serial_connection(
+                url=self.conf['device'], baudrate=self.conf['baudrate'],
+                parity=self._parity, stopbits=self.conf['stopbits'])
         while True:
-            self._state = not self._state
-            await self.bus.send(Message(self.name, {'event': 'changed',
-                                                    'state': self._state}))
-            await asyncio.sleep(10)
+            line = await reader.readline()
+            message = None
+            try:
+                message = json.loads(line.decode())
+            except UnicodeDecodeError:
+                pass
+            except json.decoder.JSONDecodeError:
+                pass
+            if message and isinstance(message, dict):
+                await self.bus.send(Message(self.name, message))
diff --git a/doc/Test.png b/doc/Test.png
new file mode 100644 (file)
index 0000000..b543de6
Binary files /dev/null and b/doc/Test.png differ
index 4a1d3c2bfa7d8ca200a23e1fb126a1acb0d5bdaa..fcd673d856002ac0d946e46bb77030069fb19b9f 100644 (file)
@@ -1,5 +1,9 @@
-# ControlPi-Plugin für <ZWECK>
-Dieses Paket enthält ein Plugin für das ControlPi-System, mit dem <...>
+# ControlPi-Plugin für das Lesen von JSON von einem seriellen Device
+Dieses Paket enthält ein Plugin für das ControlPi-System, mit dem von einem
+seriellen Device JSON-Nachrichten gelesen und auf den Message-Bus des
+ControlPi-Systems gelegt werden können.
+Alle auf der seriellen Schnittstelle gelesenen Zeilen, die keine
+JSON-Objekte sind, werden ignoriert.
 
 ## Installation
 Eine ausführliche Dokumentation ist in der Dokumentation der
@@ -8,7 +12,7 @@ finden.
 
 Der Code dieses Plugins kann mit git geclonet werden:
 ```sh
-$ git clone git://git.graph-it.com/graphit/controlpi-<NAME>.git
+$ git clone git://git.graph-it.com/graphit/controlpi-readjson.git
 ```
 (Falls Zugang zu diesem Server per SSH besteht und Änderungen gepusht
 werden sollen, sollte stattdessen die SSH-URL benutzt werden.)
@@ -21,7 +25,7 @@ Dann kann es editierbar in ein virtuelles Environment installiert werden:
 Auf dem Raspberry Pi (oder wenn keine Code-Änderungen gewünscht sind) kann
 es auch direkt, ohne einen git-Clone installiert werden:
 ```sh
-(venv)$ pip install git+git://git.graph-it.com/graphit/controlpi-<NAME>.git
+(venv)$ pip install git+git://git.graph-it.com/graphit/controlpi-readjson.git
 ```
 
 ## Benutzung
@@ -29,25 +33,26 @@ Eine minimale ControlPi-Konfiguration, die dieses Plugin benutzt, ist im
 git-Repository enthalten:
 ```json
 {
-    "Debug": {
-        "plugin": "WSServer",
-        "web": {
-            "/": {
-                "module": "controlpi_plugins.wsserver",
-                "location": "Debug"
-            }
-        }
-    },
     "Log": {
         "plugin": "Log",
         "filter": [{}]
     },
-    <...>
+    "SerialTest": {
+        "plugin": "ReadJSON",
+        "device": "/tmp/serialout"
+    }
 }
 ```
 
-<...>
+Als weitere Schlüssel können in der Konfiguration `"baudrate"`, `"parity"`
+und `"stopbits"` angegeben werden, wobei die Voreinstellungen `115200`,
+`"none"` und `1` sind.
 
-Mit dieser Beispiel-Konfiguration kann <...>  in der Debug-Oberfläche des
-ControlPi und im Log in `journalctl -u controlpi` gesehen werden:
-![Debug-Oberfläche](graphit/controlpi-<NAME>/DebugView.png)
+Wir können das Plugin ohne serielle Hardware testen, in dem wir mit `socat`
+eine virtuelle serielle Verbindung aufmachen:
+```shell
+$ socat -d -d pty,raw,echo=0,link=/tmp/serialin pty,raw,echo=0,link=/tmp/serialout
+```
+Dann kann die Beispiel-Konfiguration von `/tmp/serialout` lesen und mit
+`echo` Test-Nachrichten in `/tmp/serialin` geschrieben werden:
+![Test mit virtuellem seriellen Device](graphit/controlpi-readjson/Test.png)