mein erstes Python Skript

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
startrix
User
Beiträge: 6
Registriert: Dienstag 18. Dezember 2018, 19:42

Liebe Foren Gemeinde,

nach dem ich nun seit heute morgen 09:00 meinen Urlaub mit meinem ersten Python Skript verbringe und nicht weiterkomme, so möchte ich euch gerne um Rat fragen.
Bitte seit nachsichtig ich bin neu in Python, das Thema hab ich generell schon etwas länger auf dem Schirm, der Ursprungs Thread ist hier:

https://knx-user-forum.de/forum/support ... e-py/page2

Es geht um ein Smarthome, im welchen ein Skript eine Aktion zwischen zwei bestimmten Uhrzeiten nur Mo. - Fr. ausführen soll. Später kommt noch hinzu, das die Aktion nur 1x pro Tag ausgeführt werden soll.

Wenn ich den Code ausführe dann funktioniert alles:

Code: Alles auswählen

#!/usr/bin/env python3
# zirkulation_test.py

import time

now = time.strftime("%H:%M:%S")
if sh.Lampen.Flur_unten.Deckenlampe.schalten():
        now = time.strftime("%H:%M:%S")
        timewindow1_ok = ("06:00:00") <= now < ("22:00:00")
        if timewindow1_ok:
            sh.Lampen.Flur_oben.Deckenlampe.schalten('on')
            time.sleep(5)
            sh.Lampen.Flur_oben.Deckenlampe.schalten('off')
            time.sleep(2)
Wie oben im Code geschrieben, wird zwischen 06:00 und 22:00 Uhr die Deckenlampe im Flur oben für 5 Sekunden eingeschaltet und dann wieder ausgeschaltet, wenn jemand die Deckenlampe im Flur unten anschaltet.

Nun möchte ich dies in der nächsten Stufe aber auf die Tage: Mo-Fr beschränken - also nicht am Wochenende.

Hierzu sollte folgender Code dienen, welcher aber aktuell nicht funktioniert:

Code: Alles auswählen

#!/usr/bin/env python3
# zirkulation_test.py

import time
import datetime

now = time.strftime("%H:%M:%S")
day = time.strftime("%a")

if not any([char == day for char in ["Mon","Tue","Wed","Thu","Fri"]]):
    print ("Es ist Wochenende")
else:
    print (day)
    print (now)
    if sh.Lampen.Flur_unten.Deckenlampe.schalten():
        now = time.strftime("%H:%M:%S")
        timewindow1_ok = ("06:00:00") <= now < ("22:00:00")
        if timewindow1_ok:
            sh.Lampen.Flur_oben.Deckenlampe.schalten('on')
            time.sleep(5)
            sh.Lampen.Flur_oben.Deckenlampe.schalten('off')
            time.sleep(2)
            
Bis zu dem print (now) funktioniert es wunderbar, aber der Code welcher vorher oben funktionierte wird anscheinend nicht ausgeführt. Wo liegt hier mein Fehler? Nach so vielen Try and Error versuchen komm ich einfach nicht weiter.

Viele Grüße
Jannis
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Beide Programme sollten gar nicht funktionieren weil `sh` undefiniert ist.

Was bedeutet `char`?

An der Stelle würde ich auch eher mit dem `datetime`-Modul arbeiten und nicht mit Tagesnamen die davon abhängen in welcher Locale-Einstellung das Programm läuft.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
startrix
User
Beiträge: 6
Registriert: Dienstag 18. Dezember 2018, 19:42

Guten Morgen __blackjack__,
__blackjack__ hat geschrieben: Dienstag 18. Dezember 2018, 20:46 Beide Programme sollten gar nicht funktionieren weil `sh` undefiniert ist.
das erste Programm läuft. Du hast zwar recht das sh. nicht definiert ist, lasse ich dies durch die Konsole laufen dann bekomme ich auch die entsprechende Fehlermeldung das sh. nicht definiert ist. Aber das ganze ist ja ein Logik-Skript welches unter https://www.smarthomeng.de/ läuft.
Ich vermute mal das die Definition von sh. dann entpsrechend von dort kommt. Genau kann ich es dir aber noch nicht erklären. So tief bin ich noch lange nicht in der Materie. Wie gesagt das erste Programm läuft und schaltet das Licht entpsrechend ein und nach dem time.sleep auch wieder aus.
__blackjack__ hat geschrieben: Dienstag 18. Dezember 2018, 20:46 Was bedeutet `char`?

