From 3f3a6bcad472604e4aeb5041129643b0b6de487e Mon Sep 17 00:00:00 2001 From: Benjamin Braatz Date: Mon, 22 Mar 2021 15:35:13 +0100 Subject: [PATCH] Adapt to changes in controlpi. --- conf.json | 6 -- .../wsserver.py | 75 ++++++++++++++----- setup.py | 2 +- 3 files changed, 56 insertions(+), 27 deletions(-) rename {controlpi-plugins => controlpi_plugins}/wsserver.py (62%) diff --git a/conf.json b/conf.json index 8e4c584..bb7087a 100644 --- a/conf.json +++ b/conf.json @@ -35,11 +35,5 @@ "command": "set state", "new state": false } - }, - "Debug Logger": { - "plugin": "Log", - "filter": [ - {} - ] } } diff --git a/controlpi-plugins/wsserver.py b/controlpi_plugins/wsserver.py similarity index 62% rename from controlpi-plugins/wsserver.py rename to controlpi_plugins/wsserver.py index c19126b..2a8d625 100644 --- a/controlpi-plugins/wsserver.py +++ b/controlpi_plugins/wsserver.py @@ -1,6 +1,11 @@ -"""Provide … +"""Provide websocket server plugin. -TODO: documentation, doctests, resilient conf-parsing +… + +TODO: documentation, doctests +TODO: Mount multiple web apps from packages and file paths +TODO: Let Debug web app collapse/expand nested structures +TODO: Make Debug web app work with nested structures in commands """ import os import json @@ -10,24 +15,34 @@ from websockets.http import Headers from http import HTTPStatus from typing import Optional, Tuple -from controlpi import BasePlugin, MessageBus, Message, PluginConfiguration +from controlpi import BasePlugin, MessageBus, Message, MessageTemplate class Connection: - def __init__(self, bus: MessageBus, websocket: WebSocketServerProtocol, - path: str) -> None: + """Connection to websocket. + + Instances are created on external connections to websocket. + """ + + def __init__(self, bus: MessageBus, + websocket: WebSocketServerProtocol) -> None: + """Initialise conncection. + + Message bus and websocket are set by websocket server when creating + the connection. + """ self._bus = bus self._websocket = websocket address = self._websocket.remote_address self._address = address[0] self._port = address[1] self._name = f"{self._address}:{self._port}" - if path != '/': - self._name = path[1:] - self._bus.register(self._name, 'WSServer-Connection', - [{}], [{}], self._receive) + self._bus.register(self._name, 'WSServer', + [MessageTemplate()], [MessageTemplate()], + self._receive) async def _receive(self, message: Message) -> None: + """Receive messages from bus and relay to websocket.""" json_message = json.dumps(message) try: await self._websocket.send(json_message) @@ -35,6 +50,7 @@ class Connection: pass async def run(self): + """Listen on websocket and relay messages to bus.""" await self._bus.send({'sender': self._name, 'event': 'connection opened', 'address': self._address, @@ -56,9 +72,28 @@ Response = Optional[Tuple[HTTPStatus, Headers, bytes]] class WSServer(BasePlugin): + """Websocket server as ControlPi plugin. + + Run websocket server on host and port given in configuration, serving + the contents in given web root. + """ + + CONF_SCHEMA = {'properties': {'host': {'type': 'string'}, + 'port': {'type': 'integer'}, + 'web root': {'type': 'string'}}} + """Schema for WServer plugin configuration. + + Optional configuration keys: + + - 'host': network interfaces to listen on (default: None, meaning all + interfaces) + - 'port': port to connect to (default: 80) + - 'web root': root of files to serve (default: 'web') + """ + async def _handler(self, websocket: WebSocketServerProtocol, path: str) -> None: - connection = Connection(self._bus, websocket, path) + connection = Connection(self.bus, websocket) await connection.run() async def _process_request(self, path: str, @@ -89,25 +124,25 @@ class WSServer(BasePlugin): response_headers['Content-Length'] = str(len(body)) return HTTPStatus.OK, response_headers, body - def _process_conf(self, conf: PluginConfiguration) -> None: + def process_conf(self) -> None: + """Get host, port and path settings from configuration.""" self._port = 80 - if 'port' in conf: - self._port = conf['port'] + if 'port' in self.conf: + self._port = self.conf['port'] else: - print(f"'port' not configured for WSServer '{self._name}'." + print(f"'port' not configured for WSServer '{self.name}'." " Using 80.") web_root = 'web' - if 'web root' in conf: - web_root = conf['web root'] + if 'web root' in self.conf: + web_root = self.conf['web root'] else: - print(f"'web root' not configured for WSServer '{self._name}'." + print(f"'web root' not configured for WSServer '{self.name}'." " Using 'web'.") self._web_root = os.path.realpath(os.path.join(os.getcwd(), web_root)) - super()._process_conf(conf) async def run(self) -> None: - await super().run() + """Set up websocket server.""" await serve(self._handler, port=self._port, process_request=self._process_request) - print(f"WSServer '{self._name}' serving on port {self._port}.") + print(f"WSServer '{self.name}' serving on port {self._port}.") diff --git a/setup.py b/setup.py index a62d71e..76aca3a 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setuptools.setup( long_description=long_description, long_description_content_type="text/markdown", url="http://docs.graph-it.com/graphit/controlpi-wsserver", - packages=["controlpi-plugins"], + packages=["controlpi_plugins"], install_requires=[ "websockets", "controlpi @ git+git://git.graph-it.com/graphit/controlpi.git", -- 2.34.1