feature: toggle LED lighting of connected devices with screen locking

When the screen is locked, LED lighting is turned off. When the screen
is unlocked, it is turned on again to its previous state.
To accomplish this, the devices RGB settings are changed in a volatile
manner when the screen is locked, by writing only to the device and
not to the cache or configuration, causing the Solaars state and the state
of the devices to become intentionally out-of-sync. When the screen is
unlocked again, the local configuration values can just be applied
again.
This commit is contained in:
jkhsjdhjs 2026-07-02 20:13:59 +02:00
parent b5bff32117
commit 9ccc7231f0
No known key found for this signature in database
3 changed files with 58 additions and 5 deletions

View File

@ -802,6 +802,17 @@ class RGBPowerManager:
if logger.isEnabledFor(logging.WARNING):
logger.warning("%s: failed to wake RGB LEDs: %s", self._device, e)
# --- Suspend / Restore (used e.g. to toggle LEDs on screen lock) ---
def suspend_leds(self):
"""Force enter sleep state (e.g. on screen lock)."""
if self._state != self.SLEEPING:
self._start_sleep()
def restore_leds(self):
"""Force wake from sleep state (e.g. on screen unlock)."""
self._wake()
def _cancel_dim_timer(self):
if self._dim_timer_id is not None:
GLib.source_remove(self._dim_timer_id)

View File

@ -198,6 +198,8 @@ def main():
else:
dbus.watch_suspend_resume(lambda: listener.ping_all(True))
dbus.watch_screen_lock_state(listener.suspend_leds, listener.restore_leds)
configuration.defer_saves = True # allow configuration saves to be deferred
# main UI event loop

View File

@ -30,11 +30,8 @@ from typing import Callable
import gi
import logitech_receiver
from logitech_receiver import base
from logitech_receiver import exceptions
from logitech_receiver import hidpp10_constants
from logitech_receiver import listener
from logitech_receiver import notifications
from logitech_receiver import base, exceptions, hidpp10_constants, listener, notifications, rgb_power, settings_templates as st
from logitech_receiver.hidpp20 import LEDEffectSetting
from . import configuration
from . import dbus
@ -452,6 +449,49 @@ def ping_all(resuming=False):
break
def suspend_leds():
"""Turn off LEDs for all connected devices."""
logger.info("screen locked, suspending LED lighting on all devices")
for listener_thread in _all_listeners.values():
devices = [listener_thread.receiver] if listener_thread.receiver.isDevice else list(listener_thread.receiver)
for dev in devices:
if not dev.online:
continue
if mgr := rgb_power.get_manager(dev):
mgr.suspend_leds()
return
for s in dev.settings:
if s.name in (st.Backlight.name, st.Backlight2.name, st.Backlight2Level.name):
s.write(0, save=False, volatile=True)
elif s.name.startswith(st.LEDZoneSetting.name) or s.name.startswith(st.RGBEffectSetting.name):
s.write(LEDEffectSetting(ID=0), save=False, volatile=True)
elif s.name == st.PerKeyLighting.name and s.read():
s.write({k: 0 for k in s._value.keys()}, save=False, volatile=True)
def restore_leds():
"""Restore LED lighting for all connected devices to their saved states."""
logger.info("screen unlocked, restoring LED lighting on all devices")
for listener_thread in _all_listeners.values():
devices = [listener_thread.receiver] if listener_thread.receiver.isDevice else list(listener_thread.receiver)
for dev in devices:
if not dev.online:
continue
if mgr := rgb_power.get_manager(dev):
mgr.restore_leds()
return
for s in dev.settings:
if s.name in (st.Backlight.name, st.Backlight2.name, st.Backlight2Level.name, st.PerKeyLighting.name) \
or s.name.startswith(st.LEDZoneSetting.name) or s.name.startswith(st.RGBEffectSetting.name):
s.apply()
_status_callback = None # GUI callback to change UI in response to changes to receiver or device status
_setting_callback = None # GUI callback to change UI in response to changes to status
_error_callback = None # GUI callback to report errors