RGB Mouse as a battery indicator

I got a new gaming mouse, a logitech G203 LightSync.
This mouse has RBG LEDs, and is supported by libratbag (also known as its frontend, Piper)

Sadly, for the time being, libratbag cannot control the RGB zones individually, nor enable the rainbow wave animation.

it's very cool and pretty, but not very useful

On Windows, the G Hub software enables some integrations with games. But when you play a game, your hand is on the mouse, so you cannot see the LEDs !

Libratbag provides an Python API as well as a command-line interface, so I can program whatever color, animation (pulse, solid) and brightness I want.

Battery indicator

I decided to use the RGB LEDs as a battery indicator:

  • fast-pulsing red for > 99% and <10% battery
  • fast-pulsing orange for <30%
  • slow-pulsing green for discharging
  • slow-pulsing blue for charging

I can get the battery status through the psutil Python package.

Notification indicator

I also added another feature: a fast-pulsing bright white whenever a notification comes up.

The D-Bus API allows you to not only listen for incoming notifications, but also to listen for the dismissal event. So the notification indicator won't be a dumb timer, but will correctly react to the desktop state.

I didn't implement a timeout for the notification indicator, because my GNOME desktop automatically dimisses notifications (they remain in the notification history).

code

#!/usr/bin/env python3
from psutil import sensors_battery
from piper.ratbagd import Ratbagd, RatbagdLed
from time import sleep
import dbus
from dbus.mainloop.glib import DBusGMainLoop
from gi.repository import GLib
from threading import Thread

STATE_ALARM = 'alarme'
STATE_OK = 'ok'
STATE_CHARGING = 'charging'
STATE_UNPLUG = 'unplug'
STATE_NOTIF = 'notif'

color_ok = (115, 210, 22)
color_alarme = (255, 95, 0)
color_unplug = (255, 0, 0)
color_charge = (128, 255, 255)
color_notif = (255, 255, 255)
timing_alarme = 500
timing_ok = 3000
timing_charge = 2000


def set_rgb(color, duration, brightness=200):
    r = Ratbagd(api_version=1)
    souris = r.devices[0]
    profil = souris.profiles[0]
    led = profil.leds[0]
    led.mode = RatbagdLed.Mode.BREATHING
    led.color = color
    led.brightness = brightness
    led.effect_duration = duration
    souris.commit()


def apply_state(state):
    if state == STATE_OK:
        set_rgb(color_ok, timing_ok)
    elif state == STATE_ALARM:
        set_rgb(color_alarme, timing_alarme)
    elif state == STATE_CHARGING:
        set_rgb(color_charge, timing_charge)
    elif state == STATE_UNPLUG:
        set_rgb(color_unplug, timing_alarme)
    elif state == STATE_NOTIF:
        set_rgb(color_notif, timing_alarme)


notif = False


def handler(bus, msg):
    print(msg)
    member = msg.get_member()
    print(member)
    global notif
    if member == 'Notify':
        notif = True
    elif member == 'NotificationClosed':
        notif = False
    return


session_bus = dbus.SessionBus(mainloop=DBusGMainLoop(set_as_default=True))
loop = GLib.MainLoop()
session_bus.add_match_string("interface='org.freedesktop.Notifications',eavesdrop='true'")
session_bus.add_message_filter(handler)
Thread(target=loop.run, daemon=True).start()

saved_state = 'err'
state = 'err'

if __name__ == '__main__':
    while True:
        bat = sensors_battery()
        if bat.percent < 30:
            new_state = STATE_ALARM
        if bat.percent >= 30:
            new_state = STATE_OK
        if bat.percent < 10:
            new_state = STATE_UNPLUG
        if bat.power_plugged:
            if bat.percent >= 99:
                new_state = STATE_UNPLUG
            else:
                new_state = STATE_CHARGING
        if notif:
            new_state = STATE_NOTIF
            notif = False

        if new_state != state:
            print(new_state)
            state = new_state
            apply_state(state)
            saved_state = state
        sleep(0.5)

Conclusion & disclaimers

This script has only been tested on Ubuntu 20.04 LTS, with the Ubuntu GNOME desktop, and with my Logitech G203 LightSync.

If you have RGB mouse that has multiple LEDs, this script will only use the first one. You can modify the function set_rgb to use multiple LEDs.
You could even use each LED for a separate indicator, the possibilities are endless !