Zum Ende der Metadaten springen
Zum Anfang der Metadaten

Ich habe mir um von außen per Http mit SSL-Verschlüsselung Sachen an meinem Miniserver schalten zu können (z.B. über Tasker oder IFTTT) einen kleinen Serverdienst in Python programmiert, welcher aus solchen POST's ein UDP Paket an meinen Miniserver schickt. Das ganze läuft bei mir auf einem Raspberry Pi, müsste natürlich aber auch auf anderen System laufen:

Ihr müsst ein System haben auf welchem Python 3.x installiert ist.

Zunächst installiert ihr bottle für Python, bottle ist ein simpler http Server Dienst:

sudo apt-get install python-bottle 

 

Wenn ihr das fertig habt müsst ihr euch ein SSL-Zertifikat erstellen mit:

openssl req -new -x509 -keyout key.pem -out cert.pem -days 3650 -nodes

Wichtig ist dass ihr bei "common name" den korrekten Namen, sprich den DNS Eintrag unter dem euer RPi zu erreichen seid eintragt!

Das SSL Zertifikat hat eine gültigkeit von 3650 Tagen, wird allerdings in Browsern als nicht sicher angezeigt, da es von euch selbst zertifiziert ist! Für eine HTTP Post von Tasker oder IFTTT spielt das aber keine Rolle!

Dann erstellt ihr das Python Programm mittels

nano https2UDP.py

und fügt dann folgenden Code dort ein:

#!/usr/bin/python
# -*- config: utf-8 -*-

from __future__ import absolute_import, division, print_function
import bottle
from bottle import request, route, get, run, ServerAdapter, post

import sys
import socket

# !!!! BEGIN CHANGE ME !!!!

Miniserver_IP = ''
Miniserver_PORT = 8885
Http_Port = 21607
Http_Route = 'dhjkfgsd8345igsdf78534gdgbfg786345i6'
Zertifikat = '/home/pi/cert.pem'
Schluessel = '/home/pi/key.pem'

# !!!! END CHANGE ME !!!!

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# copied from bottle. Only changes are to import ssl and wrap the socket
class SSLWSGIRefServer(ServerAdapter):
    def run(self, handler):
        from wsgiref.simple_server import make_server, WSGIRequestHandler
        import ssl
        if self.quiet:
            class QuietHandler(WSGIRequestHandler):
                def log_request(*args, **kw): pass
            self.options['handler_class'] = QuietHandler
        srv = make_server(self.host, self.port, handler, **self.options)
        srv.socket = ssl.wrap_socket (
         srv.socket,
         certfile=Zertifikat,
         keyfile=Schluessel,  # path to certificates
         server_side=True)
        srv.serve_forever()

@post('/'+Http_Route+'/<Befehl>')
def UDP_Befehl(Befehl):
    print('Http Befehl: ')
    print (Befehl+"\n")
    sock.sendto(bytes(Befehl, 'utf-8') , (Miniserver_IP, Miniserver_PORT))
#    return 'Thank you for your post'


def main():
#    bottle.run(host='', port=Http_Port)
    srv = SSLWSGIRefServer(host='', port=Http_Port)
    run(server=srv)

if __name__ == '__main__':
    main()

Hier müsst ihr dann noch folgende Dinge eintragen:

Miniserver_IP = 'x.x.x.x'
Miniserver_PORT = 8885
Http_Port = 21607
Http_Route = 'Hallo'
Zertifikat = '/home/pi/cert.pem'
Schluessel = '/home/pi/key.pem'

 

Miniserver_IP und entsprechenden UDP Empfangs-Port

Http_Port: hier muss dann auch eine Port-Weiterleitung von aussen eingerichtet werden (Fritzbox, oder anderen Router)

