画面電源オフの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」を監視しています。モニターの後ろにバイアス照明用の LED があり、モニターでそれらのオン/オフを切り替えたいと思っています。

これは、コンピューターを手動でロックする場合は機能しますが、「画面オフ」と「画面をロックするまでの時間」の設定を異なる間隔のままにしておくと、モニターの電源がオフになると LED もオフになりますが、スクリーンセーバー ロックが作動すると LED が再びオンになります。

_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.IdleMonitor

これが私のスクリプトです:

#!/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. org.gnome.Mutter.IdleMonitor の monitor という単語は、monitor アクションから来ており、モニター、つまり画面から来ているわけではありません。つまり、基本的にこれらは、ユーザーがアイドル状態であると宣言され、gnome によってアイドル状態でないと宣言されたときに呼び出されるメソッドのようです。実際のところ、私の場合、画面の電源がオフになったときと一致します。あなたの場合はそうではないかもしれません。
  2. どうやらこれをsystemdエントリとして追加することはできないようです。画面が必要なためです。ただし、Startup ApplicationsGUIで追加することは可能で、動作します。
  3. 私はキーボードのバックライトのオン/オフを切り替えるためにg810-led実行ファイルを使用していますが、これは単なる例として扱う必要があります。他のキーボードでは動作しないのは明らかです。

PS: 「バグ」を発見しました。画面のフェードを中断すると、キーボードは消灯したままになります。

答え3

さて、その後これにイライラした後、私はついに対策を講じ、DBus を監視してセッションのロック/ロック解除イベントを適切に受信する Python ユーティリティ スクリプトを作成しました。

コードはここにホストされていますですが、以下にも記載します。私の目標は、いくつかの理由からこれを Rust で書き直すことですが、主な理由は、適切な Python ライブラリをインストールするためのパッケージをインストールしなくても、ユーザーが簡単に使用できるようにすることです。


前提条件

このコードを実行するには、次のものが必要です。

  • 最近の Python 3、私はこれを Python 3.8.5 で書きました。
  • 卵:
    • dbus-python >=1.2,<2
    • PyGObject >=3.36,<4

これらの Python egg は特定の Ubuntu パッケージによって提供されていますが、適切なバージョンではない可能性があります。16.04 では、必要なパッケージは次のとおりだと思います。

  • python3-gi、これはPyGObject
  • python3-dbus、これはdbus-python

ディストリビューションのバージョンが異なると、これらのパッケージが異なる場合があります。これが、私がこれを Rust で書き直したい理由の 1 つです。他の動機については、この回答の最後にリストします。

コード

コードを見てみましょう。

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画面がロックされたときに実行されます

関連情報