'vala beginner: how to access DBus properties>

I am trying to convert the following python code into vala

def powerStatusAvailable():
    """
    Check if org.freedesktop.UPower is available so that
    :py:func:`tools.onBattery` would return the correct power status.
    Returns:
        bool:   ``True`` if :py:func:`tools.onBattery` can report power status
    """
    if dbus:
        try:
            bus = dbus.SystemBus()
            proxy = bus.get_object('org.freedesktop.UPower',
                                   '/org/freedesktop/UPower')
            return 'OnBattery' in proxy.GetAll('org.freedesktop.UPower',
                            dbus_interface = 'org.freedesktop.DBus.Properties')
        except dbus.exceptions.DBusException:
            pass
    return False

As a complete beginner to vala, I am very lost. I do not understand the object hierarchy. The documentation and examples use a class Bus. There is also a class DBusProxy Should I be using that? ... it has a method get_cached_property_names

What is the difference between Bus and DBusProxy?

This is my attempt but of course it fails.

  using GLib;

// example of a DBus client
[DBus (name = "org.freedesktop.UPower")]
interface UPowerManager : GLib.Object {
    public abstract GLib.ObjectPath[] enumerate_devices () throws GLib.DBusError,GLib.IOError;
    public abstract GLib.ObjectPath get_display_device () throws GLib.DBusError,GLib.IOError;
    public abstract string get_critical_action () throws GLib.DBusError,GLib.IOError;
    public abstract string[] get_cached_property_names () throws GLib.DBusError,GLib.IOError;


}

int main (string[] args)
{

    UPowerManager upower_manager;
    upower_manager = Bus.get_proxy_sync(BusType.SYSTEM,"org.freedesktop.UPower","/org/freedesktop/UPower");
    string[] property_names;
    property_names = upower_manager.get_cached_property_names();


    stdout.printf ("Hello, World!\n");

    return 0;
}

but compile error

uncaught error: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such method 'GetCachedPropertyNames'



Solution 1:[1]

Here is what I did:

First I got the XML definition using DBus.Introspectable:

dbus-send --system --print-reply --type=method_call --dest=org.freedesktop.UPower /org/freedesktop/UPower org.freedesktop.DBus.Introspectable.Introspect

You can dump the result of that command into a .xml file, but be sure to only include the XML, the file should look like this:

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
                      "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<!-- GDBus 2.58.3 -->
<node>
  <interface name="org.freedesktop.DBus.Properties">
  … and so on
</node>

The next step is to create the interfaces using the vala-dbus-binding-tool which you can get from https://github.com/freesmartphone/vala-dbus-binding-tool

vala-dbus-binding-tool -v

This will generate some vala files, one of them contains the correct interface for org.freedesktop.UPower.

All that is left is to use it which is pretty easy, here is a simple example:

[DBus (name = "org.freedesktop.UPower", timeout = 120000)]
public interface UPower : GLib.Object {
    [DBus (name = "EnumerateDevices")]
    public abstract async GLib.ObjectPath[] enumerate_devices() throws DBusError, IOError;

    [DBus (name = "GetDisplayDevice")]
    public abstract async GLib.ObjectPath get_display_device() throws DBusError, IOError;

    [DBus (name = "GetCriticalAction")]
    public abstract async string get_critical_action() throws DBusError, IOError;

    [DBus (name = "DeviceAdded")]
    public signal void device_added(GLib.ObjectPath device);

    [DBus (name = "DeviceRemoved")]
    public signal void device_removed(GLib.ObjectPath device);

    [DBus (name = "DaemonVersion")]
    public abstract string daemon_version { owned get; }

    [DBus (name = "OnBattery")]
    public abstract bool on_battery {  get; }

    [DBus (name = "LidIsClosed")]
    public abstract bool lid_is_closed {  get; }

    [DBus (name = "LidIsPresent")]
    public abstract bool lid_is_present {  get; }
}

int main (string[] args)
{
    UPower upower;
    upower = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.UPower",
                                "/org/freedesktop/UPower");

    if (upower.on_battery) {
        stdout.printf ("System is running on battery\n");
    }
    else {
        stdout.printf ("System is running on line current\n");
    }

    return 0;
}

As for your question about GLib.Bus vs. GLib.DBusProxy.

I'm no expert, but if you look at the generated C code (which you can get with valac -C):

static gboolean
upower_dbus_proxy_get_on_battery (UPower* self)
{
    GVariant *_inner_reply;
    gboolean _result;
    _inner_reply = g_dbus_proxy_get_cached_property ((GDBusProxy *) self, "OnBattery");
    if (!_inner_reply) {
        GVariant *_arguments;
        GVariant *_reply;
        GVariantBuilder _arguments_builder;
        g_variant_builder_init (&_arguments_builder, G_VARIANT_TYPE_TUPLE);
        g_variant_builder_add_value (&_arguments_builder, g_variant_new_string ("org.freedesktop.UPower"));
        g_variant_builder_add_value (&_arguments_builder, g_variant_new_string ("OnBattery"));
        _arguments = g_variant_builder_end (&_arguments_builder);
        _reply = g_dbus_proxy_call_sync ((GDBusProxy *) self, "org.freedesktop.DBus.Properties.Get", _arguments, G_DBUS_CALL_FLAGS_NONE, 120000, NULL, NULL);
        if (!_reply) {
            gboolean _tmp8_ = FALSE;
            return _tmp8_;
        }
        g_variant_get (_reply, "(v)", &_inner_reply);
        g_variant_unref (_reply);
    }
    _result = g_variant_get_boolean (_inner_reply);
    g_variant_unref (_inner_reply);
    return _result;
}

The high level magic of the DBus tagged interface will automatically call its method on an internal DBusProxy object, no need to write that low level code yourself in Vala.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1