Http_Route: Diese wird benötigt um anzugeben wie der Aufruf aussehen soll. Tragt ihr dort zum Beispiel "Hallo" ein muss der Aufruf wie folgt aussehen: my.DynDNS.eintrag:21607/Hallo/Eurer_Befehle. Hierbei wird dann alles nach dem /Hallo/ als UPD Nachricht an den Miniserver geschickt. Das my.DynDNS.eintrag und der Port müssen natürlich von euch entsprechend angepasst werden. Ich habe meine Http_Route einfach aus Groß-, Kleinbuchstaben und Zahlen zusammengesetzt. Das macht es noch schwerer um von aussen zufällig etwas an den MS zu schicken.

Zertifikat und Schluessel: Hier müssen das Zertifikat und der Client-Schlüssel welche ihr oben erzeugt habt mit kompletten Pfad angegeben werden.

Wichtig noch: das ganze funktioniert nur mit Http POST Anfragen, alle andere habe ich gesperrt! Gebt ihr die Adresse daher in einen Browser ein kommt folgende Meldung:

Error: 405 Method Not Allowed

Bekommt ihr diese Meldung wisst ihr aber das alles richtig installiert ist. Bzw. vorher müsste noch die Meldung kommen das das Zertifikat nicht sicher ist, da ihr es ja selbst erstellt habt.

 

So das war es auch schon.

Nun könnt ihr das ganze testen:

python3 https2UDP.py

Um das Programm beim Starten zu starten geht ihr wie folgt vor:

sudo crontab -e

hier trag ihr dann folgendes ein:

@reboot python3 /home/pi/httpsUDP.py > /dev/null 2>&1

So und schon sollte es laufen.

 

Falls fragen sind bitte einfach melden.

 

Gruß Iksi

