Prosíme přihlašte se nebo zaregistrujte.

Přihlašte se svým uživatelským jménem a heslem.
Vaše pomoc je stále potřeba!

Autor Téma: Skript měnící obnovovací frekvenci monitoru podle (ne)připojení zdroje  (Přečteno 661 krát)

0n1ck

  • Návštěvník
  • Příspěvků: 18
Zdravím, zkouším si napsat takový malý skriptík (jsem v tom úplný nováček), který mi bude hlídat, jestli můj laptop bere energii z baterky nebo ze sítě a podle toho mi přepne frekvenci monitoru (je to pro úsporu baterky).

Toto je script:
Kód: [Vybrat]

status=$(cat /sys/class/power_supply/BAT0/status)

# Echo the status to verify the output
# echo "Battery Status: $status"

# Check if the status is "Discharging"
if [ "$status" = "Discharging" ]; then
    # echo "Setting refresh rate to 60 Hz"
    # Set refresh rate to 60 Hz
     xrandr --rate 60.00
else
    # echo "Setting refresh rate to 144 Hz"
    # Set refresh rate to 144 Hz
    # xrandr --rate 120.00
    xrandr --rate 144.00
fi

# To create or edit .service file
# sudo nano /etc/systemd/system/display_refresh_rate_changer.service
#
# To enable .service
# sudo systemctl enable display_refresh_rate_changer.service
#
# To start .service
# sudo systemctl start display_refresh_rate_changer.service
#
# To disable .service
# sudo systemctl disable display_refresh_rate_changer.service


Konec skriptu.

Toto je .service file

Kód: [Vybrat]

[Unit]
Description=Change Display Refresh Rate based on Battery Status
After=multi-user.target

[Service]
Type=oneshot
Restart=on-failure
ExecStart=/home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh

[Install]
WantedBy=multi-user.target


Konec .service file

Nápad vznikl jako reakce na článek: https://www.linuxuprising.com/2021/02/how-to-limit-battery-charging-set.html
Kde jsem si nastavil limit battery charge na 80% (prý to má dobrý vliv na životnost)

Můj problém je, že když napíšu

Kód: [Vybrat]
sudo systemctl start display_refresh_rate_changer.service
vypíše mi to toto

Kód: [Vybrat]

Job for display_refresh_rate_changer.service failed because the control process exited with error code.
See "systemctl status display_refresh_rate_changer.service" and "journalctl -xeu display_refresh_rate_changer.service" for details.


A když napíšu

Kód: [Vybrat]
systemctl status display_refresh_rate_changer.service
Vypíše se mi

Kód: [Vybrat]

