DVDs – why do they still exist? Archaic space-consuming biscuits of polycarbonate. They have no place in today’s world of self-driving cars, WiFi thermostats and smart washing machines. No sir.
As you can imagine, I’m not one to be pushing plastic into holes to watch a film. I ripped my DVD collection on to a hard drive a long time ago, and I now stream that collection to my devices using a Plex server.
Let’s build it!
The Plex API
Plex API is an unofficial Plex API that lets you read data and do all sorts of fun stuff with your Plex server.
I have very little experience with APIs so I only opted to pull a few pieces of data for this Plex server monitor project:
- Number of users currently streaming from the server
- Number of TV shows in the server
- Number of Films in the server
It’s a bit of fun really, none of this information is vital to me, but it’s just another interesting thing to have on my desk.
Installing Plex API
I used the following commands to add the Plex API Python library to my Pi.
Make sure you’re in your home directory first:
Then use this command to install the library (I use sudo here as it wouldn’t work quite right without it):
sudo pip install plexapi
That’s the library installed, nothing more to it.
Plex Token & URL
Naturally you can’t start poking around inside someone’s server without authorisation, so we need to grab a token and server URL to prove it’s yours.
You can get both the token and URL in one hit. First, log in to your Plex server using a desktop browser. You can do this on your PC/laptop – you don’t have to do it on the Pi:
Once you’re in, click on a media item – I use a film here (great film too – my man Denzel doesn’t make bad flicks):
Once you’re in the screen for a media item, use the 3-dot ‘more’ menu in the top right corner, and select ‘Get Info’:
A box pops up with media info. Now click the XML link in the bottom-left corner of that box:
The XML page will load in a new tab. The URL of this page contains your URL and token. You can see in the screenshot below where each can be found:
[Note: these are fake details, don’t go trying to watch my films with them!]
Your URL should look something like this (starts with the https part, ends with 32400):
Your token should look something like this (a big mess of numbers and letters – mine is 20 characters long):
Now you have everything you need to start using the API.
Code Basics & Terminal Testing
There’s no point trying to make the LCD work at the same time as the API, so I initially just got the API to print into a terminal to allow me to work out how to use it in a quick and easy way.
Let’s run through a simple example of using the API to get your head around what needs to go where.
First you import the library:
from plexapi.server import PlexServer
API Authorisation Details
Then you add a couple of lines for your authorisation token and URL that we grabbed in the last section:
plex_token = 'YOUR TOKEN HERE'
plex_url = 'YOUR URL HERE'
Wherever you’re using the API to grab data – be it inside a while loop or just in straight-forward code – you need to have this line first. I’m pretty sure this is doing the authentication bit:
plex = PlexServer(plex_url, plex_token)
You’re now ready to ‘ask’ the API for something. Let’s grab the number of films in the library as an example.
I’ll create a variable called ‘films’ and use the ‘section’ option of the API to pull back all the films in my library. This actually takes around 10 seconds when it runs, and I’ve only got 229 films in my collection:
films = plex.library.section('Films')
Directly underneath that line I’m using the ‘len‘ function to return the number of items (films) in my ‘films’ variable. I create a new variable here called numfilms to store that number in:
numfilm = len(films.all())
Lastly, for the sake of testing, I use a simple print to see what value is returning:
So the overall test code looks like this:
# Imports from plexapi.server import PlexServer # Add Token and URL variables plex_token = 'YOUR TOKEN HERE' plex_url = 'YOUR URL HERE' # Authorise the API plex = PlexServer(plex_url, plex_token) # Pull in the 'Films' section films = plex.library.section('Films') # Count the number of films numfilm = len(films.all()) # Print the number of films to terminal print numfilm
And this is what it returns – 229 films:
Now we’ve covered how it all works, let’s move on to the LCD.
I always wire and code my LCD modules as per this Raspberry Pi Spy tutorial, so I won’t go into the hardware setup again here – I would simply be duplicating Matt’s work which is clear and complete.
Add a comment below if you get stuck and I’ll help you out.
Combining the Code
Now it’s time to mash my Plex API code into Matt’s LCD code and get the LCD displaying information on my Plex server monitor.
The bulk of the LCD code is untouched, we’ve just added in the API calls, then told the LCD to display those strings. It’s actually very easy to display any data you like on these LCDs – it’s getting the data you want that can be the hard part.
The while loop from line 77 is where most of the magic happens. You’ll see in here the different API calls for current users streaming (lines 81-84) and TV shows (line 86-89). The film example we already went through is in lines 91-94.
The loop waits 5 minutes and then repeats, which avoids spamming the API.
[Remember: expect a small delay whilst the API grabs your data for you]
#!/usr/bin/python # #-------------------------------------- # Plex Server Monitor by @AverageManVsPi http://averagemaker.com/ #-------------------------------------- # 20x4 LCD code from https://www.raspberrypi-spy.co.uk/2012/08/20x4-lcd-module-control-using-python/ # Plex API code from https://github.com/pkkid/python-plexapi #-------------------------------------- # # The wiring for the LCD is as follows: # 1 : GND # 2 : 5V # 3 : Contrast (0-5V)* # 4 : RS (Register Select) # 5 : R/W (Read Write) - GROUND THIS PIN # 6 : Enable or Strobe # 7 : Data Bit 0 - NOT USED # 8 : Data Bit 1 - NOT USED # 9 : Data Bit 2 - NOT USED # 10: Data Bit 3 - NOT USED # 11: Data Bit 4 # 12: Data Bit 5 # 13: Data Bit 6 # 14: Data Bit 7 # 15: LCD Backlight +5V** # 16: LCD Backlight GND # Imports import RPi.GPIO as GPIO import time import subprocess import os import datetime from plexapi.server import PlexServer # Set up Plex account & server details plex_token = 'YOUR TOKEN HERE' plex_url = 'YOUR URL HERE' # Define GPIO to LCD mapping LCD_RS = 7 LCD_E = 8 LCD_D4 = 25 LCD_D5 = 24 LCD_D6 = 23 LCD_D7 = 18 LED_ON = 15 # Define some device constants LCD_WIDTH = 20 # Maximum characters per line LCD_CHR = True LCD_CMD = False LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line # Timing constants E_PULSE = 0.0005 E_DELAY = 0.0005 def main(): # Main program block GPIO.setmode(GPIO.BCM) # Use BCM GPIO numbers GPIO.setup(LCD_E, GPIO.OUT) # E GPIO.setup(LCD_RS, GPIO.OUT) # RS GPIO.setup(LCD_D4, GPIO.OUT) # DB4 GPIO.setup(LCD_D5, GPIO.OUT) # DB5 GPIO.setup(LCD_D6, GPIO.OUT) # DB6 GPIO.setup(LCD_D7, GPIO.OUT) # DB7 GPIO.setup(LED_ON, GPIO.OUT) # Backlight enable # Initialise display lcd_init() while True: # Authorise the use of the Plex API plex = PlexServer(plex_url, plex_token) # This API command shows us how many users of the Plex server are streaming right now usersonline = plex.sessions() numusers = len(plex.sessions()) numusers = "Users Streaming: %s" % numusers # This API command shows us how many TV series we hold in the server (not individual episodes) tv = plex.library.section('TV') numtv = len(tv.all()) numtvstring = "TV Shows: %s" % numtv # This API command shows us how many films we hold in the server films = plex.library.section('Films') numfilm = len(films.all()) numfilmstring = "Films: %s" % numfilm # Run a time command and turn it into a string that shows the last time the data was updated f=os.popen("date +%T") for i in f.readlines(): timereading="" timereading += i timereading = "Last check: " + timereading break # Display the data on the LCD, left-justified. Each line controls a line on the LCD in order lcd_string(numusers,LCD_LINE_1,1) #number of users streaming lcd_string(numtvstring,LCD_LINE_2,1) #number of tv series lcd_string(numfilmstring,LCD_LINE_3,1) #number of films lcd_string(timereading,LCD_LINE_4,1) #last update # Wait 5 minutes for next update time.sleep(300) def lcd_init(): # Initialise display lcd_byte(0x33,LCD_CMD) # 110011 Initialise lcd_byte(0x32,LCD_CMD) # 110010 Initialise lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size lcd_byte(0x01,LCD_CMD) # 000001 Clear display time.sleep(E_DELAY) def lcd_byte(bits, mode): # Send byte to data pins # bits = data # mode = True for character # False for command GPIO.output(LCD_RS, mode) # RS # High bits GPIO.output(LCD_D4, False) GPIO.output(LCD_D5, False) GPIO.output(LCD_D6, False) GPIO.output(LCD_D7, False) if bits&0x10==0x10: GPIO.output(LCD_D4, True) if bits&0x20==0x20: GPIO.output(LCD_D5, True) if bits&0x40==0x40: GPIO.output(LCD_D6, True) if bits&0x80==0x80: GPIO.output(LCD_D7, True) # Toggle 'Enable' pin lcd_toggle_enable() # Low bits GPIO.output(LCD_D4, False) GPIO.output(LCD_D5, False) GPIO.output(LCD_D6, False) GPIO.output(LCD_D7, False) if bits&0x01==0x01: GPIO.output(LCD_D4, True) if bits&0x02==0x02: GPIO.output(LCD_D5, True) if bits&0x04==0x04: GPIO.output(LCD_D6, True) if bits&0x08==0x08: GPIO.output(LCD_D7, True) # Toggle 'Enable' pin lcd_toggle_enable() def lcd_toggle_enable(): # Toggle enable time.sleep(E_DELAY) GPIO.output(LCD_E, True) time.sleep(E_PULSE) GPIO.output(LCD_E, False) time.sleep(E_DELAY) def lcd_string(message,line,style): # Send string to display # style=1 Left justified # style=2 Centred # style=3 Right justified if style==1: message = message.ljust(LCD_WIDTH," ") elif style==2: message = message.center(LCD_WIDTH," ") elif style==3: message = message.rjust(LCD_WIDTH," ") lcd_byte(line, LCD_CMD) for i in range(LCD_WIDTH): lcd_byte(ord(message[i]),LCD_CHR) def lcd_backlight(flag): # Toggle backlight on-off-on GPIO.output(LED_ON, flag) if __name__ == '__main__': try: main() except KeyboardInterrupt: pass finally: lcd_byte(0x01, LCD_CMD) lcd_string("Goodbye!",LCD_LINE_1,2) GPIO.cleanup()
Here’s what it looks like on the screen:
Plex Server Monitor Improvements
This isn’t perfect yet, not even close. Here’s a few things I want to add before I put the hardware in a case and call it a ‘thing’:
- Error handling – what if the WiFi suddenly cuts out? I need some code that will handle that and not kick the program out.
- Auto-start – starting the script using rc.local or similar
- Better Python – my ‘time grabbing’ command is a bit clunky – there are better ways, I just haven’t worked out how to use them yet.
- More API info – I’m really struggling to work out how to use the other Plex API functions.
Last on the list is to wrap the project in a sexy case to make it complete. There are a few options I’m considering:
20×4 LCD Enclosure – eBay £11.50 (would move the project to a Pi Zero W):
20×4 LCD Case – eBay £12.50 (accommodates a Pi3 so has the added benefit of port access)
Home-made case – self £0.00 (would require skill/cuts/bruises/broken tools/rage)
OK, this post is long enough already. Until next time…