訂閱屏斷電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 中的「監視器」一詞來自監視操作,而不是來自監視器(又稱螢幕)。所以基本上這些似乎是當 gnome 聲明用戶空閒和不空閒時調用的方法。事實上,就我而言,它與螢幕關閉同時發生。就你而言,可能不會。
  2. 顯然你不能將其添加為系統條目,因為它需要一個螢幕。但是,您可以將其添加到Startup ApplicationsGUI 中並且它可以工作
  3. 我正在使用 g810-led 可執行檔來開啟和關閉鍵盤背光,這應該只是一個範例,因為它顯然不適用於其他鍵盤

PS:發現一個「bug」。如果您中斷螢幕淡入淡出,鍵盤將保持不亮狀態。

答案3

好吧之後由於對此感到沮喪,我最終做了一些事情並編寫了一個 Python 實用程式腳本來監視 DBus 並正確接收會話鎖定/解鎖事件。

程式碼託管在這裡,但我也會將其包含在下面。我的目標是用 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 重寫這個的眾多原因之一,我將在這個答案的末尾列出我的其他動機。

程式碼

讓我們進入代碼。

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,它將在螢幕鎖定時執行

相關內容