20 Kommentare

  1. Robert sagt:

    Many thanks for great article like this. I was looking exactly for this one !

    Is there any way to install on loxberry bottle for Python please? If yes how please?

     I would like to use IFTTT to switch me outputs in miniserver exactly as you wrote...for example to manage blinds by voice (google assistent + webhooks)

    Many many thanks for help!!! 

  2. Hello,

    Bottle does not depend on any external libraries. You can just download bottle.py into your project directory and start coding:

    $ wget https://bottlepy.org/bottle.py
  3. Robert sagt:

    Thanks for reply, but could you please be more exact how to install in loxberry please?

    I am not any linux guy...(traurig). Thanks

  4. Robert sagt:

    Hello again

    So finnaly I have instaled bottle for python to loxberry as you can see in attached pictures.

    I did all steps, but finally if i tried to chcek on local network without portforwarding as you can see in the browser I have error 404 instead of 405 and if I tried command: python3 https2UDP.py I have there -bash: python3: command not found ( as you can see in the pictures)

    Can you please help me ? I am lost (traurig)

    Many many thanks

     

  5. Hello Robert,

    Looks like you have to install python3, log in and install as Root:

    apt-get install python3

    I think the Folder on loxberry must be /opt/loxberry/ ...., you forgot the /opt/

     

    Maybe you have to perform an 'chown 777 https2UDP.py' this will give every user the Access to the script, i am not sure if it has to be...

     

    Then it should work, i hope (Zwinkern)

  6. Robert sagt:

    Hi Christian,

    Thanks for help! 

    I have logged in as root and instaleld python3 as you wrote.

    Also I have changed folder as you wrote /opt/loxberry you were right as you can see in attached picture.

    Also I have changed chown to 777.

    # !!!! BEGIN CHANGE ME !!!!


    Miniserver_IP = '192.168.1.201'
    Miniserver_PORT = 15001
    Http_Port = 80
    Http_Route = 'Hello'
    Zertifikat = '/opt/loxberry/cert.pem'
    Schluessel = '/opt/loxberry/key.pem'

    # !!!! END CHANGE ME !!!!

     

    Once I have tried to place command: python3 https2UDP.py

    ------------------------------------------------------------------------------------------------

    root@loxberry:/opt/loxberry/python# python3 https2UDP.py
    Bottle v0.13-dev server starting up (using SSLWSGIRefServer())...
    Listening on http://:80/
    Hit Ctrl-C to quit.

    Traceback (most recent call last):
    File "https2UDP.py", line 55, in <module>
    main()
    File "https2UDP.py", line 52, in main
    run(server=srv)
    File "/opt/loxberry/python/bottle.py", line 3713, in run
    server.run(app)
    File "https2UDP.py", line 33, in run
    srv = make_server(self.host, self.port, handler, **self.options)
    File "/usr/lib/python3.4/wsgiref/simple_server.py", line 153, in make_server
    server = server_class((host, port), handler_class)
    File "/usr/lib/python3.4/socketserver.py", line 429, in __init__
    self.server_bind()
    File "/usr/lib/python3.4/wsgiref/simple_server.py", line 50, in server_bind
    HTTPServer.server_bind(self)
    File "/usr/lib/python3.4/http/server.py", line 133, in server_bind
    socketserver.TCPServer.server_bind(self)
    File "/usr/lib/python3.4/socketserver.py", line 440, in server_bind
    self.socket.bind(self.server_address)
    OSError: [Errno 98] Address already in use
    root@loxberry:/opt/loxberry/python#

     

    Could you please help me ?

  7. The port is already in use. You're trying to use the Standard Http Port 80, the Loxberry Web Frontend is already on that port.

    So Change the Port to something that is definetly not in use and it should work.

    When you got that and the Programm is startet by cron you can check if the process is running by typing 'ps aux | grep python'. This command will list you every instance of Python running.

     

    best regards

     

    Christian

  8. Robert sagt:

    Thanks mate for help..uff I am so tired (traurig)

    yeaa I have found that port change is needed so I have changed to port 8125

    look here:

    loxberry@192.168.1.188's password:

    The programs included with the Debian GNU/Linux system are free software;
    the exact distribution terms for each program are described in the
    individual files in /usr/share/doc/*/copyright.

    Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
    permitted by applicable law.
    Last login: Mon Nov 27 00:03:36 2017 from 192.168.1.112
    loxberry@loxberry:~$ python3 https2UDP.py
    python3: can't open file 'https2UDP.py': [Errno 2] No such file or directory
    loxberry@loxberry:~$ su -
    Password:
    root@loxberry:~# python3 https2UDP.py
    Traceback (most recent call last):
    File "https2UDP.py", line 5, in <module>
    import bottle
    ImportError: No module named 'bottle'
    root@loxberry:~# cd opt
    -su: cd: opt: No such file or directory
    root@loxberry:~# cd /opt
    root@loxberry:/opt# cd loxberry
    root@loxberry:/opt/loxberry# cd python
    root@loxberry:/opt/loxberry/python# python3 https2UDP.py
    Bottle v0.13-dev server starting up (using SSLWSGIRefServer())...
    Listening on http://:8125/
    Hit Ctrl-C to quit.

    ^Croot@loxberry:/opt/loxberry/python#
    root@loxberry:/opt/loxberry/python# ps aux | grep python
    root 1419 0.0 0.1 12728 2040 pts/0 S+ 00:16 0:00 grep python
    root@loxberry:/opt/loxberry/python# ps aux | grep python
    root 1459 0.0 0.2 12728 2228 pts/0 S+ 00:16 0:00 grep python
    root@loxberry:/opt/loxberry/python# ps aux | grep python
    root 1461 0.0 0.2 12728 2236 pts/0 S+ 00:16 0:00 grep python
    root@loxberry:/opt/loxberry/python#

    is it ok?

    and now how to check it ? 

    I did post by loxone output and by browser:

    http://192.168.1.188:8125/Hello/input=1

    nothing happens..I cant see it in udp monitor (traurig)

  9. Robert sagt:

    all workiiiing (Lächeln)

    I can manage miniserver outputs by speach (Lächeln) great.

    Thanks for help many times!

    I would like to ask, how you manage more commands in miniserver UDP inputs?

    https://my.DynDNS.eintrag:21607/Hallo/input=x or using different call?

  10. Kann man das auch nutzen um Befehle auszuführen?

  11. Wie meinst du das?
    Du bekommst dann ja eine Udp Message zum Ms und kannst damit ja beliebige Befehle ausführen....

    1. Naja ... wie kann man mit der UDP-Message was ausführen. Das ist mir nicht so ganz klar. Bis dato habe ich alles was per UDP reinkam, maximal irgendwo in der Visio verwendet. Würde mich freuen, wenn Du das kurz näher erläutern würdest.

      Danke

      1. Naja der Udp Eingang ist ja ein Eingang wie jeder andere, du kannst ihn genau so verwenden wie einen Merker, einen Hardware Eingang oder auch einen virtuellen Eingang...

        1. Habe das jetzt hinbekommen. Auch der UDP-Monitor zeigt das eingehende Kommando an. Aber es passiert irgendwie nichts.

           

           

           

  12. Hallo zusammen,

    ich habe soweit alles installiert, auch bottle. Sobald ich das Ganze aber teste, bekomme ich folgende Meldung:

    pi@raspberrypi:~ $ python3 https2UDP.py
    Traceback (most recent call last):
    File "https2UDP.py", line 5, in <module>
    import bottle
    ImportError: No module named 'bottle'

    Hat hier jemand vielleicht eine Idee?

    1. An sich steht da das du bottle nicht Installiert hast.
      Geh mal in der shell in das Verzeichnis in dem auch die https2udp.py liegt und lade bottle einfach mittels folgendem Befehl runter:

      wget https://bottlepy.org/bottle.py

      Dann sollte es gehen.

  13. Danke erstmal für die schnelle Antwort.

    Sieht jetzt auch besser aus. D.h. wenn ich meine Anfrage im Browser starte, bekomme ich auch den entsprechenden Fehler 405.

    Nur im MS kommt noch nicht wirklich etwas an. Ich habe hier einen virtuellen UDP Eingang angelegt. Als Empfangsport habe ich dann die 8885 eingetragen. Das müsste doch eigentlich alles sein was zu machen ist oder?

    In meinem Router habe ich jetzt nur eine Freischaltung für den Port 21607 eingerichtet, keine Weiterleitung..!? 

  14. Naja du musst noch einen virtuellen Eingang Befehl anlegen.
    Oder guckst du über den UDP Monitor?

    Ansonsten scheint es zu passen. Die Weiterleitung an den RPi funktioniert, sonst hättest du ja nicht den Fehler 405. Hast du auch die richtige Route eingegeben?
    Worüber testest du das? Über den Browser geht es nicht, wie beschrieben funktioniere nur Http Post anfragen, das geht nicht per Browser.

  15. Wenn ich über die App "http shortcuts" einen post Befehl absende, taucht dieser auch in meinem UDP Monitor in der Loxone auf. Was ich allerdings noch nicht hinbekommen habe, ist einen Befehl über einen Webhook vom IFTTT zu senden.. Hier bekomme ich jedesmal die Fehlermeldung "unable to make a web request Error: ETIMEDOUT" 

    Ich habe schon mehrere Einstellungen probiert. Eingestellt habe ich folgendes:

    https://192.XXX.X.XXX:21607/Hallo/VU1=1
    Method: Post
    Content Type: application/json

    Aber irgendwie will der post einfach nicht gesendet werden. Was hast du hier eingestellt?

     

    1. Ich denke Dein Problem ist dass Du die interne Netzwerk IP deiner RPi angibst. Unter der kann dich IFTTT natürlich nicht erreichen. Wenn Du eine feste Internet IP hast musst Du diese angeben, sollte das nicht der Fall sein musst Du dir einen DynDNS Account anlegen und entsprechend einen dynamischen DNS Eintrag erstellen unter welchem Dein Router dann vom Internet aus zu erreichen ist.

       

      ansonsten habe ich es genau so eingestellt, ich habe allerdings keinen Content Type angegeben, dass macht aber glaube ich keinen Unterschied.