Assine o evento DBUS de desligamento da tela

Assine o evento DBUS de desligamento da tela

Meu MacBook Pro tem uma luz de fundo do teclado que é incrível, mas há um pequeno bug: a tela desliga após um determinado período de tempo, mas a luz de fundo do teclado permanece acesa.

Copiei e mexi com um pequeno script DBUS Python para monitorar quando ocorrem alterações no estado do protetor de tela, mas ele não é acionado quando a tela é desligada, apenas quando o protetor de tela é ativado ou desativado:

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()

Isso funciona muito bem sempre que o protetor de tela é ativado, mas não é alterado se o estado de energia da tela mudar, o que pode acontecer independentemente do protetor de tela. Acessando Brilho e Bloqueio nas Configurações, você pode configurar as telas para desligar após 1 minuto e não bloquear. Você pode então definir o tempo do protetor de tela para um período diferente, digamos 10 minutos.

Tentei ouvir org.gnome.SettingsDaemon.Power.Screeno sinal na interface Changed, mas isso só acontece quando o brilho da tela é alterado manualmente.

O que posso ouvir para determinar quando o estado de energia da tela mudou? Quero escrever um script que seja executado sempre que a tela for desligada, para que eu possa desativar a luz de fundo do teclado.

Responder1

Bem, é uma pena não poder deixar um comentário porque não tenho a "reputação". Isso é mais um comentário do que uma solução.

Estou procurando por algo semelhante e estou monitorando 'org.gnome.SessionManager.Presence'. Tenho LEDs atrás do meu monitor para iluminação polarizada e quero desligá-los/ligá-los com o monitor.

Isso funciona se eu bloquear meu computador manualmente, porém se eu deixar as configurações de "tela desligada" e "tela de bloqueio após" em intervalos diferentes, os LEDs apagam quando o monitor é desligado, porém quando o bloqueio do protetor de tela entra em ação ele desliga o LEDs acesos novamente.

_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
}

Referência: https://www.organicdesign.co.nz/PoisonTap_solution

Responder2

Acabei de instalar o Ubuntu 18.04 e descobri que não há protetor de tela presente por padrão. E, honestamente, não quero um, então não vou me preocupar em instalar um.

No entanto, encontrei algumas chamadas de método do gnome que parecem funcionar: AddUserActiveWatche RemoveWatchda org.gnome.Mutter.IdleMonitorinterface.

Aqui está meu roteiro:

#!/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 ()

O resultado é:

  • quando a tela começa a escurecer, a luz de fundo do teclado é desligada
  • o teclado acende somente DEPOIS de eu fazer login com sucesso, não na tela de login (mas está perto o suficiente)

Isenções de responsabilidade:

  1. A palavra monitor em org.gnome.Mutter.IdleMonitor vem da ação de monitoramento, não da tela do monitor. Então, basicamente, esses parecem ser métodos chamados quando o usuário é declarado inativo e não inativo pelo gnome. Aliás, no meu caso, coincide com o desligamento da tela. No seu caso, pode não ser.
  2. Aparentemente você não pode adicionar isso como uma entrada do systemd, porque precisa de uma tela. No entanto, você pode adicioná-lo na Startup ApplicationsGUI e funciona
  3. Estou usando o executável g810-led para ligar e desligar a luz de fundo do teclado, que deve ser tratado apenas como exemplo, pois obviamente não funcionará em outros teclados

PS: encontrei um "bug". Se você interromper o esmaecimento da tela, o teclado permanecerá apagado.

Responder3

Ok, então depoisanosPor estar frustrado com isso, finalmente fiz algo a respeito e escrevi um script utilitário Python que monitora o DBus e recebe corretamente eventos de bloqueio/desbloqueio de sessão.

Oo código está hospedado aqui, mas vou incluí-lo abaixo também. Meu objetivo é reescrever isso em Rust por uma série de razões, mas principalmente para torná-lo mais fácil de usar sem a necessidade de instalar pacotes para instalar as bibliotecas Python corretas.


Pré-requisitos

Para executar este código, você precisará de:

  • Um Python 3 recente, escrevi isso no Python 3.8.5.
  • Ovos:
    • dbus-python >=1.2,<2
    • PyGObject >=3.36,<4

Esses ovos Python são fornecidos por determinados pacotes do Ubuntu, mas podem não ser as versões corretas. Em 16.04, acredito que os pacotes necessários são:

  • python3-gi, qual éPyGObject
  • python3-dbus, qual édbus-python

Em diferentes versões de distribuição, esses pacotes podem ser diferentes. Este é um dos muitos motivos pelos quais quero reescrever isso em Rust. Listarei minhas outras motivações no final desta resposta.

Código

Vamos entrar no código.

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()

Para que este código faça algo interessante, edite:

  • ScreenSaverEventListener.on_session_lock, que será executado quando a tela for bloqueada

informação relacionada