Our little PiOLED add on makes a very cute and easy way to display the Pi Hole stats. We were inspired to add this when we saw this tweet!
What a perfect use! Here's how to add it on for some nice stats. It also displays the hostname and IP address so if you forget it you can just look at the display. It will also tick up when its in use so you can tell its working.
Install 2x20 Header
If you are using a Pi Zero you'll need to solder in or somehow attach a 2x20 header so you can plug in the Pi OLED. Use either a plain 2x20 header and solder it in using an iron + some solder...
Or you can use Hammer headers which do not need soldering!
Either way, you'll want to end up with something like this:
Setup Virtual Environment
Starting with the Bookworm version of Raspberry Pi OS, you will need to install your python modules in a virtual environment. Even on earlier versions, it's still recommended to install a virtual environment. You can find more information in the Python Virtual Environment Usage on Raspberry Pi guide. To Install and activate the virtual environment, use the following commands:
sudo apt install python3-venv python3 -m venv pihole --system-site-packages
You will need to activate the virtual environment every time the Pi is rebooted. To activate it:
source pihole/bin/activate
To deactivate, you can use deactivate
, but leave it active for now.
Enable I2C
Ensure that I2C is enabled by running raspi-config
, selecting Interface Options, and selecting I2C.
Install CircuitPython Libraries
This guide assumes that you've gotten your Raspberry Pi up and running, and have CircuitPython installed. If not, check out the guide:
To install the library for the Pi OLED, enter the following into the terminal:
sudo apt-get install python3-pip pip3 install adafruit-circuitpython-ssd1306
We also need PIL to allow using text with custom fonts. There are several system libraries that PIL relies on, so installing via a package manager is the easiest way to bring in everything:
sudo apt-get install python3-pil
And let's also make sure the requests module is installed:
sudo apt install python3-requests
A nice font really helps with this little OLED display, something other than the default PIL font. First thing I did is update the font so its a little clearer. I used Kottke's free Silkscreen font which looks great on small screens.
It's easy to install on your Pi, run
cd ~ wget http://kottke.org/plus/type/silkscreen/download/silkscreen.zip unzip silkscreen.zip
Update stats.py program
Here's the new stats.py code which uses the PiOLED.
Create a new file with nano ~pi/stats.py and paste this code below in! Then save it.
# SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries # SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries # SPDX-FileCopyrightText: 2017 James DeVito for Adafruit Industries # # SPDX-License-Identifier: MIT # Copyright (c) 2017 Adafruit Industries # Author: Ladyada, Tony DiCola & James DeVito # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # This example is for use on (Linux) computers that are using CPython with # Adafruit Blinka to support CircuitPython libraries. CircuitPython does # not support PIL/pillow (python imaging library)! # Import Python System Libraries import json import subprocess import time # Import Requests Library import requests # Import Blinka from board import SCL, SDA import busio import adafruit_ssd1306 # Import Python Imaging Library from PIL import Image, ImageDraw, ImageFont API_TOKEN = "YOUR_API_TOKEN_HERE" api_url = "http://localhost/admin/api.php?summaryRaw&auth="+API_TOKEN # Create the I2C interface. i2c = busio.I2C(SCL, SDA) # Create the SSD1306 OLED class. # The first two parameters are the pixel width and pixel height. Change these # to the right size for your display! disp = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c) # Leaving the OLED on for a long period of time can damage it # Set these to prevent OLED burn in DISPLAY_ON = 10 # on time in seconds DISPLAY_OFF = 50 # off time in seconds # Clear display. disp.fill(0) disp.show() # Create blank image for drawing. # Make sure to create image with mode '1' for 1-bit color. width = disp.width height = disp.height image = Image.new('1', (width, height)) # Get drawing object to draw on image. draw = ImageDraw.Draw(image) # Draw a black filled box to clear the image. draw.rectangle((0, 0, width, height), outline=0, fill=0) # Draw some shapes. # First define some constants to allow easy resizing of shapes. padding = -2 top = padding bottom = height - padding # Move left to right keeping track of the current x position # for drawing shapes. x = 0 # Load nice silkscreen font font = ImageFont.truetype('/home/pi/slkscr.ttf', 8) while True: # Draw a black filled box to clear the image. draw.rectangle((0, 0, width, height), outline=0, fill=0) # Shell scripts for system monitoring from here : # https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load cmd = "hostname -I | cut -d\' \' -f1 | tr -d \'\\n\'" IP = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "hostname | tr -d \'\\n\'" HOST = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "top -bn1 | grep load | awk " \ "'{printf \"CPU Load: %.2f\", $(NF-2)}'" CPU = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "free -m | awk 'NR==2{printf " \ "\"Mem: %s/%sMB %.2f%%\", $3,$2,$3*100/$2 }'" MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "df -h | awk '$NF==\"/\"{printf " \ "\"Disk: %d/%dGB %s\", $3,$2,$5}'" Disk = subprocess.check_output(cmd, shell=True).decode("utf-8") # Pi Hole data! try: r = requests.get(api_url) data = json.loads(r.text) DNSQUERIES = data['dns_queries_today'] ADSBLOCKED = data['ads_blocked_today'] CLIENTS = data['unique_clients'] except KeyError: time.sleep(1) continue draw.text((x, top), "IP: " + str(IP) + " (" + HOST + ")", font=font, fill=255) draw.text((x, top + 8), "Ads Blocked: " + str(ADSBLOCKED), font=font, fill=255) draw.text((x, top + 16), "Clients: " + str(CLIENTS), font=font, fill=255) draw.text((x, top + 24), "DNS Queries: " + str(DNSQUERIES), font=font, fill=255) # skip over original stats # draw.text((x, top+8), str(CPU), font=font, fill=255) # draw.text((x, top+16), str(MemUsage), font=font, fill=255) # draw.text((x, top+25), str(Disk), font=font, fill=255) # Display image. disp.image(image) disp.show() time.sleep(DISPLAY_ON) disp.fill(0) disp.show() time.sleep(DISPLAY_OFF)
You'll notice its very similar to the original stats.py but we've added PiHole API support. Here's how we did that!
First up, Pi Hole stats are available through the web server, in json format, so we need to add web requests and json parsing to python. Then set the URL for the API access, which is localhost (the same computer) and through the admin page:
import subprocess import json import requests api_url = 'http://localhost/admin/api.php'
We load up the nice Silkscreen font here, in 8 point type. Note that we have to have the full path of the file.
# Load nice silkscreen font font = ImageFont.truetype("/home/pi/slkscr.ttf", 8)
This is where we grab the API data. I put it in a try block, so it would retry in case the API access failed for some reason
# Pi Hole data! try: r = requests.get(api_url) data = json.loads(r.text) DNSQUERIES = data['dns_queries_today'] ADSBLOCKED = data['ads_blocked_today'] CLIENTS = data['unique_clients'] except: time.sleep(1) continue
If you want to print out different info, run this small script in python to see what is available:
import json import requests api_url = 'http://localhost/admin/api.php' r = requests.get(api_url) data = json.loads(r.text) print(data)
You can also customize the display printout, but i liked having the IP first, then the pi hole stats below:
draw.text((x, top), "IP: " + str(IP) + "( " + HOST + ")", font=font, fill=255) draw.text((x, top+8), "Ads Blocked: " + str(ADSBLOCKED), font=font, fill=255) draw.text((x, top+16), "Clients: " + str(CLIENTS), font=font, fill=255) draw.text((x, top+24), "DNS Queries: " + str(DNSQUERIES), font=font, fill=255)
Set API Token
Newer releases of Pi-Hole have added a authentication requirement for using the API. An API token must be provided when making an API request. This token was created when Pi-Hole was installed above. It can be found via the Web Admin interface under:
Settings > API > Show API token
Once found, update the stats.py code by changing this line:
API_TOKEN = "YOUR_API_TOKEN_HERE"
and replacing YOUR_API_TOKEN_HERE
with the API token found above. It'll be a big long string of numbers and letters that looks like gibberish. Leave the double quotes so it ends up looking something like this:
API_TOKEN = "1234567890ABCDEF1234567890ABCDEF"
sudo ~pi/pihole/bin/python3 ~pi/stats.py
and look on the OLED to make sure you see your IP address and such!
Lastly we just want to make this run at boot. We'll do that the easy way by editing /etc/rc.local with sudo nano /etc/rc.local and adding sudo ~pi/pihole/bin/python3 ~pi/stats.py & before exit 0
Then save and you can reboot to test it out
Text editor powered by tinymce.