Berg's Little Printer
Writing proposals? Try Proppy, our new product!
Reading time: ~5 minutes.
I'm a big fan of the amazing London design agency Berg. So when its CEO Matt Berg tweeted this last year:
I'm after a London Python freelancer for a 2mo gig, Flask/tests/Redis/Bootstrap. Mail me... matt at interconnected dot org. Tell yr friends!— Matt Webb (@genmon) November 25, 2014
I was hooked.
We emailed Matt and it turned out that he was looking for someone to re-implement a server for their Little Printer.
The Little Printer is a thermal printer that can be controlled from the Internet. The most popular use is sending little messages to your Twitter friends. The proposition sounds a bit silly but after having used it during development I can say that it is a fun item!
Along with the sad news that Berg was shutting down came the good news that Matt wanted the Little Printer to live on. And the best way to do so was to have a simpler server, divorced from the rest of Berg's offerings.
By the time we arrived Matt had already implemented a few fairly complicated parts for encoding the binary data sent to the Little Printer. Despite that there were plenty of challenges left for us.
- All printers are constantly connected to the server, and need to react quickly to push notifications.
- The server should be easy to deploy: We needed to avoid complicated dependencies and interactions between moving parts.
- The protocol wasn't documented other than in the existing code base.
We chose to go with a simple stratified design: We have a socket-handling layer that deals with the online/offline status for each printer. On top of that we have a message encoding and decoding layer that translates the payload into Python's namedtuples. The last layer is a small website for user interactions.
namedtupleis a useful little tool that everyone should know. It creates a normal immutable tuple, but unlike a normal tuple members can also be accessed by their name. A quick example:from collections import namedtuple Character = namedtuple('Character', 'name level guild') bob = Character('bob', 42, "Wizards") # And now you can access fields with the dot notation print("%s is %d in the guild %s" % (me.name, me.level, me.guild))
The stratified design made it easy to test and exchange individual parts. E.g. we implemented a fake printer that runs on the command line in very few lines of code.
The full flow is:
- User action triggers creation of a
namedtuplemessage (e.g. print)
- Encoding of the message.
- Sending message over socket to printer.
Avoiding extra servers
To avoid an extra service for messaging we decided to keep everything - protocol handling and web serving - in a single process.
There are no printer-to-printer interactions. That makes sharding by printer-ID trivial: We can stick with the single-process architecture forever by just running more of the same.
The human aspect
Working with Matt was a great experience. He is precise in his communication and knows what he wants. Pretty much the ideal customer for a small agency like ours!
It's open source
You can find the code for the Little Printer on Github.