Code: Alles auswählen

if not any([char == day for char in ["Mon","Tue","Wed","Thu","Fri"]]):
Char sollte für character stehen, und die Funktion sollte wenn Samstag oder Sonntag ist den if Teil der Anweisung ausführen, und unter der Woche den else Teil der Anweisung.

Damit ich auf der Konsole sehe bis wohin der Code läuft, habe ich immer mal wieder die PRINT Anweisungen eingebaut. Weil der Code ja irgendwann mit sh. ist undefiniert abbricht. Das if not else funktioniert auch soweit und der Code bricht wie beim ersten Code mit sh. ist undefiniert ab.
Der erst Code schaltet mir aber in SmartHomeNG bzw. smarthome.py die Lampen, der zweite nicht. Aber ich finde den Fehler nicht.

Ausführung in der Konsole:

Code: Alles auswählen

Wed
09:19:54
Traceback (most recent call last):
  File "zirkulation_test.py", line 15, in <module>
    if sh.Lampen.Flur_unten.Deckenlampe.schalten():
NameError: name 'sh' is not defined
__blackjack__ hat geschrieben: Dienstag 18. Dezember 2018, 20:46 An der Stelle würde ich auch eher mit dem `datetime`-Modul arbeiten und nicht mit Tagesnamen die davon abhängen in welcher Locale-Einstellung das Programm läuft.
Danke für den Hinweis, mit datetime Modul und datetime.datetime habe ich es zuerst versucht, jedoch bekomme ich hier noch nicht mal eine korrekte Uhrzeit ausgegeben. Da mache ich wohl Grundlegend etwas falsch. Die Ausgabe war hier immer 00:00:00. Wenn ich dich richtig verstehe, dann würde ich mit einer deutschen Lokal Einstellung nicht Tue sondern Die ausgegeben bekommen. Das stellt natürlich bei der weiteren Verwendung auch auf anderen Systemen ein Problem dar.

Hast du vllt. ein Beispiel für mich wie du es mit datetime lösen würdest?

Nunja, neuer Tag neues Glück, mache ich mich mal daran mich weiter mit Python zu beschäftigen. Ich werde berichten wenn ich mein Problem gelöst bekomme. Vielleicht hat ja auch hier jemand noch einen guten Hinweis für mich wie __blackjack__.

Viele Grüße
Jannis
startrix
User
Beiträge: 6
Registriert: Dienstag 18. Dezember 2018, 19:42

Update... nach dem ich mal in den Log von SmartHomeNG geschaut habe:

Code: Alles auswählen

2018-12-19  08:15:17 ERROR    logics.zirkulation Logic: logics.zirkulation, File: /usr/local/smarthome/logics/zirkulation_test.py, Line: 10, Method: <listcomp>, Exception: name 'day' is not defined
Traceback (most recent call last):
  File "/usr/local/smarthome/lib/scheduler.py", line 493, in _task
    exec(obj.bytecode)
  File "/usr/local/smarthome/logics/zirkulation_test.py", line 10, in <module>
    if not any([char == day for char in ["Mon","Tue","Wed","Thu","Fri"]]):
  File "/usr/local/smarthome/logics/zirkulation_test.py", line 10, in <listcomp>
    if not any([char == day for char in ["Mon","Tue","Wed","Thu","Fri"]]):
NameError: name 'day' is not defined
Jedoch irritiert mich die Fehlermeldung jetzt doch sehr. Weil genau das was hier angemeckert wird funktioniert eigentlich in meinen Augen und das die Variable day nicht definiert ist sehe ich auch nicht so. Lasse ich das Skript über die Konsole laufen bekomme ich ja folgende Ausgabe:

Code: Alles auswählen


[pi@SmartHomeNG ../local/smarthome/logics]$ python3 zirkulation_test.py
Wed
09:19:54
Traceback (most recent call last):
  File "zirkulation_test.py", line 15, in <module>
    if sh.Lampen.Flur_unten.Deckenlampe.schalten():
NameError: name 'sh' is not defined

Verändere ich nun in der Abfrage den Wert Wed für Mittwoch auf XXX

Code: Alles auswählen

if not any([char == day for char in ["Mon","Tue","XXX","Thu","Fri"]]):
Und lasse es dann durch die Konsole laufen bekomme ich auch sauber die Antwort das wir Wochenende hätten:

Code: Alles auswählen

[pi@SmartHomeNG ../local/smarthome/logics]$ python3 zirkulation_test.py
Es ist Wochenende
Mhm, jetzt hab ich erstmal was zum grübeln, der Fehler ist wohl hier https://bugs.python.org/issue21161 beschrieben und wird nicht gefixt. Wie ich den nun aber rausbekomme, sehe ich noch nicht...
Grüße Jannis
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Nein, der Bug-Report bezieht sich auf den Debugger pdb.
Dass `day` nicht definiert sein soll, ist schon sehr merkwürdig, da es ja zwei Zeilen davor definiert wird. Hier wird auch `exec` aufgerufen, ohne einen eigenen Namespace anzulegen, was wiederum zu sehr merkwürdigen Fehlern führen kann. Insgesamt scheint da sehr viel Magie von Smarthome gemacht zu werden, die aus nicht einfach nachvollziehbaren Gründen nicht funktioniert.

Pack den ganzen Code ab Zeile 7 in eine Funktion `main` und rufe die zum Schluß einfach auf und schau was passiert.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@startrix: Folgefrage: Was bedeutet `character`. Und wie passt das an der Stelle zu dem Code?

Wenn nach ``print(now)`` nichts mehr ausgeführt wird und im Log nichts steht, dann ergibt die Bedingung beim ``if`` ganz offensichtlich `False` (oder etwas was bei `bool()` den Wert `False` ergibt).

Die zweite Zuweisung an `now` solltest Du weglassen und *der* Zeitpunkt auf den sich das alles beziehen soll, sollte insgesamt auch nur mit *einem* Aufruf ermittelt werden. So wie es jetzt ist, kann das `day` kurz vor Mitternacht ermittelt werden und der `now`-Wert der für das ``if`` verwendet wird, kann kurz nach Mitternacht ermittelt werden. Und dann passen Tag und Uhrzeit so gar nicht zusammen.

Bei `timewindow1_ok` hat die 1 nichts zu suchen. Gewöhn Dir das am besten gar nicht erst an Namen durchnummerieren zu wollen. Den Namen kann man sich auch komplett sparen und den Ausdruck gleich im ``if`` einsetzen.

Hat das zweite `time.sleep()` überhaupt irgendeinen Einfluss? Wie oft wird das Modul denn ausgeführt?

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from datetime import datetime as DateTime, time as Time
from time import sleep

now = DateTime.now()
print(now)

if now.isoweekday() in [6, 7]:
    print('Es ist Wochenende')
else:
    if sh.Lampen.Flur_unten.Deckenlampe.schalten():
        if Time(6) <= now.time() < Time(22):
            sh.Lampen.Flur_oben.Deckenlampe.schalten('on')
            sleep(5)
            sh.Lampen.Flur_oben.Deckenlampe.schalten('off')
            sleep(2)  # TODO Does this make sense?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
startrix
User
Beiträge: 6
Registriert: Dienstag 18. Dezember 2018, 19:42

@__blackjack__
Besten Dank, dein ungetesteter Code funktioniert auf Anhieb und sieht nun so aus:

Code: Alles auswählen

#!/usr/bin/env python3
# zirkulation_test.py

import sys

from datetime import datetime as DateTime, time as Time
from time import sleep

now = DateTime.now()
print(now)

