Подпишитесь на событие DBUS по отключению экрана

Подпишитесь на событие DBUS по отключению экрана

У моего MacBook Pro есть подсветка клавиатуры, что просто здорово, но есть небольшой баг: экран выключается через определенное время, но подсветка клавиатуры остается включенной.

Я скопировал и доработал небольшой скрипт DBUS Python для отслеживания изменений состояния заставки, но он не срабатывает при выключении экрана, а только при активации или деактивации заставки:

from dbus.mainloop.glib import DBusGMainLoop

import dbus
import gobject
import logging

logging.basicConfig()

logger = logging.getLogger(__name__)

dbus_loop = DBusGMainLoop(set_as_default=True)

def message_callback(bus, message):
    if message.get_interface() == "org.gnome.ScreenSaver":
        if message.get_member() == "ActiveChanged":
            screensaver_enabled = bool(message.get_args_list()[0])
            logger.info("Screen saver changed. Active: %s", screensaver_enabled)

session = dbus.SessionBus(mainloop=dbus_loop)
session.add_match_string_non_blocking("interface='org.gnome.ScreenSaver'")

session.add_message_filter(message_callback)

logger.info("Starting up.")

loop = gobject.MainLoop()
loop.run()

Это отлично работает, когда активируется заставка, но не изменяется, если изменяется состояние питания экрана, что может произойти независимо от заставки. Перейдя в раздел «Яркость и блокировка» в настройках, вы можете настроить экраны на отключение питания через 1 минуту и ​​не блокировку. Затем вы можете установить время заставки на другой промежуток времени, например, 10 минут.

Я пробовал прослушивать сигнал на org.gnome.SettingsDaemon.Power.Screenинтерфейсе Changed, но это происходит только при ручном изменении яркости экрана.

Что я могу послушать, чтобы определить, когда изменилось состояние питания экрана? Я хочу написать скрипт, который будет запускаться всякий раз, когда отключается питание экрана, чтобы я мог отключить подсветку клавиатуры.

решение1

Ну, отстой, что я не могу оставить комментарий, потому что у меня нет "репутации". Это скорее комментарий, чем решение.

Я искал что-то похожее, и вместо этого я отслеживаю 'org.gnome.SessionManager.Presence'. У меня есть светодиоды за монитором для подсветки, и я хочу выключать/включать их вместе с монитором.

Это работает, если я блокирую компьютер вручную, однако если я оставляю настройки «экран выключен» и «блокировка экрана через» на разных интервалах, светодиоды выключаются, когда выключается монитор, однако, когда срабатывает блокировка заставки, светодиоды снова включаются.

_getState () {
  dbus-monitor --session "type=signal,interface=org.gnome.SessionManager.Presence,member=StatusChanged" |
  while read x; do
    case "$x" in 
      *"uint32 3"*)
          /home/victor/bin/devices/kasa_cntrl.sh off
          echo -e "$(date)\t-\tTurned off" >> "$log"
          ;;
      *"uint32 0"*)
          /home/victor/bin/devices/kasa_cntrl.sh on
          echo -e "$(date)\t-\tTurned on" >> "$log"
          ;;
    esac
  done
}

Ссылка: https://www.organicdesign.co.nz/PoisonTap_solution

решение2

Я только что установил Ubuntu 18.04, и оказалось, что по умолчанию нет заставки. И честно говоря, она мне не нужна, поэтому я не буду заморачиваться с ее установкой.

Однако я нашел несколько вызовов методов из gnome, которые, похоже, справляются с этой задачей: AddUserActiveWatchи RemoveWatchиз org.gnome.Mutter.IdleMonitorinterface.

Вот мой сценарий:

#!/usr/bin/env python

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
from subprocess import call

def filter_cb(bus,message):
    if message.get_member() == "AddUserActiveWatch":
        print("Monitor off")
        call("/usr/bin/g810-led -dv 046d -dp c337 -a 000000", shell=True)
    elif message.get_member() == "RemoveWatch":
        print("Monitor on")
        call("/usr/bin/g810-led -dv 046d -dp c337 -p /etc/g810-led/profile", shell=True)
    return

DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

bus.add_match_string_non_blocking("interface='org.gnome.Mutter.IdleMonitor',eavesdrop='true'")
bus.add_message_filter(filter_cb)

mainloop = gobject.MainLoop ()
mainloop.run ()

Результат:

  • когда дисплей начинает гаснуть, подсветка моей клавиатуры отключается
  • клавиатура загорается только ПОСЛЕ того, как я успешно вхожу в систему, а не на экране входа (но это достаточно близко)

