Ankündigung

Einklappen
Keine Ankündigung bisher.

plot.period aus "list of dict" erstellen

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    plot.period aus "list of dict" erstellen

    Hallo,

    ich möchte den Inhalt eines Item, der als "list of dicts" mit folgenden Format vorliegt, als plot.period darstellen.

    Code:
    [
      {'Date': '2020/52', 'MAX(val_num)': 3.5, 'MIN(val_num)': -3.5},
      {'Date': '2020/51', 'MAX(val_num)': 11.0, 'MIN(val_num)': -3.0},
      {'Date': '2020/50', 'MAX(val_num)': 7.0, 'MIN(val_num)': -0.5},
      {'Date': '2020/49', 'MAX(val_num)': 4.25, 'MIN(val_num)': -1.0}
    ]
    Jedes Dict in der Liste besteht aus einem Datecode, einem MinWert und einem MaxWert.

    Klappt das? oder wie müsste die Liste / das Dict aussehen, damit es geht?
    Zuletzt geändert von bmx; 10.12.2021, 15:50. Grund: Ein wenig Umbruch zum besseren Verständnis eingefügt

    #2
    Hi Michael,

    plot.period ist darauf ausgelegt, dass die Daten als "Series" aus der Datenbank des Backends kommen. Am einfachsten ist es also, wenn Du die Werte in die Datenbank bringst. Das kann entweder eine Datenreihe für min und eine für max sein, oder es gehen alle Daten in eine Datenreihe und die Datenbank wird per "min/max" abgefragt. Für die Daten der Kalenderwochen braucht man dann noch Zeitstempel im Unix-Format. Das macht die Datenbank nur dann von selbst, wenn das item beim update direkt in die DB geschrieben wird.

    Um eine völlig andere Datenstruktur als Quelle zu verwenden, müsste man das plot.period Widget kopieren, vom Update-Mechanismus der Series abklemmen und die Seriendaten per JavaScript in das von Highcharts erwartete Format wandeln. Das ist kein Hexenwerk. Allerdings muss man den Update-Mechanismus für den Plot neu gestalten.

    Sicher gibt es noch den einen oder anderen schmutzigen Trick. Den will ich hier aber nicht näher ausführen

    Gruß
    Wolfram

    Kommentar


      #3
      Ein weniger schmutziger Trick könnte sein eine Series Funktion in den Userfunctions zu implementieren so wie im Database Plugin. Du müsstest dann nur dafür sorgen das einem Item diese userfunction auch zugewiesen wird. Über diesen Weg könnte das klappen ...

      Kommentar


        #4
        Zitat von bmx Beitrag anzeigen
        Ein weniger schmutziger Trick könnte sein eine Series Funktion in den Userfunctions zu implementieren so wie im Database Plugin. Du müsstest dann nur dafür sorgen das einem Item diese userfunction auch zugewiesen wird. Über diesen Weg könnte das klappen ...
        Das probier ich mal. Danke.

        Zitat von wvhn Beitrag anzeigen
        Daten als "Series" aus der Datenbank des Backends
        Wo kann ich schauen, welches Format/Reihenfolge die Daten genau haben müssen? Ist es das result der Funktion _series aus dem DB-Plugin?

        Kommentar


          #5
          Würde mich freuen wenn das klappt, das steht seit längerer Zeit auf meiner ToDo Liste, ich weiß nur mittlerweile nicht mehr genau wofür ich das genau haben wollte.

          Kommentar


            #6
            Zitat von Sisamiwe Beitrag anzeigen
            Wo kann ich schauen, welches Format/Reihenfolge die Daten genau haben müssen? Ist es das result der Funktion _series aus dem DB-Plugin?
            Schau Dir mal plot.heatingcurve an, da hab ich das damals über ein eigenes Datenformat gelöst (die Daten kommen auch nicht direkt aus der Datenbank - sie müssen vorher ausgelesen und in das Zielformat gebracht werden, z.B. wie von Bernd vorgeschlagen über die neuen Userfunctions).
            /tom

            Nachtrag: Ansonsten F11 bzw. F12 - in der Konsole im Browser siehst Du das Datenformat. Die Datensätze müssen an der X-Achse aufsteigend sortiert sein, wenn ich mich richtig erinnere.
            Zuletzt geändert von Tom Bombadil; 11.12.2021, 11:55.

            Kommentar


              #7
              Je länger ich darüber nachgedacht habe, warum es nicht geht, desto näher bin ich der Lösung gekommen. Die ist mal wieder unglaublich einfach. Ich bin immer wieder fasziniert von dem flexiblen Aufbau von smartVISU.

              Hintergrund: innerhalb von smartVISU wird eine Datenreihe („series“) wie folgt benannt:
              Code:
              mein.series.item.avg.5y 6m.0.100 -- count
              |--------------||--||----||-|
                    item        \    \    \ end
                                 \    \ start
                                  \ mode
              "items" mit Namen, die diesem Schema entsprechen, werden bei shNG als "series" abonniert.
              Code:
              [io.smarthome.py] sending data: {"cmd":"series","item":"mein.series.item","series": "avg","start":"5y 6m","end":"now","count":"100"}
              Der Websocket-Server leitet die Anfrage direkt als Anfrage an das Datenbank-Plugin durch und schickt die Antwort in Form von Wertepaaren aus Zeitstempel und Istwert. Am Ende folgt noch die "Series-ID" ("sid"), damit die Daten dem jeweiligen Plot eindeutig zur Verfügung gestellt werden können.
              Code:
              [io.smarthome.py] receiving data: {"cmd": "series", "series": [[1639136961636, 0.12], [1639137811805, 0.12], [1639138711563, 0.16],  .... , [1639223319001, 0.13], [1639223361640, 0.13]], "sid": "env.system.load|avg|1d|now|100"}
              Die Wertepaare müssen zeitlich aufsteigend sortiert sein und können dann ohne weitere Umwandlung direkt an Highcharts übergeben werden.

              Um Daten einzuschleusen, die nicht aus der Datenbank kommen, dürfen die Daten nicht als Serie angefragt werden, sondern als item vom Typ list. Im html-Teil des Widgets plot.period muss dazu der Aufbau des o.g. series-Namens unterbunden werden, so dass ein normales item angefragt wird. Für Versuchszwecke reicht es tatsächlich aus, die Zeile 140 der plot.html (Zeile 141 im aktuellen develop) zu ändern von
              Code:
                          {% set seriesitems = seriesitems|merge([implode(itemi, [modei, tmin, tmax, counti])]) %}
              in
              Code:
                          {% set seriesitems = seriesitems|merge([itemi]) %}
              Dann funktionieren natürlich die herkömmlichen Plots nicht mehr, aber im Test habe ich schon eine Version, die die Datenquelle als Option entgegen nimmt. Diese Version findet Ihr im Anhang in Form einer Textdatei als Ersatz für die plot.html. Sie ist kompatibel zum aktuellen Stand des develop branch.

              Im List-item stehen die Daten in der Form
              Code:
              [[1639136961636, 0.12], [1639137811805, 0.12], [1639138711563, 0.16],  .... , [1639223319001, 0.13], [1639223361640, 0.13]]
              Jede Datenreihe muss dann ihr eigenes List-item haben.

              Bitte testet mal das Widget - möglichst auch mit mehreren items und modi (z.B. min/max).

              Gruß
              Wolfram
              Angehängte Dateien

              Kommentar


                #8
                Zitat von wvhn Beitrag anzeigen
                Bitte testet mal das Widget - möglichst auch mit mehreren items und modi (z.B. min/max).
                Das mache ich gern. Die Zeit muss als UnixTimestamp in Mircosekunden sein, richtig?

                Kommentar


                  #9
                  In Millisekunden.

                  Kommentar


                    #10
                    https://currentmillis.com/

                    /tom

                    Kommentar


                      #11
                      Hallo,

                      ich konnte auch ein paar Tests machen.
                      1. Ich habe in shNG div. UserFunctions definiert, die mir bestimmte Dinge aus der DB holen. Bspw. die monatlichen min und max Temperaturwerte der letzten x Monate.'
                        Code:
                        def fetch_min_monthly_count(item, count=None):
                        	  _logger.warning(f"Die Userfunction 'fetch_min_monthly_count' wurde aufgerufen mit item {item} and count {count}")
                        	 
                        	  if type(item) is str:
                        	      item = get_item_id(item)
                        	  if count is None:
                        	      query = f"SELECT CONCAT(YEAR(FROM_UNIXTIME(time/1000)), '-', LPAD(MONTH(FROM_UNIXTIME(time/1000)), 2, '0')) AS Date, MIN(val_num) FROM  log WHERE item_id = {item} GROUP BY Date ORDER BY Date DESC"
                        	  else:
                        	      query = f"SELECT CONCAT(YEAR(FROM_UNIXTIME(time/1000)), '-', LPAD(MONTH(FROM_UNIXTIME(time/1000)), 2, '0')) AS Date, MIN(val_num) FROM  log WHERE item_id = {item} AND DATE(FROM_UNIXTIME(time/1000)) > DATE_SUB(now(), INTERVAL {count} MONTH) GROUP BY Date ORDER BY Date DESC"
                        	 
                        	  result = []
                        	  try:
                        	      connection = connect_db()
                        	      with connection.cursor() as cursor:
                        	          cursor.execute(query)
                        	          result = cursor.fetchall()
                        	  finally:
                        	      connection.close()
                        	  # _logger.warning(f'mysql result: {result}')
                        	 
                        	  value_list = []
                        	  for element in result:
                        	      timestamp = _get_dbtimestamp_from_date(element['Date'])
                        	      value_list.append([timestamp, element['MIN(val_num)']])
                        	 
                        	  _logger.warning(f'mysql.fetch_min_monthly_count value_list: {value_list}')
                        	  return value_list
                      2. Das Ergebnis wird in das Item vom "list" gefüllt, dass die UF per eval auslöst.
                        Code:
                        mysql_test:
                        	   temp_aussen:
                        	       min_monthly_15m:
                        	           type: list
                        	           eval: uf.mysql.fetch_min_monthly_count(117, 15)
                        	           
                        	       max_monthly_15m:
                        	           type: list
                        	           eval: uf.mysql.fetch_max_monthly_count(117, 15)
                      3. Im Item steht dann bspw:
                        Code:
                        [[1638313200000, 1.13], [1630447200000, 12.02], [1627768800000, 12.8], [1625090400000, 13.82], [1622498400000, 11.86], [1619820000000, -0.06], [1617228000000, -3.37], [1614553200000, -5.0], [1612134000000, -12.75], [1609455600000, -8.75], [1606777200000, -3.5], [1604185200000, -4.75], [1601503200000, 3.0], [1598911200000, 4.5]]
                      4. "Korrektur" der plot.html
                      5. in smartvisu einen Block definiert mit:
                        Code:
                         <div class="block" style="width:100%">
                        	<div class="set-1" data-role="collapsible-set" data-theme="c" data-content-theme="a" data-mini="true">
                        	<div data-role="collapsible" data-collapsed="false">
                        	<h3>Test DB Plots</h3>
                        	                    {{ plot.period('', ['mysql_test.temp_aussen.min_monthly_15m', 'mysql_test.temp_aussen.max_monthly_15m'], ['min', 'max'], '15m', '', '', '', '', ['min', 'max'], ['blue', 'red'], '', '', '', '', '', '', '', '°C') }}
                        	</div>
                        	</div>
                        	</div>
                      6. Führt zu diesem Plot:
                        Screenshot 2021-12-12 171335.jpg
                      Sieht doch doch gut aus.

                      Was meint ihr?

                      Kommentar


                        #12
                        Cool!

                        Danke auch für die Daten, mit denen ich weitere Tests machen konnte:
                        minmax-Darstellung geht auch. Einfach die items in der Reihenfolge "min", "max" eintragen.
                        Minmaxavg habe ich mangels dritten Testitems noch nicht getestet. Die anderen Modi (avg, min, max, raw, on ...) sind ohne Funktion, da sie keine Chartoptionen verändern, sondern nur die Query in der Datenbank beeinflussen.

                        Screenshot 2021-12-12.png
                        (Statt Testseite zu bauen, einfach aus dem Widget Assistant generiert )
                        Zuletzt geändert von wvhn; 12.12.2021, 19:54.

                        Kommentar


                          #13
                          Noch zwei Schnellschüsse aus dem Widget Assistant (Achtung Schleichwerbung )

                          exposure = areastack
                          Screenshot 2021-12-12_1.png

                          exposure = spline
                          Screenshot 2021-12-12_2.png

                          Das reicht IMHO erstmal, um die Änderung für plot.period ins Develop zu pushen (erledigt). Mit dem plot.xyplot warte ich noch auf weitere Testergebnisse.

                          Gruß
                          Wolfram
                          Zuletzt geändert von wvhn; 12.12.2021, 19:55.

                          Kommentar


                            #14
                            Sisamiwe
                            Beim Testen ist mir noch aufgefallen, dass die Wertepaare mit Zeitstempel in absteigender Reihenfolge in der Liste stehen. Highcharts schmeißt dann auch gleich eine Warnung. Spätestens, wenn man noch ein Option für die automatische Skalierung der x-Achse einbaut, geht das wahrscheinlich schief.

                            Du solltest die Daten daher am besten in aufsteigende Reihenfolge bringen.

                            Gruß
                            Wolfram

                            Kommentar


                              #15
                              Zitat von wvhn Beitrag anzeigen
                              Du solltest die Daten daher am besten in aufsteigende Reihenfolge bringen.
                              Danke für den Hinweis.
                              Hab die UserFunctions entsprechend geändert und auch gleich noch vereinfacht.

                              Klappt super!

                              Edit:
                              Frage: Wie kann ich bei minmaxavg die Breite der minmax Balken beeinflussen? Bei dir sind die schön breit.
                              Zuletzt geändert von Sisamiwe; 13.12.2021, 19:32.

                              Kommentar

                              Lädt...
                              X