mqttWebServer.py 3.81 KB
Newer Older
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
1 2 3 4 5
#!/usr/bin/python3

import json
import time
import paho.mqtt.client as paho
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
6
import os
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
7 8
import parse
from http.server import HTTPServer, BaseHTTPRequestHandler
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
9 10 11 12 13 14 15 16 17 18 19 20 21
import logging, argparse

logging.basicConfig(format="%(asctime)-15s %(levelname)-8s  %(message)s")
logger = logging.getLogger("MQTT-HTTP-Server")

parser = argparse.ArgumentParser(description='MQTT <-> HTTP-Server.')
parser.add_argument("-v", "--verbosity", help="increase output verbosity", default=0, action="count")
parser.add_argument("-p", "--http-port", help="TCP port of HTTP server", default=8000)
parser.add_argument("-n", "--http-hostname", help="DNS name or IP address, where HTTP server will be available on. Possible: (empty string), 'localhost', DNS name or IP address", default="") 
parser.add_argument("-b", "--mqtt-broker-host", help="MQTT broker hostname", default="localhost")
parser.add_argument("mqtt_client_name", help="MQTT client name. Needs to be unique in the MQTT namespace, eg enocean-http-server.", type=str)
args = parser.parse_args()
logger.setLevel(logging.WARNING-(args.verbosity*10 if args.verbosity <=2 else 20) )
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
22 23 24 25 26 27 28



mlast = {}

def on_connect(client, userdata, flags, rc):
  if rc==0:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
29
    logger.info("MQTT connected OK. Return code"+str(rc))
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
30
    client.subscribe("#")
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
31
    logger.info("MQTT: Subscribed to all topics")
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
32
  else:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
33
    logger.error("Bad connection. Return code="+str(rc))
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
34 35 36

def on_disconnect(client, userdata, rc):
  if rc != 0:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
37
    logger.warning ("Unexpected MQTT disconnection. Will auto-reconnect")
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

def on_message(client, userdata, message):
    global mlast
    t = time.time()
    m = message.payload.decode("utf-8")
#    print("received:",str(m), message.topic)

    # the lambda in loads is for converting the object name into a int, if appropiate
    j = json.loads(m, object_hook=lambda d: {int(k) if k.lstrip('-').isdigit() else k: v for k, v in d.items()})
#    print ("Topic:",message.topic,"JSON",j)
    if type(j) is not dict: #anpassen, falls es keine Zeitinfo hat
      j={'v':j,'time':t}
    if t-j['time'] > 10: # corrects the time of too much in the future
      j['time'] = t

    mlast[message.topic] = j

Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
55
client= paho.Client(args.mqtt_client_name)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
56 57 58 59
client.on_message=on_message
client.on_connect = on_connect
client.on_disconnect = on_disconnect

Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
60 61
logger.debug("connecting to broker "+args.mqtt_broker_host)
client.connect(args.mqtt_broker_host)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
62
client.loop_start() #start loop to process received messages in separate thread
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
63
logger.info ("MQTT Loop started.")
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
64 65 66 67 68 69 70 71 72 73 74 75 76


class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/json")
        self.end_headers()
        # self.path equals GET Parameter: "/foo/bar/".
        erg = parse.parse("/{topic}/set?{value:d}", self.path)
        if not erg: erg = parse.parse("/{topic}", self.path)
        #print(erg)
        if erg and erg['topic'] in mlast:
          if 'value' in erg:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
77
            logger.info("publish: "+str(erg['topic'])+" value: "+str(erg['value']) )
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
78 79 80 81 82 83
            client.publish(erg['topic']+"/set", erg['value'], qos=1, retain=False)  # send command to Relais
            self.wfile.write(str.encode('{"result":"sucess", "topic":"'+erg['topic']+'", "value":'+str(erg['value'])+'}'))
          else:
            self.wfile.write(str.encode("/*You accessed path: {}*/\n{}".format(erg['topic'],
                                        json.dumps(mlast[erg['topic']]) )) )
        else:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
84
          self.wfile.write(str.encode(json.dumps(mlast) ))
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
85 86

try:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
87 88
  httpd = HTTPServer((args.http_hostname, args.http_port), SimpleHTTPRequestHandler)
  logger.info("Server started on port {} and host {}".format(args.http_port, args.http_hostname))
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
89 90 91
  httpd.serve_forever()

except KeyboardInterrupt:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
92
    logger.info ('^C received, shutting down the web server')
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
93 94
    httpd.server_close()
    client.disconnect()
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
95
    client.loop_stop()