Users Online

With the PyPortal running CircuitPython code, we can have the PyPortal display the current number of users online on the Adafruit Discord server.

The PyPortal will do the following:

  • Display a custom background .bmp image
  • Determine the current number of users online on the Discord server
  • Display the number on top of the background image

Install CircuitPython Code and Assets

In the embedded code element below, click on the Download: Project Zip link, and save the .zip archive file to your computer.

Then, uncompress the .zip file, it will unpack to a folder named PyPortal_Discord.

Copy the contents of the PyPortal_Discord directory to your PyPortal's CIRCUITPY drive, and then be sure to rename the discord.py file to code.py so it will automatically run when the PyPortal re-starts.

This is what the final contents of the CIRCUITPY drive will look like:

"""
This example will access shields.io API, grab the SVG graphic and then use
regular expression search to locate the number of online discord users, then
display it on a screen.
If you can find something that spits out text, we can display it!
"""
import time
import board
from adafruit_pyportal import PyPortal

# Set up where we'll be fetching data from
DATA_SOURCE = "https://img.shields.io/discord/327254708534116352.svg"
# a regular expression for finding the data within the SVG xml text!
DATA_LOCATION = [r">([0-9]+ online)<"]

cwd = ("/"+__file__).rsplit('/', 1)[0]
pyportal = PyPortal(url=DATA_SOURCE, regexp_path=DATA_LOCATION,
                    status_neopixel=board.NEOPIXEL,
                    default_bg=cwd+"/discord_background.bmp",
                    text_font=cwd+"/fonts/Collegiate-50.bdf",
                    text_position=(70, 216), text_color=0x000000)

while True:
    try:
        value = pyportal.fetch()
        print("Response is", value)
    except RuntimeError as e:
        print("Some error occured, retrying! -", e)
    time.sleep(60)
If you run into any errors, such as "ImportError: no module named `adafruit_display_text.label`" be sure to update your libraries to the latest release bundle!

How it Works

The PyPortal Discord display does the following things:

Background

First, it displays a bitmap graphic named discord_background.bmp as the screen's background. This is a 320 x 240 pixel RGB 16-bit raster graphic in .bmp format.

Font

To display information on the screen, the PyPortal code will use a bitmapped font overlayed on top of the background. The font used here is are bitmap fonts made from the Collegiate typeface. You can learn more about converting type in this guide.

In order to speed up the display of text, the pyportal.preload_font() command is used to place the needed glyphs into memory.

Shield.io, .SVGs, and RegEx

Here's the really snazzy part: we want the PyPortal to grab the number of current users online on the Adafruit Discord server (or whichever server you specify in the code). Well, where do we get this number? Most PyPortal projects use a REST API call to grab a JSON file, but that isn't available for Discord servers. Shields.io comes to the rescue!

Shields.io is a service that can provide the data we need in convenient little graphics. The shields are often used on websites to indicate status and data on services such as GitHub repos. Here, we'll use it to get the user count we need!

If we enter this url in a web browser, we'll get a shield for the online user count for the Discord server: https://img.shields.io/discord/327254708534116352.svg

If we download the .svg file itself, this is what it looks like:

Download: file
<svg
	xmlns="http://www.w3.org/2000/svg"
	xmlns:xlink="http://www.w3.org/1999/xlink" width="108" height="20">
	<linearGradient id="b" x2="0" y2="100%">
		<stop offset="0" stop-color="#bbb" stop-opacity=".1"></stop>
		<stop offset="1" stop-opacity=".1"></stop>
	</linearGradient>
	<clipPath id="a">
		<rect width="108" height="20" rx="3" fill="#fff"></rect>
	</clipPath>
	<g clip-path="url(#a)">
		<path fill="#555" d="M0 0h33v20H0z"></path>
		<path fill="#4c1" d="M33 0h75v20H33z"></path>
		<path fill="url(#b)" d="M0 0h108v20H0z"></path>
	</g>
	<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="110">
		<text x="175" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="230">chat</text>
		<text x="175" y="140" transform="scale(.1)" textLength="230">chat</text>
		<text x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="650">1163 online</text>
		<text x="695" y="140" transform="scale(.1)" textLength="650">1169 online</text>
	</g>
</svg>

Much of this code is used to draw the graphic we saw above, however, it also contains this magical tidbit:

< text x="695" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="650">1169 online</text >

Notice at the end there is says 1169 online, which is just the data we're looking for.

In order to grab that string for display on the PyPortal, our code needs to be able to find it. Unlike a JSON file, there isn't a key:value pair in the .svg file, so instead we can hunt for it with a regular expression.

A regular expression is a special sequence of characters that is used to search for a particular pattern in a string. In this case,  r">([0-9]+ online)<" is the expression we'll use in CircuitPython to find what we need.

What we're searching for is an occurrence of a number and the world "online" to appear together. The [0-9] section of the expression says "look for any number" while the + online section says "and that number must be next to a space and the word 'online'".

Thus, it is the expression "1169 online" that will be displayed on the screen.

Data Source and Location

Our CircuitPython code uses two variables to specify the url for the .svg file and regular expression to run in order to get the data we want.

DATA_SOURCE = "https://img.shields.io/discord/327254708534116352.svg"

DATA_LOCATION = [r">([0-9]+ online)<"]

PyPortal Constructor

When we set up the pyportal constructor, we are providing it with these things:

  • url to query
  • regex_path the regular expression to run in order to locate the data
  • default_bg path and name to display the background bitmap
  • text_font path and name to the font used for displaying the follower count value
  • text_position on the screen's x/y coordinate system
  • text_color sets, well, the text color!

Fetch

With the PyPortal set up, we can then use pyportal.fetch() to do the query and parsing of the data and then display it on screen on top of the background image.

This repeats once every minute to stay current!

Customization

You can customize this project to make it your own and point to different Discord server, as well as adjust the graphics and text.

To find the channel ID number of a given Discord server, you'll need to turn on Developer mode in the advanced appearance settings of your user settings. Then, right-click on any server and click Copy ID.

Text Position

Depending on the design of your background bitmap and the length of the text you're displaying, you may want to reposition the text and caption. You can do this with the text_position and caption_position options.

The PyPortal's display is 320 pixels wide and 240 pixels high. In order to refer to those positions on the screen, we use an x/y coordinate system, where x is horizontal and y is vertical.

The origin of this coordinate system is the upper left corner. This means that a pixel placed at the upper left corner would be (0,0) and the lower right corner would be (320, 240).

Text Color

Another way to customize your display is to adjust the color of the text. The line text_color=0x000000 in the constructor shows how. You will need to use the hexadecimal value for any color you want to display.

You can use something like https://htmlcolorcodes.com/ to pick your color and then copy the hex value, in this example it would be 0x0ED9EE

Background Image

If you would like to create your own background, awesome! You'll want to save the file with these specifications:

  • 320 x 240 pixels
  • 16-bit RGB color (8-bits per channel)
  • Save file as .bmp format

You can then copy the .bmp file to the root level of the CIRCUITPY drive. Make sure you refer to this new filename in the pyportal constructor line:

default_bg=cwd+"/discord_background.bmp"

Change that line to use the new filename name, such as:

default_bg=cwd+"/my_new_background.bmp"

This guide was first published on Mar 29, 2019. It was last updated on Mar 29, 2019.

This page (Code PyPortal Discord Display) was last updated on Oct 24, 2020.