An Ubuntu One printer

At the Ubuntu Developer Summit in October 2011 there was a session on Ubuntu One cloud printing. That session was about Ubuntu One being a gateway to existing cloud printing services, like HP’s ePrint and Google Cloud Print, and I’m looking into that. However, the idea of Ubuntu One being itself a cloud printing service came up in that discussion: specifically, if I’ve got an Ubuntu machine with a printer plugged into it, can I make that printer available from other machines (or other devices such as phones) through Ubuntu One itself? Can I take my home printer and use Ubuntu One to set it up so that I can print to that printer from anywhere in the world and from any device? We (the U1 team) won’t be working on that idea directly, but in the session I suggested that it would be relatively easily done. Some people asked how it could be done, and suggested that they might be interested in working on it. This describes one way to do it.

Basically, this process involves being able to send documents to the machine with the printer connected to it, and then having that machine notice the newly-arrived documents and printing them. To do this, we need a “U1 printer daemon” on the printer machine. I propose the following architecture:

There is a machine, in your house or similar, with a printer connected to it. We’ll call this machine the printer machine. On the printer machine, set up the printer so it can be printed to locally. Next, we invent a printer daemon. This printer daemon creates a folder and marks that folder as synced with Ubuntu One, and subscribes to that folder on this machine. The printer daemon then waits for new files in that folder (see below for how to do this). When a new file arrives, the printer daemon sends the file to the local printer, and then deletes the file (so it doesn’t linger around forever).

I suggest that the folder that’s creates is named $HOME/.ubuntuone/Print Queues/printer name. It’s a hidden folder because it’s not really something that should be shown in a user’s home folder; it’s under .ubuntuone because that’s a static path across machines (putting it in $XDG_DATA_HOME is problematic because that path may not be the same on all machines), it’s a separate synced folder (rather than being under $HOME/Ubuntu One) because files for printer A only need to be synced to the machine connected to printer A, not to all machines you have connected to Ubuntu One, and it’s under .ubuntuone/Print Queues because third party apps which want to offer “print to an Ubuntu One printer” can then list all your Ubuntu One printers by enumerating the contents of that folder via the REST API.

A third-party app (say, one on a mobile phone) which wants to offer “print to an Ubuntu One printer” would then take the document to be printed, show the user the collection of Ubuntu One printers (by enumerating the contents of .ubuntuone/Print Queues using the REST API) and then upload the document to be printed to the chosen .ubuntuone/Print Queues/printer name folder. No-one ever sees the existence of the .ubuntuone/Print Queues folder, of course; it’s all transparent to the user.

Now, how does one monitor an Ubuntu One synced folder for changes? If you’re a Python app (on Ubuntu or Windows) and you’re running a very recent build of Ubuntu One (the shortly-to-be-released Windows build, or a nightly build on Ubuntu) you can use the Python SyncDaemonTool, and your daemon would look roughly like this:

import os, sys

if sys.platform != 'win32':
    from twisted.internet import glib2reactor
    glib2reactor.install()
    from dbus.mainloop.glib import DBusGMainLoop
    DBusGMainLoop(set_as_default=True)

from twisted.internet import defer, reactor
from ubuntuone.platform.tools import SyncDaemonTool
EXPECTED = os.path.expanduser('~/.ubuntuone/Print Queues/')

@defer.inlineCallbacks
def track_file_download():
    sd = SyncDaemonTool()
    success_filter = lambda path, info: path.startswith(EXPECTED)
    path, info = yield sd.wait_for_signals(signal_ok='DownloadFinished',
                                           success_filter=success_filter)
    print '==========n', path, info

if __name__ == '__main__':
    reactor.callWhenRunning(track_file_download)
    reactor.run()

This, as noted, requires a very very recent build of Ubuntu One, so it’s probably not useful right now (it will be in the future, though, so if you’re reading this as a result of a Google search, do it this way). The other way to do this on Ubuntu is to listen to the DownloadFinished signal on the com.ubuntuone.SyncDaemon.Status D-Bus interface: that code would look something like this:

import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
def track_file_download(path, info):
    print path
dbus.SessionBus().add_signal_receiver(handler_function=track_file_download,
    signal_name="DownloadFinished", 
    dbus_interface="com.ubuntuone.SyncDaemon.Status",
    bus_name="com.ubuntuone.SyncDaemon", path="/status")
loop = gobject.MainLoop()
loop.run()

A note: most files can’t be printed as is: they need the application that generated them to print them. However, printing systems know natively how to handle PDF files. What this means, in practice, is that the app which uploads the file for printing to the printer queue folder should convert it to PDF before uploading it.

That’s my proposal for how this could be done. If you’re interested in hacking on this, I’d love to hear from you!

More in the discussion (powered by webmentions)

  • (no mentions, yet.)