From 363b1a69338a775ee8bd608bfba8c5e7bf3fed1e Mon Sep 17 00:00:00 2001 From: johannes walcher <johannes.walcher@stusta.de> Date: Sat, 4 Aug 2018 18:24:59 +0200 Subject: [PATCH] Added remote-hbf-python --- haspa_web/__main__.py | 10 +---- haspa_web/haspa.py | 47 ++++++++++++---------- hauptbahnhof/__init__.py | 5 +++ hauptbahnhof/knechtmqtt.py | 82 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 29 deletions(-) create mode 100644 hauptbahnhof/knechtmqtt.py diff --git a/haspa_web/__main__.py b/haspa_web/__main__.py index 6d7a99d..1772c1d 100644 --- a/haspa_web/__main__.py +++ b/haspa_web/__main__.py @@ -1,19 +1,11 @@ -import asyncio from haspa_web.haspa import HaspaWeb def main(): """ Actually start the shit """ - loop = asyncio.get_event_loop() haspaweb = HaspaWeb() - loop.set_debug(True) - try: - loop.run_forever() - except KeyboardInterrupt: - pass - - loop.run_until_complete(haspaweb.teardown()) + haspaweb.hbf.start(spinoff=False) if __name__ == "__main__": main() diff --git a/haspa_web/haspa.py b/haspa_web/haspa.py index e2bf9cf..7e7488e 100644 --- a/haspa_web/haspa.py +++ b/haspa_web/haspa.py @@ -1,44 +1,50 @@ -import asyncio +""" +Display the haspa state as website +""" + import time +import json from pathlib import Path -from hauptbahnhof import Hauptbahnhof +import libconf + +from hauptbahnhof import MQTTBahnhofClient class HaspaWeb: """ Recreate the haspa website to represent the current haspa state + -- This will connect to a remote hauptbahnhof client! """ - def __init__(self, loop=None): - if not loop: - loop = asyncio.get_event_loop() + def __init__(self): + with open('/etc/hauptbahnhof/hauptbahnhof.conf') as cfgfile: + self.config = libconf.load(cfgfile) - self.loop = loop - self.hbf = Hauptbahnhof(loop) - self.hbf.subscribe('/haspa/status', self.command_state) + self.hbf = MQTTBahnhofClient(self.config, { + '/haspa/status': self.command_state + }) prism_path = Path(__file__).resolve().parent self.config = {} self.config['TEMPLATE_PATH'] = prism_path / "templates" self.config['OUTPUT_PATH'] = prism_path / "html" - async def teardown(self): - """ Clean it up """ - await self.hbf.teardown() - - async def command_state(self, client, message, _): - """ After the state has changed, do something! """ - del client - print(message) + def command_state(self, client, userdata, mqttmsg): + """ /haspa/status change detected """ + del client, userdata + message = json.loads(mqttmsg.payload.decode('utf-8')) + print("Received:", message) if 'haspa' in message: if message['haspa'] in ['open', 'offen', 'auf']: - self.set_state(message['haspa'], True) + self.set_state(True) elif message['haspa'] in ['close', 'zu', 'closed']: - self.set_state(message['haspa'], False) + self.set_state(False) else: print("Haspa state undetermined: ", message['haspa']) + else: + print("Invalid Message received") - def set_state(self, state, is_open): + def set_state(self, is_open): """ Export the current haspa state to the website @@ -46,7 +52,6 @@ class HaspaWeb: Change any old and glorious routines! """ for template in self.config['TEMPLATE_PATH'].glob('*.tpl'): - print("Updating templates") outfile = self.config['OUTPUT_PATH'] / template.stem with open(str(template), 'r') as orig: @@ -57,3 +62,5 @@ class HaspaWeb: time.strftime("%a, %d %b %Y %H:%M:%S")) with open(str(outfile), 'w') as new: new.write(content) + def run(self): + self.hbf.start() diff --git a/hauptbahnhof/__init__.py b/hauptbahnhof/__init__.py index 962b541..387c219 100644 --- a/hauptbahnhof/__init__.py +++ b/hauptbahnhof/__init__.py @@ -1 +1,6 @@ +""" +Initialize magic +""" + from .hauptbahnhof import Hauptbahnhof +from .knechtmqtt import MQTTBahnhofClient diff --git a/hauptbahnhof/knechtmqtt.py b/hauptbahnhof/knechtmqtt.py new file mode 100644 index 0000000..88be443 --- /dev/null +++ b/hauptbahnhof/knechtmqtt.py @@ -0,0 +1,82 @@ +import logging +from paho.mqtt.client import Client as PahoMqttClient + +class MQTTBahnhofClient: + """ + A simplified hauptbahnhof client that is able to connect to a remote mqtt + with username password and certificates + """ + def __init__(self, config, subscriptions): + logformat = '%(asctime)s | %(name)s | %(levelname)5s | %(message)s' + logging.basicConfig(format=logformat) + self.log = logging.getLogger(__name__) + if config['hauptbahnhof']['debug']: + self.log.setLevel(logging.DEBUG) + else: + self.log.setLevel(logging.INFO) + + self.host = config['hauptbahnhof']['host'] + self.subscriptions = subscriptions + + self.mqtt = PahoMqttClient() + self.mqtt.enable_logger(self.log) + self.mqtt.on_message = self.on_message + self.mqtt.on_connect = self.on_connect + + ssl = {"ca_certs": config['hauptbahnhof']['ca_crt'], + "certfile": config['hauptbahnhof']['certfile'], + "keyfile": config['hauptbahnhof']['keyfile']} + self.mqtt.tls_set(**ssl) + + auth = {"username": config['hauptbahnhof']['username'], + "password": config['hauptbahnhof']['password']} + self.mqtt.username_pw_set(**auth) + + def on_message(self, client, userdata, msg): + """ + A message was received. push it back towards the async context + """ + self.log("Unhandled message has arrived: %s %s %s", client, userdata, msg) + + def on_connect(self, client, userdata, flags, returncode): + """ After a successfull connection the topics are set and subscribed """ + del client, userdata + if returncode == 0: + print("Flags: ", flags) + self.mqtt.subscribe([(topic, 0) for topic in self.subscriptions]) + + if not 'session present' in flags or flags['session present'] == 0: + # If we have a new session + for topic, callback in self.subscriptions.items(): + if callback: + self.mqtt.message_callback_add(topic, callback) + + else: + try: + msg = { + 0: "Connection successful", + 1: "Incorrect Protocol Version", + 2: "Invalid client identifier", + 3: "Server unavailable", + 4: "Bad username or password", + 5: "Not authorized", + }[returncode] + except KeyError: + msg = "Unknown error occured: " + returncode + print("Connection refused: ", msg) + + + def publish(self, topic, data): + """ Publish a message """ + self.mqtt.publish(topic, data) + + def start(self, spinoff=True): + """ Connect and start the mqtt machine """ + self.mqtt.connect(self.host, port=8883) + self.log.info("Successfully connected to %s", self.host) + + # Spinning of a thread for the magic + if spinoff: + self.mqtt.loop_start() + else: + self.mqtt.loop_forever() -- GitLab