Уведомление о разрядке и заряде батареи на Ubuntu

Какое-то время назад обнаружил, что ноутбук, на котором стоит Lubuntu 20.04 LTS (Focal Fossa), начал внезапно полностью выключаться без каких-либо предупреждений. В первую голову, подозрения падают на разрядившийся аккумулятор. Но в системе выставлен (используется дефолтный Xfce4 Power Manager) критический уровень заряда батареи — 3% — по достижению которого система должна штатно и без потерь данных отправляться в «Ждущий режим» (он же Suspend). И это до поры до времени прекрасно работало. А тут вроде и подзаряжал недавно… Странно.

Грешил уже на материнскую плату или ещё какую проблему с самим нетбуком — благо, старенький он уже. Но не тут-то было. Экспериментальным путём выяснилось, что когда при отсоединённом питании уровень батареи достигает уровня где-то 57%, тут всё разом и заканчивается. Вместе со всеми несохранёнными данными. Тут ведь ещё за жёсткий диск реально переживаешь: подобные аварийные завершения в самый неподходящий момент совершенно ни к чему (может скачиваться кэш какого-нить видео с youtube, что-то активно записываться и т.п.).

Получается, часть «банок» в аккумуляторе предательски сдохла всего за 6 месяцев работы. Батарея, кстати, не родная, от Asus’а, а китайский ноунейм. Обидно, а что делать — пришлось заказать ещё один китайский ноунейм. 😺

Но эту-то жалко выбрасывать: отвал происходит стабильно на 57% — а на том, что выше этого порога, батарея вполне себе держит. Идеальный вариант — перепрошить бы контроллер с откалиброванными значениями. Однако ни программатора, ни соответствующих навыков, ни желания со этим всем возиться у меня — как и у 99,9% пользователей — совершенно точно нет, да и это очевидный overkill. Соответственно, исходить нужно из того, что раз с самой батарей ничего путного не выйдет, «откалибровать» надобно систему, чтобы адекватно реагировала на уровень заряда.

В принципе, варианта с всплывающим поверх всех окон предупреждением для затравки должно хватить. Но как бы это реализовать, чтобы было поменьше возни.

I. Несработавшие решения «из коробки»

Для начала пробуем тривиальные пути. Вот есть у нас индикатор заряда на панели; смотрим в «Свойства» и видим, что в настройках можно выставить максимальное значение критического уровня заряда, по достижению которого совершалось бы предложенное действие.

Но увы — максимально возможное число — 20%, так что желаемые 62% сюда не впихнёшь. Почему вдруг такое ограничение в 20%, совершенно не понятно. Так что про Xfce4 Power Manager можно забыть.

Далее. В Ubuntu есть интерфейс UPower, созданный как раз для работы с аккумуляторами и прочими источниками питания. Демон, как видим, запущен и за чем-то, видать, следит:

~$ ps -aux | grep upower

root 1391 0.0 0.4 253648 9496 ? Ssl фев19 0:23 /usr/lib/upower/upowerd

Конфиг этого хозяйства находится вот здесь: /etc/UPower/UPower.conf Но во-первых, там какая-то мутная простыня. И потом, ка̀к оно стыкуется с тем же Xfce4 Power Manager, большой вопрос. Я сходу нашёл кое-какие несоответствия между значениями, что должны отвечать вроде бы за одно и тоже и там и там — тот же критический заряд батареи, например. Однако они почему-то разные — стало быть, где-то конфликтуют. А хотелось бы чего-то более надёжного.

II. Используем cron

Меж тем, есть в linux-системах универсальные инструменты, работающие практически железобетонно — это планировщик cron. Вот его я и решил задействовать, плюнув на все эти менеджеры питания. Логика, на самом деле, проста: через какой-то промежуток времени (скажем, раз в две минуты) будем проверять заряд батареи, и если он ниже выбранного нами порогового значения, дадим какую-нибудь заметную глазу команду.

Классная новость: совершенно не обязательно вмешиваться в глобальный кроновский скрипт, который записан в /etc/crontab, а заодно и в скрипты, расположенные в /etc/cron.d, /etc/cron.daily, /etc/cron.hourly, /etc/cron.weekly и /etc/cron.monthly (кстати, по уму б их почистить — ужасаешься от одной мысли, чего там разные приложения понаоставляли за годы присутствия в системе, нужного и ненужного 🙂). Можно довольно просто создать и редактировать пользовательский скрипт, вот так:

~$ crontab -e

По сути, это удобный способ сохранения скрипта в файл /var/spool/cron/crontabs/<username>, который cron прочитывает в процессе работы.

Просматривать сохранённое можно вот так:

~$ crontab -l

Наблюдать за статусом и ловить баги — вот так:

~$ systemctl status cron

Очень удобно, что после редактирования пользовательского скрипта сам cron не нужно перезапускать — всё подхватывается на лету.

Теперь осталась самая малость: разобраться с тем, что же, собственно, писать в этот скрипт. 😺

a) Считываем заряд батареи

Для начала определимся, ка̀к будем считывать заряд аккумулятора. С одной стороны, есть готовые решения типа acpi. Что ж, смотрим:

~$ acpi