Отказ от ответственности:

  1. Слово monitor в org.gnome.Mutter.IdleMonitor происходит от действия monitoring, а не от monitor, он же screen. Так что в основном это, похоже, методы, которые вызываются, когда пользователь объявляется бездействующим, а не бездействующим gnome. По сути, в моем случае это совпадает с выключением экрана. В вашем случае это может быть не так.
  2. Видимо, вы не можете добавить это как запись systemd, потому что для этого нужен экран. Однако вы можете добавить это в Startup ApplicationsGUI, и это работает
  3. Я использую исполняемый файл g810-led для включения и выключения подсветки клавиатуры, это следует рассматривать просто как пример, поскольку он, очевидно, не будет работать на других клавиатурах.

PS: нашел "баг". Если прерывать затухание экрана, клавиатура остается неподсвеченной.

решение3

Хорошо, тогда послегодыЧтобы не расстраиваться из-за этого, я наконец что-то сделал и написал скрипт-утилиту на Python, которая отслеживает DBus и правильно принимает события блокировки/разблокировки сеанса.

Theкод размещен здесь, но я также включу его ниже. Моя цель — переписать это на Rust по ряду причин, но в основном для того, чтобы людям было проще использовать его без необходимости устанавливать пакеты для установки нужных библиотек Python.


Предпосылки

Для запуска этого кода вам понадобится:

  • Недавно выпущенный Python 3, я написал это на Python 3.8.5.
  • Яйца:
    • dbus-python >=1.2,<2
    • PyGObject >=3.36,<4

Эти яйца Python предоставляются определенными пакетами Ubuntu, но могут быть неверными версиями. На 16.04, я считаю, что требуемые пакеты следующие:

  • python3-gi, которыйPyGObject
  • python3-dbus, которыйdbus-python

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

Код

Давайте разберемся с кодом.

dbus-session-lock-watcher.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from dbus.mainloop.glib import DBusGMainLoop

from gi.repository import GLib

import dbus
import logging
import sys


class ScreenSaverEventListener(object):

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.mainloop = DBusGMainLoop()
        self.loop = GLib.MainLoop()
        self.session_bus = dbus.SessionBus(mainloop=self.mainloop)

        self.receiver_args, self.receiver_kwargs = None, None

    def setup(self):
        self.receiver_args = (self.on_session_activity_change,)
        self.receiver_kwargs = dict(dbus_interface="org.freedesktop.DBus.Properties", path="/org/gnome/SessionManager",
                                    signal_name="PropertiesChanged",
                                    # callback arguments
                                    sender_keyword="sender", destination_keyword="dest",
                                    interface_keyword="interface", member_keyword="member", path_keyword="path",
                                    message_keyword="message")

        self.session_bus.add_signal_receiver(*self.receiver_args, **self.receiver_kwargs)

    def on_session_activity_change(self, target: dbus.String, changed_properties: dbus.Dictionary, *args, **kwargs):
        if target != "org.gnome.SessionManager" or "SessionIsActive" not in changed_properties:
            return

        if changed_properties.get("SessionIsActive"):
            self.on_session_unlock()
        else:
            self.on_session_lock()

    def on_session_lock(self):
        self.logger.info("Session Locked")

    def on_session_unlock(self):
        self.logger.info("Session Unlocked")

    def run(self):
        self.logger.debug("Starting event loop.")
        self.loop.run()

    def shutdown(self):
        self.logger.debug("Stopping event loop.")
        self.session_bus.remove_signal_receiver(*self.receiver_args, **self.receiver_kwargs)
        self.loop.quit()


def main():
    setup_logging()

    listener = ScreenSaverEventListener()
    listener.setup()

    try:
        listener.run()
    except KeyboardInterrupt:
        sys.stderr.write("ctrl+c received, shutting down...\n")
        listener.shutdown()


def setup_logging():
    console = logging.StreamHandler(sys.stderr)
    console.setFormatter(
        logging.Formatter("%(asctime)s [%(levelname)-5s] %(name)s: %(message)s", datefmt="%Y-%m-%dT%H:%M:%S%z"))
    logging.addLevelName(logging.WARNING, "WARN")
    logging.getLogger().addHandler(console)
    logging.getLogger().setLevel(logging.DEBUG)


if __name__ == "__main__":
    main()

Чтобы этот код делал что-то интересное, отредактируйте:

  • ScreenSaverEventListener.on_session_lock, который будет выполнен, когда экран заблокирован

Связанный контент