Raspberry Pi Plex Server Monitor

Plex Server Monitor

Plex Server MonitorKeep a close eye on your Plex server with this Raspberry Pi LCD project

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.

I thought it’d be fund to use some old Raspberry Pi kit to create a little Plex server monitor using the unofficial PlexAPI and a 20×4 LCD module.

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:

https://app.plex.tv/desktop

Your Plex server dashboard should look like this

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):

Denzel has yet to make a bad film

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’:

Click the ‘More’ menu, then select ‘Get Info’

A box pops up with media info. Now click the XML link in the bottom-left corner of that box:

Click the XML link to see a really f’ugly version of this page

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 Plex URL and token are taken from the XML page URL

Your URL should look something like this (starts with the https part, ends with 32400):

https://192-168-1-99.b5hb3g3hd84b574fh45745btrrty55.plex.direct:32400

Your token should look something like this (a big mess of numbers and letters – mine is 20 characters long):

yRRdU74aBffu9eetAAt6

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.

Imports

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'

API Authorisation

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)

API Calls

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:

Lastly, for the sake of testing, I use a simple print to see what value is returning:

print numfilm

Overall Code

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:

Terminal printing the count of my server’s film collection

Now we’ve covered how it all works, let’s move on to the LCD.

LCD Setup

I’m using a 20×4 LCD module rather than the smaller 16×2 unit, as this gives me more space to display my server information on.

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.

It’s messy, but it’s still a prototype

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://www.averagemanvsraspberrypi.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:

The satisfying result of hours upon hours of API head-scratching

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.

Case Closed

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):

A smaller case option for use with the Pi Zero

20×4 LCD Case – eBay £12.50 (accommodates a Pi3 so has the added benefit of port access)

A slightly larger case with the added benefit of having access to a Pi3’s ports

Home-made case – self £0.00 (would require skill/cuts/bruises/broken tools/rage)

Not sure this option would be quite as polished…

Continue reading here: New Raspberry Pi Camera Module Case - The Pi Pod

Was this article helpful?

0 0