--- /dev/null
+__pycache__/
+controlpi.egg-info/
+venv/
--- /dev/null
+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.
--- /dev/null
+# Control-Pi-Infrastruktur
+
--- /dev/null
+{ "delay":
+ [ { "name": "D1",
+ "seconds": 1.0 },
+ { "name": "D2",
+ "seconds": 2.0 } ],
+ "alias":
+ [ { "name": "One-second delay",
+ "aliasfor":
+ { "name": "D1" } },
+ { "name": "Two-second delay finished",
+ "aliasfor":
+ { "name": "D2",
+ "event": "finished" } } ],
+ "log":
+ [ { "name": "Debug Logger",
+ "filter": {} },
+ { "name": "Test Logger",
+ "filter": { "name": "Test" } } ],
+ "init":
+ [ { "name": "Test Procedure",
+ "commands":
+ [ { "name": "Test", "event": "started" },
+ { "name": "D1", "command": "start" },
+ { "name": "D2", "command": "start" },
+ { "name": "D1", "command": "start" },
+ { "name": "D2", "command": "start" },
+ { "name": "Test", "event": "stopped" } ] } ] }
--- /dev/null
+import sys
+import json
+import asyncio
+import pkgutil
+import importlib
+
+import controlpi.plugins
+
+
+def get_triggered(message, triggers):
+ result = []
+ for key in triggers:
+ if key == '*':
+ result.extend(triggers[key])
+ elif key in message:
+ value = message[key]
+ if value in triggers[key]:
+ result.extend(get_triggered(message, triggers[key][value]))
+ return result
+
+
+class ControlPi:
+ def __init__(self):
+ self._queue = asyncio.Queue()
+ self._triggers = {}
+
+ def register(self, message_template, async_callback):
+ current = self._triggers
+ for key in message_template:
+ value = message_template[key]
+ if key not in current:
+ current[key] = {}
+ if value not in current[key]:
+ current[key][value] = {}
+ current = current[key][value]
+ if '*' not in current:
+ current['*'] = []
+ current['*'].append(async_callback)
+
+ async def send(self, message):
+ await self._queue.put(message)
+
+ async def run(self):
+ while True:
+ message = await self._queue.get()
+ for async_callback in get_triggered(message, self._triggers):
+ asyncio.create_task(async_callback(message))
+ self._queue.task_done()
+
+
+async def main():
+ plugins = {name: importlib.import_module(name)
+ for finder, name, ispkg
+ in pkgutil.iter_modules(controlpi.plugins.__path__,
+ controlpi.plugins.__name__ + ".")}
+ control_pi = ControlPi()
+ coros = [control_pi.run()]
+ with open(sys.argv[1]) as json_data:
+ conf = json.load(json_data)
+ for plugin_name in conf:
+ full_plugin_name = 'controlpi.plugins.' + plugin_name
+ if full_plugin_name in plugins:
+ module = plugins[full_plugin_name]
+ plugin_confs = conf[plugin_name]
+ for plugin_conf in plugin_confs:
+ coros.append(module.init(control_pi, plugin_conf))
+ await asyncio.gather(*coros)
+
+
+if __name__ == '__main__':
+ try:
+ asyncio.run(main())
+ except KeyboardInterrupt:
+ pass
--- /dev/null
+import asyncio
+import functools
+
+
+async def waiter(control_pi, name, seconds, message):
+ await asyncio.sleep(seconds)
+ await control_pi.send({"name": name, "event": "delay finished"})
+
+
+async def init_finished(name):
+ print(f"{name} initialised")
+
+
+def init(control_pi, conf):
+ control_pi.register({"name": conf['name'], "command": "start"},
+ functools.partial(waiter,
+ control_pi,
+ conf['name'],
+ conf['seconds']))
+ return init_finished(conf['name'])
--- /dev/null
+import asyncio
+
+
+async def run(control_pi, conf):
+ print(f"{conf['name']} initialised")
+ for message in conf['commands']:
+ await control_pi.send(message)
+
+
+def init(control_pi, conf):
+ return run(control_pi, conf)
--- /dev/null
+import functools
+
+
+async def logger(name, message):
+ print(f"{name}: {message}")
+
+
+async def init_finished(name):
+ print(f"{name} initialised")
+
+
+def init(control_pi, conf):
+ control_pi.register(conf['filter'],
+ functools.partial(logger, conf['name']))
+ return init_finished(conf['name'])
--- /dev/null
+import setuptools
+
+with open("README.md", "r") as readme_file:
+ long_description = readme_file.read()
+
+setuptools.setup(
+ name="controlpi",
+ version="0.1.0",
+ author="Graph-IT GmbH",
+ author_email="info@graph-it.com",
+ description="Control-Pi-Infrastruktur",
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ url="http://docs.graph-it.com/graphit/controlpi",
+ packages=setuptools.find_namespace_packages(include=['controlpi_plugins.*']),
+ classifiers=[
+ "Programming Language :: Python",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ ],
+)