Wie bekomme ich DISPLAY=:0 in Python aktiviert?

Heute ist Stammtischzeit:
Jeden Donnerstag 20:30 Uhr hier im Chat.
Wer Lust hat, kann sich gerne beteiligen. ;)
  • Hallo Python Experten,

    ich (Windows-Mensch), hat bedingt Ahnung von Linux und Python. möchte aber meinen PIR

    dazu bringen, nach einer Zeit X den Bildschirm auszuschalten und bei Abstand unter 120cm vor dem PIR wieder einzuschalten.

    Dazu benötige ich (warum auch immer) diese Befehle unter Bullseye:

    Display DISPLAY=:0 xrandr --output HDMI-1 --auto --rotate left

    DISPLAY=:0 xrandr --output HDMI-1 --off

    Wenn ich diese 2 Befehle als User pi auf der Console ausführe, dann funktioniert das einwandfrei.

    Nun habe ich gelesen, dass man Python Scripte auch so starten kann

    DISPLAY=:0 python3 pir.py

    Das Script startet oder Fehlermeldungen, aber schaltet den Bildschirm weder aus noch an ...

    Wo liegt mein Fehler?

    Am Ende soll das Script über /etc/rc.local beim Booten aus der /etc/rc.local gestartet werden.

    Inhalt /etc/rc.local:

    Und hier das Python Script:

    Ich bin für jeden Tipp dankbar!

  • Hallo,

    erstmal: Windows und Python schließt sich nicht aus. Python läuft genau so unter Windows wie unter Linux wie unter MacOS.

    Zum Thema: Das Skript kann so nicht funktionieren, da fehlen Funktionsaufrufe in den Zeile 82, 71 und 32. Du rufst nur die Funktionsnamen auf und bekommst du eine Referenz auf die Funktion. Zum Ausführen müssen da noch Klammern hinter.

    rc.local ist krass veraltet, nimmt man nicht mehr. Wenn du etwas bei jedem Systemstart starten willst, dann nimmt man dafür eine systemd Service Unit. Dazu gibt es reichlich Dokus, auch auf deutsch, im Internet.

    `call` wird aus `subprocess` importiert, aber nicht genutzt.

    Gruß, noisefloor

  • Mit DISPLAY=:0 wird garnichts geschaltet. Damit wird nur geklärt auf welchem Bildschirm das Programm ausgeführt werden soll, das danach gestartet wird.

    Das Script startet oder Fehlermeldungen, aber schaltet den Bildschirm weder aus noch an

    In Deinen Funktionen fehlt DISPLAY=:0 für xrandr. Vermutlich wird das gebraucht um zu schalten.


    Btw. Sieh Dir shlex mal an z.B.:

    Python
    from shlex import split
    
    def myfunc_Monitor_ON():
        cmd = split("DISPLAY=:0 xrandr --output HDMI-1 --auto --rotate left")
        subprocess.call(cmd)
  • Was mir jetzt noch fehlt ist die DISPLAY Geschichte.

    Kannst du mir dazu auch einen Tipp geben?

    Das DISPLAY=:0 lässt sich auch als Option hinter xrandr angeben, also abwandeln in xrandr -d :0 --output HDMI-1 --off. Und dann entsprechend nach Python umsetzen, wenn die anderen Parameter im Python-Skript so richtig waren sollte das einfach sein. Hilft das schon?

    :rolleyes: sudo !!

  • Moin!

    Vielen Dank für die Tipps.

    Das Script wird bei Boot gestartet, die Funktionsaufrufe funktionieren.

    Das Script wird nun hier gestartet:

    /lib/systemd/system/pir_sensor.service

    Code
    [Unit]
    Description=MagicMirror PIR Service
    After=multi-user.target
    
    [Service]
    Type=idle
    ExecStart=/usr/bin/python3.9 -u /home/pi/MagicMirror/pirx.py
    
    [Install]
    WantedBy=multi-user.target

    Hier ein Auszug aus dem Syslog:

    Code
    Nov 27 07:09:12 MM3 python3.9[901]: root        : INFO      Monitor gestartet : Counter = 0 - 55.7 cm
    Nov 27 07:09:12 MM3 python3.9[1622]: No protocol specified
    Nov 27 07:09:12 MM3 python3.9[1622]: Can't open display :0
    Nov 27 07:09:12 MM3 python3.9[901]: root        : INFO     MM OFF
    Nov 27 07:09:13 MM3 python3.9[901]: root        : INFO      Counter reset  - 65.2 cm

    Die Funktionsaufrufe funktionen grundsätzlich, zu sehen am INFO MM OFF

    Im Script sehen die Funktionsaufrufe Beispielhaft so aus:

    Code
    def myfunc_Monitor_OFF():
            bashCommand = "xrandr -d :0 --output HDMI-1 --off"
            process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
            output, error = process.communicate()
            logging.info('MM OFF')

    Leider scheint das mit der Übergabe des -d :0 so noch nicht zu funktionieren.

  • Hallo,

    die `After` Direktive ist an der Stelle IMHO Quatsch bzw. es gibt kaum Fälle, wo man eine finales Target als `After` angeben sollte.

    Warum benutzt du `subprocess.Popen()` statt `subprocess.call()`? Popen macht dann Sinn, wenn man mit dem gestarteten Prozess interagieren will / muss - tust du aber nicht (bzw. ist auch bei dir nicht nötig).

    Gruß, noisefloor

  • Von `call()` wird in der Dokumentation abgeraten und auf `run()` verwiesen.

    Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Besonders unübersichtlich ist es wenn man Hauptprogramm und Funktionsdefinitionen vermischt.

    Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

    Konstanten werden am Anfang des Programms definiert, nach den Importen. Damit man die leicht finden und anpassen kann.

    `myfunc_` ist ein überflüssiger, nichtssagender Präfix, der da nichts zu suchen hat. Funktionen (und Methoden) werden nach der Tätigkeit benannt, die sie durchführen. Damit der Leser weiss was passiert, und damit man Funktionen von eher passiven Werten unterscheiden kann. `abstand` und `distanz` sind da beispielsweise sehr verwirrend. Beides bedeutet im Grunde das gleiche, aber das eine ist eine Funktion die das andere berechnet. Man kann aber nicht am Namen erkennen was der Wert ist und was die Tätigkeit die zu dem Wert führt.

    Einen lokalen Namen zu haben der gleichlautend mit dem Funktionsnamen ist, verwirrt unter Umständen auch ein bisschen.

    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.

    `GPIO.cleanup()` sollte nicht nur bei Strg+C augerufen werden, sondern *immer* wenn das Programm verlassen wird. Auch wenn es eine Ausnahme oder ein anderes Signal war. Das gehört also in einen ``finally:``-Zweig.

    `time.time()` ist für Zeitmessungen nicht geeignet, da nicht garantiert ist, dass diese Zeit immer ”vorwärts” läuft. Dafür gibt es `time.monotonic()` oder falls man die höchste Auflösung braucht, die das System zur verfügung stellt: `time.perfcounter()`.

    Für Wahrheitswerte gibt es in Python einen eigenen Datentyp, da sollte man nicht 0 und 1 für missbrauchen. Dann weiss der Leser auch gleich das es nur zwei Monitorzustände gibt. Die Variable würde ich auch immer erst *nach* der Funktion setzen, die den Monitor schaltet, denn nur dort kann man (halbwegs) sicher sein, dass das auch stimmt.

    Die ``if``/``elif``-Struktur im Hauptprogramm testet in den Bedingungen Sachen doppelt und teilweise explizit das Gegenteil von vorherigen Bedingungen. Das ist unübersichtlicher als es sein müsste, und auf fehleranfälliger bei Änderungen, weil man immer daran denken muss auch die gegenteiligen Bedingungen anzupassen, wenn man etwas ändert. An der Stelle kann man dann schon fragen ob die eine 100, statt 120 im Programm schon so ein Fehler ist, oder das tatsächlich so gewollt ist.

    Ungetestet:

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

  • Danke für die Tipps - ich schaffe es heute nicht mehr, dass auszutesten.

    Werde mich die Tage wieder dran setzen und Rückmeldung geben.

    Bezgl. des .Popen : Aus dem Netz gezogen. Kenne mich, wie beschrieben, da nicht wirklich aus.

    @_blackjack_: Werde ich mir anschauen.

    Danke soweit.

  • Hallo _blackjack_,

    hallo RTFM,

    vielen Dank für eure Tipps und Hinweise.

    _blackjack_ hat das Script super optimiert - ein kleiner Fehler muss da noch drin sein, enn der Bildschirm schaltet sich recht schnell

    aus eigenen Stücken wieder ein.

    Aber leider hat RTFM auch recht. "xrandr" scheint nur mit einem angeledeten User zu funktionieren.

    Das ist ja bei einem MagicMirror nicht der Standard. Ich habe ja das Script aus dem /lib/systemd/system/ gestartet und

    damit läuft es unter root.

    Damit hänge ich wieder am ursprünglichen Problem.

    Scheinbar funktioniet "vcgencmd display_power 0" unter Bullseye nicht mehr:

    Denn wenn ich den Befehl im terminal absetze, dann bekomme ich das hier:

    Code
     pi@MM3:~/MagicMirror $ vcgencmd display_power 1
    display_power=1
    pi@MM3:~/MagicMirror $ vcgencmd display_power 0
    display_power=1

    Bei beiden Befehlen wird mir display_power=1 zurückgegeben.

    Ideen?

    Vielen Dank für eure Hilfe!

  • Moin "hyle",

    das Skript wird beim booten gestartet und sollte somit als "root" laufen.

    --> /lib/systemd/system/pir_sensor.service

    oder auch ausprobiert

    --> /etc/rc.local

    Jedenfalls interpretiere ich das so, wenn ich mir das unter htop anschaue

    Keine Ahnung, wie ich den Screenshot hier größer hineinbekomme:

    Der MM läuft im Kiosk-Mode. Also meldet sich kein User an.

    Dadurch scheint die Möglichkeit des xrandr wohl ausgeschlossen.

    Vor Bullseye habe ich meinen PIR (HC-SR04) einwandfrei mit vcgencmd ansprechen und steuern können.

    Bei Bullseye hat man wohl etwas daran gedreht.

    Ich habe schon einige Blogs gesehen, wo über das Problem gesprochen, aber leider noch nicht gelöst wurde.

    Ich möchte über mein Script, dass -blackjack- (fast) gut optimiert hat, den Monitor ansteuern.

    Wenn jemand vor dem Spiegel steht, dann wird über den PIR der Monitor angeschaltet und dann nach Zeit x wieder ausgeschaltet.

    Wenn ich mich über rdp und dem User pi anmelde, dann erhalte ich das hier als Ausgabe von "id":

    Code
    pi@MM3:~ $ id
    uid=1000(pi) gid=1000(pi) Gruppen=1000(pi),4(adm),20(dialout),24(cdrom),27(sudo),29(audio),44(video),46(plugdev),60(games),100(users),105(input),107(render),109(netdev),117(lpadmin),997(gpio),998(i2c),999(spi)

    Ideen?

    Einmal editiert, zuletzt von MagicHH (2. Dezember 2022 um 09:29)

  • Das hat schon seine Richtigkeit, wenn ein normaler User die Einstellungen im Video-Kern nur angezeigt bekommt, aber vcgencmd ist hier der falsche Weg.

    Im Eingabeprompt von #11 ist zu sehen, dass der MM im Home Verzeichnis des Users pi liegt, sodass mir mein Orakel sagt, dass der User pi automatisch angemeldet wird und bis ins Grafiksystem automatisch gebootet wird. Dann wäre der autostart - Ordner des Users pi der richtige Ort für das Script.py (mit xrandr), das die Ausschaltverzögerung nunmehr regeln soll.

    Keine Ahnung, ob mein Orakel recht hat.


    Servus !

    #14 ist vor #13 enststanden

    RTFM = Read The Factory Manual, oder so

  • Hmm, ich dachte, dass ich das ausreichend genug dargestellt hätte, aber dann mache ich das noch einmal komplett :)

    Aktuell wird das Skript hier gestartet: /etc/rc.local

    "Ja", es scheint auch andere ggf. bessere Orte zu geben, aber das wäre eine nachgelagerte Optimierung

    Inhalt des rc.local

    Inhalt des Python Skripts, welches hier liegt: /home/pi/MagicMirror/pirx.py

    Also da, wo vermutet und aufgerufen ...

    Ohne weitere User Anmeldung über RDP reagiert der PIR nicht, da es (scheinbar) unter Bullseye mit dem DISPLAY Probleme gibt.

    Wenn ich mit die Prozesse unter htop anschaue, dann läuft der Job unter dem User root.

    Irgendwo habe ich gelesen, das man kms? in einer /boot/command.txt deaktivieren kann und so vcgencmd wieder verfügbar wäre.

    Aber:

    - ich habe keine /boot/command.txt auf meinem Raspi 4 / Bullseye

    Wenn ich die Datei hätte, oder das sicher irgendwo anders gespeichert ist:

    - ich weiß nicht, welche Nebeneffekte das mit sich bringen könnte

    :/

    Jede Hilfe ist willkommen (habt Rücksicht mit mir - bin normalerweise Windows Mensch :rolleyes: )

    Einmal editiert, zuletzt von MagicHH (2. Dezember 2022 um 12:43)

  • Beitrag von MagicHH (2. Dezember 2022 um 12:21)

    Dieser Beitrag wurde gelöscht, Informationen über den Löschvorgang sind nicht verfügbar.
  • OK, ich habe einen (Um-)Weg gefunden.

    hier im Forum habe ich gelesen, dass man den tvservice und vcgencmd wieder aktivieren kann, wenn

    man kms in der /boot/config.txt deaktiviert.

    Ausprobiert - funktioniert.

    Vielen dank an alle, die geholfen haben und mir auch super Tipps bzgl. der Programmieren etc. gegeben haben!

Jetzt mitmachen!

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