× display_refresh_rate_changer.service - Change Display Refresh Rate based on Battery Status
     Loaded: loaded (/etc/systemd/system/display_refresh_rate_changer.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Wed 2024-04-10 11:05:15 CEST; 7s ago
    Process: 15739 ExecStart=/home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh (code=exited, status=1/FAILURE)
   Main PID: 15739 (code=exited, status=1/FAILURE)
        CPU: 6ms

dub 10 11:05:15 debian systemd[1]: display_refresh_rate_changer.service: Scheduled restart job, restart counter is at 5.
dub 10 11:05:15 debian systemd[1]: Stopped display_refresh_rate_changer.service - Change Display Refresh Rate based on Battery Status.
dub 10 11:05:15 debian systemd[1]: display_refresh_rate_changer.service: Start request repeated too quickly.
dub 10 11:05:15 debian systemd[1]: display_refresh_rate_changer.service: Failed with result 'exit-code'.
dub 10 11:05:15 debian systemd[1]: Failed to start display_refresh_rate_changer.service - Change Display Refresh Rate based on Battery Status.


Sám o sobě skript funguje když jej spustím samostatně, prosím o radu.

PS: Pouzívám Debian 12 Stable s 6.8.4-zabbly+ kernelem, protože tento kernel mi vyřešil nějaké niance s hardwarem, třeba tato informace pomůže kdoví
« Poslední změna: 10 Dubna 2024, 11:10:26 od Ondřej Veverka »

ramael

  • Stálý člen
  • **
  • Příspěvků: 642
Pokud je ten script zde kompletně představen, tak v něm chybí shebang
Kód: [Vybrat]
#! /usr/bin/env bash
status=$(cat /sys/class/power_supply/BAT0/status)
if [ "$status" = "Discharging" ]; then
     xrandr --rate 60.00
else
    xrandr --rate 144.00
fi
Dále se mi moc nezdá řádek v service souboru:
Kód: [Vybrat]
ExecStart=/home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh
Zde si myslím bude také chyba v adrese. Doopravdy ti příkaz echo $USER nebo id -un vypíše user? Doopravdy máš ten script takto zanořený? Má ten script x právo? Copak vypíše toto?:
Kód: [Vybrat]
ls -l /home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh
Ještě bych chtěl podotknout, že ten script se ve funkčním případě spustí jen jednou při startu systému. To znamená, že to nebude hlídat stav baterie...
Lenovo: ThinkPad X380 Yoga
Joutůůůůb

JirkaZ

  • Moderátor
  • Závislák
  • ***
  • Příspěvků: 1772
  • Dlouholetý uživatel Linuxu a open source obecně.
ramaeli, drobný dotaz: jak interpreter rozezná shebang od komentáře?

Je to tou vyhrazenou syntaxí (tj. pokud komentuju, tak nesmím použít dvojkřížek s vykřičníkem)?

Edit: asi jo.
« Poslední změna: 10 Dubna 2024, 20:56:29 od JirkaZ »

ramael

  • Stálý člen
  • **
  • Příspěvků: 642
Ano je to tak. Shebang je velice důležitý nejen v bash. Vždy musí být udána absolutní cesta k obslužnému programu. Díky programu env je to jednodušší. Protože si root může instalovat co chce, kam chce atd., nebo si vytvořit svůj systém. Program env za nás řeší kde to nalézt. Název obslužného programu je pak argument programu env.
Tohle:
Kód: [Vybrat]
#! /bin/bash
bude většinou fungovat jako toto:
Kód: [Vybrat]
#! /usr/bin/bash
stejně jako "čistě" napsané:
Kód: [Vybrat]
#! /usr/bin/env bash
Může se stát, že budou různé verze interpreta a díky tomu shebangu si to tvůrce ošéfuje.
Proto není v linuxu důležité jakou příponu má soubor. Klidně můžeš napsat bash script a pojmenovat ho třeba vypis.muj A bude stejně fungovat jako by se jmenoval vypis.sh nebo jen vypis. A to díky shebangu.
Příklad:
Vytvoříme si soubor s názvem vypis:
Kód: [Vybrat]
>vypis
Vložíme do něho toto:
Kód: [Vybrat]
#! /usr/bin/env bash
echo "******** jdu vypsat složku $( pwd) **********"
ls -l
Dáme mu právo spouštění:
Kód: [Vybrat]
chmod +x vypis
a vytvoříme dvě kopie:
Kód: [Vybrat]
cp vypis vypis.sh
cp vypis vypis.muj
a teď si je postupně spustíme:
Kód: [Vybrat]
./vypis
./vypis.sh
./vypis.muj
Výstup bude pokaždé stejný. Klidně tomu dej pythoní příponu vypis.py ale díky shebangu se to spustí tak jak má, a výstup bude opět stejný.
Samozřejmě to platí nejen pro bash. Pokud vytvoříš script v jiném scriptovacím jazyce (python, perl, lua, ..) zadáš to tam a hotovo. Můžeš i odlišit například mezi verzemi pythonu:
Kód: [Vybrat]
#! /usr/bin/env python2
nebo
Kód: [Vybrat]
#! /usr/bin/env python3
a opět nebude záležet na příponě.
Lenovo: ThinkPad X380 Yoga
Joutůůůůb

0n1ck

  • Návštěvník
  • Příspěvků: 18
Pokud je ten script zde kompletně představen, tak v něm chybí shebang
Kód: [Vybrat]
#! /usr/bin/env bash
status=$(cat /sys/class/power_supply/BAT0/status)
if [ "$status" = "Discharging" ]; then
     xrandr --rate 60.00
else
    xrandr --rate 144.00
fi
Dále se mi moc nezdá řádek v service souboru:
Kód: [Vybrat]
ExecStart=/home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh
Zde si myslím bude také chyba v adrese. Doopravdy ti příkaz echo $USER nebo id -un vypíše user? Doopravdy máš ten script takto zanořený? Má ten script x právo? Copak vypíše toto?:
Kód: [Vybrat]
ls -l /home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh
Ještě bych chtěl podotknout, že ten script se ve funkčním případě spustí jen jednou při startu systému. To znamená, že to nebude hlídat stav baterie...


shebang ve skriptu mám, asi jsem jej nějakým nedopatřením neoznačil při kopírování do fóra, konkrétně takto
Kód: [Vybrat]
#!/bin/bash

Ano skutečně
Kód: [Vybrat]
echo $USER vypíše user, jsem tak pojmenovaný

Kód: [Vybrat]
user@debian:~$ ls -l /home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh
-rwxr-xr-x 1 root root 1078 10. dub 10.41 /home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh

Právo x by mělo mít jestli je x execute

To, že skript bude fungovat jen při startu jsem netušil? Lze to nějak poupravit aby to hlídal stále?

ramael

  • Stálý člen
  • **
  • Příspěvků: 642
Trvalo mi to trochu déle, protože s časem jsem mezi půlkama. A protože myšlenka triviální systemd jednotky se změnila v testovací horor. Vypadá to, že jsem narazil na bug v systemd. Normálně se soubory hlídají pomocí jednotky path. Avšak zrovna na toto to nefunguje. Jak je vidět v manuálu ty soubory se hlídají pomocí inotify generované jádrem. Jednoduchý test nám ukáže, že jádro posílá události dál:
Kód: [Vybrat]
udevadm monitor -u -p
a odpojíme nebo připojíme napájení. A jak je vidět, jádro to pěkně hlásí kam má. Tak se koukneme na inotify. Buď si něco napíšeme v céčku podle man inotify, nebo si nainstalujeme inotify-tools a použijem z toho jeden příkaz:
Kód: [Vybrat]
inotifywait -m /sys/class/power_supply/AC/online
Yes, inotify pracuje jak má, avšak systemd to ignoruje.
V tomto případě máme dvě čisté možnosti jak dál a nespočet prasáren které by to také zvládli, jen by žrali ze systémových prostředků nějaké to procento procesoru. Takže první možnost je napsat si udev pravidlo, k němu systemd službu a samozřejmě ještě skript. Druhá možnost je o dost jednodušší. Poupravíme Tvou systemd službu a přepíšeme skript. Napíšu rovnou výsledné soubory. Ještě než to nakopíruješ je třeba zastavit a vypnout původní službu:
Kód: [Vybrat]
sudo systemctl stop display_refresh_rate_changer.service
sudo systemctl disable display_refresh_rate_changer.service
sudo systemctl daemon-reload
Pak přepíšeš display_refresh_rate_changer.service :
Kód: [Vybrat]
[Unit]
Description=Change Display Refresh Rate based on AC online

[Service]
# ta pomlčka na začátku cesty ke skriptu je správně!
ExecStart=-/home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh

[Install]
WantedBy=graphical.target

A skript /home/user/.scripts/display_refresh_rate_changer/display_refresh_rate_changer.sh :
Kód: [Vybrat]
#! /usr/bin/env bash

#Vytvoří nekonečnou smyčku. Dvojtečka v bash vždy produkuje výstup true.
#Protože je to built-in, tak je to lepší než použít externí program (třeba true)
while :
do
        #Všechny výstupy musí být zahozeny. Skripty v systemd nesmí nic produkovat na standardní výstup.
        #Ani nepotřebujeme znát výsledek výstupu. Skript stojí a čeká na event close na souboru.
        #Jakmile event nastane skript pokračuje dál.
        inotifywait -q -e 'close' /sys/class/power_supply/AC/online &> /dev/null

        #Přečte obsah souboru a matematicky ho porovná s jedničkou
        # $(<soubor) je built-in konstrukce bash. Není třeba externí program cat
if [ $(</sys/class/power_supply/AC/online) -eq 1 ]
then
                #Větev pokud je napájení připojeno
                #Následující řádek na ostrý provoz zakomentovat
echo "Jsem online $(date)" >> /var/log/display_refresh_rate_changer
                xrandr --rate 144.00
else
                #Větev kde je napájení vypnuto
                #Následující řádek na ostrý provoz zakomentovat
echo "Nejsem online $(date)" >> /var/log/display_refresh_rate_changer
                xrandr --rate 60.00
fi
done
Nejdřív si ten skript spusť jen tak
Kód: [Vybrat]
cd /home/user/.scripts/display_refresh_rate_changer/
sudo ./display_refresh_rate_changer.sh
A zkoušej jestli to dělá co má. Pokud ne, napiš podrobnosti. Skript ukončíš klávesovou zkratkou ctrl+c
Pokud funguje otestujem systemd službu jestli je to syntakticky dobře napsané:
Kód: [Vybrat]
systemd-analyze verify display_refresh_rate_changer.service
Když to nic nevypíše tak službu zapneme na test:
Kód: [Vybrat]
sudo systemctl start display_refresh_rate_changer.service
Odpoj a připoj napájení. Mělo by to fungovat. A tak si službu aktivuj aby se spouštěla pokaždé po startu:
Kód: [Vybrat]
sudo systemctl enable display_refresh_rate_changer.service

Něco k tomu skriptu. Zapisuje ti do logu /var/log/display_refresh_rate_changer událost. Jak je napsáno, pokud bude vše ok, je lepší dle instrukcí to deaktivovat zakomentováním dvou řádků. Při příštím spuštění systému už to zapisovat nebude. Usoudil jsem, dle prvotního dotazu, že bude jednodušší a hlavně účinnější testovat jestli je připojen zdroj než v jakém statusu je momentálně baterie.

No a já ještě projedu systemd.path u vývojářů a pokud na to ještě nenarazili nahlásím chybu.
Lenovo: ThinkPad X380 Yoga
Joutůůůůb

ramael

  • Stálý člen
  • **
  • Příspěvků: 642
No tak nevím. Bug jsem nahlásil. A odpověděl sám "bůh", tvůrce systemd. Z odpovědi jsem rozpačitý, ale akceptuji ji. Jak je vidět, jeho chyba to (opět) není. Navrhl jedno z řešení které jsem také navrhnul (udev pravidlo).
Jen mi není jasné proč píše, že to inotify nepokryje a přitom inotifywait -m /sys/class/power_supply/AC/online ten event zachycuje... Holt se musím hodně učit abych tomu lépe rozuměl.
Lenovo: ThinkPad X380 Yoga
Joutůůůůb

Roman Vacho

  • Moderátor
  • Závislák
  • ***
  • Příspěvků: 6052
Tak ještě že jsi se nesetkal s "bohem" GNOME. O tom taky kolují věci.
Vyřešená vlákna je vhodné uzavřít "Topic Solved" dole pod vláknem.
Dlouhé výpisy prosím dávat při psaní příspěvku do bloku funkcí Vložit kód.
Děkujeme.

 

Provoz zaštiťuje spolek OpenAlt.