a hacky way of monitoring messages in Telepathy

Sometimes people come up with some creative solutions to solve their problems. The correct way to monitor data, such as messages, coming from Telepathy is to write an Observer, however sometimes you just want to get a feed of all of the text messages (e.g. so you can feed it to your keyboard's LCD or something).

The following is a pure D-Bus solution (although it includes telepathy.interfaces to cut down on typing). It listens to all Channel.Type.Text.Received signals, looks up the connection they came from and resolves the sender handle to a name. However note: it makes a lot more D-Bus calls than is required with Telepathy. Really you should cache the results for these handles and listen to the signals that tell you when that information has updated. If you were doing things properly, that's what you'd do.

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
from telepathy.interfaces import *
from datetime import datetime

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()

def message(id, timestamp, sender, type, flags, text, path=None):

    # path is the object path of the channel, from this we can derive the
    # object path of the connection, and acquire a proxy to it
    service = '.'.join(path.split('/')[1:8])
    conn_path = '/' + service.replace('.', '/')
    conn = bus.get_object(service, conn_path)

    # request the alias and id of the sender handle
    d = dbus.Interface(conn, CONNECTION_INTERFACE_CONTACTS).GetContactAttributes([sender],
        [CONNECTION, CONNECTION_INTERFACE_ALIASING], False)
    alias = d[sender].get(CONNECTION_INTERFACE_ALIASING + '/alias',
        d[sender].get(CONNECTION + '/contact-id', "Unknown"))

    dt = datetime.fromtimestamp(timestamp)

    print "%s <%s> %s" % (dt.strftime('%H:%M'), alias, text)

# listen to all Channel.Type.Text.Received signals
bus.add_signal_receiver(message,
    dbus_interface=CHANNEL_TYPE_TEXT,
    signal_name='Received',
    path_keyword='path')

loop = gobject.MainLoop()
loop.run()

Like I said, this is not efficient use of Telepathy. If it eats your D-Bus, don't blame me.

On the other hand, this has actually started a conversation about possible new convenience classes for telepathy-python.

bowl and chopsticks

this is what goes around; and this.. this is what comes around

It used to be that no applications would compile for 64-bit architectures, because everyone was trying to cram pointers into ints. Today I had the opposite. The head of some code I'm working on wouldn't compile in a 32-bit environment, because someone was trying to store 5 bytes in a long.

We have truly come full circle.

Melbourne Tram Tracker for the N900

So Collabora's robotic and non-robotic overlords very graciously bought everyone on staff an N900 for Christmas. In my opinion, it's actually a very nice phone (although possibly a little on the large side); but the let down is there just isn't the same host of applications for it. Still, possessing both the tools and the skills, I figured I should do something about this, rather than complain.

One of the most useful iPhone applications in Melbourne is the real-time tram tracker. For stops without a display board, you can type in the stop ID and get the upcoming arrivals at that stop. You can also find nearby stops via GPS and a bunch of other things. It turns out that Yarra Trams offer a SOAP WSDL web service that is reasonably well documented, so I've spent a few days putting together a basic tram tracker for Maemo 5 (even if only two people will ever use it).


It currently can show upcoming trams for a stop by ID or by searching for stops by road names. Could possibly also do things like search for stop by route. There is a lot of information available. It doesn't yet do searching by location; the information is in the database, I've just not yet looked at how the location APIs work yet. Also need to add support for storing favourites.

I also want to add support for tracking a tram by tram ID. I'm wondering if it's possible to use the GPS to detect periods of immobility and check the upcoming tram stop after the tram starts moving again. I habitually miss stops; so what I think would be neat is to dial in a stop number or cross road you're looking for, and have your phone notify you when you're approaching it.

The web service uses python-suds, which is unfortunately not packaged for Debian, so I can't just rebuild it for Maemo (if anyone wants to package this up for me, that would be really awesome). Then I'll find out how well my app actually runs on the device.

In case anyone cares, the source code is here.

a threaded processing queue in PyGTK

I'm currently writing a PyGTK client that needs to make network requests using a library that doesn't integrate with the GLib mainloop (python-suds), so I found myself wanting to be able to make network requests without blocking the mainloop, and getting callbacks in my main thread when operations were done. The pattern to use is clearly having a dedicated network thread. In C I might have used GAsyncQueue, however I've found myself quite liking queue.Queue.

The following is a fairly generic class for queuing asynchronous requests. Calling the add_request() method from the main thread queues a function to be run in the worker thread. If the callback or error keywords are provided, these will then be called from the GLib mainloop in the main thread (queued via g_idle_add).

from threading import Thread
from Queue import Queue

import gobject

class ThreadQueue(object):
    def __init__(self):
        self.q = Queue()

        t = Thread(target=self._thread_worker)
        t.setDaemon(True)
        t.start()

    def add_request(self, func, *args, **kwargs):
        """Add a request to the queue. Pass callback= and/or error= as
           keyword arguments to receive return from functions or exceptions.
        """

        self.q.put((func, args, kwargs))

    def _thread_worker(self):
        while True:
            request = self.q.get()
            self.do_request(request)
            self.q.task_done()

    def do_request(self, (func, args, kwargs)):
        if 'callback' in kwargs:
            callback = kwargs['callback']
            del kwargs['callback']
        else:
            callback = None

        if 'error' in kwargs:
            error = kwargs['error']
            del kwargs['error']
        else:
            error = None

        try:
            r = func(*args, **kwargs)
            if not isinstance(r, tuple): r = (r,)
            if callback: self.do_callback(callback, *r)
        except Exception, e:
            if error: self.do_callback(error, e)
            else: print "Unhandled error:", e

    def do_callback(self, callback, *args):
        def _callback(callback, args):
            callback(*args)
            return False

        gobject.idle_add(_callback, callback, args)

We can then inherit this class to provide setup for our specific application:

class WebService(ThreadQueue):
    def __init__(self, guid=None, **kwargs):
        """Initialise the service. If guid is not provided, one will be
           requested (returned in the callback). Pass callback= or error=
           to receive notification of readiness."""
        ThreadQueue.__init__(self)

        self.guid = guid
        self.add_request(self._setup_client, **kwargs)

    def _setup_client(self):
        print "Setting up client"

        ...

        return self.guid

Which we call from our program like this:

class Client(object):
    def __init__(self):
        self.w = WebService(guid=guid, callback=self.client_ready)

    def client_ready(self, guid):
        print "client ready:", guid

gobject.threads_init()

Client()

gtk.main()

What's really cool though is adding methods to the API that are called asynchronously for you. Python makes this possible through the power of decorators. Add the following decorator to a method, and it instead of it being called directly, it will be added to the processing queue.

def async_method(func):
    """Makes the given method asynchronous, meaning when it is called it
       will be queued with add_request.
    """

    def bound_func(obj, *args, **kwargs):
        obj.add_request(func, obj, *args, **kwargs)

    return bound_func

class WebService(ThreadQueue):

    @async_method
    def GetStopInformation(self, stopNo):
        print "Requesting information for stop", stopNo

        ...

And that's it! If you can't follow it, don't worry too much. This is possibly the most Pythonesque bit of code I've ever written, but I've tried to make it generic enough that other people can use it for whatever they need. It's currently part of my app that's beginning to take shape, but the source is here.

Incidently, Maemo people: are there Glade definition files allowing me to use Hildon widgets, GtkBuild and Glade 3? That would be super awesome if there were.

GTK+ is crushing my spirit

I want a widget that is the combination of GtkComboBoxEntry and GtkEntryCompletion that can display a tree of options in a nice, indented way without the expanders (but with the rows expanded). Basically, a search box with a drop-down and hierarchical entries. I think I'm going to have to write my own, thought it sounds like something that might be more widely useful than just my application.

penguin in the tree
Visited '[info]'nixwilliams and '[info]'daniel_bethany for a cup of tea, which become lunch (which was delicious) which became more tea, which became waiting out the rain and watching Nicholas Crane trip over a lot. Got wet going home anyway. Now waiting out the rain again before going to the shops. It's a good thing that I like rain; though mostly I like wearing a giant, warm jumper while it rains outside.

Why Telepathy is not like libpurple

It seems, even though we talk about Telepathy a lot, that there are still people out there who believe that Telepathy is really just a reimplementation of libpurple, just with an annoyingly retentive specification, and D-Bus.

The big thing to understand about Telepathy is not that it's platform independent (even though it is, and that's pretty neat). Or that it's modular (even though it is, and that's also pretty neat). The neat thing about Telepathy is that it's a service. Telepathy provides communications as a service.

