Playing with DBus and KDE applications (part 3)

In a previous article I have shown how to handle the D-Bus resources provided in general and in particular by the Konsole and Yakuake D-Bus services, and take advantage of them in a Bash script. This time we will explore more services that provide useful features to embed in our Bash scripts.

I always liked the idea to interact with a graphical environment using a shell script, and D-Bus allows to overcome those situations where the regular collection of command line applications is not enough, so let’s see in detail the extra capability that this technology provides.

Notifications

This service displays On the Screen Dialogs, in brief, all these dialogs are placed at the center of the screen and without a title bar. The final look is a panel with an icon and a message.

They are used to notify specific system messages, like:

The screen brightness:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.brightnessChanged 50

The keyboard brightness

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.keyboardBrightnessChanged 50

Speaker volume change:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.volumeChanged 50

Microphone volume change:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.microphoneVolumeChanged 50

Media player volume change:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.mediaPlayerVolumeChanged 50 "Mpv Player" mpv

Keyboard layout change:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.kbdCLayouthanged it

Virtual desktop change:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.virtualDesktopChanged "Desktop 1"

Touchpad enabled or disabled:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.touchpadEnabledChanged 1

Wi-fi enabled or disabled:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.wifiEnabledChanged 0

Bluetooth enabled or disabled:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.bluetoothEnabledChanged 1

Mobile internet enabled or disabled:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.wwanEnabledChanged 0

Virtual keyboard enabled or disabled

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.virtualKeyboardEnabledChanged 1

There is also a custom text and icon notification dialog:

$ qdbus org.freedesktop.Notifications /org/kde/osdService org.kde.osdService.showText alienarena "I want to believe"

Under the same service name org.freedesktop.Notifications there is another path, /org/freedesktop/Notifications, that contains an interesting method: Notify which provides a passive popup dialog.

The kdialog command already provides something similar called passivepopup as seen in the first part of the Kdialog series, but in the last KDE versions, the title contains the app name which is kdialog.

Those who are looking for deeper customization might want to take advantage of that D-Bus service, but unfortunately the type of params required (QStringList for actions and QVariantMap for hints) don’t allow to use it from the command line, a more sophisticated language is required.

In the snippet below, written in Ruby, I will use a variable for each object used to browse the services to give more clarity to the single steps:

require "dbus"

session_bus = DBus.session_bus
if session_bus['org.freedesktop.Notifications'].exists?
  dbus_service = session_bus['org.freedesktop.Notifications']
  dbus_object = dbus_service['/org/freedesktop/Notifications']
  dbus_interface = dbus_object['org.freedesktop.Notifications']
  dbus_interface.send "Notify", 'My app name', 0, 'kwrite', 'Summary','Body', [], {}, 3000
end

KWin

This set of D-Bus methods involves interesting actions upon the default KDE window manager, and as expected allow us to play with the desktop objects like windows and virtual desktops.

Arrange the windows in cascade:

$ qdbus org.kde.KWin /KWin org.kde.KWin.cascadeDesktop

Fill the screen with the windows:

$ qdbus org.kde.KWin /KWin org.kde.KWin.unclutterDesktop

Kill a window:

$ qdbus org.kde.KWin /KWin org.kde.KWin.killWindow

Select a desktop:

$ qdbus org.kde.KWin /KWin org.kde.KWin.setCurrentDesktop 2

Prints the current desktop number:

$ qdbus org.kde.KWin /KWin org.kde.KWin.currentDesktop

Switch to the next desktop:

$ qdbus org.kde.KWin /KWin org.kde.KWin.nextDesktop

Switch to the previous desktop:

$ qdbus org.kde.KWin /KWin org.kde.KWin.previousDesktop

This command prints important information to inspect the current configuration:

$ qdbus org.kde.KWin /KWin org.kde.KWin.supportInformation

This command show a dialog that lists the opened windows and the properties handled by KWin:

$ qdbus org.kde.KWin /KWin org.kde.KWin.showDebugConsole

You can have the same info but for a selected window using the method:

$ qdbus org.kde.KWin /KWin org.kde.KWin.queryWindowInfo

Upower

This is a system service related to the power control and shows info about the battery status, however the same info can be found reading the /sys filesystem or using the command upower and grepping the wanted section:

$ upower -i /org/freedesktop/UPower/devices/battery_BAT1

Let’s go with D-Bus and the battery percentage:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.Percentage

And the /sys filesystem alternative:

$ cat /sys/class/power_supply/BAT1/capacity

The Charging status (1 is charging, 2 it’s not):

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.State

And the /sys filesystem alternative:

$ cat /sys/class/power_supply/BAT1/status

The battery vendor:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.Vendor

The power supply:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.PowerSupply

The battery voltage:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.Voltage

Discharging time in seconds:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.TimeToEmpty

Charging time in seconds:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.TimeToFull

The icon name that represents the status:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.IconName

Here instead three methods to understand if the battery is on and if the lid is present and closed:

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.OnBattery

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.LidIsPresent

$ qdbus --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.LidIsClosed

Here below is a practical application: a script running as a cron task that checks the battery level. Battery vendors always suggest to don’t completely charging the battery, in my case must stop it at about 70%, what this script does is display a Kdialog dialog when this percentage is reached to disconnect the AC power.

#!/bin/bash
MIN_BATTERY_LEVEL=30
MAX_BATTERY_LEVEL=70
QDBUS='qdbus-qt5'

perc=`$QDBUS --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.Percentage`

lid_present=`$QDBUS --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.LidIsPresent`
lid_closed=`$QDBUS --system org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.UPower.LidIsClosed`

# 1: Charging
# 2: Discharging (Power off)
charging=`$QDBUS --system org.freedesktop.UPower /org/freedesktop/UPower/devices/battery_BAT1 org.freedesktop.UPower.Device.State`

function show_dialog {
  switch=${1/info/msgbox}
  kdialog --title "Battery info" --$switch "$2"
}

if [[ $lid_present == 'true' ]]; then
  if [[ $lid_closed == 'true' ]]; then
    exit 0
  fi
fi

if [ $perc -le $MIN_BATTERY_LEVEL ] && [ $charging -eq 2 ]; then
  show_dialog error "Battery level is under $MIN_BATTERY_LEVEL% ($perc%)"
elif [ $perc -ge $MAX_BATTERY_LEVEL ] && [ $charging -eq 1 ]; then
  show_dialog info "Battery is charged at $MAX_BATTERY_LEVEL% ($perc%), disconnect the power plug!"
fi

To achieve a similar result without using cron but taking advantage of the Signals we can use a language like Ruby:

require "dbus"
sys_bus = DBus.system_bus
sess_bus = DBus.session_bus

dservice = sess_bus['org.kde.Solid.PowerManagement']
dobject = dservice['/org/kde/Solid/PowerManagement']
dinterface = dobject['org.kde.Solid.PowerManagement']
dinterface.on_signal("batteryRemainingTimeChanged") do |u|
  # Here you can check the battery level with a similar procedure seen above
  # or recall the script itself
  puts u
end


dmain = DBus::Main.new
dmain << DBus::SessionBus.instance
dmain.run

Login, logout, power off

Everyone knows that loginctl can be used to logout from a GUI session, but to restart, or power off the system can be achieved with the shutdown command which requires a root password.

D-Bus provides a couple of system services that provide these functions to be used by a regular user.

The first is the KSMServer which shows the logout screen:

$ qdbus org.kde.ksmserver /KSMServer org.kde.KSMServerInterface.logout 1

The second comes from the freedesktop specifications and provides more methods to:

Get the session list:

$ qdbus --system org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager.ListSessions

Terminate a specific session:

$ qdbus --system org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager.TerminateSession <session_id>

Power off the system:

$ qdbus --system org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager.PowerOff

Reboot the system:

$ qdbus --system org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager.Reboot

Suspend the system:

$ qdbus --system org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager.Suspend

Hibernate the system:

$ qdbus --system org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager.Hibernate

Final thoughts

In this third part, we explored services that might be useful to complete our Bash scripts and also how to take advantage of them within a Ruby script.

Without D-Bus, these actions would be, if not impossible, at least more difficult to achieve, and having a sort of standard thanks to the freedesktop initiative, many of them are environment independent too.


Creative Commons License This work is licensed under a Creative Commons Attribution-NoCommercial-ShareAlike 4.0 International License


Follow-up articles


Leave a Comment