Initial version
authorBenjamin Braatz <bb@bbraatz.eu>
Wed, 5 May 2021 01:12:33 +0000 (03:12 +0200)
committerBenjamin Braatz <bb@bbraatz.eu>
Wed, 5 May 2021 01:12:33 +0000 (03:12 +0200)
.gitignore [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
conf-controller.json [new file with mode: 0644]
conf-machine.json [new file with mode: 0644]
controlpi_plugins/wsclient.py [new file with mode: 0644]
doc/index.md [new file with mode: 0644]
setup.py [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..63dbcc8
--- /dev/null
@@ -0,0 +1,4 @@
+__pycache__/
+dist/
+controlpi_wsclient.egg-info/
+venv/
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..ebb8ac1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2021 Graph-IT GmbH
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..fb01570
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# ControlPi Plugin for Websocket Clients
+This distribution package contains a plugin for the
+[ControlPi system](https://docs.graph-it.com/graphit/controlpi), that
+<…>
+
+Documentation (in German) can be found at [doc/index.md](doc/index.md) in
+the source repository and at
+[https://docs.graph-it.com/graphit/controlpi-wsclient/](https://docs.graph-it.com/graphit/controlpi-wsclient/).
+Code documentation (in English) including doctests is contained in the
+source files.
+An API documentation generated by pdoc3 can be found at
+[doc/controlpi_plugins/index.html](doc/controlpi_plugins/index.html) in the source
+repository and at
+[https://docs.graph-it.com/graphit/controlpi-wsclient/controlpi_plugins/](https://docs.graph-it.com/graphit/controlpi-wsclient/controlpi_plugins/).
diff --git a/conf-controller.json b/conf-controller.json
new file mode 100644 (file)
index 0000000..d6d1cd8
--- /dev/null
@@ -0,0 +1,13 @@
+{
+    "Controller": {
+        "plugin": "WSServer",
+        "port": 8080,
+        "web": {
+            "/": { "module": "controlpi_plugins.wsserver",
+                   "location": "Debug" }
+        }
+    },
+    "Engine Clearance": {
+        "plugin": "State"
+    }
+}
diff --git a/conf-machine.json b/conf-machine.json
new file mode 100644 (file)
index 0000000..92aca76
--- /dev/null
@@ -0,0 +1,29 @@
+{
+    "Machine": {
+        "plugin": "WSServer",
+        "port": 8081,
+        "web": {
+            "/": { "module": "controlpi_plugins.wsserver",
+                   "location": "Debug" }
+        }
+    },
+    "Controller": {
+        "plugin": "WSClient",
+        "url": "ws://localhost:8080",
+        "client": "Machine",
+        "filter": { "sender": { "const": "Engine" } }
+    },
+    "Engine Clearance": {
+        "plugin": "Alias",
+        "from": { "sender": { "const": "Controller" },
+                  "original sender": { "const": "Engine Clearance" } },
+        "to": { }
+    },
+    "Engine Switch": {
+        "plugin": "State"
+    },
+    "Engine": {
+        "plugin": "AndState",
+        "states": [ "Engine Clearance", "Engine Switch" ]
+    }
+}
diff --git a/controlpi_plugins/wsclient.py b/controlpi_plugins/wsclient.py
new file mode 100644 (file)
index 0000000..45f5c13
--- /dev/null
@@ -0,0 +1,63 @@
+"""Provide …
+
+…
+
+TODO: documentation, doctests
+"""
+import json
+import websockets
+from controlpi import BasePlugin, Message, MessageTemplate
+
+
+class WSClient(BasePlugin):
+    """… plugin.
+
+    Do this and that.
+    """
+
+    CONF_SCHEMA = {'properties':
+                   {'url': {'type': 'string'},
+                    'client': {'type': 'string'},
+                    'filter': {'type': 'object'}},
+                   'required': ['url', 'client', 'filter']}
+
+    async def _receive(self, message: Message) -> None:
+        assert isinstance(message['sender'], str)
+        original_sender = message['sender']
+        if 'original sender' in message:
+            original_sender += f"/{message['original sender']}"
+        message['original sender'] = original_sender
+        del message['sender']
+        if 'target' in message:
+            assert isinstance(message['target'], str)
+            prefix = f"{self.name}/"
+            if message['target'].startswith(prefix):
+                message['target'] = message['target'][len(prefix):]
+            else:
+                message['target'] = f"{self.conf['client']}/{message['target']}"
+        json_message = json.dumps(message)
+        await self._websocket.send(json_message)
+
+    def process_conf(self) -> None:
+        """Register plugin as bus client."""
+        self.bus.register(self.name, 'WSClient', [MessageTemplate()],
+                          [self.conf['filter']], self._receive)
+
+    async def run(self) -> None:
+        """Send initial message."""
+        async with websockets.connect(self.conf['url']) as websocket:
+            self._websocket = websocket
+            async for json_message in websocket:
+                message = json.loads(json_message)
+                original_sender = message['sender']
+                if 'original sender' in message:
+                    original_sender += f"/{message['original sender']}"
+                message['original sender'] = original_sender
+                message['sender'] = self.name
+                if 'target' in message:
+                    prefix = f"{self.conf['client']}/"
+                    if message['target'].startswith(prefix):
+                        message['target'] = message['target'][len(prefix):]
+                    else:
+                        message['target'] = f"{self.name}/{message['target']}"
+                await self.bus.send(message)
diff --git a/doc/index.md b/doc/index.md
new file mode 100644 (file)
index 0000000..cded302
--- /dev/null
@@ -0,0 +1,28 @@
+# ControlPi-Plugin für Websocket-Client
+Dieses Paket enthält ein Plugin für das ControlPi-System, mit dem <…>
+
+## Benutzung
+…
+
+## Installation
+Eine ausführliche Dokumentation ist in der Dokumentation der
+[ControlPi-Infrastruktur](https://docs.graph-it.com/graphit/controlpi) zu
+finden.
+
+Der Code dieses Plugins kann mit git geclonet werden:
+```sh
+$ git clone git://git.graph-it.com/graphit/controlpi-wsclient.git
+```
+(Falls Zugang zu diesem Server per SSH besteht und Änderungen gepusht
+werden sollen, sollte stattdessen die SSH-URL benutzt werden.)
+
+Dann kann es editierbar in ein virtuelles Environment installiert werden:
+```sh
+(venv)$ pip install --editable <Pfad zum Code-Repository>
+```
+
+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-wsclient.git
+```
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..f2bf674
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,25 @@
+import setuptools
+
+with open("README.md", "r") as readme_file:
+    long_description = readme_file.read()
+
+setuptools.setup(
+    name="controlpi-wsclient",
+    version="0.1.0",
+    author="Graph-IT GmbH",
+    author_email="info@graph-it.com",
+    description="ControlPi Plugin for Websocket Clients",
+    long_description=long_description,
+    long_description_content_type="text/markdown",
+    url="http://docs.graph-it.com/graphit/controlpi-wsclient",
+    packages=["controlpi_plugins"],
+    install_requires=[
+        "websockets",
+        "controlpi @ git+git://git.graph-it.com/graphit/controlpi.git",
+    ],
+    classifiers=[
+        "Programming Language :: Python",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+    ],
+)