if now.isoweekday() in [6, 7]: # 6=Samstag, 7=Sonntag - definiert das an diesen Tagen nicht der ELSE Teil ausgeführt wird, sondern nur der IF Teil
    sys.exit(0)
else:
    if sh.Lampen.Flur_unten.Deckenlampe.schalten():
        if Time(6) <= now.time() < Time(11): # definiert den Zeitraum - Startzeit / Endzeit
            sh.Lampen.Flur_oben.Deckenlampe.schalten('on')
            sleep(5)
            sh.Lampen.Flur_oben.Deckenlampe.schalten('off')
            sys.exit(0)
__blackjack__ hat geschrieben: Mittwoch 19. Dezember 2018, 10:25 @startrix: Folgefrage: Was bedeutet `character`. Und wie passt das an der Stelle zu dem Code?
Ich habe vorhin schon mal gegoogelt und komme zu der Vermutung das es gar keinen Sinn macht weil der Ausdruck wohl chr und nicht wie bei mir char heißt und wohl verwendet wird um einen integer Wert welcher keine Zahlen oder Buchstaben enthält umzuwandeln. Habt bitte Nachsehen ich bin Anfänger :?:

__blackjack__ hat geschrieben: Mittwoch 19. Dezember 2018, 10:25 Hat das zweite `time.sleep()` überhaupt irgendeinen Einfluss? Wie oft wird das Modul denn ausgeführt?
Denke nicht das es einen großen Einfluss hat, hatte gelesen das man ab und an einen sleep einbauen sollte, damit der RPI nicht überlastet wird.
hab es nun mit sys.exit(0) ersetzt. Da das Skript an der Stelle ja zu ende ist. Oder ist dies unnötig?

Wie oft wird dies ausgeführt? Nun als WatchItem ist hier der Lichtschalter definiert. In abgewandelter Form wird das Skript ja später beim betätigen des Bad Lichtschalters unter der Woche zwischen 05:45 und 07:00 Uhr die Zirkulationspumpe für das Warmwasser für 3 Minuten anmachen.

Sprich jede Betätigung des Lichtschalters Bad wird das Skript starten.
__blackjack__ hat geschrieben: Mittwoch 19. Dezember 2018, 10:25 Bei `timewindow1_ok` hat die 1 nichts zu suchen. Gewöhn Dir das am besten gar nicht erst an Namen durchnummerieren zu wollen.
Ok, werde ich mir zu Herzen nehmen. Hintergrund war, das ich überlege ein Zweites Zeitfenster zu definieren für das Wochenende. Dies wäre dann timewindow2 geworden. Aber das sollte ja mit deiner Lösung einfach realisierbar sein, in dem ich den Code dafür im IF Teil deines Codes ausführe.

Auf jeden Fall vielen Dank - hast du vielleicht einen Tipp für mich wo man verständlich sich zu datetime einlesen kann? Ich bin froh das es nun soweit schon mal läuft. Aber ich würde den Code auch gerne noch komplett verstehen.

@Sirius3: Danke für deine Antwort und deine Hinweise. Den Fehler im Log konnte ich wegbekommen in dem ich den Code um die erste Zeile erweitert habe:

Code: Alles auswählen

globals().update(locals())
if not any([char == day for char in ["Mon","Tue","Wed","Thu","Fri"]]):
     print ("Es ist Wochenende")
Aber das hat nix daran geändert das es trotzdem nicht lief. Schön das sich das mit dem Code von __blackjack__ erledigt hat.

Als nächstes muss ich mir mal neben dem Code verstehen, noch überlegen, wie ich das Skript noch erweitere, das die Zirkulation dann nur einmalig pro Tag - also beim ersten betätigen des Lichtschalters im gültigen Zeitfenster angeschaltet wird.

