mqttWebServer.py 4.06 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
import logging, argparse
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
10
from urllib.parse import unquote # for converting url encoded string to unicode string
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
11 12 13 14 15 16 17 18 19 20 21 22

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
23 24 25 26 27 28 29



mlast = {}

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

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

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
56
client= paho.Client(args.mqtt_client_name)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
57 58 59
client.on_message=on_message
client.on_connect = on_connect
client.on_disconnect = on_disconnect
60 61
client.enable_logger(logger)
logger.info("connecting to broker "+args.mqtt_broker_host)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
62
client.connect(args.mqtt_broker_host)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
63
client.loop_start() #start loop to process received messages in separate thread
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
64
logger.info ("MQTT Loop started.")
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
65 66 67 68 69


class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
70
        self.send_header("Content-type", "text/json;charset=UTF-8")
71
        self.send_header("Access-Control-Allow-Origin", "*")
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
72 73 74 75 76
        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)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
77 78
        topic = unquote(erg['topic']) if erg else None
        if erg and topic in mlast:
79
          #print("topic:",topic)
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
80
          if 'value' in erg:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
81 82 83
            logger.info("publish: "+str(topic)+" value: "+str(erg['value']) )
            client.publish(topic+"/set", erg['value'], qos=1, retain=False)  # send command to Relais
            self.wfile.write(str.encode('{"result":"sucess", "topic":"'+topic+'", "value":'+str(erg['value'])+'}'))
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
84
          else:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
85 86
            self.wfile.write(str.encode("/*You accessed path: {}*/\n{}".format(topic,
                                        json.dumps(mlast[topic]) )) )
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
87
        else:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
88
          self.wfile.write(json.dumps(mlast, ensure_ascii=False).encode('utf8') )
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
89 90

try:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
91 92
  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
93 94 95
  httpd.serve_forever()

except KeyboardInterrupt:
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
96
    logger.info ('^C received, shutting down the web server')
Peter-Bernd Otte's avatar
Peter-Bernd Otte committed
97 98
    httpd.server_close()
    client.disconnect()
99
    client.loop_stop()