Command 'acpi' not found, but can be installed with:

sudo apt install acpi

Упс. Из коробки не работает, нам предлагается доставить в систему ещё один неведомый инстрУмент (напомню, у нас и так есть UPower и Xfce4PM, оказавшиеся бесполезными чуть менее чем полностью 🙂).

Да и зачем — само ядро linux позволяет прекрасно считывать текущее значение заряда! Я, кстати, из ppa вот это поставил, свеженькое:

~$ uname -rs

Linux 5.10.17-051017-lowlatency

Так вот, для начала смотрим вот тут:

~$ ls -al /sys/class/power_supply

lrwxrwxrwx 1 root root 0 фев 20 01:43 AC0 -> ../../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/ACPI0003:00/power_supply/AC0
lrwxrwxrwx 1 root root 0 фев 20 01:43 BAT0 -> ../../devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0

Моя батарея — это BAT0 (выделил болдом). Так что нам — по адресу /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0 где можно найти много чего занятного, включая статус (заряжается, разряжается и т.п.). Однако сейчас нас интересует конкретно заряд батареи, который хранится и динамически обновляется в файле capacity. Соответственно, команда

~$ cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/capacity

— и будет выдавать нам текущий заряд аккумулятора в процентах.

b) Пишем скрипт и запускаем через crontab

Теперь в плане оповещений. Я не нашёл ничего лучше, кроме как выдавать команду lxqt-leave: с одной стороны, она не блокирует экран, а с другой, сразу понимаешь намёк: пора б воткнуть зарядку, а не то ай-яй.

Скрипт — отдельный, который мы будем вызывать из crontab — я сочинил такой (подозреваю, неидеальный, зато рабочий):

#!/bin/bash
# custom battery charge level checking script - see also "crontab -l"

a=`cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/capacity`

if [ "$a" -lt 62 ] || [ "$a" -eq 62 ]; then
lxqt-leave > /dev/null 2>&1
else
return
fi

Здесь есть два момента. Во-первых, cron работает в фоновом режиме, и «графические» команды типа тех же окошек с оповещением и проч. просто так не запустит. Ему надо в явном виде указать переменные $DISPLAY и $DBUS_SESSION_BUS_ADDRESS (возможно, и больше, но в моём случае этого хватило). Значения можно посмотреть, открыв терминал и набрав, например,

~$ echo $DBUS_SESSION_BUS_ADDRESS

unix:path=/run/user/1000/bus

И вот эти значения надо вставить в crontab (то бишь через crontab -e).

Второй момент. Кастомный скрипт можно хранить где-то отдельно в текстовом файле и дальше запускать уже в пространстве cron’овского скрипта. Но я подумал, зачем, если всё можно уместить в одну строчку непосредственно в crontab, пускай и максимально сократив. Однострочник из этого выжал такой:

sh -c "if [ `cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/capacity` -lt 63 ]; then lxqt-leave > /dev/null 2>&1; fi"

Итого, окончательный cron’овский скрипт у меня получился таким:

# battery notification script

DISPLAY=:0
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

*/2 * * * * sh -c "if [ `cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/capacity` -lt 63 ]; then lxqt-leave > /dev/null 2>&1; fi"

Каждые 2 минуты он смотрит в заряд батареи, и если тот меньше 63 (ну, мои фатальные 57% и 5-6 сверху), выдаёт удобное диалоговое окошко завершения работы системы.

В общем, не вполне элегантное, зато, как по мне, крайне простое, удобное и маложоркое решение. При желании можно и звуковое оповещение прикрутить — на случай, если уйдёшь на кухню, например. Но мне и этого хватает. 🙂

{upd 28.02.2021}

Окончательный вариант скрипта приобрёл вот такую форму:

# battery low level notification script

DISPLAY=:0
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

*/2 * * * * sh -c "if [ `cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/capacity` -lt 63 ] && [ `cat /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/status` = Discharging ]; then lxqt-leave > /dev/null 2>&1; fi"

Содержание дополнительного файла /sys/devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/PNP0C0A:00/power_supply/BAT0/status, который мы здесь проверяем, может быть Charging и Discharging — в зависимости от того, включён ли ноутбук в розетку. Задействовать в скрипте эти значения удобно, поскольку смысл окошка с предупреждением при подзарядке батареи от сети, в общем, пропадает.

Запись опубликована в рубрике компьютерное с метками , , , , . Добавьте в закладки постоянную ссылку.

Внимание! Администрация сайта adlersky.top не имеет отношения и не несёт никакой ответственности за публикуемые ниже, т.е. под оригинальными записями и внизу страниц сайта, комментарии, не отвечает за их содержание. Все права на комментарии (и всё бремя ответственности за публикацию) принадлежат их авторам.

Добавить комментарий

Публикуя здесь что-либо, вы обязуетесь строго следовать российскому законодательству и несёте ответственность за свои комментарии самостоятельно. Ваши персональные данные здесь не обрабатываются и не хранятся. Администрация сайта adlersky.top не имеет отношения и не несёт никакой ответственности за публикуемые под записями и страницами сайта комментарии.
☝ По нажатию "Отправить" комментарий автоматически уйдёт на модерацию

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.