Viele Liebe Grüße
Jannis
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@startrix: ``sys.exit()` ist in der Tat unnötig, denn das Programm endet nach dem ``if``/``else``-Konstrukt ja auf ”natürliche” Weise in dem es einfach am Ende ankommt.

Wenn es nötig wäre, dann würde auch ein Aufruf davon fehlen, denn es gibt ja immer noch einen Weg durch die Kontrollstruktur die das Ende des Programms erreicht, ohne `sys.exit()` aufzurufen.

Und wenn es nötig wäre, würde man es einmal am Ende hinschreiben, und nicht ans Ende von jedem Zweig der da genommen werden kann.

Normal linear ablaufender Code belastet den Raspi nicht (unnötig). Also macht es auch keinen Sinn da willkürlich irgendwelche `sleep()`-Aufrufe in den Code zu streuen. Wo das Sinn macht sind Endlosschleifen die sehr schnell durchlaufen werden, aber sehr wenig machen ohne das da irgend ein Aufruf blockiert, denn dann ist der Rechner mehr mit dem Abarbeiten der Schleife an sich beschäftigt, als mit dem Inhalt der Schleife. Das macht in den meisten Fällen keinen Sinn. Das Stichwort hier ist „busy waiting“. Wenn der Rechner mit warten tatsächlich *beschäftigt* ist, dann baut man da üblicherweise ein `sleep()` ein. Oder besser: man sucht nach einer Lösung ohne „busy waiting“.

Wenn Du mehr Zeitfenster hast, kannst Du das als Code ausdrücken, aber Du könntest das auch als Datenstruktur machen. Also beispielsweise eine Liste mit Tupeln von Start- und Endzeiten, die dann in einer Schleife abgearbeitet werden. Das lässt sich leicht um weitere Zeitfenster erweitern, und man könnte das auch in eine Daten-/Konfigurationsdatei auslagern.

Die Dokumentation zum `datetime`-Modul hast Du schon gelesen?
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
startrix
User
Beiträge: 6
Registriert: Dienstag 18. Dezember 2018, 19:42

__blackjack__ hat geschrieben: Mittwoch 19. Dezember 2018, 11:29 @startrix: ``sys.exit()` ist in der Tat unnötig, denn das Programm endet nach dem ``if``/``else``-Konstrukt ja auf ”natürliche” Weise in dem es einfach am Ende ankommt.
Danke __blackjack__ , das hab ich dann mal direkt entfernt.
__blackjack__ hat geschrieben: Mittwoch 19. Dezember 2018, 11:29 Die Dokumentation zum `datetime`-Modul hast Du schon gelesen?
Du meinst https://docs.python.org/3/library/datetime.html? Jein, ich habe reingelesen. Aber wenn ich ehrlich bin ist das für einen Anfänger ziemlich viel Input und alles sehr theoretisch. Ich lerne lieber und besser Praxisnah, hab mir aber den link generell mal gespeichert.

In deutsch und praxisorientiert ist mir am liebsten.

Viele Grüße
Jannis
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@startrix: Was ist denn daran theoretisch? Da werden ganz konkret die Datentypen und deren Methoden beschrieben die im `datetime`-Modul vorhanden sind. Und Beispielcode ist da ja auch dabei.

Bezüglich Deutsch gibt's hier im Forum das Thema „It's english, get over it”. So ziemlich alle aktuelle Dokumentation ist in Englisch. Insbesondere Referenz- und API-Dokumentationen. Beim Thema programmieren allgemein, nicht nur bei Python.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
startrix
User
Beiträge: 6
Registriert: Dienstag 18. Dezember 2018, 19:42

@__blackjack__ das hier alles ganz konkret beschrieben ist, stell ich nicht in Frage und ich finde die Seite auch super um etwas nachzuschlagen. Aber wenn einem noch ganz viele Basics wie mir fehlen, dann ist es trotz den hilfreichen Beispielcodes doch eben keine praxisorientierte Lösung.

Was das Englisch angeht, ich musste es vor Jahren schon in der Firma akzeptieren, ich akzeptiere es auch hier und ja es macht auch Sinn.
Aber... wenn man sich in seiner Freizeit mit so einem Thema einfach nur aus Lust und Laune beschäftigt, wäre es in deutsch einfach angenehmer ;-)

Viele Grüße & schöne Festtage
Jannis
Antworten