What does this mean? This means that Telepathy is not just a library for enabling communications in Empathy, or Kopete. Telepathy can enable communications in everything. Any program can listen to and interact with Telepathy. This means that you can send a user files, straight from Nautilus; or share your desktop with Vinagre/Vino, via a Telepathy Tube; all without having to set up your accounts in each of these programs (this information is stored in the Account Manager, a session-wide Telepathy service responsible for maintaining accounts and connections — Empathy's accounts dialog is just a user interface to this service) or needing to establish a connection per application.

Telepathy components on the bus
Because the information you need is available everywhere, this allows communications features to be integrated into places where they make the most sense in your desktop, rather than implemented in your contact roster (like another chat client does). For instance, mail notifications that Telepathy learns about from webmail services such as MSN and Yahoo (note: not yet implemented) could be plugged into the existing mail-notification applet, or into Evolution to hint when to pull from IMAP. GNOME Shell could provide an embedded GMail-like chat-UI, with popout chat-windows provided by Empathy. All of this is possible without those applications having to have their own preferences dialog for your accounts.

telepathy-glib provides classes for talking to the the Account Manager and Channel Dispatcher, setting up channels and handling contacts. In the future this will be expanded to make it much easier to develop Tube clients and other common tasks (note: avoid libtelepathy and libmission-control, they are deprecated traps and not to be used). Hopefully soon there will be a telepathy-gtk to provide common, reusable GTK+ widgets that applications can use. Telepathy-Qt4 has made it's first shared-library pre-release for those who prefer the other toolkit. If you're interested, I've been working on the Telepathy Developer's Manual.

Useful python trick of the day: dict.get()

How often in Python have you written something like:

d[k] if k in d else "Default"

Admittedly this has gotten a lot shorter since the inclusion of ternary operators in Python, but did you know that Python provides a get method for dictionaries that allows you to provide a default?

d.get(k, "Default")

I only found out today when reviewing code. Exciting!

status update on Clutter-GTK

Some time ago I wrote about my work on Clutter-GTK. Well, I finally got around to rebasing this branch so it could be merged and ebassi merged it for me last night.

There are still bugs in the code that I've not fixed, but many eyes make all bugs shallow, right?

The code includes a number of examples of how to do things.

If you're going to use these classes, you should understand how they work. There are caveats. Certainly I wouldn't base your entire toolkit around the interchanged use of GtkClutterActor and GtkClutterStandin.

GtkClutterActor takes a GtkWidget, rendered offscreen to an XPixmap, and paints it onto a GL texture which it draws on the Clutter stage. As a result, it can draw that texture anywhere, at any stacking level, and do things like taking events and sending them back to GTK+. Updating of the offscreen pixmap is done using Damage events, and things are generally peachy.

GtkClutterActor Implementation
Things basically work like you expect. Although there are some performance issues where widgets that draw using two expose events will sometimes have a delay between the two Damage events that I've not yet explained, which can cause a visual rip.

If GtkClutterActor lets you embed a GtkWidget into a Clutter stage, then GtkClutterStandin goes the other way around, allowing a ClutterActor to be placed within a GtkContainer; but the implementation is different. The ClutterActor is not rendered into offscreen memory, and then copied into an XPixmap and blitted into GTK+, this would not allow for all of those exciting animations you want to do.

Instead what happens is that GtkClutterStandin reserves space within a GtkContainer for the actor it proxies and then places that actor on top of the texture containing the widget contents.

Bugs should be filed in the Opened Hand Bugzilla.

bump

So, I quite like the idea of Bump, a technology that enables data transfer by matching a bump read by your phone's motion sensors along with information like your GPS location. However, it does require sending all of your contact information to a 3rd party server, maybe that's fine? Maybe it's not.

It seems like this is exactly the sort of thing you could achieve using telepathy-salut (link-local XMPP) and Telepathy Tubes. You would have a MUC (multi-user, many-to-many) D-Bus tube where people's devices announced bumps, when clients found a match. Clients could then open up a 1-to-1 tube to transfer their data. Matching it slightly easier, because you'd only have to look at bumps on your MUC (plus you have the bump sensor reading + GPS)

Unfortunately this idea does full down with one problem. Multicast (as used in link-local XMPP) requires you to be on the same network segment, which it not something that can always been ensured, especially for phones and especially if there is no wireless network around. If the radios were also joined to an ad-hoc network for finding people nearby, this could work. Some wireless chipsets can multiplex their radios onto several networks, but you probably wouldn't be able to do this portably across many devices.

Creative Commons Attribution-ShareAlike 2.5 Australia
This work by Danielle Madeley is licensed under a Creative Commons Attribution-ShareAlike 2.5 Australia.