Mein neues Projekt, Data von .json auslesen und weiter verwenden

  • Wenn ich das sehe mit den 'subproess'-Aufrufe könnte ich mir vorstellen, dass eventuell dieses Skript selbst auch von einem anderen aufgerufen wird und das vielleicht innerhalb kürzester Zeit?

    Wie viele Skripte gibt es denn und welches ist dass das alles steuert?

    Kannst du das mal bitte alles zeigen?

    Vielleicht übersehe ich aber auch etwas, aber nachstellen konnte ich das Verhalten nicht.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Mein neues Projekt, Data von .json auslesen und weiter verwenden? Schau mal ob du hier fündig wirst!

  • Dennis89 Guter Tipp mit dem subprocess.

    Habe jetzt all anderen scripts gesichert und dann geloescht und nur noch die am laufen:

    Code
    pi@solaranzeige:~/th $ ls
    ms.log  SG_Ready.py  WP-Licht.py
    pi@solaranzeige:~/th $

    Alles mit subprocess mit # versehen, d.h. jetzt sind nur noch diese 2 script aktive und schreiben beide auf ms.log:

    script, SG-Ready.py

    und

    script WP-Licht.py

    Nach reboot sieht ms.log aber immer noch nicht so toll aus.

  • Was auf jeden schon mal falsch ist, ist Sachen die genau einmal passieren sollen *in* eine Schleife zu schreiben wo sie dann wiederholt ausgeführt werden. Das kann sinnlos Rechenzeit verbrauchen, es kann aber auch sein, dass etwas gar nicht dafür vorgesehen ist mehrfach gemacht zu werden. In diesem Fall zum Beispiel das Konfigurieren vom Logging.

    Die Leerzeichensetzung ist teilweise komisch. Bei Aufrufen und Indexzugriffen kommt kein Leerzeichen vor der öffnenden Klammer. Und es ist teilweise auch etwas zu viel geklammert. Wenn da öffnende Klammern sind, geht man als Leser ja davon aus, das die auch was bewirken und ist irritiert wenn das gar nicht der Fall ist. Und schaut noch mal hin ob man sich auch nicht verlesen hat.

    Hat eigentlich schon mal jemand angemerkt, dass die Programme ein bisschen unübersichtlich sind? ?

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • @Denis89

    die 2 scripts starten mit einer systemd unit.

    @_blackjack_

    Habe jetzt die while True: sleep(30) entfernt, alle Zeilen 4 nach links geschoben, das logging für beide Werte (Temp und Watt) sind jetzt in einem script.

    in crontab -e habe ich das script mit den Temp und Watt loggen alle 20 sec. aufgerufen und das Resultat ist jetzt viel besser, für mich ok:

    Kannst Du bitte in meinem script ein Beispiel zeigen was nicht gut ist mit der Leerzeichensetzung und das mit den öffnenden Klammern?

    Vielen Dank, dann werde ich das noch korrigieren.

    Unübersicht, hat keepfear in #36 schon bemängelt.....

    crontab alle 20 sec

    Code
    * * * * *  /home/pi/th/SG_Ready.py               >dev/null
    * * * * *  sleep 20; /home/pi/th/SG_Ready.py     >dev/null
    * * * * *  sleep 40; /home/pi/th/SG_Ready.py     >dev/null
  • Zum Beispiel das mit subprocess.run().

    Python
    subprocess.run ("/home/pi/th/Mail_WP_Aus.py")

    Da hast du ein Funktionsaufruf, die Klammern gehören da zur Funktion. Da gehört kein Leerzeichen hin.

    Dann hast du ja noch das

    Python
    obj ["emeters"][0]["power"]

    Zwischen obj und der Klammer darf auch kein Leerzeichen sein. Das gehört ebenfalls zusammen.

    Das mit dem zuviel Klammern könnte ich mir vorstellen, das __blackjack__ folgendes meint:

    Python
    timewindow1 = (9*60+0) <= Now < (16*60+0)    # ein

    Zumindest hab ich jetzt nichts anderes gefunden.

    Was bedeutet die Berechnung in der Klammer?

    Es wäre für dich von Vorteil, wenn du dir Funktionen anschauen würdest.

  • Danke, keepferar, für die Erklärungen, werde es so korrigieren.

    z.B. timewindow1 = (9*60+0) <= Now < (16*60+0) ist ein Zeitfenster zwischen 9.00h und 16.00h

    oder timewindow1 = (9*60+15) <= Now < (16*60+22) ist ein Zeitfenster zwischen 9.15h und 16.22h

    Das habe ich mal irgendwo gefunden und brauche es jetzt so wie es da steht, funktioniert sehr gut.

  • gwaag Unnötige Klammern sind beispielsweise bei ``winter = not (sommer)`` und ``if (obj ["emeters"][0]["power"]) > 50 and (obj ["emeters"][0]["power"]) < 250:``. Im zweiten Fall vielleicht damit man besser sehen kann, das `obj` zu dem []-Zugriff gehört? Da würde man aber wie schon gesagt kein optisch trennendes Leerzeichen zwischen setzen, eben weil das sehr eng zusammengehört und der Leser erwartet dass das nicht getrennt geschrieben wird.

    Durch das entfernen der ``while``-Schleife jetzt hier wahrscheinlich kein Problem mehr sind die Kommentare die nicht eingerückt waren. Einrückung hat ja eine Bedeutung in Python, und durch diese Kommentare kann der Eindruck entstehen das wäre nicht alles *ein* Schleifenkörper, weil der komplette Schleifenkörper ja eine einheitliche Mindesteinrückung hat an der man optisch eigentlich sehr leicht erkennen kann wo die Schleife anfängt und wo sie aufhört. Wenn da nicht diese Kommentare wären.

    Die Importe müssten mal aufgeräumt werden. Direkt dort sieht man bereits, dass ``import datetime`` unsinnig ist, weil gleich in der nächsten Zeile der Name `datetime` neu an etwas anderes gebunden wird. `time` und `json` werden importiert, aber nirgends verwendet.

    Bei Deiner Erklärung zu den Zeitfenstern weiss ich jetzt nicht so genau ob Du die damit einfach nur erklären wolltest, oder das eine Begründung für die Klammern dort sein sollte: Die sind überflüssig.

    Das ganze ist viel zu lang und Namen werden meilenweit vor ihrer Verwendung definiert. Sofern sie denn verwendet werden. Werden alle Zeitfenster verwendet? Wird `now` oder `Now` noch irgendwann später verwendet? Um solche Fragen zu beantworten muss man sich immer das gesamte Programm genau anschauen. Das `low` und `high` zwar definiert werden, aber nicht verwendet, ist etwas das man nicht so leicht sehen kann — wenn der Editor/die IDE nicht so nett wären darauf hinzuweisen. Unbenutztes sollte da nicht stehen. Auch das geht auf die Lesbarkeit/Verständlichkeit.

    Bei den Zeitfenstern sieht man auch gut das Problem mit wiederholten Daten. Weil die Namen so schlecht sind — man nummeriert keine Namen, dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen verwenden, sondern eine Datenstruktur — müssen da Kommentare stehen. Und die stehen dann nicht nur bei der Definition, sondern auch zig Zeilen weiter noch mal dort wo die Namen dann verwendet werden. Die Zeiten did im Code stehen, stehen dort dann auch noch mal als Kommentar, und *noch* *mal* in Zeichenketten die ausgegeben werden. Das sind Fehler die geradezu darum betteln passieren zu dürfen, dass man da mal irgendwo bei dem ganzen kopierten Codeschnippseln vergisst die Kommentare und Werte manuell synchron zu halten. Das macht unnötig Arbeit und ist fehleranfällig.

    Das das tatsächlich ein relevantes Problem ist, sieht man daran, dass es in dem Quelltext ja bereits Fehler gibt. Im ersten Zeitfenster steht in der ersten Bedingung und Logausgabe -1500, bei der zweiten Bedingung steht dagegen in der Bedingung -100, in der Logausgabe aber -500. Und die -500 steht auch in der Logausgabe beim zweiten Zeitfenster, es ist aber überhaupt nicht ersichtlich *warum* die da steht, denn in der Bedingung steht nur das Zeitfenster selbst.

    Und wenn bei den Bedingungen Zeiten als Kommentare stehen, dann ist das immer die Startzeit — ausser beim zweiten Zeitfenster, das ist die Endzeit minus eine Minute als Zeit kommentiert.

    „Ein“ und „Aus“ sollte man nicht an gerade oder ungerade nummerierten Namen erkennen müssen, und „Nachbooster“/„Nachtabsenkung“ nicht an gerade/ungerade Zahlenpaaren in Namen und Kommentaren ablesen können, sondern an den Namen selbst. Dann kann man sich die Kommentare sparen.

    Was bei den Zeitfensternamen auch etwas verwirrt, ist dass die Werte dahinter gar keine Zeitfenster sind, sondern Wahrheitswerte die beschreiben ob der aktuelle Zeitpunkt gerade in einem Zeitfenster ist oder nicht. Da bei der Verarbeitung später die Zeit von dem jeweiligen Fenster ausgegeben wird, und dort noch mal händisch in die Zeichenkette geschrieben wurde, ist das keine gute Idee die Zeitfenster als Boolean zu modellieren. Und auch nicht die Zeitpunkte als Minuten seit Mitternacht, denn für die Ausgabe braucht man das dann ja wieder in Stunden und Minuten. Etwas das vom `datetime`-Modul als `time`-Objekte netterweise ja schon zur Verfügung steht, der Code aber quasi weg wirft, in dem daraus eine Minutenanzahl berechnet wird. `datetime.time`-Objekte sind auch vergleichbar — man kann damit also auch die Zeiten der Zeitfenster ausdrücken. Das liesse sich also auch so schreiben, mit einem Datentyp der besser geeignet ist eine Zeit zu repräsentieren als die reine Minutenanzahl seit Mitternacht:

    Bleibt noch das Problem, dass das Wahrheitswerte sind, sind statt tatsächlich Zeitfenster. Da wäre das einfachste ein Tupel aus Start- und Endzeit. Oder das man, zumindest im ersten Schritt, diese Daten nicht an sechs unterschiedliche nummerierte Namen bindet, sondern an immer die gleichen zwei Namen, beispielsweise `start` und `end`, an der Stelle wo das dann auch tatsächlich verwendet wird.

    Den aktuellen Zeitpunkt sollte man nur *einmal* ermitteln, denn wenn man das mehrfach macht ist das ja nicht mehr *ein* Zeitpunkt, weil zwischen den Aufrufen von `now()` Zeit vergeht. Da kann es dann an bestimmten Grenzen passieren, dass die Informationen nicht mehr zusammenpassen, weil der eine Aufruf von `now()` von dem die Zeitinformation verwendet wird im Sommer passiert und der Aufruf der entscheidet ob Sommer oder Winter ist, dann schon im Winter passiert. Nun kann es sein, dass es hier nicht so schlimm ist wenn an der Grenze zwischen Sommer und Winter etwas passiert oder nicht passiert was eigentlich in der anderen Jahreshälfte (nicht) passieren sollte, aber das ist Fehler die in anderen Kontexten durchaus schwerwiegendere Folgen haben kann, darum sollte man sich das gar nicht erst angewöhnen.

    `sommer` wird etwas umständlich bestimmt — bei den Zeitfenstern war doch Operatorverkettung schon bekannt:

    Python
        sommer = monat >= 3 and monat <= 9
        
        # =>
        
        sommer = 3 <= monat <= 9

    `sommer` wird aber nur benutzt um `winter` zu definieren und das ist mit einer Operation so simpel, dass man doch nicht *wirklich* diesen kleinen Zwischenschritt an einen eigenen Namen binden muss. Und `monat` ist auch nur `now.month`. Das lässt sich also auf eine (lesbare) Zeile eindampfen:

    Python
        monat = now.month
        sommer = monat >= 3 and monat <= 9
        winter = not sommer
        
        # =>
        
        is_winter = not 3 <= now.month <= 9

    Jetzt ist das eine Teilbedingung von *jedem* ``if`` das im folgenden etwas schaltet, das sollte also dort nicht in jedem ``if`` stehen, sondern *einmal* in einem ``if`` das nur im Winter betreten wird. Dann kann das aus all den anderen Bedingungen rausgenommen werden.

    Bei der Teilbedingung mit `timewindow1` ist es ähnlich. Das wird in zwei ``if``-Bedingungen verwendet und sollte dort herausgezogen werden.

    Das lesen des Einspeisungswerts ist komisch. Warum wird die `get_points()`-Methode direkt vor dem Aufruf an den Namen `points` gebunden, der dann auch nur für diesen einen Aufruf verwendet wird? Das macht keinen Sinn, das verwirrt bloss den Leser.

    Dann wird `point` mit 0 initialisiert, dieser Wert wird aber niemals irgendwo verwendet, weil der Name in der Schleife danach gleich an einen anderen Wert gebunden wird *und* der Wert hat auch noch einen ganz anderen Typ als `int`. Das ist ebenfalls verwirrend und total unnötig.

    Und dann wird in der Schleife, die genau einmal durchlaufen wird, also eigentlich gar keine Schleife ist, der kryptische Name `shelly_eks` eingeführt, der nur einmal einen Wert zugewiesen bekommt, und nur verwendet wird um diesen Wert gleich danach an den Namen zu binden der dann im weiteren tatsächlich verwendet wird.

    Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht. Nach einem `logging`-Aufruf zu kommentieren, dass da ein Logeintrag gemacht wird, und das auch noch *jedes mal* macht wenig Sinn.

    Beim Auslesen der Raumtemperatur ist ein Fehler bei `zu_kalt` und `zu_warm` — die beziehen sich gar nicht auf "Wohnen west", sondern auf den letzten Raum der da abgearbeitet wird, weil der Code für *jeden* Raum ausgeführt wird, und am Ende haben diese beiden Variablen den Wert des letzten Schleifendurchlaufs.

    Letztlich haben wir bei den beiden Variablen aber auch wieder ein ähnliches Problem wie bei den Zeitfenstern: die Information was da jeweils die Grenztemperatur ist, steht dort in der Bedingung und dann zig Zeilen weiter noch mal als Kommentar wo das Ergebnis des Vergleichs verwendet wird. Das ist unübersichtlich und fehleranfällig. Die beiden Namen werden nur einmal verwendet, also wäre es sinnvoller die *Raumtemperatur* zu bestimmen und die dann später direkt in der ``if``-Bedingung zu verwenden. Dann muss man die Temperatur dort nicht noch einmal als Kommentar hinschreiben.

    So Ausdrücke wie ``raum["sensoren"][1]["wert"]`` sollten auch nicht mehrfach im Quelltext stehen. Das nervt doch das immer wieder zu tippen. Falls es nicht getippt, sondern kopiert war: Warnzeichen! Immer wenn man Code kopiert sollte man kurz innehalten und überlegen ob man da nicht gerade etwas falsch macht, denn sehr oft ist das der Fall.

    Wenn man "Wohnen west" gefunden hat, kann man die Schleife abbrechen, denn es macht ja nicht wirklich Sinn weiter zu suchen.

    Beim lesen des Energieverbrauchs kann man die ``if``-Bedingung wieder deutlich einfacher und damit lesbarer schreiben wenn man Operatorverkettung verwendet:

    Python
        if (obj ["emeters"][0]["power"]) > 50 and (obj ["emeters"][0]["power"]) < 250:
            logging.info("WP Start") 
    
        # =>
    
        if 50 < obj["emeters"][0]["power"] < 250:
            logging.info("WP Start")

    Wobei ich diese Aktion ein bisschen komisch finde, denn da wird ja wirklich nur diese eine Protokollzeile geschrieben. Sonst passiert da weiter nichts.

    Die Antwort auf HTTP-Anfragen kann auch eine Fehlermeldung sein. Das würde ich immer prüfen. Die `requests.Response`-Objekte haben dafür eine praktische Methode. An anderen Stellen würde eine Fehlerbehandlung auch Sinn machen. Beispielsweise falls die Raumtemperatur nicht gefunden wird, oder die Influx-Datenbank noch gar keine Daten enthält.

    Bei den Relais würde es Sinn machen die beiden Informationen aus denen die anscheinend bestehen: Name und IP, zu einem Wert zusammenzufassen. Dann muss man das auch nicht kommentieren. Und hier ist der Punkt erreicht wo man eine Funktion zum schalten braucht, statt das immer und immer wieder im Code zu kopieren.

    An der Stelle kann man dann auch leicht `http.client` durch `requests` ersetzen, denn es macht wenig Sinn beide Wege im Programm zu haben.

    Schon diese eine Funktion verkürzt den Code deutlich, allerdings ist das für meinen Geschmack immer noch zu viel in der Hauptfunktion. Trotzdem erst mal der Zwischenstand (ungetestet):

    Das mit den Zeitfenstern von einer Minute ist nicht so wirklich robust. Wenn es blöd läuft blockiert irgendwas den Rechner/den cron-Daemon eine Minute lang, und die entsprechende Schaltung wird dann nicht ausgeführt.

    Das Programm könnte sicher auch von einem `TimeWindow`- oder `Timespan`-Typ profitieren, den man mit einer einfach lesbaren Textrepräsentation erstellen kann und er `__contains__()` implementuert, so dass man so etwas wie ``if now.time() in Timespan.parse("20:30-23:00"):`` schreiben kann.

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • hyle, Danke, ist ein Kopierfehler, ich habe 2 Raspberrys einer den ich selber aufgesetzt habe und einen den ich von Solaranzeige.de habe.

    Bei meinen kann ich in crontab copy past machen , bei dem von Solaranzeige nicht, daher der Fehler, musste es abtippen.

    ..

    @_blackjack_

    Vielen Dank für Deine Mühe alles zu Erklären und sogar einen code zu schreiben.

    Um das alles zu verstehen muss ich es erst mal absacken lassen und mehrfach lesen/studieren.Ich ü66 brauche da etwas mehr Zeit, da ich das mit den Raspberrys ja nur als Hobby mache. Und ja, Du hast natürlich Recht, vieles ist aus dem Internet copy past und von mir angepasst.

    Nochmals Vielen Dank. :)

    Gruss

    gwaag

  • Hallo,

    noch eine Frage, wie kann ich dem ausgegebenen Wert von print (raum["sensoren"][1]["wert"]) noch einen Korrekturwert dazu addieren.

    d.h. Wert ist 21.38 + 0.5, Wert zum weiterarbeiten soll also 21.83 sein

    Code
    print (raum["sensoren"][1]["wert"]) 
  • Danke funktioniert.

    Code
    for raum in obj[0]["raeume"]:
        if raum["name"] == "Wohnen west":
            if raum["sensoren"][1]["wert"]:
                wert = raum["sensoren"][1]["wert"]
                print (wert + 0.5)                           # Korrekturfaktor 0.5
                logging.info("Temp: %.2f", wert + 0.5)
  • Hallo,

    Korrekturfaktor wäre doch eine Konstante, die ändert sich im Programmdurchlauf nicht.

    Dann könntest du die auch zu Programmbeginn als so eine definieren und so nennen. Jetzt hast du wieder einen unnötigen Kommentar im Code, weil du ein schlechte oder in dem Fall keinen Namen gewählt hast.

    'wert' war von Tell sicherlich nur ein Name um dir das Prinzip zu zeigen. Dann könnte man doch jetzt eher 'temperature' nennen, dann sieht man gleich was dahinter steckt.

    Dann würde ich den Korrekturfaktor einmal addieren und zwar in der Zeile, in der du das Ergebnis an den Name bindest. Oder brauchst du denn Wert nochmals ohne Korrekturfaktor?

    Um beim aktuellen Code von __blackjack__ zu bleiben:

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Neue Herausforderung fuer mich.

    Wenn ich die Temperaturen beider Raume ( west und ost) so mache wie im codeblock, zeigt print die richtige Temperatur fuer west und ost an.

    Wenn ich aber nun den durchschnitt beider Raeme anzeigen will kommt immer error dass raum_temperatur2 nicht definiert ist.

    Was mach ich falsch?


  • Danke, funktioniert perfekt. :thumbup:

  • gwaag: Schon wieder kopierter und leicht angepasster Code. ?

    Und er ist fehlerhaft, denn der jeweilige Name wird ja nur definiert falls die innere ``if``-Bedingung in der Schleife wahr ist. Falls nicht, endet das wieder in der Ausnahme weil ein Name nicht definiert ist der in einer Rechnung verwendet wird.

    Ausgehend davon, dass es jeweils nur einen Raum mit dem gleichen Namen gibt, sollte man die Schleife auch abbrechen wenn man den gefunden hat, und nicht sinnlos weitersuchen. Was sich bei einer Funktion mehr oder weniger ”natürlich” ergibt, wenn man an der Stelle wo man den Wert hat, ihn sofort zurückgibt — was die Funktion und damit die Schleife beendet.

    Ungetestet:

    Falls das mehr Temperaturen werden, die man aus den Raumdaten holt, würde es sich vielleicht auch anbieten eine Datenstruktur aus dem Raumdaten zu erstellen wo man das einfacher Abfragen kann. Also beispielsweise ein Wörterbuch das Raumnamen auf Temperaturwerte abbildet.

    Alternativ mit einem Generatorausdruck und `next()` — da bekommt man eine Ausnahme schon mitgeliefert falls kein Raum mit dem Namen gefunden werden kann:

    Python
    def get_temperature(rooms, room_name):
        return next(
            room["sensoren"][1]["wert"]
            for room in rooms
            if room["name"] == room_name
        )

    Oder mit `more_itertools.one()` (externes Modul) statt `next()` — da bekommt man auch eine Ausnahme falls es mehr als einen Raum mit dem gleichen Namen gibt:

    Python
    from more_itertools import one
    
    
    def get_temperature(rooms, room_name):
        return one(
            room["sensoren"][1]["wert"]
            for room in rooms
            if room["name"] == room_name
        )

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!