Navigation

    egnite Forum
    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    1. Home
    2. Marco T
    3. Posts
    • Profile
    • Following
    • Followers
    • Topics
    • Posts
    • Best
    • Groups

    Posts made by Marco T

    • RE: HTTP-Push in Firmware 6.x

      @tim said in HTTP-Push in Firmware 6.x:

      Man könnte den API-Key bei Palamoa auch als HTTP-Header übertragen, damit er nicht mehr Teil der URL ist.

      Das ist ein guter Hinweis. Wobei ich in der Doku keine Möglichkeit sehe, custom header zu setzen.
      Aber Palamoa bietet ja auch die Möglichkeit, den Key ins JSON zu packen... das muss ich mal ausprobieren.

      A few minutes later...
      Cool, es hat funktioniert!

      {%option url%}http://palamoa.de/json/{%endoption-%}
      {%option method%}POST{%endoption-%}
      {%option num_variables%}1{%endoption-%}
      {%option title1%}API-Key{%endoption-%}
      Content-Type: application/json; charset=utf-8
      {# #}
      {
      "apikey": "{{actiontab_var1}}",
      "device_name":   "{{hostname}}",
      "{{sensortab_name.0}}": 
      {
      "unit":     "{{sensortab_unit.0}}",
      

      Jetzt ist der API-Key schön im JSON verpackt...
      Achja, in der Doku auf palamoa.de fehlen die Anführungszeichen um "apikey". Ohne die Danführungszeichen gibt es nen http error 400 vom palamoa Server.
      Screenshot von palamoa.de

      posted in Generelle Diskussion
      Marco T
    • RE: HTTP-Push in Firmware 6.x

      @tim said in HTTP-Push in Firmware 6.x:

      {%option url%}http://palamoa.de/json/{{actiontab_var1}}{%endoption-%}

      Mit dieser Information habe ich ich "mein" Template für palamoa jetzt angepasst:

      {%option url%}http://palamoa.de/json/{{actiontab_var1}}{%endoption-%}
      {%option method%}POST{%endoption-%}
      {%option num_variables%}1{%endoption-%}
      {%option title1%}API-Key{%endoption-%}
      add=/tpl/j/p_thpdp.tpl
      
      Content-Type: application/json; charset=utf-8
      {# #}
      {
          "device_name":   "{{hostname}}",
          "{{sensortab_name.0}}": 
          {
            "unit":     "{{sensortab_unit.0}}",
            "color":     "#5F8AFF",
            "label":     "{{sensortab_name.0}}",
            "value":     "{{sensortab_value.0}}"
          },
          "{{sensortab_name.1}}": 
          {
            "unit":     "{{sensortab_unit.1}}",
            "color":     "#32d08e",
            "label":     "{{sensortab_name.1}}",
            "value":     "{{sensortab_value.1}}"
          },
          "{{sensortab_name.2}}": 
          {
            "unit":     "{{sensortab_unit.2}}",
            "color":     "#e86161",
            "label":     "{{sensortab_name.2}}",
            "value":     "{{sensortab_value.2}}"
          },
          "{{sensortab_name.3}}": 
          {
            "unit":     "{{sensortab_unit.3}}",
            "color":     "#2222F0",
            "label":     "{{sensortab_name.3}}",
            "value":     "{{sensortab_value.3}}"
          },
      	"{{sensortab_name.0}}_Status": 
          {
            "unit":     "Status",
            "color":     "#5F8AFF",
            "label":     "{{sensortab_name.0}}_Status",
            "value":     "{{sensortab_status.0}}"
          },
          "{{sensortab_name.1}}_Status": 
          {
            "unit":     "Status",
            "color":     "#32d08e",
            "label":     "{{sensortab_name.1}}_Status",
            "value":     "{{sensortab_status.1}}"
          },
          "{{sensortab_name.2}}_Status": 
          {
            "unit":     "status",
            "color":     "#e86161",
            "label":     "{{sensortab_name.2}}_Status",
            "value":     "{{sensortab_status.2}}"
          },
          "{{sensortab_name.3}}_Status": 
          {
            "unit":     "Status",
            "color":     "#2222F0",
            "label":     "{{sensortab_name.3}}_Status",
            "value":     "{{sensortab_status.3}}"
          }
        }
      

      So funktioniert alles wieder wie bisher und erst noch schöner 🙂

      Was ich festgestellt habe ist, dass jetzt im Syslog die url nicht mehr angezeigt wird. Solange ich nur einen http push habe ist das kein Thema, könnte aber bei mehreren pushes schnell unübersichtlich werden.

      2022-11-30_15h00_05.png
      Dafür kann ich den Screenshot davon jetzt posten ohne den API-Key zuerst verschwinden lassen zu müssen.

      posted in Generelle Diskussion
      Marco T
    • RE: HTTP-Push in Firmware 6.x

      Hallo Tim

      Vielen Dank für die schnelle Antwort.
      Ich verwende für Palamoa eben ein custom template das auch den Status mit überträgt. Mal sehen, ob ich die Originaldatei noch finde (sie ist wahrscheinlich auch hier im Forum, also kein Problem 🙂 ) Ich werde mein Template also entsprechend überarbeiten.

      Das mit dem Logeintrag habe ich nochmals ausprobiert... Ich bekomme jetzt fast immer 2 Einträge (Configuration x saved und den http request).
      Ausser, wenn ich einen Test auslöse, wenn gerade etwas anderes verarbeitet wird:
      2022-11-30_14h20_29.png

      Kann es sein, dass einfach die Log-Einträge von den letzten x Sekunden angezeigt werden? Vom Timing her könnte es in der Original-Nachricht stimmen da ich alle 5 Minuten die Daten an palamoa sende.

      posted in Generelle Diskussion
      Marco T
    • HTTP-Push in Firmware 6.x

      Guten Tag

      Seit dem Update auf Version 6 hat bei mir der HTTP-Push auf Palamoa nicht mehr funktioniert. Heute habe ich herausgefunden, wieso.
      2022-11-30_10h38_30.png

      Um etws flexibler zu sein, hatte ich "$1" in der Service URL für die "Variable 1" verwendet. Dies wird offenbar nicht mehr unterstützt. "Test" ergab einen 404 vom Server.

      Lösung: ich hab das $1 direkt durch den API-Key ersetzt und es funktioniert wieder.

      Natürlich habe ich nach 2 Minuten noch ein 2. Mal getestet indem ich auf "Test" geklickt habe und festgestellt, dass für alle Logeinträge immer die Zeit des letzten Ereignisses angezeigt wird.
      2022-11-30_10h35_40.png

      Bei der Unterstützung von Variablen in der Service-URL weiss ich nicht, ob dies überhaupt ein Bug ist. Die Zietanzeigt bei den Log-Einträgen scheint mir aber nicht so gewollt.

      Heute getestet mit 6.0.10.1, zuvor mit 6.0.7.1

      posted in Generelle Diskussion
      Marco T
    • RE: Windows 11, Webinterface zeigt Momentanwerte nicht sofort

      Ein unter WSL 2 laufender Firefox auf demselben Computer ist nicht betroffen. Dies ergibt insofern Sinn, als dass mit WSL 2 ein komplettes, virtualisiertes Linux unter Windows läuft.
      Win11 QuerxQuirk
      Links Firefox nativ Windows 11, Rechts Firefox unter Debian in Windows Subsystem for Linux 2

      Unter WSL 1, das den Netzwerkstack von Windows zu nutzen scheint, kann ich es nicht so leicht testen, da dort das Laufelassen von GUI Applikationen nicht ohne weiteres möglich ist (Ja, es gibt Workarounds)

      posted in Generelle Diskussion
      Marco T
    • Windows 11, Webinterface zeigt Momentanwerte nicht sofort

      Wenn ich auf meinem neuen Gerät mit Windows 11 das Webinterface des Querx THP aufrufe (getestet mit Firmware 5.0.14.1 und 5.0.16.1) werden die Momentanwerte nicht angezeigt.
      2022-05-09_08h06_53.png
      Nach 1 Minute werden die Werte dann angezeigt (hängt von der eingestellten refersh-rate des Webinterface ab, ich hab es normal auf 1min eingestellt. Stelle ich es auf 10s bekomme ich die Werte nach 10s).
      2022-05-09_08h07_38.png

      Überraschend: auf meinem alten Gerät mit Windows 10 tritt das Phänomen nicht auf. Auf beiden Geräten läuft Firefox Version 100.

      Getestet:
      Windows 10: Firefox 100, Chrome 101.0.4951.54, Edge 101.0.1210.39 -> Momentanwerte werden sofort dargestellt
      Windows 11: Forefox 100, Edge 101.0.1210.39 -> Momentanwerte werden erst angezeigt, wenn die Seite das Erste mal sich selber aktualisiert

      Durch Reload der Website (mit F5 oder Reload-Symbol) wird das Verhalten von Neuem getriggert (Auf Windows 11 werden die Momentanwerte dann nicht mehr dargestellt).

      Ein für mich völlig unerklärliches Phänomen das ich nicht "Bug" oder "Fehler" nennen will. Aber möglicherweise von Interesse für euch.

      posted in Generelle Diskussion
      Marco T
    • RE: Python / Datenbank / Querx Beispiel

      Und wer den Taupunkt wissen will, kann den SQL-Server rechnen lassen. Der macht das sehr effizient :). Bei der Abfrage von 19'000 Zeilen aus der DB lag der Zeitunterschied für die Verarbeitung der Abfrage unterhalb der mehr oder weniger zufälligen Varianz zwischen mehreren Anfragen.

      SELECT *,  ROUND(-4283.58 / ((LN( HumidityAvg / 100 ) + ((17.625*TemperatureAvg) / (243.04+TemperatureAvg))) - 17.625) - 243.04, 1) AS DewPoint
        FROM ClimateData
        WHERE TIME BETWEEN '2022-01-1 00:00' AND '2022-03-11 09:00'
      

      Die Resultate der Rechnung stimmen mit jenen überein, die vom Querx-Sensor selber ausgegeben werden.
      Die ursprünglich Formel war:

      ROUND( (243.04 * (LN( HumidityAvg / 100) + ((17.625 * TemperatureAvg) / (243.04 + TemperatureAvg)))) / (17.625 - (LN( HumidityAvg / 100 ) + ((17.625*TemperatureAvg) / (243.04+TemperatureAvg)))), 1) AS DewPoint
      

      Da ist aber (LN( HumidityAvg / 100) + ((17.625 * TemperatureAvg) / (243.04 + TemperatureAvg)) 2 mal drin, deshalb habe ich mit WolframAlpha rumgespielt, bis etwas kürzeres heraus kam 🙂 Die Ergebnisse sind identisch, die umgestellte Rechung ist aber deutlich kürzer und sollte auch effizienter sein.

      posted in Generelle Diskussion
      Marco T
    • RE: Python / Datenbank / Querx Beispiel

      Etwas flexibler als die 1. Abfrage ist folgende Abfrage, die die Maximal-, Minimal- und Durchschnittswerte über beliebige Zeitintervalle anzeigt:

      SELECT Sensor, Time, MIN(TemperatureLow), ROUND(AVG(TemperatureAvg), 1) , MAX(TemperatureHigh), MIN(HumidityLow), ROUND(AVG(HumidityAvg), 1), MAX(HumidityHigh), MIN(PressureLow), ROUND(AVG(PressureAvg), 1), MAX(PressureHigh) 
        FROM ClimateData
        WHERE Time BETWEEN '2022-01-01 00:00' AND '2022-02-01 00:00'
        GROUP BY UNIX_TIMESTAMP(TIME) DIV 3600
      

      Die 3. Zeile mit BETWEEN wählt den Zeitbereich, von welchem Daten angezeigt werden sollen.
      Die 3600 in der 4. Zeile ist das Intervall in Sekunden, über welches die Minimal-, Maximal- und Durchschnittswerte ermittelt werden.
      Mit der obigen Abfrage erhalte ich alle Werte vom Januar 2022 mit einem Intervall von 1h. Mit dieser Methode kann ich recht bequem die Daten reduzieren bevor ich sie z.B. in einer Tabellenkalkulation weiter verarbeite.
      Die Abfrage ist auch recht schnell. Alle Daten aus 2021 mit 1h Intervall abzufragen hat gerade mal 2.23s gedauert (MariaDB unter WSL auf einem Intel I5 3320 M mit 2.6GHz). Von diesen 2.23 wurden 2.17s für das Übertragen des Resultates zum Client benötigt 🙂

      Bei Abfragen mit grösseren Intervallen (z.B. 86400 für 1 Tag) ist zu beachten, dass unix_timestamp immer in UTC ist. Das muss man ev. manuell korrigieren.

      GROUP BY (UNIX_TIMESTAMP(TIME)+3600) DIV 86400
      

      zB wenn man UTC+1 (Mitteleuropäische Zeit) als Lokalzeit hat.

      posted in Generelle Diskussion
      Marco T
    • Druckwelle Vulkanesplosion Tonga

      Die Druckwelle der Vulkanexplosion bei Tonga war auch bei uns trotz 17000km Distanz gut messabar. Die Zeitschritte von 5min die ich für unseren Querx gewählt habe, reichen gerade um die Druckwelle zu erfassen. Mit +1.4hPa und -1.2hPa war die erste Welle um ca. 20:30 MEZ sehr gut von den natürlich auftretenden Luftdruckänderungen zu unterscheiden. Die 2. Welle um ca. 02:00 war da schon weniger deutlich.
      Druckwelle_Vulkanexplosion_Tonga.png
      Position: Arbon (Schweiz) 47.516669°, 9.433338°, 400müM (Luftdruck auf Stationshöhe)

      Wer hat die Welle auch gemessen und mag seine Daten teilen?

      posted in Generelle Diskussion
      Marco T
    • RE: Sommer / Winterzeit, csv export

      @Harald-Kipp ok. Danke für die Information.
      Ein weiterer Grund, endlich diese leidige Umstellung der Uhren 2 mal im Jahr abzuschaffen 🙂

      Dass time_local und time_gmt identisch sind, sollte meiner Meinung nach trotzdem nicht sein. Weder in Sommer- noch in Winterzeit.

       <part timestamp="1635638212">
        <date_local>31.10.2021</date_local>
        <time_local>01:56:52</time_local>
        <date_gmt>31.10.2021</date_gmt>
        <time_gmt>01:56:52</time_gmt>
       </part>
       <part timestamp="1635638512">
        <date_local>31.10.2021</date_local>
        <time_local>01:01:52</time_local>
        <date_gmt>31.10.2021</date_gmt>
        <time_gmt>01:01:52</time_gmt>
       </part>
      
      posted in Generelle Diskussion
      Marco T
    • Sommer / Winterzeit, csv export

      Guten Morgen

      Ich habe gerade meine Daten bezüglich Probleme mit Sommer/Winterzeit angeschaut und dafür auf meinem QuerxTHP mit FW 4.4.29 einen CVS-Export veranlasst. Der Export sowohl der csv als auch der xml daten erfolgte in diesem Fall über das Webinterface des Querx (was interessanterweise zu einem komplett anderen xml format führt als eine Anfrage per Web-Request).

      31.10.2021 00:56:52;23.0;23.1;23.1;41;41;41;965.9;966.0;966.1
      31.10.2021 01:01:52;23.0;23.1;23.1;41;41;41;966.0;966.1;966.2
      31.10.2021 01:06:52;23.0;23.1;23.1;41;41;41;966.1;966.3;966.3
      31.10.2021 01:11:52;23.0;23.1;23.1;41;41;41;966.2;966.2;966.3
      31.10.2021 01:16:52;23.0;23.1;23.1;41;41;41;966.3;966.3;966.4
      31.10.2021 01:21:52;23.0;23.1;23.1;41;41;41;966.2;966.3;966.5
      31.10.2021 01:26:52;23.0;23.1;23.1;41;41;41;966.3;966.4;966.5
      31.10.2021 01:31:52;23.0;23.1;23.1;41;41;41;966.3;966.3;966.4
      31.10.2021 01:36:52;23.0;23.1;23.1;41;41;41;966.3;966.4;966.4
      31.10.2021 01:41:52;23.0;23.1;23.1;41;41;41;966.3;966.3;966.4
      31.10.2021 01:46:52;23.0;23.1;23.1;41;41;41;966.3;966.4;966.6
      31.10.2021 01:51:52;23.0;23.1;23.1;41;41;41;966.5;966.6;966.7
      31.10.2021 01:56:52;23.0;23.1;23.1;41;41;41;966.5;966.6;966.6
      31.10.2021 01:01:52;23.0;23.0;23.1;41;41;41;966.6;966.6;966.7
      31.10.2021 01:06:52;23.0;23.0;23.1;41;41;41;966.6;966.7;966.7
      31.10.2021 01:11:52;23.0;23.0;23.1;41;41;41;966.6;966.8;966.9
      31.10.2021 01:16:52;23.0;23.0;23.1;41;41;41;966.8;966.9;967.0
      31.10.2021 01:21:52;23.0;23.0;23.1;41;41;41;966.8;966.9;966.9
      31.10.2021 01:26:52;23.0;23.0;23.1;41;41;41;966.7;966.8;966.9
      31.10.2021 01:31:52;23.0;23.0;23.1;41;41;41;966.7;966.8;966.9
      31.10.2021 01:36:52;23.0;23.0;23.1;41;41;41;966.8;966.9;966.9
      31.10.2021 01:41:52;23.0;23.0;23.1;41;41;41;966.8;966.9;967.0
      31.10.2021 01:46:52;23.0;23.0;23.1;41;41;41;966.9;967.0;967.1
      31.10.2021 01:51:52;23.0;23.0;23.1;41;41;41;967.0;967.1;967.1
      31.10.2021 01:56:52;23.0;23.0;23.1;41;41;41;966.9;967.0;967.1
      31.10.2021 02:01:52;23.0;23.0;23.1;41;41;41;966.8;966.9;967.0
      31.10.2021 02:06:52;23.0;23.0;23.1;41;41;41;966.7;966.8;966.9
      

      Es scheint, dass die Zeit beim Export nach 01:59:59 auf 01:00:00 zurück springt. Meiner Meinung nach müsste dies aber eine Stunde später erfolgen.

      Hier meine Zeiteinstellungen:
      2022-01-14_10h32_59.png

      Auch im xml-Export ist dieses Verhalten zu sehen:

       <part timestamp="1635638512">
        <date_local>31.10.2021</date_local>
        <time_local>01:01:52</time_local>
        <date_gmt>31.10.2021</date_gmt>
        <time_gmt>01:01:52</time_gmt>
        <temperature>
         <max>23.1</max>
         <average>23.0</average>
         <min>23.0</min>
        </temperature>
        <humidity>
         <max>41</max>
         <average>41</average>
         <min>41</min>
        </humidity>
        <pressure>
         <max>966.7</max>
         <average>966.6</average>
         <min>966.6</min>
        </pressure>
       </part>
      

      Die Timestamp ist ergibt: Sun Oct 31 2021 02:01:52 GMT+0200 (Mitteleuropäische Sommerzeit)

      posted in Generelle Diskussion
      Marco T
    • RE: Python / Datenbank / Querx Beispiel

      Daten aus der SQL-DB ziehen ist ganz einfach... Mit der Zeit wird die Datenmenge aber erheblich. Aber wir können den SQL-Server für uns arbeiten lassen.
      Wenn ich z.B. nur einen Wert pro Tag abfragen will ermittelt dieses Snippet den Tagesdurchschnitt sowie die Minimal- und Maximalwerte.

      SELECT TIME, round(avg(PressureAvg), 1) AS 'avg pressure', MAX(PressureHigh) AS 'max pressure', MIN(PressureLow) AS 'min pressure'
          FROM ClimateData
          GROUP BY CAST(Time AS DATE)
      

      Aber vielleicht will man wissen, wie schnell sich ein Wert geändert hat. Hier kommt die Funktion "LAG()" wie gerufen.

      WITH pressure_lag AS (SELECT Time, PressureAvg,
        LAG(PressureAVG, 11) OVER (ORDER BY Time) AS Pressure1h
        FROM ClimateData)
        SELECT *, COALESCE(ROUND(PressureAVG - Pressure1h,1)) AS Pdiff FROM pressure_lag
        ORDER BY Pdiff DESC
      

      In diesem Beispiel wird eine temporäre Tabelle angelegt in welcher die Zeit, der Luftdruckwert sowie der Luftdruckwert von 11 zeilen davor (1h früher) als Spalten enthalten sind. Der 2. Teil rechnet dann pro Zeile die Differenz aus und gibt das Ergebnis absteigend sortiert aus. So bekommt man die höchsten Werte zuerst angezeigt. Will man die kleinsten Werte zuerst sehen, lässt man einfach das "DESC" am Ende weg.

      posted in Generelle Diskussion
      Marco T
    • RE: Python / Datenbank / Querx Beispiel

      Danke an @tim für die inputs. Ich habe etwas weiter gearbeitet und eine (hoffentlich) verbesserte Version erstellt.

      Die Zeit wird nun in UTC in der DB abgelegt und die Daten werden in sichererer (und schnellerer) weise in der DB gespeichert (wobei das Speichern in der DB schon vorher nicht den grössten Teil der Zeit beanspruchte).
      Die neue Version nimmt die Ausführungszeiten der einzelnen Arbeitsschritte auf und zeigt diese am Ende an.

      #!/usr/bin/python3
      
      
      # transfer the data from the querx sesor into the mariadb / mysql-DB
      # procedure:
      # - get the time of the latest data in the database as a Unix Timestamp
      # - forge a request string for the querx http interface and get the data in xml format
      # - convert the data from xml to SQL and store it in the SQL DB
      #
      # by Marco Tedaldi, m.tedaldi aequator.ch 12.2021
      # 20220113 MTE time is stored in UTC in DB
      # 20220112 MTE switched from using mysql to mariadb
      #              split parsing and storing into two separate actions
      #              using .executemany to store all data in one go instead of using many separate SQL statements
      # 20220104 MTE added collection and display of timing information
      
      
      import sys
      import mariadb # mariadb
      import http.client  # to get the data from the querx
      import xml.etree.ElementTree as ET  # to parse the XML from the querx
      from datetime import datetime  # to conver the timestamps to datetime for the DB
      import time  # to measure execution time
      import math
      
      # access information for the Querx (copied from https://www.egnite.de/support/tutorials/tutorial-zugriff-auf-querx-messdaten-mit-python/)
      querx_address = "192.0.2.24"  # name or address of the Querx Sensor
      querx_http_port = 80
      xml_url = "/tpl/document.cgi?tpl/j/datalogger.tpl&format=xml"
      
      # databse access information
      conn_params= {
          "host" : "localhost",
          "user" : "username",
          "password" : "verysecurepassword",
          "database" : "phys_data",
      }
      db_table = "ClimateData"
      
      #copied from https://stackoverflow.com/questions/17973278/python-decimal-engineering-notation-for-mili-10e-3-and-micro-10e-6
      # function to display values in engineering or technical notation
      def eng_string(x, format='%s', si=False):
          '''
          Returns float/int value <x> formatted in a simplified engineering format -
          using an exponent that is a multiple of 3.
      
          format: printf-style string used to format the value before the exponent.
      
          si: if true, use SI suffix for exponent, e.g. k instead of e3, n instead of
          e-9 etc.
      
          E.g. with format='%.2f':
              1.23e-08 => 12.30e-9
                   123 => 123.00
                1230.0 => 1.23e3
            -1230000.0 => -1.23e6
      
          and with si=True:
                1230.0 => 1.23k
            -1230000.0 => -1.23M
          '''
          sign = ''
          if x < 0:
              x = -x
              sign = '-'
          exp = int(math.floor(math.log10(x)))
          exp3 = exp - (exp % 3)
          x3 = round(x / (10 ** exp3), 2)
      
          if si and exp3 >= -24 and exp3 <= 24 and exp3 != 0:
              exp3_text = 'yzafpnum kMGTPEZY'[exp3 // 3 + 8]
          elif exp3 == 0:
              exp3_text = ''
          else:
              exp3_text = 'e%s' % exp3
      
          return ('%s'+format+'%s') % (sign, x3, exp3_text)
      
      
      # function to get the timestamp of the last entry
      def get_lastTS(cursor):
          try:
              cursor.execute("SELECT UNIX_TIMESTAMP(MAX(Time)) FROM ClimateData")
              result = cursor.fetchone()
              ts = int(result[0])
          except:
              print("could not get timestamp from database, using 1.1.1970 00:00")
              ts = 0
          return(ts)
      
      
      # copied from https://www.egnite.de/support/tutorials/tutorial-zugriff-auf-querx-messdaten-mit-python/
      # adapted to get historical values
      def get_xml(address, port, url):
          try:
              # Python 3.x: httplib => http.client
              conn = http.client.HTTPConnection(address, port)
              conn.request("GET", url)
              res = conn.getresponse()
              xml = res.read()
              return(xml)
          except Exception as e:
              print("Error: " + str(e))
              sys.exit(1)
      
      
      # main program
      if __name__ == "__main__":
          runtime = [time.monotonic()]
          print('connecting to SQL database')
          db = mariadb.connect(**conn_params)
          cursor = db.cursor()
          runtime.append(time.monotonic())
          print('connected to SQL database')
          print('getting date and time of last entry')
          lastdata = get_lastTS(cursor)
          runtime.append(time.monotonic())
          date = datetime.fromtimestamp(lastdata).strftime('%d.%m.%Y %H:%M:%S')
          print('timestamp of last data: ' + str(lastdata) + " (" + date + ")")
          xml_url = xml_url + "&start=" + str(lastdata) + "&step=300"
          print("Getting data from http://" + querx_address + xml_url)
          climatedata = get_xml(querx_address, querx_http_port, xml_url)
          runtime.append(time.monotonic())
          print("Data received, now starting to parse")
          root = ET.fromstring(climatedata)
          sensorname = root.findtext('hostname')
          data = root.find('data')
          print("Number of records = " + str(len(data) - 1))
          datalist=[]
          query = 'INSERT INTO ' + str(db_table) + ' VALUES (?,?'
          for rnr, record in enumerate(data):
              if rnr == 0:
                  for entry in record:
                      query = query + ',?'
                  query = query + ')'
              else:
                  datatuple = ()
                  dtime = datetime.utcfromtimestamp(int(record.get('timestamp'))).strftime('%Y-%m-%d %H:%M:%S')
                  row = [ str(sensorname), dtime]
                  for entry in record:
                      row.append(str(entry.get('value')))
                  datatuple = tuple(row)
                  datalist.append(datatuple)
                  print('Records processed: ' + str(rnr), end='\r')
          runtime.append(time.monotonic())
          print("\nWriting data do DB")
          cursor.executemany(query, datalist)
          db.commit()
          runtime.append(time.monotonic())
          cursor.close()
          db.close()
          runtime.append(time.monotonic())
          print('\n')
          print("Runtimes:")
          print("Total Runtime = " +
                eng_string(runtime[5]-runtime[0], si=True) + "s")
          print("Connecting to DB took " +
                eng_string(runtime[1]-runtime[0], si=True) + "s")
          print("Getting Timestamp of last entry took " +
                eng_string(runtime[2]-runtime[1], si=True) + "s")
          print("Getting Data from Querx took " +
                eng_string(runtime[3]-runtime[2], si=True) + "s")
          print("Parsing and converting the data took " +
                eng_string(runtime[4]-runtime[3], si=True) + "s")
          print("Storing the data to DB took " +
                eng_string(runtime[5]-runtime[4], si=True) + "s")
          print("Disconnecting from DB took " +
                eng_string(runtime[6]-runtime[5], si=True) + "s")
      
      
      posted in Generelle Diskussion
      Marco T
    • RE: Python / Datenbank / Querx Beispiel

      @tim Oh, das sind gute Einwände! Danke.
      Die Timestamp im xml scheint in UTC zu sein was die Verarbeitung eigentich sehr einfach machen sollte. Einfach statt datetime.fromstimestamp() durch datetime.utcfromtimestamp() verwenden... allerdings muss ich dann alle Daten aus der Vergangenheit (die aus anderen Backups stammen) ich noch konvertieren...
      Der Aufwand dürfte sich lohnen um zukünftigen Problemen aus dem Weg zu gehen... Am Besten wohl gleich in der DB selbst Timestamp speichern.
      Es wird wohl Zeit für Version 0.2 (die dann wohl auch gar keine Abfrage macht, wenn der neueste Record in der DB weniger als timestep sekunden in der Verganenheit liegt.

      Das mit der Sicherheit stimmt natürlich. Und auch wenn der Zeitgewinn nicht gross sein dürfte, könnte das Zusammenfassen der Anfragen mit Library-Funktionen die Anzahl der Requests auf dem DB-Server u.U. deutlich Reduzieren.
      Andererseits ist soweit ich weiss schon der XML-Parser eigentlich nur für vertrauenswürdiges XML geeignet. D.h. wenn ich das Thema "Sicherheit" ernst nehmen will, muss ich auch dort vorkehrungen treffen.

      posted in Generelle Diskussion
      Marco T
    • RE: Bug: datalogger.tpl xml export mit nur 1 record

      @tim Danke für das Aufnehmen der Meldung.
      Mein Script werde ich eventuell anpassen (das ist wie du schreibst wirklich kein grosser Aufwand). Allerdings ist dies sowieso eher ein Grenzfall, in welchem sowieso keine Daten in die Datenbank zu schreiben sind und daher die Fehlermeldung höchstens ein kosmetisches Problem ist.

      Mie Erwähnung des Python-Programmes war eher als Hinweis gedacht, wie ich diesen Bug überhaupt entdeckt habe.

      posted in Generelle Diskussion
      Marco T
    • Bug: datalogger.tpl xml export mit nur 1 record

      Guten Tag allerseits

      Setup:
      querx THP 1.2
      FW 4.4.29.2
      Aufzeichnung der Daten: alle 5min

      Wenn ich mit http://192.88.99.2/tpl/document.cgi?tpl/j/datalogger.tpl&format=xml&start=1639057800&step=300 eine Abfrage starte mit einer Timestamp die weniger als 5 Minuten in der Vergangenheit liegt, bekomme ich folgende Antwort. Statt eines einzelnen <record> sind 2 vorhanden, der 2. enhält jedoch keine gültigen Daten. Mein kürzlich vorgestelltes Python-Script hat daran keine Freude 🙂

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE querx PUBLIC "-//egnite//DTD Querx 1.0//EN"
       "http://www.egnite.de/dtds/querx.dtd">
      <querx version="1.0">
      <hostname>AEQsnsmtgthp</hostname>
      <ip></ip>
      <port>80</port>
      <date_gmt>Thu, 09 Dec 2021 13:54:27</date_gmt>
      <date_local>Thu, 09 Dec 2021 14:54:27</date_local>
      <contact></contact>
      <location></location>
      <sensors>
       <sensor id="sensor_1" name="Temperatur" unit="&deg;C"></sensor>
       <sensor id="sensor_2" name="rel_Luftfeuchte" unit="%RH"></sensor>
       <sensor id="sensor_3" name="Luftdruck" unit="hPa"></sensor>
      </sensors>
      <data>
       
       <record timestamp="1639057800" date="09.12.2021" time="14:50:00">
        <entry sensorid="sensor_1" name="minimum" value="23.9"/>
        <entry sensorid="sensor_1" name="average" value="24.0"/>
        <entry sensorid="sensor_1" name="maximum" value="24.0"/>
        <entry sensorid="sensor_2" name="minimum" value="34"/>
        <entry sensorid="sensor_2" name="average" value="35"/>
        <entry sensorid="sensor_2" name="maximum" value="35"/>
        <entry sensorid="sensor_3" name="minimum" value="961.3"/>
        <entry sensorid="sensor_3" name="average" value="961.4"/>
        <entry sensorid="sensor_3" name="maximum" value="961.4"/>
       </record>
       
       <record timestamp="" date="" time="">
        <entry sensorid="sensor_1" name="minimum" value=""/>
        <entry sensorid="sensor_1" name="average" value=""/>
        <entry sensorid="sensor_1" name="maximum" value=""/>
        <entry sensorid="sensor_2" name="minimum" value=""/>
        <entry sensorid="sensor_2" name="average" value=""/>
        <entry sensorid="sensor_2" name="maximum" value=""/>
        <entry sensorid="sensor_3" name="minimum" value=""/>
        <entry sensorid="sensor_3" name="average" value=""/>
        <entry sensorid="sensor_3" name="maximum" value=""/>
       </record>
       
      </data>
      </querx>
      
      posted in Generelle Diskussion
      Marco T
    • Python / Datenbank / Querx Beispiel

      Da der Querx nur begrenzten Speicher hat behält der nur eine gewisse Anzahl an Messungen. Ältere Messungen werden automatisch gelöscht. Mit einem Aufzeichnungsintervall von 5min und aktivierter Kompression komme ich so auf respektable rund 300 Tage.

      Ich will die Daten aber länger speichern und für den schnellen Zugriff in einer SQL-Datenban ablegen (MariaDB unter WSL in meinem Fall).

      Ich habe 1 Querx THP und daher eine DB Namens "phys_data" mit einer Tabelle namens "ClimateData" angelegt mit folgenden Feldern:

      Sensor [TINYTEXT], Time {DATETIME], TemperatureLow [FLOAT], TemperatureAvg [FLOAT], TemperatureHigh [FLOAT], HumidityLow [FLOAT], HumidityAvg [FLOAT], HumidityHigh [FLOAT], PressureLow [FLOAT], PressureAvg [FLOAT], PressureHigh [FLOAT]
      

      Mein Script ist sehr einfach gestrickt und basiert auf der Annahme, dass alle Querxe dieselbe Anzahl Datenfelder haben wie die DB. Es funktioniert auch nur, solange nur ein Querx genutzt wird.

      Das Script sucht in der DB nach dem neuesten Eintrag (egal von welchem Querx). Anschliessend lädt es per Web-API die Daten ab diesem Zeitpunkt in 5min Schritten vom Qerx im XML-Format herunter. Der erste Eintrag wird ignoriert, da er ja mit dem letzten Eintrag in der DB übereinstimmt.

      Diese XML-Daten werden auf Naive Art und Weise geparst und in SQL-Statements verpackt, die dann als Queries an die DB geschickt werden.

      Damit kann ich einfach ab und zu (z.B. einmal pro Tag) die neuesten Daten vom Querx in die DB laden.

      #!/usr/bin/python3
      
      # by Marco Tedaldi, 2021
      # published as public domain except for the parts that are under copyright by someone else (ie the parts copied from egnite example code)
      
      # transfer the data from the querx sesor into the mariadb / mysql-DB
      # procedure:
      # - get the time of the latest data in the database as a Unix Timestamp
      # - forge a ruequest string for the querx http interface and get the data in xml format
      # - convert the data from xml to SQL and store it in the SQL DB
      
      
      import sys
      from MySQLdb import _mysql as mysql  # we want to access a mysql / mariadb
      import http.client # to get the data from the querx
      import xml.etree.ElementTree as ET # to parse the XML from the querx
      from datetime import datetime # to conver the timestamps to datetime for the DB
      
      # access information for the Querx (copied from https://www.egnite.de/support/tutorials/tutorial-zugriff-auf-querx-messdaten-mit-python/)
      querx_address = "192.88.99.2" #name or address of the Querx Sensor
      querx_http_port = 80
      xml_url = "/tpl/document.cgi?tpl/j/datalogger.tpl&format=xml"
      
      # databse access information
      db_address = "localhost"
      db_user = "username"
      db_pass = "VerySecurePW"
      db_name = "phys_data"
      db_table = "ClimateData"
      
      # function to get the timestamp of the last entry
      def get_lastTS(db):
        db.query("""SELECT UNIX_TIMESTAMP(MAX(Time)) FROM ClimateData""")
        result = db.store_result().fetch_row()
        ts = int(result[0][0])
        return(ts)
      
      # copied from https://www.egnite.de/support/tutorials/tutorial-zugriff-auf-querx-messdaten-mit-python/
      # adapted to get historical values
      def get_xml(address, port, url):
        try:
          conn = http.client.HTTPConnection(address, port) # Python 3.x: httplib => http.client
          conn.request("GET", url)
          res = conn.getresponse()
          xml = res.read()
          return(xml)
        except Exception as e:
           print ("Error: " + str(e))
           sys.exit(1)
      
      # main program
      if __name__ == "__main__":
        db = mysql.connect( db_address, db_user, db_pass, db_name)
        lastdata = get_lastTS(db)
        print("Timestamp = " + str(lastdata))
        xml_url = xml_url + "&start=" + str(lastdata) + "&step=300"
        print("xml_url = " + xml_url)
        climatedata = get_xml(querx_address, querx_http_port, xml_url)
        root = ET.fromstring(climatedata)
        sensorname = root.findtext('hostname')
        data = root.find('data')
        for rnr, record in enumerate(data):
          if rnr != 0:
            time = datetime.fromtimestamp(int(record.get('timestamp'))).strftime('%Y-%m-%d %H:%M:%S')
            query = 'INSERT INTO ' + str(db_table) + ' VALUES (\'' + str(sensorname) + '\', \'' + time
            for idx, entry in enumerate(record):
              query = query + '\', \'' + str(entry.get('value'))
            query = query + '\' );'
            print('-> ' + query)
            db.query(query)
        db.close()
      

      Ich bin kein Programmierer sondern hacke mir meine Tools einfach so zusammen, dass sie für mich funktionieren. Diese Software könnte von einem fähigen Programmierer in kurzer Zeit wahrscheinlich massiv verbessert werden um mit Kommandozeilen-Argumenten, mehr als einem Querx und einer anderen Anzahl Spalten in der DB umgehen zu können.

      Ich veröffentliche diesen Code so wie er ist (abgesehen von den Teilen, die ich von egnite kopiert habe) zur freien Verwendung.

      posted in Generelle Diskussion
      Marco T
    • RE: Design Querx Webinterface

      @Daniel said in Design Querx Webinterface:

      Ich weiss nicht, ob es zum Anwendungsfall passt, aber es gäbe die Möglichkeit über so etwas wie Tampermonkey ein Userscript im Browser zu installieren, was das Favicon ändert.
      Siehe https://stackoverflow.com/questions/16745773/embed-favicon-using-a-userscript
      Per Userscript ließe sich prinzipiell auch das CSS überschreiben. Geht es um ein bestimmtes Interface-Element?

      Danke @Daniel
      Die Frage wegen des Favicon war vor allem, um in den Schnellstart-Icons im Firefox nicht nur das hässliche default-icon aus dem Anfangsbuchstaben des Seitennamens zu sehen oder aber dieses auf jedem Rechner von Hand setzen zu müssen.

      Ich kann aber die Begründung des Entwicklungsaufwandes absolut nachvollziehen (auch in Anbetracht der wohl recht beschränkten Resourcen auf dem Sensor selbst).

      Ich kann gut damit leben. Es wäre aber schade, wenn die Möglichkeit bestünde, aber ich sie nicht nutze weil ich nichts davon weiss 🙂

      posted in Generelle Diskussion
      Marco T
    • Design Querx Webinterface

      Guten Morgen

      Kann man für das Webinterface des Quex ein eigenes Favicon definieren? Können auch andere Elemente auf dem Interface durch eine eigene Vorlage umgestaltet werden (z.B. durch ein eigenes CSS-file)?

      posted in Generelle Diskussion
      Marco T
    • RE: Gelöst: Ungültiges xml in xml export

      Hat perfekt funktioniert! Vielen Dank!

      posted in Generelle Diskussion
      Marco T