Screens tend to keep me awake in the evening so I wondered if I could print out my Twitter home timeline, live, as I read a book in the evening.
Here's the final project in action:
I was gifted a receipt printer for Christmas to use with my Raspberry Pi. A POS58 USB Thermal Receipt Printer also known as a ZJ-5890K. It's sold by a few different brands under different names.
I couldn't get the drivers to work on Linux, MacOS, or Windows. I was stuck until I found vpatron/usb_receipt_printer — a guide that walks through setting the device up on a Raspberry Pi. It shows how to write to the USB port directly (a tactic I previously tried and failed). The included demo program solved all of my printing problems. Thanks Vince Patron!
Tweepy
Twitter has a powerful API but it's a little cryptic at first glance. Tweepy is a Python library for accessing the Twitter API — it has good documentation and community support.
In order to use Tweepy, we need to create an application on Twitter's Developer platform to get the keys and tokens we need to authenticate.
We will have three files in total.
config.py
— for managing our keys.poller.py
— for polling Twitter's API via Tweepy, and converting tweets to text.posprint.py
— for calling the receipt printer.
We save the keys in a separate file. A better way of managing keys is for your application to access them at runtime through environment values. This can be safer (it avoids keys accidentally being pushed to GitHub) and lets our application use different keys in different environments (e.g. local versus live production deployment).
# config.pyconsumer_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'consumer_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'access_token = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'access_token_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Twitter has a streaming data API but it was harder to set up than the chosen solution.
We ask for the latest 20 tweets every minute and check that we haven't seen them before. Then we format, clean, and print the new ones.
# poller.pyimport tweepyfrom emoji import demojizefrom time import sleepfrom collections import OrderedDictimport configimport posprintauth = tweepy.OAuthHandler(config.consumer_key,config.consumer_secret)auth.set_access_token(config.access_token,config.access_token_secret)api = tweepy.API(auth)# we use an ordered dictionary so we can remove old tweets# from the data structure. Our program may be long-running# and we don't want to run out of memoryseen_statuses = OrderedDict()while True:public_tweets = api.home_timeline(tweet_mode='extended')for status in public_tweets:# we're interested in statuses we haven't seen beforeif status.id not in seen_statuses:# store the statuses we useseen_statuses[status.id] = status# dump old statuses that can't appear againif len(seen_statuses) > 20:seen_statuses.popitem()user = status.user.screen_name + ':\n'# get the full tweet text regardless of type# https://github.com/tweepy/tweepy/issues/935if hasattr(status, 'retweeted_status'):tweet = user + status.retweeted_status.full_textelse:tweet = user + status.full_text# trim unprintable characters that POS58 can't output# by turning emojis into textual representation.# 😅 becomes :grinning_face_with_sweat:tweet = demojize(tweet).encode(encoding='ascii',# throwaway non ascii characterserrors='ignore').decode()posprint.output(tweet)# twitter restricts statuses/home_timeline to once per minutesleep(65)
Let's modify Vince's demo program vpatron/usb_receipt_printer to export the function we call above — posprint.output(tweet)
— with our textual tweet.
# posprint.pyimport usb.coreimport usb.utilimport textwrap"""Demo program to print to the POS58 USB thermal receipt printer. This islabeled under different companies, but is made by Zijiang. Seehttp://zijiang.comMIT License — Copyright (c) 2019 Vince Patron"""# In Linux, you must:## 1) Add your user to the Linux group "lp" (line printer), otherwise you will# get a user permissions error when trying to print.## 2) Add a udev rule to allow all users to use this USB device, otherwise you# will get a permissions error also. Example:## In /etc/udev/rules.d create a file ending in .rules, such as# 33-receipt-printer.rules with the contents:## # Set permissions to let anyone use the thermal receipt printer# SUBSYSTEM=="usb", ATTR{idVendor}=="0416", ATTR{idProduct}=="5011", MODE="666"def output(data):# find our device# 0416:5011 is POS58 USB thermal receipt printerdev = usb.core.find(idVendor=0x0416, idProduct=0x5011)# was it found?if dev is None:raise ValueError('Device not found')# disconnect it from kernelneeds_reattach = Falseif dev.is_kernel_driver_active(0):needs_reattach = Truedev.detach_kernel_driver(0)# set the active configuration. With no arguments, the first# configuration will be the active onedev.set_configuration()# get an endpoint instancecfg = dev.get_active_configuration()intf = cfg[(0,0)]ep = usb.util.find_descriptor(intf,# match the first OUT endpointcustom_match = \lambda e: \usb.util.endpoint_direction(e.bEndpointAddress) == \usb.util.ENDPOINT_OUT)assert ep is not None# print!lines = textwrap.wrap(data, width=30)for line in lines:ep.write(line + '\n')ep.write('\n\n\n\n')# reattach if it was attached originallydev.reset()if needs_reattach:dev.attach_kernel_driver(0)print('Reattached USB device to kernel driver')
Our program is started by running poller.py
.
If everything went correctly, our POS58 printer should output tweets as they arrive in the following width-restricted format which avoids breaking words where possible.
jessfraz: Ive started usingPocket to keep articles I wantto read later and alsocombined it with a bot to readRSS feeds. Its alright so far.What are some tools like thisyou cant live without?
Find the project files on GitHub. Thanks again to Vince Patron for his comprehensive guide!